ARM汇编向C 函数传递参数

编程语言 2026-07-10

我在从内联汇编调用C 函数时遇到了一个问题。我得说我尝试了来自另一个话题的一个解决方案。

我在Android上工作。用Cxxdroid编译(没问题)。

下面是代码。

#include <stdio.h>

struct iphdr {
    uint8_t   hdrlen:4;
    uint8_t   version:4;
    uint8_t   ecn:2;       // Explicit Congestion Notification - RFC 3168
    uint8_t   dscp:6;      // DiffServ Code Point
    uint16_t  length;
    uint16_t  ident;
    uint16_t  fragoff:13;
    uint16_t  flags:3;
    uint8_t   ttl;
    uint8_t   protocol;
    uint16_t  checksum;
    uint32_t  srcip;
    uint32_t  dstip;
    uint32_t  options[ ];  // Present if hdrlen > 5
} __attribute__((__packed__));

int _print_screen(void)
{   printf("Hello");
    //eventually print r0 -r3
}

int sum(int k, int l, int m, uint8_t n, uint32_t o, uint32_t p)
{
  int res =0;

__asm ("sum:\n"
    "CMP %[input_i], #0\n"
    "BEQ endrec\n"
    "endrec:\n"

    "SUB %[input_i], %[input_i], #1\n"
    "BL %[p_screen]\n"
    :[result] "+r" (res)
    :[input_i] "r" (k), [input_j] "r" (l), [input_k] "r" (m), [input_l] "r"(n), [input_m] "r" (o), [input_n] "r" (p), [p_screen] "r" (_print_screen)
    : "r0" //clobber
    );

 return res;
}

int main(void)
{
    iphdr iph;

    iph.version='\x4';
    iph.hdrlen='\x5';
    //iph.ip_tos='\x00';
    iph.protocol='\x03';
    iph.srcip='1.1.1.2';
    iph.dstip='1.1.1.1';

    d=1;

  e = sum(d, iph.version, iph.hdrlen, iph.protocol, iph.srcip, iph.dstip);
}

error: expected label or encodable integer pc offset "BL %[p_screen]\n"

我还无法让汇编调用我的C 函数。此外我也希望在C 函数中读取r0到 r3。

解决方案

你目前正在尝试做的是通过寄存器传递函数的地址([p_screen] "r" (_print_screen))。这需要你使用 BLX 进行间接调用,示例如下:

"BLX %[p_screen]\n"

然而这既低效又在此处并非必要,所以最好直接通过 BL 调用函数,而不使用寄存器。真正的问题是,直接方法为什么不起作用?

"BL _print_screen\n"

答案是,CxxDroid实际上默认编译的是 C++ 代码,而不是C。

在C++中,函数名的链接符号会被进行名称混淆(mangling,用于支持重载、模板等),因此你不能直接按名称调用它们。例如 _print_screen 函数的符号可能会被混淆为 _Z13_print_screenv——不过这取决于平台/编译器。解决办法是在函数上添加 extern "C",使其禁用混淆,导致链接符号为 _print_screen。这样你也不用通过 [p_screen] "r" (_print_screen) 把函数地址放入寄存器:

extern "C"
int _print_screen(void)
{ ... }

int sum(int k, int l, int m, uint8_t n, uint32_t o, uint32_t p)
{
__asm ("BL _print_screen\n");
}

另外,你也可以通过设置菜单把CxxDroid切换到C 模式。

你也可以把 extern "C" 部分包裹在 #ifdef __cplusplus 里,这样它只在你编译为C++代码时生效,而在C 时不生效:

#ifdef __cplusplus
extern "C"
#endif
int _print_screen(void)
{ ... }

除了前面提到的关于多字符常量 '1.1.1.2' 的方面(注意那些编译器警告!),代码还有一些其他问题。

不要把函数的前序/后序写入内联汇编中,也不要使用标签,例如代码中的 "sum:\n"

  • 在C++中这看起来似乎“有效”,因为C++函数的实际名称 sum 会被混淆,即你会得到一个符号 sum,以及一个类似的符号 _Z3sumiiihjj,很可能会造成问题
  • 在C 中你会得到错误,因为C 函数 sum 保留了自己的名称,你将有两个符号定义 sum
  • 对于内联汇编代码中的跳转,使用 本地标签

完整示例(C和 C++):

#ifdef __cplusplus
extern "C"
#endif
int _print_screen(void)
{   printf("Hello");
    //eventually print r0 -r3
}

int sum(int k, int l, int m, uint8_t n, uint32_t o, uint32_t p)
{
  int res =0;

__asm (
    "CMP %[input_i], #0\n"
    "BEQ 1f\n"
    "1:\n"

    "SUB %[input_i], %[input_i], #1\n"
    "BL _print_screen\n"
    :[result] "+r" (res)
    :[input_i] "r" (k), [input_j] "r" (l), [input_k] "r" (m), [input_l] "r"(n), [input_m] "r" (o), [input_n] "r" (p)
    : "r0" //clobber
    );

 return res;
}

内联汇编确实可以非常棘手,尤其是在启用优化时。如果你的整个函数体都是汇编代码,直接把整个函数放到一个单独的汇编文件中,而不使用内联汇编,通常会简单得多。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章