首页 > 解决方案 > 为什么clang从声明为'extern“C”'的函数中删除下划线?

问题描述

我正在观看一段视频,试图更好地理解目标文件。演示者使用以下作为生成非常简单的目标文件的程序示例:

extern "C" void _start() {
    asm("mov $60, %eax\n"
        "mov $24567837, %edi\n"
        "syscall\n");
}

该程序是通过编译

clang++ -c step0.cpp -O1 -o step0.o

并通过链接

ld -static step0.o -o step0

尝试链接时收到此错误消息:

Undefined symbols for architecture x86_64:
  "start", referenced from:
     -u command line option
     (maybe you meant: __start)
ld: symbol(s) not found for inferred architecture x86_64

我没有通过 -u 命令行选项,所以我不确定为什么会收到该错误消息。

标签: linkerclang++

解决方案


clang 不是删除下划线,而是添加下划线。您的程序实际上是在导出一个__start符号,但 ld 期望您有一个start符号作为您的入口点,即 ld 在-u start您的体系结构中默认运行。

您可以在 ld with -U start(抑制未定义符号的错误)start或 via -undefined suppress(抑制所有未定义的符号错误)中禁用此检查。但是,您最终会得到一个没有架构入口点的可执行文件,因此该程序实际上无法工作。

我建议不要抑制错误,而是直接控制 clang 选择的符号。您可以使用独立asm声明告诉 clang 要生成什么符号:

void _start() asm ("start");

确保此独立声明与函数定义分开。

您可以在此处阅读有关控制 gcc 生成的符号的更多信息:https ://stackoverflow.com/a/1035937/12928775

此外,正如在对类似答案的评论中指出的那样,您很可能希望__attribute__((naked))在函数定义上使用以防止 clang 在入口时生成堆栈帧。请参阅:https ://stackoverflow.com/a/60311490/12928775


推荐阅读