注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

FY

Johnson 's Blog

 
 
 

日志

 
 

p197-p199的个人总结  

2012-03-05 16:58:56|  分类: C Programming L |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
默认情况下,动态链接生成的可执行文件(我想大家很少会采用静态来生成可执行文件的吧) 是PIC的,但又和.so的有点不同,不同之处就是在可执行文件里对外部.so的全局变量的引用也是用绝对地址的,而不像.so一样用got来访问自己内部和外部的全局变量
,也就是多个进程共享代码段,可执行文件里也有.got段,可执行文件里对.so里的所有varible的引用全部转化为自己内部.data段的内容,而对.so的function的引用则采用.got的方式


#################################################################
/* lib.c */

int global;
void foobar();



void foobar(){

global=4;

}

#################################################################
/* main.c */

#include<stdio.h>

extern int global;

int main()
{
foobar();
printf("%d global:\n",global);
global=44;
printf("%d global:\n",global);

}

#################################################################
gcc -fPIC -shared -o lib.so lib.c
 gcc -o main main.c lib.so

#################################################################
查看可执行文件 main的汇编代码


080484c4 <main>:
 80484c4:       8d 4c 24 04             lea    0x4(%esp),%ecx
 80484c8:       83 e4 f0                and    $0xfffffff0,%esp
 80484cb:       ff 71 fc                pushl  0xfffffffc(%ecx)
 80484ce:       55                      push   %ebp
 80484cf:       89 e5                   mov    %esp,%ebp
 80484d1:       51                      push   %ecx
 80484d2:       83 ec 14                sub    $0x14,%esp
 80484d5:       e8 ca fe ff ff          call   80483a4 <foobar@plt>  通过.got的方式来调用外部lib.so里定义的函数foobar
 80484da:       a1 78 97 04 08          mov    0x8049778,%eax      0x8049778这个地址在可执行文件main的.data段,它是全局变量global的所在地址,可见在可执行文件里,对外部全局变量的引用的对外部全局函数的引用是不一样的,但总体系来说可执行文件的代码段是可以 被 多个进程共享的,为什么这里指令里使用了绝对地址 依然还可以被多个进程共享呢?而在讲解.so时不是重点强调不能在指令里有绝对地址,才能达到PIC(地址无关),从而被多个进程共享吗?其原因是那些.so调用另一个.so,这些.so有个共同点,他们加载的虚拟地址是不同的,所以绝对要保证指令中不能有绝对地址才能达到被多个共享的目的,而可执行文件在被生成后,所有的加载地址都“定死”了,都定了,所以这里即使使用绝对地址来表达对外部全局变量的引用,也没关系,依然是可以的,而对外部全局函数的引用,则还得通过类似.so里运用的 GOT方式才行
 80484df:       89 44 24 04             mov    %eax,0x4(%esp)
 80484e3:       c7 04 24 f0 85 04 08    movl   $0x80485f0,(%esp)
 80484ea:       e8 e5 fe ff ff          call   80483d4 <printf@plt>
 80484ef:       c7 05 78 97 04 08 2c    movl   $0x2c,0x8049778
 80484f6:       00 00 00
 80484f9:       a1 78 97 04 08          mov    0x8049778,%eax
 80484fe:       89 44 24 04             mov    %eax,0x4(%esp)
 8048502:       c7 04 24 f0 85 04 08    movl   $0x80485f0,(%esp)
 8048509:       e8 c6 fe ff ff          call   80483d4 <printf@plt>
 804850e:       83 c4 14                add    $0x14,%esp
 8048511:       59                      pop    %ecx
 8048512:       5d                      pop    %ebp
 8048513:       8d 61 fc                lea    0xfffffffc(%ecx),%esp
 8048516:       c3                      ret   
 8048517:       90                      nop   
 8048518:       90                      nop

#############################################################
查看 lib.so
我们可以看到即使对于定义在自己.so内部的全局变量,编译生成的汇编代码中,也是能过GOT来访问,而同时由于可执行文件里也用到了这个global变量,所以GOT里实际指向的是可执行文件里的.data段中的地址

000003cc <foobar>:
 3cc:   55                      push   %ebp
 3cd:   89 e5                   mov    %esp,%ebp
 3cf:   e8 14 00 00 00          call   3e8 <__i686.get_pc_thunk.cx>
 3d4:   81 c1 58 11 00 00       add    $0x1158,%ecx
 3da:   8b 81 f8 ff ff ff       mov    0xfffffff8(%ecx),%eax

 3e0:   c7 00 04 00 00 00       movl   $0x4,(%eax)
 3e6:   5d                      pop    %ebp
 3e7:   c3                      ret   

000003e8 <__i686.get_pc_thunk.cx>:
 3e8:   8b 0c 24                mov    (%esp),%ecx
 3eb:   c3                      ret   
 3ec:   90                      nop   
 3ed:   90                      nop   
 3ee:   90                      nop   
 3ef:   90                      nop



========================================
更新:

我今天认真的研究了一下。有点思路了,也明白了p197 chapter7.3.4的内容

可执行文件编译好后,如果对外部有符号引用,且这些符号引用是定义在某个.so里的,那么在可执行文件中会有两种情况1.函数 2变量

书上p186的一段话:“如果foobar()是一个定义在某个动态共享对象中的函数,那么链接器就会将这个符号的引用标记为一个动态链接的符号,不对它进行地址重定位,把这个过程留到装载时再进行” 这句话现在明白了,只针对函数的。不是针对变量的

如果是针对变量的,那么在指令里是写死的,是一个绝对地址,虽然这个符号是在外面定义的

我自己试验了一下:

main.c

int main()

{

load_printf();

var_in_load=44; //这里要用到定义在foobar中的全局变量

load_printf();

return 0;

}



######################

foobar.c

int var_in_load=88;



void load_printf()

{

printf("%d\n",var_in_load);

}



########################

gcc -fPIC -shared -o foorbar.so foorbar.c

gcc -c main.c

gcc -o main main.o ./foorbar.so

########################

objdump -dS main

080484a4 <main>:

80484a4: 8d 4c 24 04 lea 0x4(%esp),%ecx

80484a8: 83 e4 f0 and $0xfffffff0,%esp

80484ab: ff 71 fc pushl 0xfffffffc(%ecx)

80484ae: 55 push %ebp

80484af: 89 e5 mov %esp,%ebp

80484b1: 51 push %ecx

80484b2: 83 ec 04 sub $0x4,%esp

80484b5: e8 fa fe ff ff call 80483b4 <load_printf@plt>

80484ba: c7 05 28 97 04 08 2c movl $0x2c,0x8049728 // var_in_load=44;

80484c1: 00 00 00

80484c4: e8 eb fe ff ff call 80483b4 <load_printf@plt>





#############################

readefl -a main 查看可执行文件的头信息



Program Headers:

Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align

PHDR 0x000034 0x08048034 0x08048034 0x00100 0x00100 R E 0x4

INTERP 0x000134 0x08048134 0x08048134 0x00013 0x00013 R 0x1

[Requesting program interpreter: /lib/ld-linux.so.2]

LOAD 0x000000 0x08048000 0x08048000 0x00624 0x00624 R E 0x1000

LOAD 0x000624 0x08049624 0x08049624 0x00104 0x00110 RW 0x1000

DYNAMIC 0x000638 0x08049638 0x08049638 0x000d0 0x000d0 RW 0x4

NOTE 0x000148 0x08048148 0x08048148 0x00020 0x00020 R 0x4

GNU_EH_FRAME 0x0005b0 0x080485b0 0x080485b0 0x0001c 0x0001c R 0x4

GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4



Section to Segment mapping:

Segment Sections...

00 

01 .interp

02 .interp .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame

03 .ctors .dtors .jcr .dynamic .got .got.plt .data .bss

04 .dynamic

05 .note.ABI-tag

06 .eh_frame_hdr

07 



Relocation section '.rel.dyn' at offset 0x344 contains 2 entries:

Offset Info Type Sym.Value Sym. Name

08049708 00000106 R_386_GLOB_DAT 00000000 __gmon_start__

08049728 00000b05 R_386_COPY 08049728 var_in_load





##########################

我发现针对外部全局变量var_in_load进行赋值时,在可执行文件里已写死了,0x8049728 这个地址查看了可执行文件的头部,发现是在RW属性的数据segment里,而再查看segment的构成,推断是在.bss这个section里。

可执行文件里只要用到这个全局变量的,都用0x8049728这个地址,如果是foobar.c要用到自己定义的这个全局变量时,还必须经过GOT去访问可执行文件的.bss中某个地址,也就是对foobar.c来说,var_in_load虽是亲生的,但却生活在别人的地盘

##########################

p198而于是还提到

(1)

“如果全局变量已在共享模块中被初始化,那么动态链接器需要将这个值”复制“到可执行文件中的.bss中去”---------------这句话我理解了

接下来

(2)

“如果这个全局变量在可执行文件中没有副本,那么GOT中的相应地址就指向模块内部的该变量副本”

-----------------这句话后半句没明白

它的意思是可执行文件没用到,也就是可能是共享模块里各部分自己用到了,比如a.o和b.o两个都是PIC的,相互用到对方的全局变量,然后-shared成一个 xx.so共享模块

但后半句讲模块内部的该变量“副本”?是指GOT中“指向”模块自己的.data这个segment里的全局变量所在的地址吗?我想只能是这样。但书上还讲了,默认都把定义在模块内部的全局变量当作定义在其他模块的全局变量,也就是当作前面的类型四。通过GOT来实现变量的访问“,那这个全局变量既然默认是通过这个GOT访问的,就不会在自己模块的.data中”开辟“一块存储空间了吧,那上面的(2)这句,模块内部的该变量副本从何一说?

seufy88 2011-11-24 14:55
一个A.so被编译生成后,假设这里面有自己定义的全局变量 它肯定不知道也不可能知道将来可执行文件中会不会用到这个全局变量。于是它肯定只能为这个全局变量创建空间,即位于.data或是.bss,视它有没有初始化而定

接下来,A.so是所有要使用这个全局变量的指令部分,全部用GOT来实现



而接着如果可执行文件中使用到了这个全局变量,那么A.so里也要用可执行文件里的,如果没可执行文件不用到这个全局变量,那么就用A.so自己的。



于是关键就是GOT。所以应该是这样的,不管可执行文件里用不用的到, A.so里加载后。它的.data部分肯定有这个全局变量的一个空间。A.so自己有没有用这个还是用的是可执行文件.bss里的那个不影响全局变量在A.so的.data段的存在



------>接着,再加上另一个共享模块B.so 这里前提:可执行文件里用到了A.so里的全局变量,而B.so也要用到这个全局变量, 这是书上的第三种情况,我觉得这时书上说的其他模块的.data,这个其他模块应该包含可执行文件的.data,即把可执行文件也当一种模块,而 B.so的GOT里指向的地址就不是A.so里的全局变量的地址,而是可执行文件里的.data中的地址。这里的GOT内容要写入哪个的地址,我想都是动态链接器的活。虽然方法原理都是没变,但这种情况应该加上,就能更加”周全“了



我这样想不知道对不对,有点不可思议,应该A.so里的全局变量现在有“两个”,但指令层面又做了处理,保证都用的是确定的一个。不会出叉子





以上是我的理解。刚看完7.3.4 至此总算有点收获了
  评论这张
 
阅读(55)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018