加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

共享库 链接(extern、static关键词\头文件\

发布时间:2020-12-16 09:09:26 所属栏目:百科 来源:网络整理
导读:一、编译、链接、运行 /*?stack.c?*/char?stack[512];int?top?=?-1; ???? /*?push.c?*/extern?char?stack[512];extern?int?top;void?push(char?c){????????stack[++top]?=?c;} ???? /*?pop.c?*/extern?char?stack[512];extern?int?top;char?pop(void){??????

一、编译、链接、运行

  

/*?stack.c?*/
char?stack[512];
int?top?=?-1;

????

/*?push.c?*/
extern?char?stack[512];
extern?int?top;
void?push(char?c)
{
????????stack[++top]?=?c;
}

????

/*?pop.c?*/
extern?char?stack[512];
extern?int?top;
char?pop(void)
{
????????return?stack[top--];
}

????

/*?is_empty.c?*/
extern?int?top;
int?is_empty(void)
{
????????return?top?==?-1;
}
/*?stack.h?*/
#ifndef?STACK_H
#define?STACK_H
extern?void?push(char);
extern?char?pop(void);
extern?int?is_empty(void);
#endif
/*?main.c?*/
#include?<stdio.h>
#include?"stack.h"
int?main(void)
{
????????push('a');
????????return?0;
}

????目录结构为:

????|–main.c

????|–stack

????? ?|–is_empty.c

????? ?|–pop.c

????? ?|–push.c

????? ?|–stack.c

????? ?|–stack.h

?

  组成共享库的目标文件和一般的目标文件有所不同,在编译时要加-fPIC选项,例:

$ gcc -c -g stack/stack.c stack/push.c stack/pop.c stack/is_empty.c

  【实际以上命令在运行时并没有生成共享库,详见《链接(extern、static关键词头文件静态库共享库)》】

  -fPIC生成的目标文件和一般的目标文件有什么不同呢?下面就来分析这个问题。

  一般的目标文件称为Relocatable,在链接时可以把目标文件中各段的地址做重定位,重定位需要修改指令。我们先不加-fPIC选项生成目标文件:

  

$ gcc -c -g stack/stack.c stack/push.c stack/pop.c stack/is_empty.c

  反汇编查看push.o:

  

  指令中凡是用到stack和top的地址都用0x0表示,准备在重定位时修改,再看readelf输出的.rel.text段的信息:

  

  标出了指令中有四处需要在重定位时修改。下面编译链接成可执行文件之后再做反汇编分析:

  

  原来指令中的0x0被修改成了0x804a010和0x804a040,这样做了重定位之后,各段的加载地址就定死了,因为在指令中使用了绝对地址。

  现在看用-fPIC编译生成的目标文件有什么不同:

  

  

  指令中用到的stack和top的地址不再以0x0表示,而是以0x0(%ebx表示),但其中还留有0x0准备做进一步修改。再看readelf输出的.rel.text段:

  

  (以下为实验结果:

  

  )

  top和stack对应的记录类型不再是R_386_32了,而是R_386_GOT32,有什么区别呢?我们先编译生成共享库再做反汇编分析:

  

  

  和先前的结果不同,指令中的0x0(%ebx)被修改成-0xc(%ebx)和-0x8(%ebx),而不是修改成绝对地址,所以共享库各段的加载地址并没有定死,可以加载到任意位置,因为指令中没有使用绝对地址,因此称为位置无关代码。另外,注意这几条指令:

  

  和先前的指令对比一下:

  

  可以发现,-0xc(%ebx)这个地址并不是变量top的地址,这个地址的内存单元中又保存了另外一个地址,这另外一个地址才是变量top的地址,所以mov -0xc(%ebx),%eax是把变量top的地址传给eax,而mov (%eax),%eax才是从top的地址中取出top的值传给eax。lea 0x1(%eax),%edx是把top的值加1存到edx中,如下图所示:

  

  top和stack的绝对地址保存在一个地址表中,而指令通过地址表做间接寻址,因此避免了将绝对地址写死在指令中,这也是一种避免硬编码的策略。

?

二、动态链接的过程

  现在研究一下在main.c中调用共享库的函数push是如何实现的。首先反汇编看一下main的指令:

  

  

  和静链接库不同,push函数并没有链接到可执行文件。而且call 80483d8<push@plt>这条指令调用的也不是push函数的地址。共享库是位置无关代码,在运行时可以加载到任意地址,其加载地址只有在动态链接时才能确定,所以在main函数中不可能直接通过绝对地址调用push函数,也是通过间接寻址来找push函数的。我们用gdb跟踪一下:

  

  跳转到.plt段中,现在将要执行一条jmp *0x804a008指令,我们看看0x804a008这个地址里存的是什么:

  

  原来就是下一条指令push $0x10的地址,继续跟踪下去:

  

  最终进入了动态链接器/lib/ld-linux.so.2,在其中完成动态链接的过程并调用push函数,更深入就不再深究了【原文没有深究...】直接用finish命令返回到main函数:

  

  

  这时再看看0x804a008这个地址里存的是什么:

  

  动态链接器已经把push函数的地址存在这里了,所以下次再调用push函数就可以直接从jmp *0x804a008指令跳转到它的地址,而不必再进入/lib/ld-linux.so.2做动态链接了。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读