在Win32上使用GCC将前导下划线添加到装配符号?

Mak*_*ver 29 c assembly winapi gcc mingw

我有一段C代码调用程序集中定义的函数.举个例子,假设foo.c包含:

int bar(int x);  /* returns 2x */
int main(int argc, char *argv[]) { return bar(7); }
Run Code Online (Sandbox Code Playgroud)

并且bar.s包含x86程序集中bar()的实现:

.global bar
bar:    movl 4(%esp), %eax
        addl %eax, %eax
        ret
Run Code Online (Sandbox Code Playgroud)

在Linux上,我可以轻松地使用GCC编译和链接这些源,如下所示:

% gcc -o test foo.c bar.s
% ./test; echo $?
14
Run Code Online (Sandbox Code Playgroud)

在使用MinGW的Windows上,此操作失败并显示"未定义引用"栏'的错误.事实证明,在Windows上,所有具有C调用约定的函数标识符都以下划线为前缀,但由于"bar"在汇编中定义,因此它不会获得此前缀并且链接失败.(所以错误信息实际上是抱怨错过了符号_bar,而不是吧.)

总结一下:

% gcc -c foo.c bar.s
% nm foo.o bar.o
foo.o:
00000000 b .bss
00000000 d .data
00000000 t .text
         U ___main
         U _bar
00000000 T _main

bar.o:
00000000 b .bss
00000000 d .data
00000000 t .text
00000000 T bar
Run Code Online (Sandbox Code Playgroud)

现在的问题是:我怎样才能很好地解决这个问题?如果我只是为Windows编写,我可以在bar.s中将下划线添加到标识符中,但随后代码在Linux上中断.我看过gcc -fleading-underscore-fno-leading-underscore选项,但似乎都没有做任何事情(至少在Windows上).

我现在看到的唯一选择是通过C预处理器传递汇编文件,并在定义WIN32时手动重新定义所有声明的符号,但这也不是很漂亮.

有人有一个干净的解决方案吗?也许是我监督的编译器选项?也许GNU汇编程序支持一种特定的方式,这个特定的符号引用一个使用C调用约定的函数,并且应该被这样修改?还有其他想法吗?

eph*_*ent 27

一种选择虽然很危险,但是要说服海湾合作委员会省略ABI要求的领先下划线.

  • -fleading-underscore

    此选项及其对应项-fno-leading-underscore强制更改目标文件中C符号的表示方式.一个用途是帮助链接传统的汇编代码.

    警告:-fleading-underscore开关使GCC来生成代码为不与没有这种开关生成的代码二进制兼容的.使用它来符合非默认应用程序二进制接口.并非所有目标都为此交换机提供完整支持.

另一个更安全的选择是明确告诉GCC使用的名称.

5.39控制汇编代码中使用的名称

您可以通过在声明符后面写asm(或__asm__)关键字来指定要在C函数或变量的汇编代码中使用的名称,如下所示:

     int foo asm ("myfoo") = 2;
Run Code Online (Sandbox Code Playgroud)

这指定foo汇编代码中用于变量的名称应为"myfoo ' rather than the usual \``_foo".

在下划线通常附加到C函数或变量名称的系统上,此功能允许您为不以下划线开头的链接器定义名称.

将此功能与非静态局部变量一起使用是没有意义的,因为这些变量没有汇编程序名称.如果您尝试将变量放在特定寄存器中,请参阅Explicit Reg Vars.GCC目前接受此类代码并发出警告,但可能会在将来更改为发出错误而非警告.

你不能asm在函数定义中使用这种方式; 但是你可以通过在定义之前为函数编写声明并放在asm那里来获得相同的效果,如下所示:

 extern func () asm ("FUNC");

 func (x, y)
      int x, y;
 /* ... */
Run Code Online (Sandbox Code Playgroud)

您可以确保您选择的汇编器名称不与任何其他汇编器符号冲突.此外,您不得使用注册名称; 这会产生完全无效的汇编程序代码.GCC还没有能力将静态变量存储在寄存器中.也许这会被添加.

在你的情况下,

extern int bar(int x) asm("bar");
Run Code Online (Sandbox Code Playgroud)

应告诉GCC" bar使用asm名称``bar`',即使它是一个ccall函数".

  • `clang:错误:未知参数:' - no-leading-underscore'``clang:错误:未知参数:' - fno-leading-underscore'``clang:错误:未知参数:'-fleading-underscore' (4认同)
  • 在Linux上,遵循标准C调用约定(ccall,cdecl,无论你想调用它)的函数都没有装饰.在Windows上,stdcall是"默认"调用约定,其他任何函数(如标准C调用约定)都会被装饰. (3认同)

小智 8

您可以使用C预处理器预处理程序集,并使用宏在Windows上添加缺少的下划线.首先,您需要将程序集文件从bar.s重命名为bar.S(大写'S').这告诉gcc使用cpp来预处理文件.

要添加缺少的下划线,您可以像这样定义宏"cdecl":

#if defined(__WIN32__)
# define cdecl(s) _##s
#else
# define cdecl(s) s
#endif
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它:

.global cdecl(bar)
cdecl(bar):
    movl 4(%esp), %eax
    addl %eax, %eax
    ret
Run Code Online (Sandbox Code Playgroud)

请注意,Mac OSX还需要前导下划线,因此您可以像这样更新宏的第一行:

#if defined(__WIN32__) || defined(__APPLE__)
Run Code Online (Sandbox Code Playgroud)


Car*_*ers 5

你可以两次申报吗?

.global bar
.global _bar
Run Code Online (Sandbox Code Playgroud)

我有一段时间没有编写汇编,但.global标识符的行为有点像标签吗?