第十一周符号及符号解析
第1讲 符号及符号表
符号和符号表的基本概念(27分钟)
main.c
文件内容如下:
int buf[2] = {1,2};
void swap();
int main()
{
swap();
return 0;
}
swap.c
文件内容如下:
extern int buf[];
int *bufp0 = &buf[0];
static int *bufp1;
void swap()
{
int temp;
bufp1 = &buf[1];
temp = *bufp0;
*bufp0 = *bufp1;
*bufp1 = temp;
}
guangwudi@debian:~/csapp$ readelf -s p
Symbol table '.dynsym' contains 7 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FUNC GLOBAL DEFAULT UND _[...]@GLIBC_2.34 (2)
2: 00000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterT[...]
3: 00000000 0 FUNC WEAK DEFAULT UND [...]@GLIBC_2.1.3 (3)
4: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
5: 00000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMC[...]
6: 00002004 4 OBJECT GLOBAL DEFAULT 16 _IO_stdin_used
Symbol table '.symtab' contains 43 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS Scrt1.o
2: 000001cc 32 OBJECT LOCAL DEFAULT 3 __abi_tag
3: 00000000 0 FILE LOCAL DEFAULT ABS main.c
4: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
5: 000010b0 0 FUNC LOCAL DEFAULT 14 deregister_tm_clones
6: 000010f0 0 FUNC LOCAL DEFAULT 14 register_tm_clones
7: 00001140 0 FUNC LOCAL DEFAULT 14 __do_global_dtors_aux
8: 00004018 1 OBJECT LOCAL DEFAULT 25 completed.0
9: 00003eec 0 OBJECT LOCAL DEFAULT 20 __do_global_dtor[...]
10: 00001190 0 FUNC LOCAL DEFAULT 14 frame_dummy
11: 00003ee8 0 OBJECT LOCAL DEFAULT 19 __frame_dummy_in[...]
12: 00000000 0 FILE LOCAL DEFAULT ABS swap.c
13: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
14: 00002114 0 OBJECT LOCAL DEFAULT 18 __FRAME_END__
15: 00000000 0 FILE LOCAL DEFAULT ABS
16: 00003ef0 0 OBJECT LOCAL DEFAULT 21 _DYNAMIC
17: 00002008 0 NOTYPE LOCAL DEFAULT 17 __GNU_EH_FRAME_HDR
18: 00003ff4 0 OBJECT LOCAL DEFAULT 23 _GLOBAL_OFFSET_TABLE_
19: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_mai[...]
20: 00000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterT[...]
21: 000010a0 4 FUNC GLOBAL HIDDEN 14 __x86.get_pc_thunk.bx
22: 00004004 0 NOTYPE WEAK DEFAULT 24 data_start
23: 00004018 0 NOTYPE GLOBAL DEFAULT 24 _edata
24: 000011c8 0 FUNC GLOBAL HIDDEN 15 _fini
25: 00001195 0 FUNC GLOBAL HIDDEN 14 __x86.get_pc_thunk.dx
26: 00000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@G[...]
27: 00004004 0 NOTYPE GLOBAL DEFAULT 24 __data_start
28: 00004014 4 OBJECT GLOBAL DEFAULT 24 bufp0
29: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
30: 00004008 0 OBJECT GLOBAL HIDDEN 24 __dso_handle
31: 00002004 4 OBJECT GLOBAL DEFAULT 16 _IO_stdin_used
32: 0000401c 0 NOTYPE GLOBAL DEFAULT 25 _end
33: 00001070 44 FUNC GLOBAL DEFAULT 14 _start
34: 00002000 4 OBJECT GLOBAL DEFAULT 16 _fp_hw
35: 0000400c 8 OBJECT GLOBAL DEFAULT 24 buf
36: 00004018 0 NOTYPE GLOBAL DEFAULT 25 __bss_start
37: 00001050 30 FUNC GLOBAL DEFAULT 14 main
38: 000011c3 0 FUNC GLOBAL HIDDEN 14 __x86.get_pc_thunk.ax
39: 00004018 0 OBJECT GLOBAL HIDDEN 24 __TMC_END__
40: 00000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMC[...]
41: 000011a0 35 FUNC GLOBAL DEFAULT 14 swap
42: 00001000 0 FUNC GLOBAL HIDDEN 11 _init
guangwudi@debian:~/csapp$ readelf -s hello.o
Symbol table '.symtab' contains 9 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS hello.c
2: 00000000 0 SECTION LOCAL DEFAULT 2 .text
3: 00000000 0 SECTION LOCAL DEFAULT 6 .rodata
4: 00000000 0 SECTION LOCAL DEFAULT 7 .text.__x86.get_[...]
5: 00000000 60 FUNC GLOBAL DEFAULT 2 main
6: 00000000 0 FUNC GLOBAL HIDDEN 7 __x86.get_pc_thunk.ax
7: 00000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
8: 00000000 0 NOTYPE GLOBAL DEFAULT UND puts
使用nm obj_file
列出目標文件obj_file的符號表
guangwudi@debian:~/csapp$ nm hello.o
U _GLOBAL_OFFSET_TABLE_
00000000 T main
U puts
00000000 T __x86.get_pc_thunk.ax
这里提到偏移量是0时,在“Ot”属性下的“0”处划了红线。这里老师把“Ot”看成了“of”,想当然认为是offset,表示偏移量,实际上应该是“Other”属性。正确的做法应该是在( value )属性下的取值处划线呢?
全局符号的强弱特性(8分钟)
多重符号定义举例(21分钟)
/*main.c*/
int x = 10;
int p1(void);
int main()
{
x = p1();
return x;
}
/*p1.c*/
int x = 20;
int p1()
{
return x;
}
guangwudi@debian:~/csapp$ gcc -static -o myproc main.c p1.c
/usr/bin/ld: /tmp/ccIGgPqC.o:(.data+0x0): multiple definition of `x'; /tmp/ccskziV9.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
在Code::Blocks中报错error: ld returned 1 exit status
/*main.c*/
#include <stdio.h>
int y = 100;
int z;
void p1(void);
int main()
{
z = 1000;
p1();
printf("y=%d,z=%d\n",y,z);
return 0;
}
/*p1.c*/
int y;
int z;
void p1()
{
y = 200;
z = 2000;
}
在Code::Blocks中能正常输出结果:
y=200,z=2000
但在Debian32-bit虚拟机中一直报错multiple definition
,暂时无力解决。
guangwudi@debian:~/csapp$ gcc -c p1.c
guangwudi@debian:~/csapp$ ar rcs mylib.a p1.o
guangwudi@debian:~/csapp$ gcc -c main.c
guangwudi@debian:~/csapp$ gcc -static -o myproc main.o ./mylib.a
/usr/bin/ld: ./mylib.a(p1.o):(.bss+0x0): multiple definition of `y'; main.o:(.data+0x0): first defined here
/usr/bin/ld: ./mylib.a(p1.o):(.bss+0x4): multiple definition of `z'; main.o:(.bss+0x0): first defined here
collect2: error: ld returned 1 exit status
/*main.c*/
#include <stdio.h>
int d = 100;
int x = 200;
void p1(void);
int main()
{
p1();
printf("d=%d,x=%d\n",d,x);
return 0;
}
/*p1.c*/
double d;
void p1()
{
d = 1.0;
}
在Code::Blocks中能正常输出结果:
d=0,x=1072693248
第2讲 静态链接和符号解析
静态共享库的创建(15分钟)
/*myproc1.c*/
#include<stdio.h>
void myfunc1()
{
printf("This is myfunc1!\n");
}
/*myproc2.c*/
#include<stdio.h>
void myfunc2()
{
printf("This is myfunc2!\n");
}
/*main.c*/
void myfunc1(void);
int main()
{
myfunc1();
return 0;
}
guangwudi@debian:~/csapp$ vim myproc1.c
guangwudi@debian:~/csapp$ vim myproc2.c
guangwudi@debian:~/csapp$ gcc -c myproc1.c myproc2.c
guangwudi@debian:~/csapp$ ar rcs mylib.a myproc1.o myproc2.o
guangwudi@debian:~/csapp$ vim main.c
guangwudi@debian:~/csapp$ gcc -c main.c
guangwudi@debian:~/csapp$ gcc -static -o myproc main.o ./mylib.a
guangwudi@debian:~/csapp$ ./myproc
This is myfunc1!
符号解析过程(13分钟)
链接顺序问题(10分钟)
第十一周小测验
1以下是链接过程中对符号定义的判断,其中错误的是( A )。
A.全局变量声明“int *xp=&x;
”中,xp和x都是符号的定义
B.函数内的局部变量声明“short x=200;”中,x不是符号的定义
C.静态局部变量声明“static int x=*xp
;”中,x是符号的定义
D.全局变量声明“int x, y;”中,x和y都是符号的定义
解释:局部变量不是符号,静态全局变量是局部符号。
2.若x为局部变量,xp、y和z是全局变量,则以下判断中错误的是(A )。
A.赋值语句“int y=x+z;”中,y和z都是符号的引用
B.赋值语句“y=x+*xp;
”中,y和xp都是符号的引用
C.静态局部变量声明“static int x=*xp;
”中,xp是符号的引用
D.赋值语句“y=x+z;”中,y和z都是符号的引用
3以下有关ELF目标文件的符号表的叙述中,错误的是(C )。
A.符号表定义在.symtab节中,每个表项描述某个符号的相应信息
B.通过符号表可获得符号的名称、所在节及在节中偏移地址和长度
C.符号表中包含了所有定义符号的描述信息,包括局部变量的相关信息
D.可重定位和可执行两种目标文件中都有符号表且数据结构一样
4以下是有关链接过程中符号解析(符号绑定)的叙述,其中错误的是( B )。
A.全局符号(包括外部全局符号)需将模块内的引用与模块外的定义符号绑定
B.同一个符号名可能在多个模块中有定义,每个定义处的符号都须分配空间
C.符号解析的目的是将符号引用与某目标模块中定义的符号建立关联
D.本地符号的解析比较简单,只要与本模块内定义的符号关联即可
解释:一个符号变量只能有一个地址
5以下有关强符号和弱符号的符号解析的叙述中,错误的是( C )。
A.一个符号名只能有一个强符号,否则符号解析失败
B.一个符号名可以有多个弱符号,任选一个为其定义
C.一个符号名可以仅出现在引用处或仅出现在定义处
D.一个符号名可以有一个强符号和多个弱符号,强符号为其定义
6以下是两个源程序文件:
/* m1.c */
int p1(viod);
int main()
{
int p1= p1();
return p1;
}
/* m2.c */
static int main=1;
int p1()
{
main++;
}
对于上述两个源程序文件链接时的符号解析,错误的是( D )。
A.在m1中,定义了一个强符号main和一个弱符号p1
B.在m2中,定义了一个强符号p1和一个局部符号main
C.在m1中,对m2中定义的强符号p1的引用只有一处
D.因为出现了两个强符号main,所以会发生链接错误
7以下是两个源程序文件:
/* m1.c */
int p1;
int main()
{
int p1= p1();
return p1;
}
/* m2.c */
int main=1;
int p1()
{
int p1=main++;
return main;
}
对于上述两个源程序文件链接时的符号解析,错误的是( C )。
A.在m1中,定义了一个强符号main和一个弱符号p1
B.在m2中,定义了一个强符号p1和一个强符号main
C.在模块m1的所有语句中,对符号p1的引用一共有三处
D.因为出现了两个强符号main,所以会发生链接错误
8以下是两个源程序文件:
/* m1.c */
int x=100;
int p1(void);
int main()
{
x = p1();
return x;
}
/* m2.c */
float x;
static main=1;
int p1()
{
int p1=main + (int)x;
return p1;
}
对于上述两个源程序文件链接时的符号解析,错误的是( C )。
A.m1中对x的两处引用都与m1中对x的定义绑定
B.m2中的变量p1与函数p1被分配在不同存储区
C.m2中对x的引用与m2中对x的定义绑定
D.虽然x、main和p1都出现了多次定义,但不会发生链接错误
解释:从这里可以看出,return x不是对x的引用。。。
9以下是两个源程序文件:
/* m1.c */
#include <stdio.h>
int x=100;
short y=1, z=2;
int main()
{
p1();
printf("x=%d, z=%d\n",x,z);
}
/* m2.c */
double x;
void p1()
{
x= -1.0;
}
上述程序执行的结果是( B )。提示:1074790400=2^30+2^20,16400=2^14+2^4。
A.x=100, z=2
B.x=0, z=-16400
C.x=-1, z=2
D.x=-1074790400, z=0
解析: B、该题中变量x在m1.c中为强符号,在m2.c中为弱符号。在调用p1函数后,x处原来存放的100被替换,-1.0的double类型表示为1 0111 1111 111 00…0,十六进制表示为BFF0 0000 0000 0000。因为x、y和z都是初始化变量,同在.data节中,链接后空间被分配在一起,x占4B,随后y和z各占2B。因为IA-32为小端方式,所以,x的机器数为全0,y的机器数也为全0,z的机器数为BFF0H。执行printf函数后x=0, z=-(2^14+2^4)=-16400。
10假设调用关系如下:func.o→libx.a和liby.a中的函数,libx.a→libz.a中的函数,libx.a和liby.a之间、liby.a和libz.a相互独立,则以下几个命令行中,静态链接发生错误的命令是( B )。
A.gcc -static –o myfunc func.o libx.a libz.a liby.a
B.gcc -static –o myfunc func.o liby.a libz.a libx.a
C.gcc -static –o myfunc func.o liby.a libx.a libz.a
D.gcc -static –o myfunc func.o libx.a liby.a libz.a