ELF 文件格式 -- 程序员的自我修养学习笔记

前言

参考书籍第 3 章

笔记

 ELF 中的段介绍:
        .text:      代码段,存储二进制的机器指令,这些指令可以被机器直接执行
        .rodata:    只读数据段,存储程序中使用的复杂常量,例如字符串等。
        .data:      数据段,存储程序中已经被明确初始化的全局数据,包括C语言中的全局变量和静态变量,如果这些全局
                    数据被初始化为0,则不存储在数据段中,而是存储在块数据段中。C语言局部数据保存在栈中,不出现
                    在数据段中。
        .bss:      块数据段,存储未被明确初始化的全局数据,在目标文件中,这个段并不占有实际空间,而仅仅是一个占
                    位符,以告知指定位置上应当预留全局数据的空间,块缓存段存在的原因是为了提高磁盘空间的利用率。
        .rodata1:   Read only Data, 这种段里存放的是只读数据,比如字符串常量,全局 const 变量。跟 .rodata 一样 
        .comment:   存放的是编译器版本信息,比如字符串: "GCC(GNU)4.2.0"
        .debug:     调试信息
        .dynamic:   动态链接信息 
        .hash:      符号哈希表 
        .line:      调试时的行号表,即源代码行号与编译后指令的对应表 
        .note:      额外的编译器信息,比如程序的公司名,发布版本号等 
        .strtab:    String Table, 字符串表,用于存储 ELF 文件中用到的各种字符串 
        .symtab:    Symbol Table.符号表 
        .shstrtab:  Section Header String Table. 段表字符串表, 用来保存段表中用到的字符串,最常见的就是段名。
        .rel.段名:  重定位段,如 .rel.text 根据名字他就是 .text 的重定位段


        .plt 
        .got:      动态链接的跳转表和全局入口表 

        .init      
        .fini:      程序初始化与终结代码段 

    自定义段:
        GCC 提供了一个扩展机制,使得程序员可以指定变量所处的段:
            __attribute__((section(*FOO))) int global = 42;

            __attribute__((section("BAR"))) void  foo()
            {
            }
        我们在全局变量或函数之前加上 __attribute__((section("name"))) 属性就可以把相应的变量
        或函数放到以 "name" 作为段名的段中 

 测试程序如下:
        // simplesection.c
        int printf(const char *formt,...);
        int global_init_var = 84;
        int global_uninit_var;
        void func1(int i)
        {
                printf("%d\n",i);
        }
        int main(void)
        {
                static int static_var = 85;
                static int static_var2;
                int a = 1;
                int b;
                func1(static_var + static_var2 +a + b);
                return a;
        }

一. 使用 gcc 编译这个文件,生成中间目标文件, -c 只编译不链接 
        gcc -c simplesection.c
    会生成对应的 simplesection.o 文件。

    使用 objdump -h simplesection.o 查看编译出来的文件结构和内容  
            -x, --all-headers        Display the contents of all headers
            -h, --[section-]headers  Display the contents of the section headers

        $ objdump -h simplesection.o

        simplesection.o:     file format elf32-i386

        Sections:
        Idx Name          Size      VMA       LMA       File off  Algn
          0 .text         00000050  00000000  00000000  00000034  2**2          // 代码段 
                          CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE              // CONTENTS: 表示该段在文件中存在 
          1 .data         00000008  00000000  00000000  00000084  2**2          // 数据段:初始化了的全局静态变量和局部静态变量
                          CONTENTS, ALLOC, LOAD, DATA
          2 .bss          00000004  00000000  00000000  0000008c  2**2          // BSS(Block Started by Symbol)未初始化全局变量,局部静态变量段
                          ALLOC                                                     // ALLOC: 运行时分配的,文件中并不存存储的
          3 .rodata       00000004  00000000  00000000  0000008c  2**0          // 只读数据段 
                          CONTENTS, ALLOC, LOAD, READONLY, DATA
          4 .comment      00000024  00000000  00000000  00000090  2**0          // 注释信息段 
                          CONTENTS, READONLY
          5 .note.GNU-stack 00000000  00000000  00000000  000000b4  2**0        // 堆栈提示段 
                          CONTENTS, READONLY

    它们在 ELF 中布局如下:

                    ------------------------------ 0x0000 0440(文件大小)
                    |         Other data         |
                    |                            |
            -------------------------------------- 0x0000 00bc
                    |          .comment          |
            0x24    |                            |
                    |                            |
            -------------------------------------- 0x0000 0090 
                    |          .rodata           |
            0x04    |                            |
                    |                            |
            -------------------------------------- 0x0000 008c 
                    |           .data            |
            0x08    |                            |
                    |                            |
            -------------------------------------- 0x0000 0084
                    |           .text            |
            0x50    |                            |
                    |                            |
            -------------------------------------- 0x0000 0034
                    |         ELF Header         |
            0x34    |                            |
                    |                            |
            -------------------------------------- 0x0000 0000

    Linux 中有一个专门的命令 size 可以用来查看 ELF 文件的代码段、数据段和 BSS 段的长度
    其中 dec 表示 3 个段长度的和的 十进制,hex 表示长度和的十六进制
            $ size simplesection.o
               text    data     bss     dec     hex filename
                 84       8       4      96      60 simplesection.o

二、对各个段内容的解析:
    
        objdump 的 -s 参数可以将所有段的内容以十六进制的方式打印出来,-d 参数可以将所有包含指令的段反汇编
            $ objdump -s -d simplesection.o

            simplesection.o:     file format elf32-i386

            // 代码段
            Contents of section .text:
          // 偏移量            十六进制内容                  .text 段的 AScII 码形式 
             0000    5589e583 ec188b45 08894424 04c70424     U......E..D$...$
             0010    00000000 e8fcffff ffc9c355 89e583e4     ...........U....
             0020    f083ec20 c744241c 01000000 8b150400     ... .D$.........
             0030    0000a100 0000008d 04020344 241c0344     ...........D$..D
             0040    24188904 24e8fcff ffff8b44 241cc9c3     $...$......D$...
            // 数据段 
            Contents of section .data:
             0000 54000000 55000000                    T...U...      // 初始化了的全局静态变量和局部静态变量 
                                                                     // 这里对应程序的 global_uninit_var 和 static_var
                                                                     // 每变量 4 字节,正好 8 字节 
                                                                     // 0x54 = 84           0x55 = 85

            // 只读数据段 
            Contents of section .rodata:
             0000 25640a00                             %d..           // 调用 printf 时,用到的那个字符串常量 "%d\n"
                                                                      // 这个段的 4 个字节正好是这个字符串常量的 ASCII 
                                                                      // 字节序,最后以 \0 结尾

            // 注释信息段
            Contents of section .comment:
             0000 00474343 3a202855 62756e74 7520342e  .GCC: (Ubuntu 4.
             0010 342e312d 34756275 6e747539 2920342e  4.1-4ubuntu9) 4.
             0020 342e3100                             4.1.            

            // 代码段反汇编内容
            Disassembly of section .text:

            00000000 <func1>:
               0:   55                      push   %ebp
               1:   89 e5                   mov    %esp,%ebp
               3:   83 ec 18                sub    $0x18,%esp
               6:   8b 45 08                mov    0x8(%ebp),%eax
               9:   89 44 24 04             mov    %eax,0x4(%esp)
               d:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
              14:   e8 fc ff ff ff          call   15 <func1+0x15>
              19:   c9                      leave  
              1a:   c3                      ret    

            0000001b <main>:
              1b:   55                      push   %ebp
              1c:   89 e5                   mov    %esp,%ebp
              1e:   83 e4 f0                and    $0xfffffff0,%esp
              21:   83 ec 20                sub    $0x20,%esp
              24:   c7 44 24 1c 01 00 00    movl   $0x1,0x1c(%esp)
              2b:   00 
              2c:   8b 15 04 00 00 00       mov    0x4,%edx
              32:   a1 00 00 00 00          mov    0x0,%eax
              37:   8d 04 02                lea    (%edx,%eax,1),%eax
              3a:   03 44 24 1c             add    0x1c(%esp),%eax
              3e:   03 44 24 18             add    0x18(%esp),%eax
              42:   89 04 24                mov    %eax,(%esp)
              45:   e8 fc ff ff ff          call   46 <main+0x2b>
              4a:   8b 44 24 1c             mov    0x1c(%esp),%eax
              4e:   c9                      leave  
              4f:   c3                      ret    

            // 函数栈中的数据位图示意图 

                        ------------------------- -----
                        |         参数          |   |                    
                        -------------------------   |                    
                        |       返回地址        |   |                    
               ebp----->-------------------------   |                    
                        |        Old EBP        |   |                    
                        ------------------------- 活动记录                   
                        |     保存的寄存器      |   |                    
                        -------------------------   |                    
                        |       局部变量        |   |                    
                        -------------------------   |                    
                        |       其他数据        |   |                    
               esp----->------------------------- ------


3.4 ELF 文件结构描述 
            
    ELF 文件结构:
            ------------------------------------
            |            ELF Header            |
            ------------------------------------
            |              .text               |
            ------------------------------------
            |              .data               |
            ------------------------------------
            |               .bss               |
            ------------------------------------
            |               …                  |
            ------------------------------------
            |          other sections          |
            ------------------------------------
            |       Section Header table       |
            ------------------------------------
            |          String Tables           |
            ------------------------------------
            |          Symbol Tables           |
            ------------------------------------

    还是以上面的程序为例子:
        1. 读取文件头
            $ readelf -h simplesection.o
            ELF Header:
              Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00              // ELF 魔数
              Class:                             ELF32
              Data:                              2's complement, little endian
              Version:                           1 (current)
              OS/ABI:                            UNIX - System V
              ABI Version:                       0
              Type:                              REL (Relocatable file)
              Machine:                           Intel 80386
              Version:                           0x1
              Entry point address:               0x0
              Start of program headers:          0 (bytes into file)
              Start of section headers:          264 (bytes into file)
              Flags:                             0x0
              Size of this header:               52 (bytes)
              Size of program headers:           0 (bytes)
              Number of program headers:         0
              Size of section headers:           40 (bytes)
              Number of section headers:         11
              Section header string table index: 8

            文件头定义在 /usr/include/elf.h 
                    typedef struct
                    {
                      unsigned char e_ident[EI_NIDENT];  // Magic number and other info
                                                            // 16 个字节:
                                                            //      7f 45 4c 46      01                 01              01          00 00 00 00 00 00 00 00 00 
                                                            //      ELF 文件魔数   ELF 文件类型       字节序         版本号         这 9 个字节未定义,可用于扩展
                                                            //                        0x01: 32 位的     0x01: 小端      一般为 1
                                                            //                        0x02: 64 位的     0x02: 大端      只出到 1.2
                      Elf32_Half    e_type;              // ELF 文件类型
                                                            // ET_REL: 1 可重定位文件,一般为 .o 文件
                                                            // ET_EXEC: 2 可执行文件
                                                            // ET_DYN: 3 共享目标文件,一般为 .so 文件 
                      Elf32_Half    e_machine;           // ELF 文件的 CPU 平台属性,相关常量以 EM_ 开头
                                                            // EM_M32: 1 AT&T WE 32100
                                                            // EM_SPARC: 2 SPARC
                                                            // EM_386: 3 Intel x86
                                                            // EM68K: 4 Motorola 68000
                                                            // EM88K: 5 Motorola 88000
                                                            // EM_860: 6 Intel 80860
                      Elf32_Word    e_version;           // ELF 版本号,一般为常数 1
                      Elf32_Addr    e_entry;             // 入口地址,规定 ELF 程序的入口虚拟地址,操作系统在加载完该程序
                                                            // 后从这个地址开始执行进程的命令,可重定义文件一般没有入口地
                                                            // 址,则这个值为 0
                      Elf32_Off e_phoff;                 // Program header table file offset
                      Elf32_Off e_shoff;                 // 段表在文件中的偏移 
                      Elf32_Word    e_flags;             // ELF 标志位,用来标识一些 ELF 文件平台相关属性。相关常量的格式,
                                                            // 一般为  EF_machine_flag, machine 为平台,flag 为标志 
                      Elf32_Half    e_ehsize;            // ELF 文件头本身的大小 
                      Elf32_Half    e_phentsize;         // Program header table entry size
                      Elf32_Half    e_phnum;             // Program header table entry count
                      Elf32_Half    e_shentsize;         // 段表描述符的大小,一般等于 sizeof(Elf32_Shdr)
                      Elf32_Half    e_shnum;             // 段表描述符数量,这个值等于 ELF 文件中拥有的段的数量 
                      Elf32_Half    e_shstrndx;          // 段表字符串表所在的段在段表中的下标,段表字符串表即 .shstrtab
                                                                // 也是 ELF 文件中的一个普通段,这个值就是表示在在段表中
                                                                // 下标,例如 Section header string table index: 8
                                                                //  则表示在 .shstrtab 段表 8 中 [ 8] .shstrtab  
                    } Elf32_Ehdr;         

        2. 段表
                段表是 ELF 文件中除了文件头以外最重要的结构,它描述了 ELF 的各个段的信息,比如每个段的段名、段的长度、
            在文件中的偏移、读写权限及段的其它属性。ELF 文件的段的结构就是由段表决定的,编译器、链接器和装载器都是依
            靠段表来定位和访问各个段的属性的。段表在 ELF 文件中的位置由 ELF 文件头的 e_shoff 成员决定的。
                前面我们使用了 objdump -h 来查看 ELF 文件中包含的段,实际上这个命令只是把 ELF 关键的段显示了出来,而
            省略了其他辅助性的段,比如:符号表、字符串表、段名字符串表、重定位表等。我们可以使用 readelf 工具来查看
            ELF 文件的段,它显示出来的结果才是真正的段表结构:
                    $ readelf -S simplesection.o
                    There are 11 section headers, starting at offset 0x108:

                    Section Headers:
                      [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
                      [ 0]                   NULL            00000000 000000 000000 00      0   0  0
                      [ 1] .text             PROGBITS        00000000 000034 000050 00  AX  0   0  4
                      [ 2] .rel.text         REL             00000000 000418 000028 08      9   1  4
                      [ 3] .data             PROGBITS        00000000 000084 000008 00  WA  0   0  4
                      [ 4] .bss              NOBITS          00000000 00008c 000004 00  WA  0   0  4
                      [ 5] .rodata           PROGBITS        00000000 00008c 000004 00   A  0   0  1
                      [ 6] .comment          PROGBITS        00000000 000090 000024 01  MS  0   0  1
                      [ 7] .note.GNU-stack   PROGBITS        00000000 0000b4 000000 00      0   0  1
                      [ 8] .shstrtab         STRTAB          00000000 0000b4 000051 00      0   0  1
                      [ 9] .symtab           SYMTAB          00000000 0002c0 0000f0 10     10  10  4
                      [10] .strtab           STRTAB          00000000 0003b0 000066 00      0   0  1
                    Key to Flags:
                      W (write), A (alloc), X (execute), M (merge), S (strings)
                      I (info), L (link order), G (group), x (unknown)
                      O (extra OS processing required) o (OS specific), p (processor specific)

                段表在 elf.h 中是以 Elf32_Shdr 数组形式保存的,对应的 Elf32_Shdr 结构体为:
                        typedef struct
                        {
                          Elf32_Word    sh_name;      // 段名是个字符串,它位于一个叫做 ".shstrtab" 的字符串表。
                                                            // sh_name 是段名字符串在 .shstrtab 中的偏移      
                          Elf32_Word    sh_type;      // 段的类型
                          Elf32_Word    sh_flags;     // 段的标志位
                          Elf32_Addr    sh_addr;      // 如果该段可以被加载,则 sh_addr 为该段被加载后在进程地址空间的
                                                            // 虚拟地址,否则 sh_addr 为 0 
                          Elf32_Off sh_offset;        // 如果该存在于文件中,则表示该段在文件中的偏移,否则无意义,比如
                                                            // sh_offset 对于 BSS 段来说就没有意义 
                          Elf32_Word    sh_size;      // 段的长度 
                          Elf32_Word    sh_link;      // Link to another section
                          Elf32_Word    sh_info;      // Additional section information
                          Elf32_Word    sh_addralign; // 段地址对齐,有些段对段地址对齐有要求,比如我们假设有个段刚开始
                                                            // 的位置包含了一个 double 变量。因为 Intel x86 系统要求浮点
                                                            // 数的存储地址必须是本身的整数倍,即 double 必须 8 字节对齐
                                                            // 这里写的是对齐的指数,即 2^sh_addralign ,0或1表示该没有
                                                            // 对齐要求 
                          Elf32_Word    sh_entsize;   // 项的长度,有些段包含了一些固定大小的项,比如符号表,它包含的每
                                                            // 个符号所占的大小都是一样的。对于这种段,sh_entsize 表示 
                                                            // 每个项的大小,如果为 0,则表示该段不包含固定大小的项 
                        } Elf32_Shdr;

            simplesection.o 的 Section Table 及所有段的位置和长度:
                        ---------------------------- 0x0000 0000
                    0x34|        ELF Header        |             ==>Start of section headers: 264 (bytes into file)
                        ---------------------------- 0x0000 0034                               |                             
                    0x50|          .text           |                                           |                 
                        ---------------------------- 0x0000 0084                               |                             
                    0x84|          .data           |                                           |                 
                        ---------------------------- 0x0000 008c                               |                             
                    0x04|           .bss           |                                           |                 
                        ---------------------------- 0x0000 008c                               |                             
                    0x04|         .rodata          |                                           |                 
                        ---------------------------- 0x0000 0090                               |                             
                    0x24|         .comment         |                                           |                 
                        ---------------------------- 0x0000 00b4                               |                             
                    0x00|      .note.GNU-stack     |                                           |                 
                        ---------------------------- 0x0000 00b4                               |                             
                    0x51|        .shstrtab         |                                           |                 
                        ---------------------------- 0x0000 0108 <------------------------------ ELF 头中指定段表位移                             
                   0x1b8|      Section table       |                段表长度 = 段表描述符的大小 * 段表描述符数量 = 40*11 = 480 = 0x1b8                                                       
                        ---------------------------- 0x0000 02c0                                                            
                    0xf0|         .symtab          |                                                            
                        ---------------------------- 0x0000 03b0                                                            
                    0x66|         .strtab          |  
                        ---------------------------- 0x0000 0416                                                          
                        ---------------------------- 0x0000 0418   这里是因为对齐的原因,与前面的段有两个字节的间隔                                                          
                    0x28|        .rel.text         |                                                            
                        ---------------------------- 0x0000 0440(文件大小)

3.5 ELF 链接的接口 ---- 符号 
            链接过程中很关键的一部分就是符号的管理,每一个目标文件都会有一个相应的符号表(Symbol Table), 这个表里面记录
        了目标文件中所用到的所有符号。每个定义的符号有一个对应的值,叫做符号值(Symbol Value),对于变量和函数来说,符号
        值就是它们的地址。除了函数和变量之外,还存在其他几种不常用到的符号。我们将符号表中的所有符号进行分类,它们有可
        能是下面这些类型中的一种:
                1. 定义在本目标文件的全局符号,可以被其他目标文件引用。比如 simplesection.o 里面的 func1、main 和 global_init_var
                2. 在本目标文件中引用的全局符号,却没有定义在本目标文件,这一般叫做外部符号(External Symbol),也就是
                    符号引用,比如 simplesection.o 里面的 printf
                3. 段名,这种符号往往由编译器产生,它的值就是该段的起始地址。比如 simplesection.o 里面的 .text .data 等。
                4. 局部符号,这类符号只在编译单元内部可见。比如 simplesection.o 里面的 static_var 和 static_var2。调试
                    器可以使用这些符号来分析程序或崩溃时的核心转储文件。这些局部符号对于链接过程没有作用,链接器往往也
                    忽略它们。
                5. 行号信息,即目标文件指令与源代码中代码行的对应关系,它也是可选的。

        可以使用 nm 查看 simplesection.o 的符号表:
                $ nm simplesection.o
                00000000 T func1
                00000000 D global_init_var
                00000004 C global_uninit_var
                0000001b T main
                         U printf
                00000004 d static_var.1255
                00000000 b static_var2.1256

    1. ELF 符号表的结构 
            ELF 文件中的符号表往往是谁的中的一个段,段名一般叫 .symtab,符号表的结构很简单,它是一个 Elf32_Sym 结构
        的数组,定义如下:
                    typedef struct
                    {
                      Elf32_Word    st_name;    // 符号名,这个成员包含了该名在字符串表中的下标 
                      Elf32_Addr    st_value;   // 符号相对应的值,这个值跟符号有关,可能是一个绝对值,也可能是一个
                                                    // 地址等,不同的符号,它所对应的值含义不同,它分以下情况:
                                                    //      1. 在目标文件中,如果是符号的定义并且该符号不是 COMMON 块 
                                                    //          类型的,即 st_shndx 不是 SHN_COMMON,则 st_value 表示
                                                    //          该符号在段中的偏移。即符号所对应的函数或变量位于由 
                                                    //          st_shndx 指定的段,偏移 st_value 的位置。这也是目标
                                                    //          文件中定义全局变量的符号的最后常见情况,比如 simplesection.o
                                                    //          中的 func1、main 和 global_init_var
                                                    //      2. 在目标文件中,如果符号是 COMMON 块 类型的,则 st_value
                                                    //          表示该符号的对齐属性,比如 simplesection.o 中的 global_uninit_var
                                                    //      3. 在可执行文件中,st_value 表示符号的虚拟地址。这个虚拟
                                                    //           对于动态链接器十分有用 
                      Elf32_Word    st_size;    // 符号大小,对于包含数据的符号,这个值是该数据类型的大小,比如一个
                                                    // double 型的符号它占用 8 个字节。如果该值为 0 ,则表示该符号
                                                    // 大小为 0 或未知
                      unsigned char st_info;    // 符号类型和绑定信息,该成员低 4 位表示符号类型,高 28 位表示符号
                                                    // 符号绑定信息 
                                                    // 符号的类型: 
                                                    //      STB_LOCAL: 0 局部符号,对于目标文件的外部不可见
                                                    //      STB_GLOBAL: 1 全局符号,外部可见
                                                    //      STB_WEAK: 2 弱引用 
                                                    // 符号的绑定信息:
                                                    //      STT_NOTYPE: 0 未知类型符号
                                                    //      STT_OBJECT: 1 该符号是个数据对象,比如变量、数组等
                                                    //      STT_FUNC: 2 该符号是个函数或其他可执行代码
                                                    //      STT_SECTION:3 该符号表示一个段,这种符号发须是 STB_LOCAL 的 
                                                    //      STT_FILE: 4 该符号表示文件名,一般都是该目李海霞文件所对应
                                                    //                      的源文件名,它一定是 STB_LOCAL 类型的,并
                                                    //                      且它的 st_shndx 一定是 SHN_ABS 
                      unsigned char st_other;   // Symbol visibility 该成员目前为 0,没用 
                      Elf32_Section st_shndx;   // 符号所在的段, 如果符号定义在本目标文件中,那么这个成员表示符号所在的
                                                    // 段在段表中的下标,但是如果符号不是定义在本目标文件中,或者对于有
                                                    // 些特殊符号,sh_shndx 的值有些特殊:
                                                    //       SHN_ABS: 0xfff1 表示该符号包含了一个绝对的值。比如表示文件
                                                    //                      名的符号就属于这种类型的
                                                    //       SHN_COMMON: 0xfff2 表示该符号是一个 COMMON 块 类型的符号,
                                                    //                      一般来说,未初始化的全局符号定义就是这种类
                                                    //                      型的,比如 simplesection.o 里面的 global_uninit_var
                                                    //       SHN_UNDEF: 0 表示该符号未定义,这个符号表示该符号在本目标
                                                    //                      文件被引用到,但是定义在其他目标文件中  
                    } Elf32_Sym;

            上面了解了 ELF 文件的符号表,下面以 simplesection.o 里面的符号为例子,分析各个符号在符号表中的状态             
                        $ readelf -s simplesection.o

                        Symbol table '.symtab' contains 15 entries:
                           Num:    Value  Size Type    Bind   Vis      Ndx Name
                             0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
                             1: 00000000     0 FILE    LOCAL  DEFAULT  ABS simplesection.c   // 表示编译单元的源文件名
                             2: 00000000     0 SECTION LOCAL  DEFAULT    1 
                             3: 00000000     0 SECTION LOCAL  DEFAULT    3 
                             4: 00000000     0 SECTION LOCAL  DEFAULT    4 
                             5: 00000000     0 SECTION LOCAL  DEFAULT    5 
                             6: 00000004     4 OBJECT  LOCAL  DEFAULT    3 static_var.1255    // 两个静态变量,只是编译
                             7: 00000000     4 OBJECT  LOCAL  DEFAULT    4 static_var2.1256   // 单元内部可见
                             8: 00000000     0 SECTION LOCAL  DEFAULT    7 
                             9: 00000000     0 SECTION LOCAL  DEFAULT    6 
                            10: 00000000     4 OBJECT  GLOBAL DEFAULT    3 global_init_var     // 在 .bss 段,下标为 3
                            11: 00000004     4 OBJECT  GLOBAL DEFAULT  COM global_uninit_var   // 定义未初始化,不在 bss 
                            12: 00000000    27 FUNC    GLOBAL DEFAULT    1 func1               // .text 段表下标为 1 
                            13: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND printf              // 引用未定义
                            14: 0000001b    53 FUNC    GLOBAL DEFAULT    1 main                // .text 段表下标为 1

猜你喜欢

转载自blog.csdn.net/wangjun7121/article/details/88243939