美文网首页
操作系统(2)---- 可执行文件布局

操作系统(2)---- 可执行文件布局

作者: 特立独行的佩奇 | 来源:发表于2021-10-31 09:16 被阅读0次
    静态布局和动态布局

    这里分析可执行文件,静态布局是指可执行文件在存储器上的内部结构,
    动态布局是指程序被加载到内存上的动态布局

    可执行文件包含包含了程序编译形成的指令和数据,这是对可执行文件的粗略的理解,这里我会更加详细的探究其内部

    Linux 中,可执行文件的标准是 ELF(Excutable and Linkable Foramt)格式

    ELF的静态布局

    ELF文件结构包含一个固定长度的文件头和多个可以拓展的数据块
    文件头是可执行文件总地图,描述了整个文件的组织结构
    数据块分为两种视图 如下图所示:


    链接视图和执行视图.png
    • 链接视图下,数据块的单位是节(section), 用节区头索引其他内容
    • 执行视图下,数据块的单为是段(segment), 用程序头索引所有的段

    下面以一个在 Ubuntu 上用gcc 编译出来的ELF文件为例,剖析一下bin文件的内部

    ELF 头文件

    使用 readelf -h 可以读取ELF 头文件的内容


    ELF_header.png

    • 生成的可执行文件的大小是 6836 字节,文件格式是 64 位的 Linux 标准可执行文件(ELF 64-bit LSB Executable),目标平台是 x86-64。
    • 程序的入口地址是 0x770,该文件的 ELF 头的大小是 64 字节。
    • 文件中有 9 个 Program Header,每个 Program Header 的长度是 56 字节 ,信息存放在从文件头算起 56 字节的位置。
    • 另外还有 35 个 Section Header,每个 Section Header 的大小是 64 字节,信息位于从文件头算起 14592 字节的位置。

    程序头和节区头

    使用 readelf -S 读取ELF文件的节区头信息


    ELF_sectionheader.png

    Type : 表示节区的类型
    Address: 表示该节区占用虚拟内存的起始地址
    Offset: 该节区在可执行文件中的存放位置
    Size: 节区

    使用 readelf -l 命令读取bin文件的程序头表信息


    ELF_programeheader.png

    表示该可执行文件有九个段,并且列出了每个段在文件中的起始地址(Offset 列)、程序加载后占据的虚拟地址(VirtAddr 列)、物理地址(PhysAddr 列)、在文件中占据的空间大小(FileSiz 列)、在内存中占据的空间大小(MemSiz 列)等信息,另外,还列出了节区与段的映射关系,指出了哪些节区在运行时会归入哪个段。

    其他分析指令

    objdump -s -j sectionName dump ELF 文件 section 内容
    比如: objdump -s -j .rodata demobin 可以dump出 demobin 的 rodata 节区

    静态布局总结

    到这里,ELF文件的静态布局就已经分析完了,我们可以在总结一下,可执行文件从前到后依次为 ELF文件头,程序头表,多个节区,节区头表,最后编译系统还会附加.symtab 和 .strtab 节区

    影响静态布局的因素
    .text 节区

    .text 节区存放的是程序源码编译后产生的机器指令,所有的程序逻辑都会存放在这里
    编译时可以添加优化选项,使用 -O1 优化选项编译程序可以生成尽量紧凑的 .text 节区,而用 -O2 优化选项会使编译器倾向于生成执行速度更快的指令组合,但有可能让 .text 节区的体积轻微地增大。

    .rodata 节区

    存储了程序中的常量数据,比如下面的常量数组
    比如下面的语句:
    const char ptr[] = "demo data tdebug";
    会最终编译到.rodata 段


    ELF_rodata.png

    程序运行的时候 .rodata 和 .text section 都会被加载到相同的 segment 中,它们的权限只有读和执行,如果强制写会触发 segment fault 错误.
    但需要注意的是,只有静态和全局的 const 数据才能享受这样的待遇,如果在函数内部声明 const value, 其本质上还是一个函数内的局部变量,存储区在该函数的栈帧内,而程序对该内存区拥有修改的权限。

    .data 节区

    存放全局和静态的已经初始化的变量,比如下面的语句:
    char ptr[] = "demo data tdebug";
    使用objdump 验证:


    ELF_data.png
    .bss 节区

    该节区存储了所有未初始化或初始化为 0 的全局和静态变量

    .got和 .glt 节区

    这两个节区存储了动态链接用到的全局入口表和跳转表。当程序中用到动态链接库中的某个函数时,会在该节区内记录相应的数据

    相关文章

      网友评论

          本文标题:操作系统(2)---- 可执行文件布局

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