美文网首页
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

    相关文章

      网友评论

          本文标题:3章 目标文件

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