zoukankan      html  css  js  c++  java
  • 自己动手学编译器的链接和加载(一)

    最近学习链接器的链接和装载过程。

    首先说说一个程序从源代码到可执行文件的流程(以linux平台上c程序为例):

    第一步预编译过程的命令如下:

    gcc -E test.c -o test.i 或 cpp test.c > test.i

    由.c文件生成.i预处理文件

    第二步:

    gcc -S test.i -o test.s

    由.i生成.s汇编文件

    第三步:

    as test.s -o test.o 或

    gcc -c test.s -o test.o

    生成目标文件

    第四步:

    ld -static /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/gcc/i686-linux-gnu/4.6.3/crtbeginT.o -L/usr/lib/gcc/i686-linux-gnu/4.6.3 -L/usr/lib -L/usr/lib -L/lib test.o --start-group -lgcc -lgcc_eh -lc --end-group /usr/lib/gcc/i686-linux-gnu/4.6.3/crtend.o /usr/lib/crtn.o

    现在就是来看看这一坨乱七八糟的东西是啥

    这里先从最基本的看起吧。

    首先看看目标文件的格式,在linux上中间文件的格式和可执行文件的格式很像,差别无非是中间文件中一些引用的函数和变量的地址是相对地址,经过链接后在可执行文件中变成了绝对地址

    linux上可执行文件的格式是ELF(Executable Linkable Format)

    以下是一个实例小程序test.c 这个东西应该是非常具备典型行的。

     1 #include<stdio.h>
     2 
     3 int a = 1; 
     4 double b;
     5 static int e;
     6 
     7 static int c = 2; 
     8 static double d; 
     9 
    10 void func(int x, double y){
    11 printf("%d, %f\n", x, y); 
    12 }
    13 int main(){ 
    14 
    15 static int c_m = 4;
    16 static double d_m; 
    17 
    18 int a_m = 3;
    19 double b_m; 
    20 func(a_m, d_m);
    21 return 0;
    22 }

    以下是test.c代码与生成的test.o文件一些最主要段的对应关系:

    这个是通过objdump -h test.o命令得来的:

    size表示段的大小,VMA表示段的虚拟内存地址,LMA表示段的加载地址,在链接之前都是0,file off表示段在文件中的偏移量

    .text是文件代码段,存放可执行代码,局部变量的声明和定义也是在其中。

    .data是数据段,存放已初始化的全局变量,全局静态变量,和局部静态变量。

    .bss存放的是未初始化的全局静态变量,局部静态变量,可能还有未初始化的全局变量(这个取决于不同的编译器,我的gcc编译器是不将未初始化的全局变量放在.bss中的,只是声明了一个符号,等到最终链接成可执行文件的时候再在.bss段分配空间)

    从上图也可以看到,.bss段的CONTENTS属性不存在,即表明这个段实际上是不存在的,只是标示了符号而已。

    还有一点,变量的存放是要字节对齐的,故一个int和两个double实际上占了24个字节。

    .rodata是只读数据段,在这里存放的是printf("%d, %f\n", x, y);中的d和f

    .comment段.note.GNU-stack和.eh_frame暂时不管了

    可以使用objdump -s -d test.o察看各个段中的内容:

    先看section .text中的内容:

    对照程序的反汇编结果

     可以发现.text中存放的正是func和main函数的指令0000-002e存放func的指令。002f-005c存放的是main的指令。

    在看section .data:

    发现从低地址至高地址12个字节依次存放的是a, c, c_m值

    看section .rodata:

    从后面的ASII码就知道他存放的确实是%d,%f两个只读的数据

    现在已经基本弄清楚了ELF文件的一些布局和结果,之后再继续研究研究。

  • 相关阅读:
    Mysql简单使用
    yum与rpm常用选项
    vim常用配置
    Python模块安装方式
    VirtualBox新建虚拟机常用配置
    Linux中单引号与双引号区别
    etc/profile /etc/bashrc ~/.bash_profile ~/.bashrc等配置文件区别
    virtualenv简单使用
    SqlDataSource学习笔记20091111:ConflictDetection属性
    TreeView学习笔记20091114:遍历树(叶子节点设置多选框)并设置展开级别
  • 原文地址:https://www.cnblogs.com/ylan2009/p/2508938.html
Copyright © 2011-2022 走看看