如何从 gcc 内联 arm7 程序集中调用 c++ 成员函数

     2023-02-22     93

关键词:

【中文标题】如何从 gcc 内联 arm7 程序集中调用 c++ 成员函数【英文标题】:How to call a c++ member function from gcc inline arm7 assembly 【发布时间】:2012-08-04 22:04:20 【问题描述】:

我有以下在 x86 上工作的代码,我需要将其转换为 gcc/arm7 内联汇编。

似乎替换堆栈只是将堆栈指针移动到r15 而不是esp 的简单问题,但我不知道如何调用该函数,或将实际变量放入手臂汇编代码。

Object *c;

unsigned long _stack = (unsigned long)c->stack + 65535;
void (Object::*_run)() = &Object::_run;

__asm

    mov ecx, _this // store pointer to this Object
    mov edx, _run  // store address of _run() function
    mov esp, stack // replace stack pointer with Object's internal stack
    call edx       // call the _run() function

编辑:

到目前为止,我有这个:

unsigned long _stack = (unsigned long)c->stack + 65535;
void (Object::*_run)() = &Object::_run;

asm volatile("mov %[__this], %r0\n\t"
             "mov %[__run], %r5\n\t"
             "mov %[__stack], %%sp\n\t"
             "blx %r5\n\t"
             : /* no output operands */
             : [__stack] "r" (_stack), 
               [__this] "r" (_this), 
               [__run] "r" (_run)
             : /* no clobbers */);

问:在 x86 asm 中,使用 thiscall 调用约定,“this”指针进入 ecx。 它在哪里?

编辑:

答:For C++, an implicit this parameter is passed as an extra argument that immediately precedes the first user argument. Other rules for marshalling C++ arguments are described in CPPABI.

编辑:

我正在尝试做的事情的完整实现在这里: http://pastebin.com/6mrUC7td

它在 Visual Studio 中完美运行,但我无法让它在 XCode/iOS 中正常运行

【问题讨论】:

【参考方案1】:

arm abi 指定寄存器r0r3 用于参数传递。 this 指针始终位于第一个寄存器中,这将是 r0

【讨论】:

【参考方案2】:

call 来自 gcc 内联汇编器的任何东西都不是特别安全,因为没有办法告诉编译器它(没有 约束 到 gcc 内联 asm 指令可以表示“这个东西包含一个函数调用,做所有必要的事情来保存/恢复你保存在寄存器中的任何东西,这些东西在进行函数调用时可能会改变”)。

理论上,您可以通过给出一个 clobber 列表来“手动”执行此操作,该列表指定在根据平台 ABI 进行函数调用时可能更改的所有寄存器(在 ARM 上很多,我认为它是几乎所有这些都不包括sp ...)。但是这样做经常会导致 gcc 告诉您无法满足约束的情况(即,它没有任何寄存器可以将“需要保留的东西”填充到其中),具体取决于使用内联 asm 的上下文。

为什么你需要从内联程序集中调用一个函数?难道你不能使用 static inline 函数来代替普通的 C 代码(执行函数调用)和内联汇编来完成只能在内联汇编中完成的事情吗?

如果 整个 函数只是内联汇编,ARM 上的 gcc 提供了一个出路 - 即 __attribute__((naked)) 选项。这告诉编译器不要为其创建函数序言/结语,并且您有义务自己编写这些,即添加保存/恢复此函数使用但声明由 EABI 保留的寄存器的代码。通过将执行函数调用的内联程序集“外包”到static inline __attribute__((naked)) 函数中,您可以避免必须指定大量的clobber 列表,因为编译器会识别出它是一个函数调用(并根据需要保留/恢复它,即使当内联代码)。

【讨论】:

我正在尝试从程序集中调用该函数,因为我的主要目标是覆盖堆栈指针。因此,在调用函数之前,我需要将任何我想要保留的内容移出堆栈,并放入寄存器中。 @al bundy:有系统接口——UN*X 下的setcontext()(包括Android/iOS)和Windows 下的SetThreadContext()(包括WinCE / WinPhone)。只有在编写自己的操作系统时才需要手动重新实现它。 实际上,ucontext 似乎已被弃用完成......不知道为什么它在任何地方都不受支持.. Windows 有光纤,但除此之外,真的不支持用户级线程。我试图组合一些我可以用来实现协程/协作多任务处理的东西,但这种事情结果证明有点不合我意。 Boost 最近接受了 Boost::Context,但它还没有正式发布 =/ 所以我想我现在只能坚持使用线程。 不确定您所说的已弃用是什么意思。 struct ucontext 存在于最新的 UNIX 规范中,pubs.opengroup.org/onlinepubs/009696799/basedefs/… - 只是通用规范留下了有趣的部分,mcontext_t,未指定/特定于系统。您可以在 UNIX 中通过信号和 sigaltstack() 实现协程 - 比完整的 setcontext() 稍微轻一些。不确定许多人是否还在研究协作式多任务……Sun 在 2002 年从他们的 Solaris libthread 中放弃了这一点,本机线程性能变得更好,没有任何东西可以证明其复杂性。 也就是说……线程与协程……区别主要在于设置开销。如果您只在想要进行切换时创建线程,那么它肯定会比makecontext()/setcontext()(或他们自己开发的等价物)慢。如果协程的线程(池)已经存在,那么使用信号量作为触发器并让操作系统为您进行上下文切换应该给出与协程/手动上下文切换相同的大致数字。这是 DrDobbs 文章中使用的技术:drdobbs.com/cpp/cross-platform-coroutines-in-c/184404529【参考方案3】:

看看this。虽然它使用 x86 汇编,但同样适用于 arm 汇编。

【讨论】:

【参考方案4】:

我不太确定你在用那里的堆栈指针做什么 - 这是你在 ARM 上不需要的 x86 习惯用法,还是你故意尝试设置一个新的堆栈指​​针?

在任何情况下,堆栈指针都在r13 - 程序计数器是r15 - LDRMOVPC 影响一个分支。在函数序言之外,SP 已经指向堆栈上的下一个位置,因此如果由于寄存器 r0-r3 不足而需要在堆栈上传递参数时,您只需要修改它 - 这不是案例在这里。

如果你修改了堆栈指针,你需要在函数调用之后恢复它,否则你会在函数退出时遇到一个不错的小崩溃。

最后,您需要将 ARM [E]ABI 允许被调用者在函数调用上修改的所有寄存器声明为已破坏。这是r0-r3r9 也许 和r12

如果您在 iOS 上执行此操作,ISTR 会在寄存器使用方面存在细微差异。

【讨论】:

我实际上是在尝试实现协程。我正在尝试为该函数设置一个新的堆栈指​​针,然后将longjmp退出该函数以恢复原始堆栈。然后我可以 longjmp 回到函数中,新的堆栈仍然存在。我没有指定被破坏的寄存器,因为我认为如果我立即调用新函数,然后在它之后立即执行 longjmp,那么这些寄存器的内容将无关紧要。 这更有意义 - 因此,您的堆栈已经在对象中分配,并且算法会为您提供递减堆栈的指针 - 您希望它足够大!被破坏的寄存器列表是编译器必须假定在 asm 块末尾修改的所有寄存器。如果您从不打算返回,那真的不是问题 - 分支和链接 (blx) 指令的存在表明您可能是。但是,如果返回是可能的,那么 longjmp 会浪费很多东西!【参考方案5】:

获取 ABI 所需信息的简单解决方案(例如在哪些寄存器中获取传递的值):只需编写几行调用 c++ 方法的虚拟代码,然后反汇编结果(在您的目标平台上)。

【讨论】:

我实际上一直在尝试这样做......但是在 Visual Studio 会给我一个近乎完美的代码翻译的地方,xcode 会生成一些非常奇怪的程序集......例如,在 vs12 中,“ mov esp, _stack" 就是这样翻译的,在 xcode 中 "mov %[__stack], %%sp\n\t" 翻译成大约 4 行.. 在将其移入寄存器之前似乎添加了一些奇怪的偏移量 = /

如何从 C 程序内部或使用内联汇编获取 C 函数的大小?

】如何从C程序内部或使用内联汇编获取C函数的大小?【英文标题】:HowtogetthesizeofaCfunctionfrominsideaCprogramorwithinlineassembly?【发布时间】:2012-07-0917:39:57【问题描述】:假设我有如下函数:#cat003.cintfoo(inta,intb)returna+b;并像这样编译... 查看详情

Linux AMD64 从复制的程序集中调用 C 库函数

...mcopiedassembly【发布时间】:2015-05-3017:44:14【问题描述】:如何从memcpy的汇编函数调用C库函数?我正在制作一个示例测试代码,如何在Linux、AMD64上分配和更改内存保护以运行从C中任意生成的代码。我所做的是在我的主程序(用C... 查看详情

在 gcc 中使用内联汇编程序调用方法

】在gcc中使用内联汇编程序调用方法【英文标题】:Callingmethodusinginlineassembleringcc【发布时间】:2012-10-0417:22:35【问题描述】:正如我所说,我正在尝试使用gcc调用使用内联asm的方法。所以,我搜索了x86的工作原理,以及调用约... 查看详情

从 C++ 调用 C 函数时,如何告诉 gcc 放宽对类型转换的限制?

】从C++调用C函数时,如何告诉gcc放宽对类型转换的限制?【英文标题】:HowdoItellgcctorelaxitsrestrictionsontypecastingwhencallingaCfunctionfromC++?【发布时间】:2011-01-0601:09:53【问题描述】:我正在尝试使用Cmockery模拟从C++代码调用的C函数。... 查看详情

使用内联汇编器从 GCC 中的共享库调用函数

】使用内联汇编器从GCC中的共享库调用函数【英文标题】:CallfunctionfromsharedlibraryinGCCwithinlineassembler【发布时间】:2012-02-2219:41:12【问题描述】:我已经创建了共享库(将像插件一样使用)。有很多函数,比如aextern"C"long__attribute... 查看详情

GCC 无法从具有内联汇编的函数生成 32 位代码

】GCC无法从具有内联汇编的函数生成32位代码【英文标题】:GCCfailstogenerate32-bitcodefromafunctionwithinlineassembly【发布时间】:2013-10-2219:29:39【问题描述】:我尝试生成这样的32位代码:gcc-S-m32BMPTransformer.c-oBMPTransformer.s我使用的是Ubunt... 查看详情

如何从应用程序域中所有加载的程序集中获取所有静态类并使用反射调用静态方法

】如何从应用程序域中所有加载的程序集中获取所有静态类并使用反射调用静态方法【英文标题】:Howtogetallthestaticclassesfromalltheloadedassembliesinanapp-domainandinvokeastaticmethodusingreflection【发布时间】:2015-02-1211:20:52【问题描述】:我... 查看详情

调用“ret”与调用sys_exit数字程序集gcc之间有什么区别(代码片段)

在gcc程序集中,main函数可以返回或退出,两者都可以工作。在这里,我有两个程序,其中一个退出系统调用int$0x80,另一个只调用ret。有什么不同?.datahello:.string"Hello,World!".globlmainmain:push%rbxmovq$hello,%rdicallputspop%rbxret和.datahello:.st... 查看详情

升级版|c和汇编互相调用及规则

...3;星标公众号,不错过精彩内容转自| 一口Linux一、gcc内联汇编内联汇编即在C中直接使用汇编语句进行编程,使程序可以在C程序中实现C语言不能完成的一些工作,例如,在下面几种情况中必须使用内联汇编或嵌入... 查看详情

从内联助手调用扩展助手 - 如何?

】从内联助手调用扩展助手-如何?【英文标题】:Callingextensionhelpersfrominlinehelpers-how?【发布时间】:2011-06-0808:27:33【问题描述】:我读到了这个[usefularticle],它说我可以通过将它们放在特殊文件夹App_Code的视图中来创建一个内联... 查看详情

gcc 如何知道内联汇编中使用的寄存器大小?

】gcc如何知道内联汇编中使用的寄存器大小?【英文标题】:Howdoesgccknowtheregistersizetouseininlineassembly?【发布时间】:2014-09-1917:17:53【问题描述】:我有内联汇编代码:#defineread_msr(index,buf)asmvolatile("rdmsr":"=d"(buf[1]),"=a"(buf[0]):"c"(index... 查看详情

如何强制 gcc 内联函数?

】如何强制gcc内联函数?【英文标题】:HowdoIforcegcctoinlineafunction?【发布时间】:2012-01-1222:56:42【问题描述】:__attribute__((always_inline))是否强制一个函数被gcc内联?【问题讨论】:GCC使用代码大小作为启发式来确定是否内联。我... 查看详情

最牛x的gcc内联汇编

...是Linux中使用的基本汇编程序语法。本文将讲解GCC提供的内联汇编特性的用途和用法。对于阅读这篇文章,这里只有两个前提要求,很明显,就是x86汇编语言和C语言的基本认识。1.简介1.1版权许可Copyright(C)2003SandeepS.本文档自由共... 查看详情

最牛x的gcc内联汇编

...是Linux中使用的基本汇编程序语法。本文将讲解GCC提供的内联汇编特性的用途和用法。对于阅读这篇文章,这里只有两个前提要求,很明显,就是x86汇编语言和C语言的基本认识。1.简介1.1版权许可Copyright(C)2003SandeepS.本文档自由共... 查看详情

gcc 可以通过函数指针的常量数组内联间接函数调用吗?

】gcc可以通过函数指针的常量数组内联间接函数调用吗?【英文标题】:Cangccinlineanindirectfunctioncallthroughaconstantarrayoffunctionpointers?【发布时间】:2011-07-0303:29:45【问题描述】:假设我们有这个代码:inlineintfunc_2(inta,intb)returntime()+a... 查看详情

从反射生成的程序集中调用 lambda

】从反射生成的程序集中调用lambda【英文标题】:CallingalambdafromReflection-generatedassembly【发布时间】:2013-03-2017:28:28【问题描述】:我正在使用Reflection.Emit创建一个程序集,我希望它调用一个特殊的回调。这是代码的简化版本:pu... 查看详情

GCC内联汇编中的C数组?

】GCC内联汇编中的C数组?【英文标题】:CarraysinGCCinlineassembly?【发布时间】:2011-08-1515:37:01【问题描述】:我正在尝试在C/x86_64内联汇编中编写一个函数,但运气不佳。我们可以归结为:voidf(constunsignedchar*a,unsignedchar*b,constunsigned... 查看详情

为啥 -O3 GCC Optimization 没有内联这个函数?

】为啥-O3GCCOptimization没有内联这个函数?【英文标题】:Whydidn\'t-O3GCCOptimizationinlinethisfunction?为什么-O3GCCOptimization没有内联这个函数?【发布时间】:2010-09-2623:58:55【问题描述】:在GCC编译器中,每当标记-O3时,编译器主要通过... 查看详情