美文网首页
3章 目标文件

3章 目标文件

作者: my_passion | 来源:发表于2022-07-06 12:50 被阅读0次

3章 目标文件

1. Executable (File)

Executable = COFF (Common File Format) 

    Windows:    PE (Portable Executable)
    Linux  :    ELF (Executable Linkable Format)    


Executable File 
        
    Windows:    PE 文件
    Linux  :    ELF 文件


——————————————————————————————————————————————————————————————————————————————————————————                              
Executable File 类型|     说明                                  | Windows / Linux 下 实例
——————————————————————————————————————————————————————————————————————————————————————————                          
可重定位文件      | 含 code + data                         | .obj / .o
Relocatable File    | 可 链接 成 可执行文件 或 共享目标文件     |
                    | 代表: 静态链接库                         | 
——————————————————————————————————————————————————————————————————————————————————————————                          
可执行文件           | 可 直接执行                                | .exe / bash文件  / .out
Executable File     | 代表: ELF 文件                            |
——————————————————————————————————————————————————————————————————————————————————————————                          
共享目标文件      | 含 code + data                         |
Shared Object File  | 2种用途                                  |   DLL / .so
                    |   1) 多个... + 可重定位文件 -> 链接器    |
                    |       -> 新目标文件                        |
                    |                                           |
                    |   2) 多个... + 可执行文件   -> 动态链接器 |
                    |   -> 作 进程映像 的一部分 运行           |
——————————————————————————————————————————————————————————————————————————————————————————                          
核心转储文件      | 进程 意外终止 -> OS 将 进程的           | Linux 下的 core dump
Core Dump File      | `地址空间内容 + 其他信息` 转储到 核...件 |
——————————————————————————————————————————————————————————————————————————————————————————                          

2. 目标文件 content

2.1 含 link 所需信息: 符号表、字符串 等

    (1) File Header
        
        文件属性
            是否可执行
            静/动 态链接
            入口地址 (若为 可执行文件)
            目标硬件
            目标 OS
            
        段表 (Section Table)
            是 array, 每个 elem 是 `段描述`
                在 Object/Executable File 中的 偏移
                段属性
            
    (2) 代码段 (Code Section)
        .code .text
            local non-static var
        
    (3) 数据段 (Data Section)
        .data

    (4) BSS 段 
        .bss 段
        
2.2 分离 code 与 data

    多线程/多进程, codePart 相同,  memory 中只存1份 codePart

3. SimpleSection.o

(1) 代码段

    $ objdump -s -d SimpleSection.o
    
        -s
            段 `内容` 以 十六进制 打印
            
        -d
           `指令段 反汇编`

(2) 数据段 和 只读数据段

    $ objdump -x -s -d SimpleSection.o
    
    .data 段
        已初始化 的 global var / local static var

    .rodata 段
        1) 只读变量 (const var)
        2) 字符串常量 -> 有的 Compiler 将其放 .data 段
        
        作用
            1] 语义上 支持 C++ const 关键字
            
            2] OS 加载时, 可将 .rodata 映射成 `只读` -> 阻止 非法操作 -> 安全
            
            3] 嵌入式 -> ROM

(3) BSS 段: 预留 空间, link 时才真正分配
                                                  
    global var / local static var
        1] init -> 放 .data 段 -> 初始化 为 0 -> 优化到 .bss 段
                                  
        2] uninit -> `COMMON 块` -> 链接 时, 再在 .bss 段 分配空间 
        
    static: 仅 编译单元 (.o) 内可见
    
(4) 自定义段
        
    func / global var 前加 __attribute__( (section("sectionName") ) )

// -c: 只编译 不链接
    $ gcc -c SimpleSection.c
        |
        |   工具 objdumo 查 object 内部结构
        |/

    $ objdump -h SimpleSection.o

        -h
            
        1) 段 索引 / name / 长度 (Size) / 位置偏移 (File Offset)
            => .o 的 分布结构
            
        2) 段属性

    $ size SimpleSection.o
        查 代码段 数据段 BSS 段 的 长度

4. ELF 文件结构

(1) 文件头: ELF Header

    // 查看 文件头
    $ readelf -h SimpleSection.o
    
    描述文件的 基本属性
        
    Elf32_Ehdr
    ————————————————————————————————————————————————————————————————————————
    1) type 
        4 种
        
    2) machine 
        elf 文件的 CPU 平台
    
    3) entry
        elf 程序的 `入口虚拟地址`: OS 加载完 程序后, 从该 地址开始 `执行 进程指令`
            可重定位文件 无 入口地址
    
    4) shoff
        段表在文件中的偏移
    ————————————————————————————————————————————————————————————————————————
    
(2) 段表 (Section Header Table)
    编译器 链接器 装载器 据 段表 来 `定位 和 访问` 各段的属性

    是 array
        elem 是 `段描述符结构 Elf32_Shdr`
                    |
                    |
                    |
                    |
                ——————————————————————————————————————  
                段名
                
                段类型
                        SHT_NULL        无效
                        SHT_PROGBITS    程序段: .code .data 
                        SHT_SYMTAB      `符号表`
                        SHT_STRTAB      `字符串表`
                        SHT_RELA        `重定位表`
                    
                段偏移
                
                段长度
                ——————————————————————————————————————  
                    
                        

(3) 重定位表 .rel.text

    `代码段 和 数据段` 中 对 `绝对地址 的 引用` 处 -> 重定位
    
        printf 的调用
        
(4) 字符串表

    段名 字符串常量
        |
        |
    字符串: 长度不定
        | 
        |
    存到 `表`, 用 `字符串 在表中的 (首字符)偏移` 来引用- (均以 '\0' 结尾)
    
    ——————————————————————————————————————
    .strtab     字符串表        普通字符串
    
    .shstrtab   段表字符串表  段名
    ——————————————————————————————————————

5. 链接 的 接口 —— 符号

    `目标文件` 间对 `符号 (func / var)地址 的 引用`
        |                                                           
        | 1 个 .o 1 个 符号表                                
        |/               
                            
    符号表 (Symbol Table)
                                

    目标文件(.o) 符号表 中 符号分类: 4 类
    ————————————————————————————————————————————————————————
    `定义的 global symbol` |   可被 other 目标文件 引用 
                            |   func1 / main / global_init_var
    ————————————————————————————————————————————————————————        
    `引用的 global symbol`     |
                            |   称: 外部符号 (External Symbol)
                            |       printf
    ————————————————————————————————————————————————————————
    local symbol:           |   只在 本编译单元 (.o) 内部可见
                            |   static_var / static_var2
                            |   linker 忽略之
    ————————————————————————————————————————————————————————        
    段名                  |   compiler 产生, 值 = 段起始地址
    ————————————————————————————————————————————————————————

(1) ELF 符号表 结构 Elf32_Sym

    $ readelf -s SimpleSection.o
    
    ——————————————————————————————————————————————————————————————————————————————————————————————  
    name  符号名
    ——————————————————————————————————————————————————————————————————————————————————————————————  
    size  符号大小      |   含 数据 的 符号 数据类型 的大小
    ——————————————————————————————————————————————————————————————————————————————————————————————  
          符号类型      |   .._OBJECT       数据对象: 变量、数组 等
          Symbol Type   |   .._FUNC         函数、其他可执行代码
    info  ————————————————————————————————————————————————————————————————————————————————————————        
                        |   ..LOCAL         局部符号, 目标文件外部不可见
          符号绑定信息  | .._GLOBAL       全局符号, 外部可见
          Symbol Binding|   .._WEAK         弱引用
    ——————————————————————————————————————————————————————————————————————————————————————————————    
    shndx 符号所在段 |   .._ABS          值 0xfff1 绝对值, 如 表示文件名的符号 
                        |   .._`COMMON`     `uninit global` symbol
                        |   .._UNDEF        本目标文件中引用 而非定义
    ——————————————————————————————————————————————————————————————————————————————————————————————
    value 符号值       |               符号定义 + 非 "COMMON块" : 符号在 `段中偏移`
                        |                                           func1 / main / global_init_var
                        |  目标文件     ——————————————————————————————————————————————————————————
                        |                             "COMMON块" : 符号的对齐属性
                        |  ————————————————————————————————————————————————————————————————————-——
                        |  可执行文件                         : 符号虚拟地址,
                        |                                          动态链接器 用                                  
    ——————————————————————————————————————————————————————————————————————————————————————————————              

(2) C++ 符号修饰 与 函数签名

    不同/同一 语言间 目标文件 相互引用
        |
        |/
    符号冲突 
        |
        |/
     C 语言: 符号名前统一加 下划线 "_"
        |
        |/
    C++ 引入 `名字空间 namespace`
        |
        |   C++ 类 / 继承 / 虚机制 / 重载 / namespace 等 特性
        |   => 符号管理 更复杂
        |/
        
    `C++ 符号修饰`
    
        1) 函数重载
            最简情形: funcName 相同、paraList 不同
            
        2) 不同 namespace、相同 name 
        
            |
            |   `函数签名 -> 符号修饰` -> 修饰后符号
            |       |
            |       |/
            |
            |    funcName + paraType + 所在 Class / namespace
            |/
        ——————————————————————————————————————
        函数签名                修饰后符号
        ——————————————————————————————————————
        int func(int)           _Z4funci
        float func(float)       _Z4funcf
        int C::func(int)        _ZN1C4funcEi
        int N::C::func(int)     _ZN1N1C4funcEi
        ——————————————————————————————————————
        
        int N::C::func(int) 
        
            _Z N         1N         1C  4func           E       i
               |         |          |     |             |       |       
           1) nest 嵌套  3) 名字前 加 数字:名字长度   3) end   paraList 类型缩写
        
        
        解析 修饰后的名称: binutils 中 c++filt 工具
            $ c++filt _ZN1N1C4funcEi
            N::C::func(int) 

(3) extern "C"

C++ 编译器 将 extern "C" 括号内代码 当 C 代码处理, C++ 的名称修饰 不起作用

        |
        |   但 C 不支持 extern "C" 语法, 为 解决 C / C++ 兼容 extern "C" 
        |/
        
    C++ `宏 __cplusplus`
    
        C++ 编译器 编译 C++ 程序时, 默认定义 该宏, 而 C 编译器 不定义
    
        #ifdef __cplusplus
        extern "C" {
        #endif

        // 若不用 extern, `C++ 编译器 之 符号修饰 -> -Z6memsetPvii` 
        //      -> 链接器 与 C 库中 `_memset 符号 链接 -> symbol undefined
        void* memset(void*, int, size_t); 

        #ifdef __cplusplus
        }
        #endif

(4) 强/弱 符号

    1) `多目标文件 & 符号重复定义`
                
    2) 强/弱 符号 | 引用 : 是 符号 `定义 | 引用`
        
        强 符号/引用 
          |     
          |     __attribute__( (weak / weakref) ) 可 转换
          |/
        弱 符号/引用
            
    3)
        ————————————————————————————————————————————————————    
        编译器 怎么区别 强/弱 符号 ?
        ————————————————————————————————————————————————————
        func / globalInitVar 定义 -> compiler 默认为 强符号     
        ————————————————————————————————————————————————————
        globalUninitVar      定义 ->               弱符号          
        ————————————————————————————————————————————————————
            
    4)  
        ———————————————————————————————————————————————————————————————————————————————         
                | 链接器 如何 区别 ? | 链接器 如何处理 ?
        ———————————————————————————————————————————————————————————————————————————————             
        强 引用    | 必须 正确 决议   |  没找到 符号定义, 报错 `符号未定义 Symbol Undefind`
        ———————————————————————————————————————————————————————————————————————————————             
        弱 引用    | 不必 明确决议        |  找到 符号定义 -> 用
                | 链接器 可给 默认值 |  `没找到定义   -> 不报错, 默认其为 0` 或 特殊值
        ———————————————————————————————————————————————————————————————————————————————         
        
    5)  弱符号/引用 应用
        
        1) 库 的链接
        
            库中定义的 `弱符号`, 可被 `用户定义 的 强符号 覆盖`
            
                => 程序支持 `自定义库函数`
                    
                    用户自定义 printf 函数
        
        2) `扩展功能模块 的 引用` -> 声明为 -> `弱引用`
            
            => 易 裁剪 和 组合
            
        3) `弱引用` 修饰 pthread_create  
        
            => 据 `编译时 是否有 -lphread 选项`, 
               
               在 `运行时 判断` 是否 `链接到 pthread 库 (=> 是否 能找到 pthread_create 的定义)`
               
            => 从而决定 执行 多线程版本 还是 单线程版本
        
            #include <stdio.h>
            #include <pthread.h>
            
            int pthread_creat(
                pthread_t*,
                const pthread_attr_t*,
                void* (*)(void*),
                void*) __attribute( (weakref) );
            
            int main()
            {
                if(pthread_creat) 
                {
                    printf("multi-thread version\n");
                }
                else // 单线程编译 => 找不到 pthread_creat 定义 => 默认为 0
                {
                    printf("single-thread version\n");
                }
            }
c 程序 映射到 目标文件.jpg .o 结构.jpg

相关文章

  • 《深入理解计算机系统》

    第七章 链接 源文件-->编译器-->目标文件:可重定位目标文件(a),可执行目标文件(b),共享目标文件(c) ...

  • 目标文件

    目标文件有什么 目标文件的格式 PC平台流行的可执行文件格式主要为 Windows下:PE Linux下:ELF ...

  • 开发流程

    .m文件(源文件) 编译成 .o文件(目标文件) 链接 a.out文件(目标文件)

  • 第二部分-目标文件

    二、目标文件 1、目标文件:编译器编译源代码后生成的文件叫目标文件; 2、目标文件的格式: 1)现在PC平台流行的...

  • 相对路径设置

    $(inherited)文件夹名字/目标文件 路径为,项目文件夹地址/文件夹名字/目标文件

  • 04--目标文件的格式

    [TOC] 一、ELF文件格式 1、什么是目标文件 编译器编译源代码后生成的文件叫做目标文件 目标文件从结构上讲,...

  • 关于make file(1)

    1、用于生产目标文件的自动化执行过程;基本过程是确定各源文件于目标文件间的依赖关系: 目标文件:依赖的源文件...

  • Makefile使用小结

    基本格式 目标文件: 依赖文件[tab] 命令 1[tab] 命令 2…… 符号 $@ 目标文件 $^ 所有依赖文...

  • 目标-文件管理

    其实一直都觉得自己在处理文件上有点乱,每次做文件都会开很多个,最后都不会再去看,也不知道哪一个是最终版,然后时间久...

  • 引用import和include

    (一)import import可以在该文件中使用目标文件定义的template (二)include 将目标文件...

网友评论

      本文标题:3章 目标文件

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