当前位置: 首页 > 知识库问答 >
问题:

ASM内联调用C外部函数

高锦
2023-03-14

我正在尝试通过与分支内联的am调用c中的外部函数。我正在编译为arm m0指令集,但它返回错误表达式。

代码是:

__asm volatile (
                "   cmp     r3,#0                   \n"                     
                "   b %[my_function]                \n" //Call function
                "   bx r14                          \n"
                : // no output
                : [my_function] "i" (my_function) // input
                : "r0" // clobber
            );

回报是:

/tmp/ccICkDIE.s: Assembler messages:
/tmp/ccICkDIE.s:152: Error: bad expression -- `b #my_function'

我们需要做什么?

共有2个答案

姜磊
2023-03-14

有趣的是,GCC的内联汇编程序没有办法处理调用帧。我的回答试图通过添加一个函数包装器并给出获取地址的语法来解决这个问题。“不喜欢那个”的同样抱怨是对克雷格·埃斯蒂的回答的评论。确实,如果需要保存/恢复浮点寄存器等。那个答案行不通。您还需要提示编译器需要发出一个堆栈帧。我试图通过包装功能来实现这一点。

这个答案并不适用于所有情况,不要复制粘贴。它试图解决获取寄存器的函数地址和转移控制的概念。c '是一种过程编程语言,所以编译器会根据函数的上下文执行许多优化。

没有信息是通用的,但可能会帮助您找到特定的解决方案。GCC的标签变量和其他概念可能是合适的(并且更可移植),这取决于用例。该代码旨在提供信息。我所有的答案都是为那些能够思考问题,并且从来没有固定解决方案的人而写的。

写完下面,我想起了以太坊教程。他几乎有同样的答案,

asm volatile(
    "mov lr, %1\n\t"
    "bx %0\n\t"
    : : "r" (main), "r" (JMPADDR)
    : // probably unsafe without "lr" clobber in most use-cases
   );
 // and also __builtin_unreachable() to tell the compiler execution doesn't leave

OP最好阅读本教程;即使它适用于传统的ARM而不是“m0”。

这种方法适用于简单的函数,但不是通用的解决方案。然而,它演示了如何将地址输入寄存器。OP的原始查询。这里的用例是在start函数中,您正在执行最终将调用main()的尾调用。

您可以使用< code>'r'约束将地址放入寄存器并转移到它。

可以在在线编译器godbolt上找到一个示例。

extern int my_function(void);

// prevent inlining and optimization across functions, 
// so we can take advantage of the calling convention instead of using proper clobbers
void f(void) __attribute__((noinline,noipa))
{
__asm volatile (
                "   cmp     r3,#0                   \n"                     
// Below is a tail call, not setting LR, it will return to the caller of f()
// It relies on the caller of f() having populated the 'lr'.
// See further comments below.
                "   b %[my_function]                \n"
                "   bx r14 \n" // does nothing...
                : // no output
                : [my_function] "r" (my_function) // input
            );
 
 }

此示例依赖于它嵌入在函数中的事实。该函数将执行准备调用特定函数所需的堆栈操作。通常可能无法内联此内容,因为您可以指定诸如r0-r3,内存和“cc”之类的clobbers,但它不会告诉编译器此时堆栈帧必须格式良好。一些答案将取决于被调用函数的作用,因为您需要匹配EABI(预期的调用约定)。然而,这远远超出了原始海报的能力,他们正在努力解决更基本的概念。

添加__attribute__((noinline,noipa))以防止外部函数的内联。因为答案是写出来的,所以GCC更有可能执行全局优化,尝试内联例程。

使用输出(使用特定编译器版本的特定构建选项),

f():
    ldr r3, .L2
       cmp     r3,#0                   
   b r3                
   bx r14                          

    bx  lr
.L2:
    .word   my_function()

我们可以看到输出的几个问题。r14是lrb r3将直接转移控制权并返回给f的调用者。cmp r3,#0似乎完全不需要(给定有限的问题上下文)。

上面的例子回答了这个问题,它可以用于尾调用宏或其他用途,但它显然需要一些工作。像这样的函数指针,

int (*g)(void) = my_function;

也将作为GCC扩展汇编程序的参数“my_function”。

另一种方法是使用“C”宏字符串连接。这里是起始样品,

#define xstr(s) str(s)
#define str(s) #s
#define TAIL_CALL(func) __asm volatile(" b  " str(func) "\n")

对于大多数代码大小(跳转距离),分支将能够解析(4MB?)。如果使用函数指针方法,则没有问题。此示例(如在 godbolt 上)需要嵌入到函数包装器中以处理堆栈帧。它只是作为如何将函数地址获取到寄存器的示例(OP原始问题)。

白君之
2023-03-14

您想要 BL 指令。这是“分支和链接”。它执行跳转并将返回地址存储在 r14 中。

但是,您仍然有一个问题...当你做BL时,它会破坏你需要的r14。您还有更多工作要做,即使在以下操作之后:

stmfd   sp!,{v1-v6,lr}              // preserve caller registers
bl      %[my_function]              // call function
ldmfd   sp!,{v1-v6,pc} @std         // restore caller registers and return

您将需要更多的调查。您可能希望反汇编已编译的函数,并查看内联 asm 周围的包装器并进行相应的调整。它可能会为您执行标准操作。尝试将 r14 标记为氯苯。

使用BL可能会更好。没有恢复的BX可能会产生无限循环或不可预测的结果。我会把它关掉。

 类似资料:
  • 函数是一个可以重复使用的代码块,CPU 会一条一条地挨着执行其中的代码。CPU 在执行主调函数代码时如果遇到了被调函数,主调函数就会暂停,CPU 转而执行被调函数的代码;被调函数执行完毕后再返回到主调函数,主调函数根据刚才的状态继续往下执行。 一个 C/ C++ 程序的执行过程可以认为是多个函数之间的相互调用过程,它们形成了一个或简单或复杂的调用链条,这个链条的起点是 main(),终点也是 ma

  • C++ 类 & 对象 C++ 内联函数是通常与类一起使用。如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。 对内联函数进行任何修改,都需要重新编译函数的所有客户端,因为编译器需要重新更换一次所有的代码,否则将会继续使用旧的函数。 如果想把一个函数定义为内联函数,则需要在函数名前面放置关键字 inline,在调用函数之前需要对函数进行定义。如果已定义的函数多

  • 问题内容: 我正在寻找一个Java库,该库可以根据其ID / class属性将外部文件与文档内联。 我找到了 jStyleParser, 但不确定这是否适合我。我似乎无法理解它是否可以内联HTML中的元素。文档和示例不是我所期望的。 有没有人可以回答这个问题,或者有另一个图书馆可以解决这个问题? 谢谢 问题答案: 您可以尝试CSSBox。只需查看软件包中包含的 ComputeStyles 演示(有

  • 我正在尝试同时处理MSVC和GCC编译器,同时更新这个代码库以在GCC上工作。但我不确定GCCs内联ASM是如何工作的。现在我并不擅长将ASM转换为C,否则我就会使用C而不是ASM。 我假设ROR13的工作方式类似于,但代码不会产生相同的输出。 什么是将这个内联ASM翻译成GCC的正确方法,或者这个代码的C翻译是什么?

  • 简而言之:我想创建一个类型为的泛型类,它应该调用一个类型为的具体化内联函数,以便能够更通用地使用Gson 但是我希望我的其他类通过某个类实例调用这个函数。例如,类似于应该返回列表,而不是直接调用。我如何实现这样的功能?

  • 例外情况。kt: 在科特林: 它在kotlin中工作,函数是内联的。 但是当在Java代码中使用时,它就是不能内联,仍然是一个正常的静态方法调用(从反编译的内容中可以看出)。 像这样的东西: