【问题标题】: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 不在global 或extern 符号上使用下划线。如果你碰巧使用的是 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