最近在学习使用c++时遇到编译问题,所以去看了《程序员的自我修养》,讲链接和库的。
在讲解到符号表时我发现符号表(.symtab
,似乎也叫静态符号表,和动态符号表.dynsym
区分)似乎和c/c++中的函数声明与调用直接相关。所以开始以下探索。
环境:
linux: Linux 5.10.102.1-microsoft-standard-WSL2 Ubuntu20.04tls
gcc: gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1)
1 |
|
使用gcc -c a.c -o a.o
生成a.o
中间文件,使用nm a.o
分析目标文件。
0000000000000000 T main
发现并没有myPrintf
字段。
修改代码
1 |
|
使用nm指令分析目标文件。
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T main
U myPrintf
发现多出来了myPrintf
,且此符号的符号值为空。
添加一个实现了函数的源文件。
1 |
|
使用gcc -c a.c -o b.o
生成b.o
中间文件,使用nm b.o
分析目标文件。
0000000000000000 T myPrintf
此时发现myPrintf
符号已有符号值。
随后链接两者并生成可执行程序gcc a.o b.o -o a.out
,使用nm a.out
分析符号表.
0000000000003e00 d _DYNAMIC
0000000000003fc0 d GLOBAL_OFFSET_TABLE
0000000000002000 R _IO_stdin_used
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
0000000000002154 r FRAME_END
0000000000002004 r __GNU_EH_FRAME_HDR
0000000000004010 D TMC_END
0000000000004010 B __bss_start
w __cxa_finalize@@GLIBC_2.2.5
0000000000004000 D __data_start
00000000000010e0 t __do_global_dtors_aux
0000000000003df8 d __do_global_dtors_aux_fini_array_entry
0000000000004008 D __dso_handle
0000000000003df0 d __frame_dummy_init_array_entry
w gmon_start
0000000000003df8 d __init_array_end
0000000000003df0 d __init_array_start
00000000000011d0 T __libc_csu_fini
0000000000001160 T __libc_csu_init
U __libc_start_main@@GLIBC_2.2.5
0000000000004010 D _edata
0000000000004018 B _end
00000000000011d8 T _fini
0000000000001000 t _init
0000000000001040 T _start
0000000000004010 b completed.8061
0000000000004000 W data_start
0000000000001070 t deregister_tm_clones
0000000000001120 t frame_dummy
0000000000001129 T main
0000000000001142 T myPrintf
00000000000010a0 t register_tm_clones
发现此时myPrintf
已有符号值。
修改a.c
1 |
|
使用gcc -c c.c -o c.o
生成c.o
中间文件,使用nm c.o
分析目标文件。
0000000000000000 T main
0000000000000019 T myPrintf
发现myPrintf
的符号值不为空。
然后重新链接gcc c.o b.o
,报错
/usr/bin/ld: b.o: in function
myPrintf
:
b.c:(.text+0x0): multiple definition of `myPrintf’; c.o:a.c:(.text+0x19): first defined here
collect2: error: ld returned 1 exit status
所以编译失败了。由于符号表中myPrintf重复存在导致无法成功链接。
但是不知道对不对
总结
只有在函数在源文件内声明并调用后,函数名会添加到符号表。同样的,cpp的重载也是通过某个规则生成了同函数名不同输入输出的特殊的符号来实现的。