美文网首页
程序编译链接(二)-- 目标文件

程序编译链接(二)-- 目标文件

作者: wayyyy | 来源:发表于2018-10-02 18:20 被阅读0次

    编译器编译源代码后生成的文件叫做目标文件。从结构上讲,它已经是编译后的可执行文件格式,只是还没有经过链接的过程,其中可能有些符号或有些地址还没有被调整。其本身是按照可执行文件格式存储的。

    现在PC上流行的可执行文件格式,主要是Windows下的PE和Linux的ELF。
    动态链接库(Windows的.dll和Linux的.so)和静态链接库(Windows的.lib和Linux的.a)文件也按照可执行文件格式存储。

    目标文件格式

    /* test.c */
    int printf(const char* format, ...);
    
    int global_init_var = 84;
    int global_uninit_var;
    
    void func1(int i)
    {
        printf("%d\n", i);
    }
    
    int main()
    {
        static int static_var = 85;
        static int static_var2;
    
        int a = 1;
        int b;
    
        func1(static_var + static_var2 + a + b);
        return a;
    }
    

    一般C语言编译后执行语句都编译成机器代码,保存在.txt段。
    已初始化的全局变量和局部静态变量都保存在.data段。
    未初始化的全局变量和局部静态变量一般放在.bss段。

    进程映像.png
    比如上面的global_init_varstatic_var.data段,global_uninit_varstatic_var2bss段。

    总的来说,程序源代码被编译后主要分成两种段:程序指令程序数据,代码段属于程序指令,而数据段和.bss段属于程序数据。
    也许会问:为什么要把程序的指令和数据分开?主要因为:

    • 一方面当程序被装载后,数据和指令分别被映射到两个虚存区域,由于数据区域对于进程来说是可读写的,而指令区域对于进程来说是只读的,所以这两个区域的权限可以被分别设置成可读写和只读,这样可以防止程序的指令被有意或无意地改写。

    • 另一方面是对于现代CPU来说,它们有着极为强大的缓存体系。指令区和数据区的分离有利于提高程序的局部性,现代CPU的缓存一般都被设计成数据缓存和指令缓存分离。

    • 当系统中运行着多个程序的副本时,它们的指令都是一样的,所以内存中只需要保存一份改程序的指令部分。对于程序的指令如此,其他只读数据也类似,比如:

    $ gcc -m32 -c test.c -o test.o    # 
    $ objdump -h test.o    # -h 把ELF文件的各个段的基本信息打印出来
    

    objdump可用来查看各目标文件的结构和内容。参数-h表示把ELF文件的各个段的基本信息打印出来。

    段信息.png

    代码段

    $ objdump -d test.o
    

    -d将包含指令的段反汇编。


    数据段和只读数据段

    • .data
      .data段保存是初始化了得全局变量,静态变量和局部静态变量,前面有2个这样的变量是global_init_var = 84 = 0x00000054static_var = 85 = 0x00000055,一共8字节。(x86 的是小端序,高位存放在高地址中)

      .data段.png
    • .rodata只读数据段
      保存的是只读数据,一般是程序的只读变量(如const修饰的变量)和字符串常量。比如上面我们用到了字符串常量 %d\n,它是一种只读数据。"25640a00"也恰好是这个字符串常量的ASCII字节序。

      .rodata段.png

    .bss

    .bss段存放的是未初始化的全局变量和局部静态变量。可以看到该段的大小只有4字节,但global_uninit_varstatic_var2的大小应该是8字节。这不是矛盾了?
    实际上,只有static_var2被存放到了被存放到了.bss段,而global_uninit_var却没有存放在任何段,只是一个未定义的COMMON符号。这跟编译器实现有关,有些编译器会将全局的未初始化变量存放在目标文件的.bss段,有些则不存放,只是预留一个未定义的全局变量符号,等到最终链接成可执行文件的时候再在.bss段分配空间。

    .comment

    .comment段一般存放编译器版本信息

    .comment段.png

    其他段

    自定义段

    相关文章

      网友评论

          本文标题:程序编译链接(二)-- 目标文件

          本文链接:https://www.haomeiwen.com/subject/onqriftx.html