手动设置入口点时MSVC Linker与lld-link为何异常?
当你在Windows环境下用Clang指定/ENTRY:main时,实际上跳过了标准CRT的默认入口(比如mainCRTStartup或_mainCRTStartup)——这些入口函数负责完成CRT的初始化工作,同时会引导链接器自动拉入CRT库中必要的模块。一旦跳过它们,链接器就不会自动包含那些依赖CRT初始化的常规函数实现,比如memcpy的标准版本。
为什么memcpy_s能正常链接?
memcpy_s是UCRT提供的安全标准函数,它的符号在ucrt.lib中是直接导出的独立实现。当你的代码引用memcpy_s时,链接器会从ucrt.lib中拉入对应的目标模块,而这些模块恰好间接包含了memcpy的实现(或者触发链接器拉入更多UCRT的核心模块),因此顺带解决了memcpy的未定义问题。
解决方案
1. 显式链接完整的CRT依赖库
手动指定链接UCRT及相关的CRT支持库,让链接器能找到memcpy的实现:
clang test.c -lucrt -lvcruntime -lcmt -Xlinker /SUBSYSTEM:CONSOLE -Xlinker /ENTRY:main -o test-llvm.exe
这里-lvcruntime和-lcmt是静态CRT的支持库,如果你用动态CRT,可以换成-lvcruntimedll和-lmsvcrt。
2. 直接调用UCRT的底层memcpy实现
通过编译宏将memcpy映射到UCRT中不依赖CRT初始化的底层实现__memcpy:
clang test.c -Dmemcpy=__memcpy -lucrt -Xlinker /SUBSYSTEM:CONSOLE -Xlinker /ENTRY:main -o test-llvm.exe
3. 改用CRT默认入口并手动控制初始化
如果不需要完全跳过CRT初始化,可以指定CRT的默认入口,然后在main中手动控制初始化时机(比如推迟某些初始化步骤):
clang test.c -lucrt -Xlinker /SUBSYSTEM:CONSOLE -Xlinker /ENTRY:mainCRTStartup -o test-llvm.exe
这种方式下,CRT会自动完成初始化,你可以在main开头加入自定义的初始化逻辑。
为什么-fno-builtin等选项没用?
你尝试的-fno-builtin、-ffreestanding是编译阶段的选项,作用是阻止Clang将memcpy内联或替换为内置实现,但你的问题本质是链接阶段找不到符号,所以这些选项无法解决问题。
内容的提问来源于stack exchange,提问作者Wilderness Ranger

