【问题标题】:ld MinGW link to standard C libraryld MinGW 链接到标准 C 库
【发布时间】:2013-09-19 05:29:57
【问题描述】:

下面的代码有问题。

extern printf
global _main
main:
push msg
call printf
ret
msg db "Hello world",0

我使用 nasm -fwin32 test.asm 将其与 NASM 组装,然后使用 ld test.obj 链接它。 它告诉我“test.obj:test.asm:(text+0x6): undefined reference to 'printf'

如何将我的文件链接到标准 C 库?我有最新的 MinGW 的 ld。

【问题讨论】:

    标签: windows assembly x86 mingw nasm


    【解决方案1】:

    汇编代码:

    nasm -fwin32 test.asm
    

    Microsoft 将使用带下划线的 cdecl 调用约定为函数添加前缀。
    要匹配 C 调用约定 printf 应该是 _printf
    这同样适用于_main 而不是main

    并链接到:

    ld test.obj -lmsvcrt -entry=_main -subsystem=console -o test.exe

    这里-entry命令行选项用于调用ld来指定程序的入口点。
    然后使用-l 选项将msvcrt 库传递给ld 链接器,否则会收到错误消息(undefined reference to `printf'),这意味着链接器在 NASM 生成的指定目标文件中找不到符号 printf。

    这里是完整的源代码:

    global  _main
    extern  _printf
    section .text
    _main:
    push msg
    call _printf
    add esp, 4 ;adjust the stack
    ret
    msg db "Hello world",0
    

    【讨论】:

    • TDM-GCC的最新版本中,libmsvcrt.a存在于\MinGW\lib目录下,并且ld成功链接目标文件。可以尝试用-L<dir>开关指定位置:ld test.obj -L"c:\mingw\bin\../../MinGW/lib/" -lmsvcrt -entry=_main -o test.exe 其中-L后面的路径是你的 MinGW安装目录的路径。你也可以看看How do I specify the libraries...和@987654323 @ 3. msvcrt vs crtdll?
    【解决方案2】:

    我可以看到您的代码存在几个问题。首先,您在global _main 上有一个下划线,但在main: 上没有。这些应该匹配。您可以在整个过程中使用下划线,或者 - 我会做的 - 根本不用......对于 Windows,组合为 nasm -f win32 --prefix _ test.asm。这将使它“可移植”,因为对于 Linux,它可以组装,没有 --prefix _ 没有下划线。 Linux 不在globalextern 符号上使用下划线。如果你碰巧使用的是 OpenWatcom C,你可以使用--postfix _。是的,OpenWatcom 使用尾随下划线。是的,我知道他们告诉我们 C 是标准化的。但是一旦你深入了解,这并不是真的。

    另一个大问题是,在调用_printf 之后,您需要add esp, 4(或pop 一个虚拟寄存器)来“清理堆栈”。如果您使用的是 Windows API,它们使用“被调用者清理”的 STDCALL 调用约定,因此您不想这样做。混合 C 调用(CDECL 调用约定)和 Windows API 可能会让人感到困惑,但应该可以。

    我认为 Carl 使用 gcc 链接它的想法是正确的。没有什么可以“编译”的,但 gcc 知道 ld 的正确命令行。 gcc -o test.exe test.obj 可能就足够了(如果最新的 MinGW 预计使用 64 位代码,则可能添加 -m32)。这将链接到一些调用_main 的“启动代码”。这会稍微增加可执行文件的大小,并且您“可能”可以在没有它的情况下相处,但这样做会更容易。

    在 Linux 中,我们可以直接使用 ld(命令行很糟糕),但 ld 寻找的是_start,而不是main,作为入口点。我们可以告诉 ld -e main,但是这个入口点没有被调用(!)并且没有可能的方法从它 ret ! Windows 中的情况可能有所不同。您至少需要 -lc 告诉 ld 我们想要那些 C 库。最容易“让 gcc 去做”——它不会触及你的 .asm 代码(但会链接到那个“启动代码”)。快乐你好世界! :)

    【讨论】:

      【解决方案3】:

      使用编译器前端链接:

      cc test.obj
      

      如果你真的想直接使用ld(你不应该),使用-v 标志到cc 来确定你需要什么完整的命令行。例如,在我的机器上,它是:

      ld -demangle -dynamic -arch x86_64 -macosx_version_min 10.8.0 \
        -o a.out test.obj -lSystem                                  \
        /usr/bin/../lib/clang/4.2/lib/darwin/libclang_rt.osx.a
      

      如果我使用 GCC 而不是 Clang,那就更疯狂了:

      ld -dynamic -arch x86_64 -macosx_version_min 10.8.4 -weak_reference_mismatches                             \
        non-weak -o a.out -lcrt1.10.6.o                                                                          \
        -L/usr/llvm-gcc-4.2/bin/../lib/gcc/i686-apple-darwin11/4.2.1/x86_64                                      \
        -L/Applications/Xcode.app/Contents/Developer/usr/llvm-gcc-4.2/lib/gcc/i686-apple-darwin11/4.2.1/x86_64   \
        -L/usr/llvm-gcc-4.2/bin/../lib/gcc/i686-apple-darwin11/4.2.1                                             \
        -L/usr/llvm-gcc-4.2/bin/../lib/gcc                                                                       \
        -L/Applications/Xcode.app/Contents/Developer/usr/llvm-gcc-4.2/lib/gcc/i686-apple-darwin11/4.2.1          \
        -L/usr/llvm-gcc-4.2/bin/../lib/gcc/i686-apple-darwin11/4.2.1/../../..                                    \
        -L/Applications/Xcode.app/Contents/Developer/usr/llvm-gcc-4.2/lib/gcc/i686-apple-darwin11/4.2.1/../../.. \
        test.obj -lgcc -lSystem
      

      【讨论】:

        猜你喜欢
        • 2017-11-05
        • 1970-01-01
        • 1970-01-01
        • 2011-03-10
        • 2011-06-16
        • 2018-02-10
        • 2013-05-22
        • 1970-01-01
        • 2023-03-11
        相关资源
        最近更新 更多