为何strlen未出现在ELF文件的符号表中?
关于gcc编译后strlen符号未出现在符号表中的疑问
我执行gcc -O0 -g ./1.c编译带有调试信息的1.c文件,代码内容如下:
// 1.c #include <stdio.h> #include <string.h> int main() { char c[222]; scanf("%s", c); int a = strlen("dadw"); return 0; }
但通过readelf -a a.out或nm ./a.out命令无法找到strlen符号。我原以为strlen由libc.so提供,会通过动态链接解析,理应出现在符号表中,这是否是gcc的特殊处理机制导致?
以下是nm ./a.out的输出结果:
❯ nm ./a.out 000000000000038c r __abi_tag 0000000000004010 B __bss_start 0000000000004010 b completed.0 w __cxa_finalize@GLIBC_2.2.5 0000000000004000 D __data_start 0000000000004000 W data_start 00000000000010b0 t deregister_tm_clones 0000000000001120 t __do_global_dtors_aux 0000000000003db8 d __do_global_dtors_aux_fini_array_entry 0000000000004008 D __dso_handle 0000000000003dc0 d _DYNAMIC 0000000000004010 D _edata 0000000000004018 B _end 00000000000011cc T _fini 0000000000001160 t frame_dummy 0000000000003db0 d __frame_dummy_init_array_entry 00000000000020e8 r __FRAME_END__ 0000000000003fb0 d _GLOBAL_OFFSET_TABLE_ w __gmon_start__ 0000000000002008 r __GNU_EH_FRAME_HDR 0000000000001000 T _init 0000000000002000 R _IO_stdin_used U __isoc99_scanf@GLIBC_2.7 w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable U __libc_start_main@GLIBC_2.34 0000000000001169 T main 00000000000010e0 t register_tm_clones U __stack_chk_fail@GLIBC_2.4 0000000000001080 T _start 0000000000004010 D __TMC_END__
解答
这是因为GCC的常量折叠优化导致的:你代码中调用strlen("dadw")时,传入的是字符串字面量,编译阶段GCC就能直接计算出这个字符串的长度是4,完全不需要生成调用strlen函数的代码,自然不会在符号表中留下strlen的引用条目。
可以通过两种方式验证这个结论:
- 查看汇编代码:执行
gcc -O0 -g -S ./1.c生成1.s文件,打开后会发现main函数里没有call strlen的指令,而是直接把数值4赋值给了变量a。 - 修改代码测试:把
strlen("dadw")改成strlen(c)(c是运行时输入的字符串,长度无法提前确定),重新编译后再用nm ./a.out查看,就能看到U strlen@GLIBC_2.2.5这条未定义符号记录了。
需要注意的是,即使指定了-O0(关闭大部分优化),GCC也会对这类明显的常量表达式做折叠处理,这属于编译前端的基础优化,不属于全局优化范畴,目的是生成更高效的目标代码。
内容的提问来源于stack exchange,提问作者syheliel
相关产品推荐
相关产品推荐

