共享库

一、编译、链接、运行

  

/* 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:

  图片 1

  指令中凡是用到stack和top之处都用0x0表示,准备在重平昔时校订,再看readelf输出的.rel.text段的音讯:

  图片 2

  标出了指令中有随处要求在重一向时改进。下边编写翻译链接成可推行文件之后再做反汇编剖判:

  图片 3

  原本指令中的0x0被涂改成了0x804a010和0x804a040,那样做了重一直之后,各段的加载地址就定死了,因为在指令中选取了相对地址。

  现在看用-fPIC编写翻译生成的靶子文件有如何两样:

  图片 4

  图片 5

  指令中用到的stack和top之处不再以0x0表示,而是以0x0(%ebx表示),但中间还留有0x0备选做进一层改革。再看readelf输出的.rel.text段:

  图片 6

  (以下为试验结果:

  图片 7

  )

  top和stack对应的笔录类型不再是大切诺基_386_32了,而是R_386_GOT32,有怎么样界别吧?大家先编写翻译生成分享库再做反汇编解析:

  图片 8

  图片 9

  和早前的结果分化,指令中的0x0(%ebx)被校正成-0xc(%ebx)和-0x8(%ebx),并非修正成相对地址,所以共享库各段的加载地址并从未定死,能够加载到任性地点,因为指令中从未运用相对化地址,因而称为地点非亲非故代码。其它,注意这几条指令:

  图片 10

  和原先的下令相比一下:

  图片 11

  能够窥见,-0xc(%ebx)这么些地方实际不是变量top的地点,这么些地址的内部存款和储蓄器单元中又保留了此外叁个地点,那其余叁个地点才是变量top之处,所以mov -0xc(%ebx), %eax是把变量top的地址传给eax,而mov (%eax), %eax才是从top的地点中收取top的值传给eax。lea 0x1(%eax), %edx是把top的值加1存到edx中,如下图所示:

  图片 12

  top和stack的断然地址保存在二个地方表中,而下令通过地点表做直接寻址,因而幸免了将相对地址写死在命令中,这也是后生可畏种制止硬编码的计划。

 

二、动态链接的进程

  现在研讨一下在main.c中调用分享库的函数push是何许完毕的。首先反汇编看一下main的指令:

  图片 13

  图片 14

  和静链接库分化,push函数并从未链接到可奉行文件。并且call 80483d8<push@plt>那条指令调用的亦非push函数的地址。分享库是岗位毫无干系代码,在运作时方可加载到任意地址,其加载地址唯有在动态链接时技巧鲜明,所以在main函数中不容许直接通过相对地址调用push函数,也是因此直接寻址来找push函数的。我们用gdb跟踪一下:

  图片 15

  跳转到.plt段中,现在将在推行一条jmp *0x804a008命令,我们看看0x804a008那个地方里存的是什么样:

  图片 16

  原本正是下一条指令push $0x10的地点,继续跟踪下去:

  图片 17

  最后步向了动态链接器/lib/ld-linux.so.2,在里头完结动态链接的过程并调用push函数,更深刻就不再追究了【原作未有深究...】间接用finish命令重临到main函数:

  图片 18

  图片 19

  当时再看看0x804a008这几个地址里存的是何许:

  图片 20

  动态链接器已经把push函数的地点存在那了,所以下一次再调用push函数就足以一向从jmp *0x804a008限令跳转到它之处,而不供给再进来/lib/ld-linux.so.2做动态链接了。

本文由银河网址发布于银河网址,转载请注明出处:共享库

您可能还会对下面的文章感兴趣: