第十一周符号及符号解析

在线阅读PDF文档

第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