ELF格式学习

ELF格式学习

一、简介

ELF的英文全称是Executable and Linking Format,最初是由UNIX系统实验室开发、发布的ABI(Application Binary Interface)接口的一部分,也是Linux的主要可执行文件格式。

ELF文件种类:

  • 可执行文件(.out):Executable File,包含代码和数据,是可以直接运行的程序。其代码和数据都有固定的地址 (或相对于基地址的偏移 ),系统可根据这些地址信息把程序加载到内存执行。
  • 可重定位文件(.o文件):Relocatable File,包含基础代码和数据,但它的代码及数据都没有指定绝对地址,因此它适合于与其他目标文件链接来创建可执行文件或者共享目标文件。
  • 共享目标文件(.so):Shared Object File,也称动态库文件,包含了代码和数据,这些数据是在链接时被链接器(ld)和运行时动态链接器(ld.so.l、libc.so.l、ld-linux.so.l)使用的。

二、文件结构

在这里插入图片描述
在这里插入图片描述

  • ELF header: 描述整个文件的组织。
  • Program Header Table: 描述文件中的各种segments,用来告诉系统如何创建进程映像的。
  • sections 或者 segments:segments是从运行的角度来描述elf文件,sections是从链接的角度来描述elf文件,也就是说,在链接阶段,我们可以忽略program header table来处理此文件,在运行阶段可以忽略section header table来处理此程序(所以很多加固手段删除了section header table)。从图中我们也可以看出,segments与sections是包含的关系,一个segment包含若干个section。
  • Section Header Table:包含了描述文件节区的信息,每个节区在表中都有一项,每一项给出诸如节区名称、节区大小这类信息。用于链接的目标文件必须包含节区头部表,其他目标文件可以有,也可以没有这个表。

三、数据结构

ELF相关的数据结构在linux系统下有定义,路径在/usr/include/elf.h里面。

Elf32_Ehdr是32位 ELF header的结构体。Elf64_Ehdr是64位ELF header的结构体。

ELF头格式(ELF header)
#define EI_NIDENT (16)

typedef struct
{
  unsigned char	e_ident[EI_NIDENT];	/* Magic number and other info */
  Elf64_Half	e_type;				/* 文件类型 */
  Elf64_Half	e_machine;			/* 机器类型 */
  Elf64_Word	e_version;			/* 文件版本 */
  Elf64_Addr	e_entry;			/* 进程开始的虚拟地址 */
  Elf64_Off		e_phoff;			/* 指向程序头部表的开始 */
  Elf64_Off		e_shoff;			/* 指向节头部表的开始 */
  Elf64_Word	e_flags;			/* Processor-specific flags */
  Elf64_Half	e_ehsize;			/* ELF header size in bytes */
  Elf64_Half	e_phentsize;		/* Program header table entry size */
  Elf64_Half	e_phnum;			/* Program header table entry count */
  Elf64_Half	e_shentsize;		/* Section header table entry size */
  Elf64_Half	e_shnum;			/* Section header table entry count */
  Elf64_Half	e_shstrndx;			/* Section header string table index */
} Elf64_Ehdr;

e_type的取值:

0ET_NONENo file type
1ET_REL重定位文件
2ET_EXEC可执行文件
3ET_DYN共享库文件
4ET_CORE核心转储文件
5ET_NUM表示已经前面定义了5种文件类型
0xfe00ET_LOOSOS-specific range start
0xfeffET_HIOSOS-specific range end
0xff00ET_LOPROCProcessor-specific range start
0xffffET_HIPROCProcessor-specific range end

ELF文件格式的最前部是ELF文件头,它描述整个文件的基本属性,比如ELF文件版本,目标机器型号,程序的入口地址。

可以使用readelf -h命令来读取ELF头信息

root@ubuntu:/media/code_test# readelf -h hello.o
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          976 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           64 (bytes)
  Number of section headers:         14
  Section header string table index: 13

在这里插入图片描述

程序头格式(program header)
typedef struct
{
  Elf64_Word	p_type;			/* 当前Program header所描述的段的类型 */
  Elf64_Word	p_flags;		/* 与段相关的标志 */
  Elf64_Off		p_offset;		/* 段的第一个字节在文件中的偏移 */
  Elf64_Addr	p_vaddr;		/* 段的第一个字节在内存中的虚拟地址 */
  Elf64_Addr	p_paddr;		/* 在物理内存定位相关的系统中,此项是为物理地址保留 */
  Elf64_Xword	p_filesz;		/* 段在文件中的长度 */
  Elf64_Xword	p_memsz;		/* 段在内存中的长度 */
  Elf64_Xword	p_align;		/* 根据此项值来确定段在文件及内存中如何对齐 */
} Elf64_Phdr;

p_type取值:

0PT_NULL无意义,可忽略
1PT_LOAD可加载段。段数据由文件映射到内存,如果 p_memsz 大于 p_filesz,则额外部分填充为 0
2PT_DYNAMIC动态段。包含动态链接所需的信息
3PT_INTERP本段包含一个路径字符串,该路径存放解释器
4PT_NOTE注释段,包含一些辅助信息
5PT_SHLIB保留的段类型,暂不关心
6PT_PHDR程序头段。指明程序头表在文件和内存映像中的位置和大小
7PT_TLS线程本地存储段
8PT_NUM表示已经前面定义了8种段类型

程序头定义了可执行文件在装入内存后的段内存布局,每个程序头对应进程的一个内存段,程序头对于目标文件不是必须的,而且用gcc编译的.o文件没有程序头。

可以使用readelf -l命令来读取程序头信息

root@ubuntu:/media/code_test# readelf -lW hello.o

There are no program headers in this file.

root@ubuntu:/media/code_test# readelf -lW hello

Elf file type is DYN (Shared object file)
Entry point 0x1060
There are 13 program headers, starting at offset 64

Program Headers:
  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  PHDR           0x000040 0x0000000000000040 0x0000000000000040 0x0002d8 0x0002d8 R   0x8
  INTERP         0x000318 0x0000000000000318 0x0000000000000318 0x00001c 0x00001c R   0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x000000 0x0000000000000000 0x0000000000000000 0x0005f8 0x0005f8 R   0x1000
  LOAD           0x001000 0x0000000000001000 0x0000000000001000 0x0001f5 0x0001f5 R E 0x1000
  LOAD           0x002000 0x0000000000002000 0x0000000000002000 0x000160 0x000160 R   0x1000
  LOAD           0x002db8 0x0000000000003db8 0x0000000000003db8 0x000264 0x000278 RW  0x1000
  DYNAMIC        0x002dc8 0x0000000000003dc8 0x0000000000003dc8 0x0001f0 0x0001f0 RW  0x8
  NOTE           0x000338 0x0000000000000338 0x0000000000000338 0x000020 0x000020 R   0x8
  NOTE           0x000358 0x0000000000000358 0x0000000000000358 0x000044 0x000044 R   0x4
  GNU_PROPERTY   0x000338 0x0000000000000338 0x0000000000000338 0x000020 0x000020 R   0x8
  GNU_EH_FRAME   0x002014 0x0000000000002014 0x0000000000002014 0x000044 0x000044 R   0x4
  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10
  GNU_RELRO      0x002db8 0x0000000000003db8 0x0000000000003db8 0x000248 0x000248 R   0x1

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .interp 
   02     .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt 
   03     .init .plt .plt.got .plt.sec .text .fini 
   04     .rodata .eh_frame_hdr .eh_frame 
   05     .init_array .fini_array .dynamic .got .data .bss 
   06     .dynamic 
   07     .note.gnu.property 
   08     .note.gnu.build-id .note.ABI-tag 
   09     .note.gnu.property 
   10     .eh_frame_hdr 
   11     
   12     .init_array .fini_array .dynamic .got
节头格式(section header)
typedef struct
{
  Elf64_Word	sh_name;		/* 节区名称相对于字符串表的位置偏移 */
  Elf64_Word	sh_type;		/* 节区类型 */
  Elf64_Xword	sh_flags;		/* 节区标志位集合 */
  Elf64_Addr	sh_addr;		/* 节区装入内存的地址 */
  Elf64_Off		sh_offset;		/* 节区相对于文件的位置偏移 */
  Elf64_Xword	sh_size;		/* 节区内容大小:字节 */
  Elf64_Word	sh_link;		/* 指定链接的节索引,与具体的节有关 */
  Elf64_Word	sh_info;		/* 指定附加信息 */
  Elf64_Xword	sh_addralign;	/* 节装入内存的地址对齐要求 */
  Elf64_Xword	sh_entsize;		/* 指定某些节的固定表大小,与具体的节有关 */
} Elf64_Shdr;

sh_type取值:

0SHT_NULL无对应节区,该节其他字段取值无意义
1SHT_PROGBITS程序数据
2SHT_SYMTAB符号表
3SHT_STRTAB字符串表
4SHT_RELA带附加的重定位项
5SHT_HASH符号哈希表
6SHT_DYNAMIC动态链接信息
7SHT_NOTE提示性信息
8SHT_NOBITS无数据程序空间(bss)
9SHT_REL无附加的重定位项
10SHT_SHLIB保留
11SHT_DYNSYM动态链接符号表
14SHT_INIT_ARRAY构造函数数组
15SHT_FINI_ARRAY析构函数数组

节头定义了目标文件的代码及数据在文件中的位置

可以使用readelf -S命令来读取节头信息

root@ubuntu:/media/code_test# readelf -SW hello.o
There are 14 section headers, starting at offset 0x3d0:

Section Headers:
  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        0000000000000000 000040 000022 00  AX  0   0  1
  [ 2] .rela.text        RELA            0000000000000000 000310 000030 18   I 11   1  8
  [ 3] .data             PROGBITS        0000000000000000 000064 00000c 00  WA  0   0  4
  [ 4] .bss              NOBITS          0000000000000000 000070 000008 00  WA  0   0  4
  [ 5] .rodata           PROGBITS        0000000000000000 000070 00000d 00   A  0   0  1
  [ 6] .comment          PROGBITS        0000000000000000 00007d 00002c 01  MS  0   0  1
  [ 7] .note.GNU-stack   PROGBITS        0000000000000000 0000a9 000000 00      0   0  1
  [ 8] .note.gnu.property NOTE            0000000000000000 0000b0 000020 00   A  0   0  8
  [ 9] .eh_frame         PROGBITS        0000000000000000 0000d0 000038 00   A  0   0  8
  [10] .rela.eh_frame    RELA            0000000000000000 000340 000018 18   I 11   9  8
  [11] .symtab           SYMTAB          0000000000000000 000108 0001c8 18     12  14  8
  [12] .strtab           STRTAB          0000000000000000 0002d0 00003d 00      0   0  1
  [13] .shstrtab         STRTAB          0000000000000000 000358 000074 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

这里有个概念需要解释清楚,就是节(section)和段(segment)。段是程序执行的必要组成部分,在每个段中,会有代码或者数据被划分为不同的节。每一个节都保存了某种类型的代码或者是数据,数据可以是程序中的全局变量,也可以是链接器所需要的动态链接信息。segment是从运行的角度来描述elf文件,section是从链接的角度来描述elf文件。

常见的节:

.text保存了程序代码指令
.rodata保存只读数据,如c语言代码中的字符串“Hello World”。因为它是只读的,所以只能存在于一个可执行文件的只读段中(text段)。
.plt.plt节包含了动态链接器调用从共享库导入的函数所需要的相关代码,也是存在于text段中。
.data这里不要将.data节和data段混淆了,.data节存在于data段中,保存了初始化的全局变量数据。
.bss.bss节保存了未初始化的全局数据,是data段的一部分,占用空间不超过4字节(使用占位符),程序加载时数据被初始化为0。
.got.plt.got节保存了全局偏移表。.got节和.plt节一起提供了对导入的共享库函数的访问入口。
.dynsym.dynsym节保存了从共享库导入的动态符号信息,该节保存在text段中。
.dynstr.dynstr节保存了动态符号字符串表,表中存放了一系列字符串。
.rel.*.rel.*节又叫重定位节,保存了重定位相关信息,这些信息描述了如何在链接或者运行时,对ELF目标文件的某部分内容或者进程镜像进行补充或修改。
.hash.hash节有时又称.gnu.hash,保存了一个用于查找符号的散列表。
.symtab.symtab节保存了Elf64_Sym或者Elf32_Sym类型的符号信息。
.strtab.strtab节保存的是符号字符串表,表中的内容会被.symtab的Elfx_Sym结构体中的st_name成员引用。
.shstrtab.shstrtab节保存节头字符串表。

从 ELF Header 中可以看出 Section header 的 offset 是976字节,每个 Section Header 大小为64 bytes,一共有14个。

root@ubuntu:/media/code_test# readelf -h hello.o
ELF Header:
...
  Start of section headers:          976 (bytes into file)
...
  Size of section headers:           64 (bytes)
  Number of section headers:         14
  Section header string table index: 13
Section Header undefined
root@ubuntu:/media/code_test# hexdump -C -s976 -n64 hello.o
000003d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000410

可以看到 index=0 的 Section Header,内容全部为0。(在某些情况下,它的某部分字段不为0,这些特殊情况当前先跳过)
这是一个非常特别的 Section Header,它的作用是表示 undefined 。它的 index(=0) 也有一个特别的名字,叫 SHN_UNDEF.

Section Header .text
root@ubuntu:/media/code_test# hexdump -C -s1040 -n64 hello.o
00000410  20 00 00 00 01 00 00 00  06 00 00 00 00 00 00 00  | ...............|
00000420  00 00 00 00 00 00 00 00  40 00 00 00 00 00 00 00  |........@.......|
00000430  22 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |"...............|
00000440  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000450

这里dump出了.text的section header内容,总共64字节,对照Elf64_Shdr结构体分析即可。后续的section header也是类似的方法来分析。

ELF符号

来看一下一个64位ELF文件符号项的结构:

typedef struct
{
  Elf64_Word	st_name;		/* Symbol name (string tbl index):4 Bytes */
  unsigned char	st_info;		/* Symbol type and binding:1 Byte */
  unsigned char st_other;		/* Symbol visibility:1 Byte */
  Elf64_Section	st_shndx;		/* Section indexL:2 Bytes */
  Elf64_Addr	st_value;		/* Symbol value:8 Bytes */
  Elf64_Xword	st_size;		/* Symbol size:8 Bytes*/
} Elf64_Sym; //total size = 24 Bytes

符号项保存在.symtab和.dynsym节中,因此它们对应的section header大小与Elfx_Sym的大小相等。

用 readelf 查看一下 Symbol Table:(这里小写的 s,代表 Symbols,大写的 S,代表 Sections/Section Headers

root@ubuntu:/media/code_test# readelf -sW hello.o

Symbol table '.symtab' contains 19 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     5: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    3 c
     6: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 d
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     8: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    4 h.2323
     9: 0000000000000008     4 OBJECT  LOCAL  DEFAULT    3 g.2322
    10: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
    11: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
    12: 0000000000000000     0 SECTION LOCAL  DEFAULT    9 
    13: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
    14: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 a
    15: 0000000000000004     4 OBJECT  GLOBAL DEFAULT  COM b
    16: 0000000000000000    34 FUNC    GLOBAL DEFAULT    1 main
    17: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    18: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND puts

这里包含了hello.o中出现的所有 Symbol。其中 Num=0(index=0) 的 Symbol 用作 undefined Symbol,它的 index 有个特别的名字,叫做 STN_UNDEF

四、符号表& 符号

Symbol Table 包含了一组 Symbol。这些 Symbol 在程序中,要么表示定义,要么表示引用,它们的作用是在编译和链接的过程中,进行定位或者重定位。

先查看它在 Section Header 列表中的信息:

root@ubuntu:/media/code_test# readelf -SW hello.o
There are 14 section headers, starting at offset 0x3d0:

Section Headers:
  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
...
  [11] .symtab           SYMTAB          0000000000000000 000108 0001c8 18     12  14  8
...

由上可知 Symbol Table Section:

  1. sh_name是 “.symtab”
  2. sh_type是 SYMTAB
  3. sh_offset = 0x108 = 264 Bytes
  4. sh_size = 0x1c8 = 456 Bytes
  5. sh_entsize = 0x18 = 24 Bytes
  6. Symbol 数为: 456 / 24 = 19

我们尝试从字节级别解读 Symbol 的定义。先获取Symbol信息:

root@ubuntu:/media/code_test# readelf -sW hello.o

Symbol table '.symtab' contains 19 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     5: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    3 c
     6: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 d
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     8: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    4 h.2323
     9: 0000000000000008     4 OBJECT  LOCAL  DEFAULT    3 g.2322
    10: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
    11: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
    12: 0000000000000000     0 SECTION LOCAL  DEFAULT    9 
    13: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
    14: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 a
    15: 0000000000000004     4 OBJECT  GLOBAL DEFAULT  COM b
    16: 0000000000000000    34 FUNC    GLOBAL DEFAULT    1 main
    17: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    18: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND puts

选取Num = 6,也就是Name = d 的 Symbol。

上面已近计算出了Symbol Table 在对象文件中的 offset = 264,每个 Symbol 的 size = 24,那么 Num = 6 的 Symbol 它的 offset = 264 + 24 * 6 = 408。

root@ubuntu:/media/code_test# hexdump -C -s408 -n24 hello.o
00000198  09 00 00 00 01 00 04 00  00 00 00 00 00 00 00 00  |................|
000001a8  04 00 00 00 00 00 00 00                           |........|
000001b0

st_name = 0x00000009 (4 Bytes)

Symbol 名字的索引,根据它去.strtab里寻找字符串

root@ubuntu:/media/code_test# readelf -SW hello.o
There are 14 section headers, starting at offset 0x3d0:

Section Headers:
  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
...
  [12] .strtab           STRTAB          0000000000000000 0002d0 00003d 00      0   0  1
...

可以看到,.strtab的偏移是0x2d0

root@ubuntu:/media/code_test# hexdump -C -s720 hello.o
000002d0  00 68 65 6c 6c 6f 2e 63  00 64 00 68 2e 32 33 32  |.hello.c.d.h.232|
000002e0  33 00 67 2e 32 33 32 32  00 61 00 62 00 6d 61 69  |3.g.2322.a.b.mai|
000002f0  6e 00 5f 47 4c 4f 42 41  4c 5f 4f 46 46 53 45 54  |n._GLOBAL_OFFSET|
00000300  5f 54 41 42 4c 45 5f 00  70 75 74 73 00 00 00 00  |_TABLE_.puts....|
00000310  16 00 00 00 00 00 00 00  02 00 00 00 07 00 00 00  |................|
00000320  fc ff ff ff ff ff ff ff  1b 00 00 00 00 00 00 00  |................|
00000330  04 00 00 00 12 00 00 00  fc ff ff ff ff ff ff ff  |................|
00000340  20 00 00 00 00 00 00 00  02 00 00 00 02 00 00 00  | ...............|
00000350  00 00 00 00 00 00 00 00  00 2e 73 79 6d 74 61 62  |..........symtab|
00000360  00 2e 73 74 72 74 61 62  00 2e 73 68 73 74 72 74  |..strtab..shstrt|
00000370  61 62 00 2e 72 65 6c 61  2e 74 65 78 74 00 2e 64  |ab..rela.text..d|
00000380  61 74 61 00 2e 62 73 73  00 2e 72 6f 64 61 74 61  |ata..bss..rodata|
00000390  00 2e 63 6f 6d 6d 65 6e  74 00 2e 6e 6f 74 65 2e  |..comment..note.|
000003a0  47 4e 55 2d 73 74 61 63  6b 00 2e 6e 6f 74 65 2e  |GNU-stack..note.|
000003b0  67 6e 75 2e 70 72 6f 70  65 72 74 79 00 2e 72 65  |gnu.property..re|
000003c0  6c 61 2e 65 68 5f 66 72  61 6d 65 00 00 00 00 00  |la.eh_frame.....|
000003d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
...

索引值是9,所以数9个字节,直到遇到00为止(ASCII码值 0 表示空字符,空字符就是平时所说的 ‘\0’。)

64 —> d

00 —> \0

这里得到的结果就是字符d,和上面Symbol table的Name结果是一致的。

st_info = 0x01 (1 Byte)

这是个组合字段。它的高4位表示 Symbol Binding,低4位表示 Symbol Type。

0x01 = 0b0000 0001

Symbol Binding = 0

0STB_LOCAL局部符号。这些符号在包含其定义的目标文件的外部不可见。名称相同的局部符号可存在于多个文件中而不会相互干扰。
1STB_GLOBAL全局符号。这些符号对于合并的所有目标文件都可见。一个文件的全局符号定义满足另一个文件对相同全局符号的未定义引用。
2STB_WEAK弱符号。这些符号与全局符号类似,但其定义具有较低的优先级。
10STB_LOOS此范围内包含的值(包括这两个值)保留用于特定于操作系统的语义。
12STB_HIOS此范围内包含的值(包括这两个值)保留用于特定于操作系统的语义。
13STB_LOPROC此范围内包含的值(包括这两个值)保留用于特定于处理器的语义。
15STB_HIPROC此范围内包含的值(包括这两个值)保留用于特定于处理器的语义。

Symbol Type = 1

0STT_NOTYPE未指定符号类型。
1STT_OBJECT此符号与变量、数组等数据目标文件关联
2STT_FUNC此符号与函数或其他可执行代码关联。
3STT_SECTION此符号与节关联。此类型的符号表各项主要用于重定位,并且通常具有 STB_LOCAL 绑定。
4STT_FILE通常,符号的名称会指定与目标文件关联的源文件的名称。文件符号具有 STB_LOCAL 绑定和节索引 SHN_ABS。此符号(如果存在)位于文件的其他 STB_LOCAL 符号前面。符号索引为 1 的 SHT_SYMTAB 是表示目标文件的 STT_FILE 符号。通常,此符号后跟文件的 STT_SECTION 符号。这些节符号又后跟已降为局部符号的任何全局符号。
5STT_COMMON此符号标记未初始化的通用块。此符号的处理与 STT_OBJECT 的处理完全相同。
6STT_TLS此符号指定线程局部存储实体。定义后,此符号可为符号指明指定的偏移,而不是实际地址。线程局部存储重定位只能引用 STT_TLS 类型的符号。从可分配节中引用 STT_TLS 类型的符号只能通过使用特殊线程局部存储重定位来实现。

st_other = 0x00 (1 Byte)

指定了 Symbol 的可见性

0STV_DEFAULT具有 STV_DEFAULT 属性的符号的可见性与符号的绑定类型指定的可见性相同。全局符号和弱符号在其定义组件(可执行文件或共享目标文件)外部可见。局部符号处于隐藏状态。另外,还可以替换全局符号和弱符号。可以在另一个组件中通过定义相同的名称插入这些符号
1STV_INTERNAL此可见性属性当前被保留。
2STV_HIDDEN如果当前组件中定义的符号的名称对于其他组件不可见,则该符号处于隐藏状态。必须对这类符号进行保护。此属性用于控制组件的外部接口。由这样的符号命名的目标文件仍可以在另一个组件中引用(如果将目标文件的地址传到外部)。如果可执行文件或共享目标文件中包括可重定位目标文件,则该目标文件中包含的隐藏符号将会删除或转换为使用 STB_LOCAL 绑定。
3STV_PROTECTED如果当前组件中定义的符号在其他组件中可见,但不能被替换,则该符号处于受保护状态。定义组件中对这类符号的任何引用都必须解析为该组件中的定义。即使在另一个组件中存在按缺省规则插入的符号定义,也必须进行此解析。具有 STB_LOCAL 绑定的符号将没有 STV_PROTECTED 可见性。
4STV_EXPORTED此可见性属性确保符号保持为全局。不能使用任何其他符号可见性技术对此可见性进行降级或消除。具有 STB_LOCAL 绑定的符号将没有 STV_EXPORTED 可见性。
5STV_SINGLETON此可见性属性确保符号保持为全局。不能使用任何其他符号可见性技术对此可见性进行降级或消除。具有 STB_LOCAL 绑定的符号将没有 STV_EXPORTED 可见性。
6STV_ELIMINATE此可见性属性扩展 STV_HIDDEN。当前组件中定义为要消除的符号对其他组件不可见。该符号未写入使用该组件的动态可执行文件或共享目标文件的任何符号表中。

st_shndx = 0x0004 (2 Bytes)

关联到section header table,这里的值为4,对应[ 4] .bss

st_value = 0x0000000000000000 (8 Bytes)

对于 relocatable (.o) 文件,如果该符号已定义,则 st_value 保存的是该符号在其所定义的 section (由 st_shndx 指定) 中的偏移量。

st_size = 0x0000000000000004 (8 Bytes)

表示 Symbol 的大小,例如数据对象占据多少字节。如果 Symbol 没有大小或者大小未知,则值为0。

总结一下,一个symbol起始和结束如下:

Start:

EL64_Shdr[Elf64_Sym.st_section].sh_offset + El64_Sym.st_value

End:

Start + Elf64_Sym.size -1

五、参考文档

1.《Oracle Solaris 11 Information Library

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
文件分成两个版本, “合并sof和elf生产jic文件脚本.rar”为EP4CE10+EPCS16的版本,适用于小梅哥所有以EP4CE6、EP4CE10 FPGA芯片开发的FPGA开发板。如AC620、AC601、Starter等等 “合并sof和elf生产jic文件脚本 - 4ce30.rar”为EP4CE30+EPCS64的版本,适用于小梅哥所有以EP4CE30、EP4CE40 FPGA芯片开发的FPGA开发板。如AC6102 工具说明 本工具实现将Quartus ii编译产生的sof文件和NIOS II EDS编译产生的elf文件合并为jic文件,以方便烧写。 根据测试,本工具使用需要具备以下条件: 1、quartus ii软件版本在13.0及以上 2、quartus ii编译生成的sof文件输出目录为根目录下的output_files文件夹下。很多从老版本修改过来工程sof输出目录在工程根目录,因此需要用户自行修改脚本和cof文件 3、nios ii的软件工程路径为quartus 工程根目录下的software文件夹下(一般都能满足) 使用方法 1、将generate_jic.tcl、generate_jic.sh、generate_jic.cof文件拷贝到你的nios ii软件工程下。 2、在eclipse中选中应用工程,注意是应用工程,不是BSP工程,右键->NIOS II->NIOS command shell。 3、NIOS command shell中输入"./generate_jic.sh"。 (运行完成后,会在Quartus II工程根目录下生成一个myoutput_files的文件夹,同时将generate_jic.tcl、generate_jic.cof文件拷贝到工程根目录下。) 4、在quartus ii中点击Tools -> Tcl Scripts,选中generate_jic.tcl,点击run, (运行成功,会在myoutput_files目录下生成名叫hs_combined.jic的文件) 5、烧写hs_combined.jic到FPGA中,对板卡断电重新上电,新固件就可以开始运行了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值