编译链接基本素养笔记(一)ELF文件结构

源代码:

int printf(const char * format,...);
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;
}

头文件结构:

[email protected]:~/c/static$ objdump -h SimpleSection.o 

SimpleSection.o:     file format elf32-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         00000053  00000000  00000000  00000034  2**0
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  1 .data         00000008  00000000  00000000  00000088  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000004  00000000  00000000  00000090  2**2
                  ALLOC
  3 .rodata       00000004  00000000  00000000  00000090  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .comment      0000002a  00000000  00000000  00000094  2**0
                  CONTENTS, READONLY
  5 .note.GNU-stack 00000000  00000000  00000000  000000be  2**0
                  CONTENTS, READONLY
  6 .eh_frame     00000058  00000000  00000000  000000c0  2**2
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA

生成的汇编语言和代码段内容:

karl@ubuntu:~/c/static$ objdump -s -d SimpleSection.o

SimpleSection.o:     file format elf32-i386

Contents of section .text:
 0000 5589e583 ec188b45 08894424 04c70424  U......E..D$...$
 0010 00000000 e8fcffff ffc9c355 89e583e4  ...........U....
 0020 f083ec20 c7442418 01000000 8b150400  ... .D$.........
 0030 0000a100 00000001 c28b4424 1801c28b  ..........D$....
 0040 44241c01 d0890424 e8fcffff ff8b4424  D$.....$......D$
 0050 18c9c3                               ...
Contents of section .data:
 0000 54000000 55000000                    T...U...
Contents of section .rodata:
 0000 25640a00                             %d..
Contents of section .comment:
 0000 00474343 3a202855 62756e74 7520342e  .GCC: (Ubuntu 4.
 0010 382e342d 32756275 6e747531 7e31342e  8.4-2ubuntu1~14.
 0020 30342920 342e382e 3400               04) 4.8.4.
Contents of section .eh_frame:
 0000 14000000 00000000 017a5200 017c0801  .........zR..|..
 0010 1b0c0404 88010000 1c000000 1c000000  ................
 0020 00000000 1b000000 00410e08 8502420d  .........A....B.
 0030 0557c50c 04040000 1c000000 3c000000  .W..........<...
 0040 1b000000 38000000 00410e08 8502420d  ....8....A....B.
 0050 0574c50c 04040000                    .t......        

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 18 01 00 00    movl   $0x1,0x18(%esp)
  2b:   00
  2c:   8b 15 04 00 00 00       mov    0x4,%edx
  32:   a1 00 00 00 00          mov    0x0,%eax
  37:   01 c2                   add    %eax,%edx
  39:   8b 44 24 18             mov    0x18(%esp),%eax
  3d:   01 c2                   add    %eax,%edx
  3f:   8b 44 24 1c             mov    0x1c(%esp),%eax
  43:   01 d0                   add    %edx,%eax
  45:   89 04 24                mov    %eax,(%esp)
  48:   e8 fc ff ff ff          call   49 <main+0x2e>
  4d:   8b 44 24 18             mov    0x18(%esp),%eax
  51:   c9                      leave
  52:   c3                      ret

段说明:

  • 代码段第一列为偏移量,中间代码段中的内容(16进制),往下为代码段的汇编指令。可以看到代码段中的内容和会汇编指令的序列一样。代码段的长度也和头文件结构中的长度(0x53)相同。
  • 数据段(data)保存的是初始化了的全局变量和局部静态变量。程序中有两个变量,global_init_varstatic_var,共八个字节。和头文件结构中的data段大小一样。
  • rodata段存放的是只读数据,const修饰的变量和字符串常量(%d\n)。
  • bss段存放的是未初始化的全局变量和局部静态变量。

观察文件头:

[email protected]:~/c/static$ readelf -h SimpleSection.o
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  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:          376 (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:         13
  Section header string table index: 10

32位文件头数据结构:/usr/include/elf.h

#define EI_NIDENT (16)

typedef struct
{

  unsigned char e_ident[EI_NIDENT];
  Elf32_Half    e_type;         /* Object file type */
  Elf32_Half    e_machine;      /* Architecture */
  Elf32_Word    e_version;      /* Object file version */
  Elf32_Addr    e_entry;        /* Entry point virtual address */
  Elf32_Off e_phoff;        /* Program header table file offset */
  Elf32_Off e_shoff;        /* Section header table file offset */
  Elf32_Word    e_flags;        /* Processor-specific flags */
  Elf32_Half    e_ehsize;       /* ELF header size in bytes */
  Elf32_Half    e_phentsize;        /* Program header table entry size */
  Elf32_Half    e_phnum;        /* Program header table entry count */
  Elf32_Half    e_shentsize;        /* Section header table entry size */
  Elf32_Half    e_shnum;        /* Section header table entry count */
  Elf32_Half    e_shstrndx;     /* Section header string table index */
} Elf32_Ehdr;
  • Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 ,这个16字节魔术数对应了ELF文件的平台属性,字长,字节序,版本等。操作系统在加载的时候会确认魔术数是否正确,如果不正确,会拒绝加载。
  • e_type ELF文件类型。
/* Legal values for e_type (object file type).  */

#define ET_NONE     0       /* No file type */
#define ET_REL      1       /* Relocatable file */
#define ET_EXEC     2       /* Executable file */
#define ET_DYN      3       /* Shared object file */
#define ET_CORE     4       /* Core file */

段表

[email protected]:~/c/static$   readelf -S SimpleSection.o
There are 13 section headers, starting at offset 0x178://注意在文件头中,Start of section headers的值为十进制。

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 000053 00  AX  0   0  1
  [ 2] .rel.text         REL             00000000 0004e8 000028 08     11   1  4
  [ 3] .data             PROGBITS        00000000 000088 000008 00  WA  0   0  4
  [ 4] .bss              NOBITS          00000000 000090 000004 00  WA  0   0  4
  [ 5] .rodata           PROGBITS        00000000 000090 000004 00   A  0   0  1
  [ 6] .comment          PROGBITS        00000000 000094 00002a 01  MS  0   0  1
  [ 7] .note.GNU-stack   PROGBITS        00000000 0000be 000000 00      0   0  1
  [ 8] .eh_frame         PROGBITS        00000000 0000c0 000058 00   A  0   0  4
  [ 9] .rel.eh_frame     REL             00000000 000510 000010 08     11   8  4
  [10] .shstrtab         STRTAB          00000000 000118 00005f 00      0   0  1
  [11] .symtab           SYMTAB          00000000 000380 000100 10     12  11  4
  [12] .strtab           STRTAB          00000000 000480 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), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)
  • 段表其实是一个段结构体的数组。
  • 段表描述符结构:
typedef struct
{
  Elf32_Word    sh_name;        /* Section name (string tbl index) */
  Elf32_Word    sh_type;        /* Section type */
  Elf32_Word    sh_flags;       /* Section flags */
  Elf32_Addr    sh_addr;        /* Section virtual addr at execution */
  Elf32_Off sh_offset;      /* Section file offset */
  Elf32_Word    sh_size;        /* Section size in bytes */
  Elf32_Word    sh_link;        /* Link to another section */
  Elf32_Word    sh_info;        /* Additional section information */
  Elf32_Word    sh_addralign;       /* Section alignment */
  Elf32_Word    sh_entsize;     /* Entry size if section holds table */
} Elf32_Shdr;
  • 对于编译器和链接器来说,主要决定段的属性的是段的类型(sh_type)和段的标志位(sh_flags).
  • 段类型:
/* Legal values for sh_type (section type).  */

#define SHT_NULL      0     /* Section header table entry unused */
#define SHT_PROGBITS      1     /* Program data */
#define SHT_SYMTAB    2     /* Symbol table */
#define SHT_STRTAB    3     /* String table */
#define SHT_RELA      4     /* Relocation entries with addends */
#define SHT_HASH      5     /* Symbol hash table */
#define SHT_DYNAMIC   6     /* Dynamic linking information */
#define SHT_NOTE      7     /* Notes */
#define SHT_NOBITS    8     /* Program space with no data (bss) */
#define SHT_REL       9     /* Relocation entries, no addends */
#define SHT_SHLIB     10        /* Reserved */
#define SHT_DYNSYM    11        /* Dynamic linker symbol table */
#define SHT_INIT_ARRAY    14        /* Array of constructors */
#define SHT_FINI_ARRAY    15        /* Array of destructors */
#define SHT_PREINIT_ARRAY 16        /* Array of pre-constructors */
#define SHT_GROUP     17        /* Section group */
#define SHT_SYMTAB_SHNDX  18        /* Extended section indeces */
#define SHT_NUM       19        /* Number of defined types.  */
  • 段标志:段的标志位(sh_flag) 段的标志位表示该段在进程虚拟地址空间中的属性,比如是否可写,是否可执行等。
/* Legal values for sh_flags (section flags).  */

#define SHF_WRITE        (1 << 0)   /* Writable */
#define SHF_ALLOC        (1 << 1)   /* Occupies memory during execution */
#define SHF_EXECINSTR        (1 << 2)   /* Executable */
#define SHF_MERGE        (1 << 4)   /* Might be merged */
#define SHF_STRINGS      (1 << 5)   /* Contains nul-terminated strings */
  • 分析文件头,能够得到短表和短表字符串表的位置,从而了解整个ELF文件。

符号表

  • 查看目标文件的符号表:ELF文件中的符号表往往是文件中的一个段,段名一般叫”.symtab”。
karl@ubuntu:~/c/static$ 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.1378
00000000 b static_var2.1379
  • 符号表的结构很简单,它是一个Elf32_Sym结构(32位ELF文件)的数组,每个Elf32_Sym结构对应一个符号。
/* Symbol table entry.  */

typedef struct
{
  Elf32_Word    st_name;        /* Symbol name (string tbl index) */
  Elf32_Addr    st_value;       /* Symbol value */
  Elf32_Word    st_size;        /* Symbol size */
  unsigned char st_info;        /* Symbol type and binding */
  unsigned char st_other;       /* Symbol visibility */
  Elf32_Section st_shndx;       /* Section index */
} Elf32_Sym;
  • 符号类型和绑定信息(st_info) 该成员低4位表示符号的类型(Symbol Type),高28位表示符号绑定信息(Symbol Binding)
  • 绑定信息:
/* Legal values for ST_BIND subfield of st_info (symbol binding).  */

#define STB_LOCAL   0       /* Local symbol 局部符号,对于目标文件的外部不可见*/
#define STB_GLOBAL  1       /* Global symbol 全局符号,外部可见*/
#define STB_WEAK    2       /* Weak symbol 弱引用*/
#define STB_NUM     3       /* Number of defined types.  */
#define STB_LOOS    10      /* Start of OS-specific */
#define STB_GNU_UNIQUE  10      /* Unique symbol.  */
#define STB_HIOS    12      /* End of OS-specific */
#define STB_LOPROC  13      /* Start of processor-specific */
#define STB_HIPROC  15      /* End of processor-specific */
  • 符号的类型
/* Legal values for ST_TYPE subfield of st_info (symbol type).  */

#define STT_NOTYPE  0       /* Symbol type is unspecified 未知类型符号*/
#define STT_OBJECT  1       /* Symbol is a data object 该符号是个数据对象,比如变量、数组等*/
#define STT_FUNC    2       /* Symbol is a code object 该符号是个函数或其他可执行代码*/
#define STT_SECTION 3       /* Symbol associated with a section 该符号表示一个段,这种符号必须是STB_LOCAL的*/
#define STT_FILE    4
/* Symbol‘s name is file name 该符号表示文件名,一般都是该目标文件所对应的源文件名,它一定是STB_LOCAL类型的,
并且它的st_shndx一定是SHN_ABS*/
#define STT_COMMON  5       /* Symbol is a common data object */
#define STT_TLS     6       /* Symbol is thread-local data object*/
#define STT_NUM     7       /* Number of defined types.  */
#define STT_LOOS    10      /* Start of OS-specific */
#define STT_GNU_IFUNC   10      /* Symbol is indirect code object */
#define STT_HIOS    12      /* End of OS-specific */
#define STT_LOPROC  13      /* Start of processor-specific */
#define STT_HIPROC  15      /* End of processor-specific */
  • 符号所在段 st_shndx )如果符号定义在本目标文件中,那么这个成员表示符号所在的段在段表中的下标,但是如果符号不是定义在本目标文件中,或者对于有些特殊符号,sh_shndx的值有些特殊。
#define SHN_UNDEF   0
/* Undefined section
符号块未定义,在本目标文件被引用到,但是定义在其他目标文件中*/
#define SHN_ABS     0xfff1
/* Associated symbol is absolute
表示该符号包含了一个绝对的值。比如表示文件名的符号就属于这种类型的*/
#define SHN_COMMON  0xfff2
/* Associated symbol is common
表示该符号是一个“COMMON块”类型的符号,一般来说,未初始化的全局符号定义就是这种类型的
*/

查看SimpleSection.o中的符号表详细信息

[email protected]:~/c/static$ readelf -s SimpleSection.o

Symbol table ‘.symtab‘ contains 16 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.1378
     7: 00000000     4 OBJECT  LOCAL  DEFAULT    4 static_var2.1379
     8: 00000000     0 SECTION LOCAL  DEFAULT    7
     9: 00000000     0 SECTION LOCAL  DEFAULT    8
    10: 00000000     0 SECTION LOCAL  DEFAULT    6
    11: 00000000     4 OBJECT  GLOBAL DEFAULT    3 global_init_var
    12: 00000004     4 OBJECT  GLOBAL DEFAULT  COM global_uninit_var
    13: 00000000    27 FUNC    GLOBAL DEFAULT    1 func1
    14: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND printf
    15: 0000001b    56 FUNC    GLOBAL DEFAULT    1 main
时间: 2024-11-06 11:41:13

编译链接基本素养笔记(一)ELF文件结构的相关文章

C++应用程序在Windows下的编译、链接(二)COFF/PE文件结构

2.1概述 在windows操作系统下,可执行文件的存储格式是PE格式:在Linux操作系统下,可执行文件的存储格式的WLF格式.它们都是COFF格式文件的变种,都是从COFF格式的文件演化而来的. 在windows平台下,目标文件(.obj),静态库文件(.lib)使用COFF格式存储:而可执行文件(.exe),动态链接库文件(.dll)使用PE格式存储.静态库文件其实就是一堆目标文件的集合. 在“WinNT.h”头文件中定义了COFF格式文件,以及PE格式文件的数据结构.这些定义是一系列的结

Linux ELF 文件结构

Linux下ELF文件类型分为以下几种: 1.可重定位文件,例如SimpleSection.o: 2.可执行文件,例如/bin/bash: 3.共享目标文件,例如/lib/libc.so. 再接下来的文章中,我们会使用objdump,readelf,hexdump,nm等来分析一个Linux中可重定位文件SimpleSection.o. 首先附上SimpleSection.c源代码: int printf( const char* format, ... ); int global_init_v

程序的编译链接过程

还是从HelloWorld开始说吧... #include <stdio.h> int main(int argc, char* argv[]) { printf("Hello World!\n"); return 0; } 从源文件Hello.cpp编译链接成Hello.exe,需要经历如下步骤: 可使用以下命令,直接从源文件生成可执行文件 linux: gcc -lstdc++ Hello.cpp -o Hello.out // 要带上lstdc参数,否则会报undef

android笔记3——项目文件结构说明

以下是andoid项目文件结构图,常用操作部分已经用矩形框标注好了.. 接下来,我们队每一个文件夹或者文件说明一下: 1.核心文件AndroidManifest.xml,App清单文件,就像我们去饭店享食美味,饭店给我们提供的菜单一样.. 2.src,顾名思义,当然就是放置源代码的文件夹, MainActivity 表示的为Activity程序,Activity代表的为屏幕. 3.gen文件夹,这个其实也是代码文件夹,只不过这里面包含了R.java(只要往资源文件夹相应的位置中放如资源文件,会自

gcc编译链接原理及使用

gcc 的使用方法: gcc    [选项]    文件名 gcc常用选项: -v:查看gcc 编译器的版本,显示gcc执行时的详细过程 -o    < file >             Place  the output  into   < file > 指定输出文件名为file,这个名称不能跟源文件名同名 -E Preprocess only; do not compile, assemble or link 只预处理,不会编译.汇编.链接 -S   Compile onl

GCC编译链接过程

编译链接过程 代码 #cat main.c #include <stdio.h> int add(int x, int y); int sub(int x, int y); int mul(int x, int y); int div(int x, int y); int main(void) { printf("add:%d\n", add(1,2)); printf("sub:%d\n", sub(10,100)); printf("mul

GCC编译器编译链接

在gcc编译器环境下,常见的文件扩展名的含义如下: .c:C源程序,经过预编译后的源程序也为.c文件,它可以通过-E参数输出. .h:头文件 .s:经过编译得到的汇编程序代码,它可以通过-S参数输出. .o:目标文件 .a:函数库 Gcc编译器常见语法: -c:只进行编译,不进行链接,输出的是与源文件同名的.o文件. -o:指定生成的文件的名称.链接生成可执行文件,这个参数后可以带可执行文件的名字,如果没有指定可执行文件的名字,则会默认为a.out. -S:输出汇编代码文件,输出一个与源文件同名

编译链接总结

1. -L增加一个搜索路径,不一定要跟-l放在一起:不区别静态链接和动态链接. 2. 用-lxx与 libxx.a的区别是:前者会搜索多个路径. 3. 使用-lxx链接动态库时,动态库所在的目录不一定在搜索路径,可以加到/etc/ld.so.conf中,或者/etc/ld.so.conf.d/libxx.conf,并重启ldconfig.(ldd) 4. 动态链接库可以访问可执行程序内定义的函数,动态链接库可以相互访问函数,使用dlopen指定RTLD_GLOBAL. 5. 使用libxx.a与

【转】关于编译链接——gcc/g++

添加运行时共享库目录 运行使用共享库的程序需要加载共享库(不同于G++ 编译时指定的链接库),添加共享库的步骤: 修改文件 /etc/ld.so.conf 添加共享库目录 运行 ldconfig 同步更新一下 如: $ gedit /etc/ld.so.conf #添加 /root/dreamlove/lib $ ldconfig 添加include,lib的搜寻路径 对所有用户有效修改/etc/profile 对个人有效则修改~/.bashrc #在PATH中找到可执行文件程序的路径. exp