【问题标题】:Adding leading underscores to assembly symbols with GCC on Win32?在 Win32 上使用 GCC 为汇编符号添加前导下划线?
【发布时间】:2010-11-05 07:29:13
【问题描述】:

我有一段 C 代码调用在汇编中定义的函数。例如,假设 foo.c 包含:

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

而 bar.s 包含了 x86 汇编中 bar() 的实现:

.global bar
bar:    movl 4(%esp), %eax
        addl %eax, %eax
        ret

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

% gcc -o test foo.c bar.s
% ./test; echo $?
14

在带有 MinGW 的 Windows 上,此操作失败并出现“未定义对 `bar' 的引用”的错误。事实证明,造成这种情况的原因是在 Windows 上,所有具有 C 调用约定的函数标识符都带有下划线前缀,但由于“bar”是在程序集中定义的,它没有得到这个前缀并且链接失败。 (所以错误信息实际上是在抱怨缺少符号_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

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

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

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

【问题讨论】:

    标签: c winapi gcc assembly mingw


    【解决方案1】:

    你能声明两次吗?

    .global bar
    .global _bar
    

    我已经有一段时间没有编写程序集了,但是 .global 标识符的作用是不是有点像标签?

    【讨论】:

    • .global 指令仅指定此标识符指的是全局符号,因此如果我还为该函数定义两个标签,则可以使其工作,例如:.global bar .global _bar bar: _bar: 除了重复之外,我还在 Windows 上得到一个无用的“bar”符号,在 Linux 上得到一个无用的“_bar”符号。我希望有更清洁的东西,但这确实有效,所以我感谢你的建议。
    【解决方案2】:

    虽然很危险,但一种选择是说服 GCC 省略 ABI 要求的前导下划线。

    • -fleading-underscore

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

      警告:-fleading-underscore 开关会导致 GCC 生成与不使用该开关生成的代码二进制不兼容的代码。使用它来符合非默认应用程序二进制接口。并非所有目标都为此开关提供完全支持。

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

    5.39 Controlling Names Used in Assembler Code

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

         int foo asm ("myfoo") = 2;
    

    这指定在汇编代码中用于变量foo 的名称应该是``myfoo' rather than the usual \``_foo'。

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

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

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

     extern func () asm ("FUNC");
    
     func (x, y)
          int x, y;
     /* ... */
    

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

    在你的情况下,

    extern int bar(int x) asm("bar");
    

    应该告诉 GCC “bar 使用 asm 名称 ``bar`',即使它是一个 ccall 函数”。

    【讨论】:

    • 这是否意味着 GCC 在 Linux 上的默认行为应该是“C”名称有一个前导下划线?如果是这样,OP 的构建环境中是否有某些东西将其关闭?
    • 在 Linux 上,遵循标准 C 调用约定(ccall、cdecl,无论您想调用什么)的函数都不会被修饰。在 Windows 上,stdcall 是“默认”调用约定,遵循其他任何规则(如标准 C 调用约定)的函数都会被修饰。
    • 正如我所说, -fleading-underscore 和 -fno-leading-underscore 选项似乎没有做任何事情(它们既不删除 C 函数中的下划线也不为汇编符号添加它们) ;如果你谷歌一下,你会发现其他人也有同样的经历,所以我觉得这些选项毫无用处。 asm() 建议很好;我很可能最终会使用它。唯一的缺点是符号本身仍然没有正确的名称(在 Windows 上是 _bar),但至少我可以在两个平台上链接而无需进一步修改源代码。
    • clang: error: unknown argument: '-no-leading-underscore' clang: error: unknown argument: '-fno-leading-underscore' clang: error: unknown argument: '-fleading-underscore'
    • 原型中不需要externgodbolt.org/z/kpFShC 表明 void start(void) asm("_mystart"); 有效。此外,您可能希望在示例中使用类型名称,而不是使用旧版 C 默认值-int
    【解决方案3】:

    默认情况下,ELF 目标的编译器不添加前导下划线。在编译为 ELF 格式(在 Linux 下)时,您可以添加 -fleading-underscore。在 makefile 中使用条件。

    参考:http://opencores.org/openrisc,gnu_toolchain(在页面上搜索“保持全局名称不变”)

    【讨论】:

      【解决方案4】:

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

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

      #if defined(__WIN32__)
      # define cdecl(s) _##s
      #else
      # define cdecl(s) s
      #endif
      

      然后像这样使用它:

      .global cdecl(bar)
      cdecl(bar):
          movl 4(%esp), %eax
          addl %eax, %eax
          ret
      

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

      #if defined(__WIN32__) || defined(__APPLE__)
      

      【讨论】:

        猜你喜欢
        • 2010-10-08
        • 1970-01-01
        • 1970-01-01
        • 2011-01-08
        • 2015-03-14
        • 2010-11-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多