ELF重定位简介

Relocation 重定位

Relocation is the process of connecting symbolic references with symbolic definitions. For example, when a program calls a function, the associated call instruction must transfer control to the proper destination address at execution. Relocatable files must have ``relocation entries'' which are necessary because they contain information that describes how to modify their section contents, thus allowing executable and shared object files to hold the right information for a process's program image.

重定位操作是连接符号引用(symbolic references)和符号定义(symbolic definitions)的过程。例如,程序中调用一个(外部)函数,代码中我们只需要指定函数名(符号引用)即可,但是当程序实际运行的时候,相关的CALL指令必须能够正确无误地跳转到函数实际地址处(符号定义)去执行函数代码。可是在链接阶段之前,符号的虚拟地址(亦可称运行时地址)并没有分配,只有在链接阶段的符号解析过程中链接器才会为符号分配虚拟地址。在符号地址确认后,链接器这才会修改机器指令(即重定位操作是在符号解析之后),可是链接器并不会聪明到可以自动找到可重定位文件中引用外部符号的地方(即需要修改的地方),所以可重定位文件必须提供相应的信息来帮助链接器,换句话说,可重定位文件中必须包含相关的信息来告诉链接器如何去修改节的内容,只有这样,最后生成的可执行文件或者共享库才会包含正确的信息来构建最终的进程映像。可重定位项就是帮助链接器进行重定位操作的信息。

/* Relocation table entry without addend (in section of type SHT_REL).  */

typedef struct
{
  Elf32_Addr    r_offset;               /* Address */
  Elf32_Word    r_info;                 /* Relocation type and symbol index */
} Elf32_Rel;

/* I have seen two different definitions of the Elf64_Rel and
   Elf64_Rela structures, so we'll leave them out until Novell (or
   whoever) gets their act together.  */
/* The following, at least, is used on Sparc v9, MIPS, and Alpha.  */

typedef struct
{
  Elf64_Addr    r_offset;               /* Address */
  Elf64_Xword   r_info;                 /* Relocation type and symbol index */
} Elf64_Rel;

/* Relocation table entry with addend (in section of type SHT_RELA).  */

typedef struct
{
  Elf32_Addr    r_offset;               /* Address */
  Elf32_Word    r_info;                 /* Relocation type and symbol index */
  Elf32_Sword   r_addend;               /* Addend */
} Elf32_Rela;

typedef struct
{
  Elf64_Addr    r_offset;               /* Address */
  Elf64_Xword   r_info;                 /* Relocation type and symbol index */
  Elf64_Sxword  r_addend;               /* Addend */
} Elf64_Rela;

/* How to extract and insert information held in the r_info field.  */

#define ELF32_R_SYM(val)                ((val) >> 8)
#define ELF32_R_TYPE(val)               ((val) & 0xff)
#define ELF32_R_INFO(sym, type)         (((sym) << 8) + ((type) & 0xff))

#define ELF64_R_SYM(i)                  ((i) >> 32)
#define ELF64_R_TYPE(i)                 ((i) & 0xffffffff)
#define ELF64_R_INFO(sym,type)          ((((Elf64_Xword) (sym)) << 32) + (type))

* r_offset

This member gives the location at which to apply the relocation action. For a relocatable file, the value is the byte offset from the beginning of the section to the storage unit affected by the relocation. For an executable file or a shared object, the value is the virtual address of the storage unit affected by the relocation.

r_offset给出的是需要做重定位操作的位置,代表的意思对于不同的文件来说有所不同。

对可重定位文件来说,r_offset代表的是段内偏移,即需要重定位操作的地方在所在节中的偏移值。

对于可执行文件或者共享库来说,r_offset代表的是需要重定位操作的地方de虚拟地址。

* r_info

This member gives both the symbol table index with respect to which the relocation must be made, and the type of relocation to apply. For example, a call instruction's relocation entry would hold the symbol table index of the function being called. If the index is STN_UNDEF, the undefined symbol index, the relocation uses 0 as the ``symbol value''. Relocation types are processor-specific; descriptions of their behavior appear in the processor supplement. When the text below refers to a relocation entry's relocation type or symbol table index, it means the result of applying ELF32_R_TYPE (or ELF64_R_TYPE) or ELF32_R_SYM (or ELF64_R_SYM), respectively, to the entry's r_info member.

r_info中包含重定位类型和需要重定位的符号在符号表中的索引值。
举例来说,一个跟函数重定位相关的重定位项中的r_info会给出该函数符号在符号表中的索引值。如果索引值是STN_UNDEF(0),证明符号是未定义的,那么重定位过程中将使用0作为该符号的符号数值(st_value)。
重定位类型是处理器相关的一种属性。
可以使用宏ELF32_R_SYM(或者ELF64_R_SYM)和宏ELF32_R_TYPE(或者ELF64_R_TYPE)分别得到需要重定位的符号的符号表索引值以及重定位类型。

	#define ELF32_R_SYM(i)	((i)>>8)
	#define ELF32_R_TYPE(i)   ((unsigned char)(i))
	#define ELF32_R_INFO(s,t) (((s)<<8)+(unsigned char)(t))

	#define ELF64_R_SYM(i)    ((i)>>32)
	#define ELF64_R_TYPE(i)   ((i)&0xffffffffL)
	#define ELF64_R_INFO(s,t) (((s)<<32)+((t)&0xffffffffL))

* r_addend

This member specifies a constant addend used to compute the value to be stored into the relocatable field.

r_addend一般是个常量,用来辅助计算修订值。

As specified previously, only Elf32_Rela and Elf64_Rela entries contain an explicit addend. Entries of type Elf32_Rel and Elf64_Rel store an implicit addend in the location to be modified. Depending on the processor architecture, one form or the other might be necessary or more convenient. Consequently, an implementation for a particular machine may use one form exclusively or either form depending on context.

从上面的定义可以看到,Elf32_Rela/Elf64_Rela和Elf32_Rel/Elf64_Rel的区别仅仅是多了一个r_addend。Elf32_Rela/Elf64_Rela中用r_addend显示地指出Addend,而对于Elf32_Rel/Elf64_Rel来说,它的Addend被填充在引用外部符号的地址处,即需要进行重定位操作的地方。具体的体系结构可以选择适合自己的一种格式,或者两种格式都使用,只不过在不同的上下文中使用更合适的格式。

 

A relocation section references two other sections: a symbol table and a section to modify. The section header's sh_info and sh_link members, described in ``Sections'' above, specify these relationships. Relocation entries for different object files have slightly different interpretations for the r_offset member.

一个重定位节与其它两个节相关联,一个是符号表(节),另一个是该重定位节对应的需要进行重定位操作的节,这两个节头表索引值分别由节头表成员sh_info和sh_link指定。

成员r_offset的意义因文件的不同而有所区别,如下:

  • In relocatable files, r_offset holds a section offset. The relocation section itself describes how to modify another section in the file; relocation offsets designate a storage unit within the second section.

如果是可重定位文件,那么r_offset的数值代表的是需要进行重定位操作的地方在所在节中的偏移值。也就是说,在可重定位文件中,重定位节就是描述如何修改需要进行重定位操作的节的内容,r_offset就是告诉链接器需要修改的地方在节中的位置。

  • In executable and shared object files, r_offset holds a virtual address. To make these files' relocation entries more useful for the dynamic linker, the section offset (file interpretation) gives way to a virtual address (memory interpretation).

如果是可执行文件或者共享库文件,r_offset数值代表的是一个虚拟地址。在程序运行过程中动态链接器会用到该虚拟地址。(为了让这些重定位项对动态链接器来说更加有用,r_offset代表的并不是偏移值,而是代表的虚拟地址) 

Although the interpretation of r_offset changes for different object files to allow efficient access by the relevant programs, the relocation types' meanings stay the same.

尽管一个重定位项中的r_offset的意义会因为文件的不同而不同,但是r_info中的重定位类型却总是一直保持不变的。  

The typical application of an ELF relocation is to determine the referenced symbol value, extract the addend (either from the field to be relocated or from the addend field contained in the relocation record, as appropriate for the type of relocation record), apply the expression implied by the relocation type to the symbol and addend, extract the desired part of the expression result, and place it in the field to be relocated.

If multiple consecutive relocation records are applied to the same relocation location (r_offset), they are composed instead of being applied independently, as described above. By consecutive, we mean that the relocation records are contiguous within a single relocation section. By composed, we mean that the standard application described above is modified as follows:

 

  • In all but the last relocation operation of a composed sequence, the result of the relocation expression is retained, rather than having part extracted and placed in the relocated field. The result is retained at full pointer precision of the applicable ABI processor supplement.
  • In all but the first relocation operation of a composed sequence, the addend used is the retained result of the previous relocation operation, rather than that implied by the relocation type.

Note that a consequence of the above rules is that the location specified by a relocation type is relevant for the first element of a composed sequence (and then only for relocation records that do not contain an explicit addend field) and for the last element, where the location determines where the relocated value will be placed. For all other relocation operands in a composed sequence, the location specified is ignored.

An ABI processor supplement may specify individual relocation types that always stop a composition sequence, or always start a new one.

Relocation Types (Processor-Specific)

This section requires processor-specific information. The ABI supplement for the desired processor describes the details.

 

以下程序用于输出ELF所有重定位的信息(即sh_type为SHT_REL或者SHT_RELA),模仿readelf的--relocs选项:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <link.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <elf.h>
#include <error.h>
#include <errno.h>

#define ELFW(type)	_ELFW (ELF, __ELF_NATIVE_CLASS, type)
#define _ELFW(e,w,t)	_ELFW_1 (e, w, _##t)
#define _ELFW_1(e,w,t)	e##w##t

static const char *r_type(unsigned int machine, unsigned int type)
{
	switch (machine) {
	case EM_386:
		switch (type) {
		case 0: return "R_386_NONE";
		case 1: return "R_386_32";
		case 2: return "R_386_PC32";
		case 3: return "R_386_GOT32";
		case 4: return "R_386_PLT32";
		case 5: return "R_386_COPY";
		case 6: return "R_386_GLOB_DAT";
		case 7: return "R_386_JUMP_SLOT";
		case 8: return "R_386_RELATIVE";
		case 9: return "R_386_GOTOFF";
		case 10: return "R_386_GOTPC";
		case 11: return "R_386_32PLT"; /* Not in psabi */
		case 14: return "R_386_TLS_TPOFF";
		case 15: return "R_386_TLS_IE";
		case 16: return "R_386_TLS_GOTIE";
		case 17: return "R_386_TLS_LE";
		case 18: return "R_386_TLS_GD";
		case 19: return "R_386_TLS_LDM";
		case 20: return "R_386_16";
		case 21: return "R_386_PC16";
		case 22: return "R_386_8";
		case 23: return "R_386_PC8";
		case 24: return "R_386_TLS_GD_32";
		case 25: return "R_386_TLS_GD_PUSH";
		case 26: return "R_386_TLS_GD_CALL";
		case 27: return "R_386_TLS_GD_POP";
		case 28: return "R_386_TLS_LDM_32";
		case 29: return "R_386_TLS_LDM_PUSH";
		case 30: return "R_386_TLS_LDM_CALL";
		case 31: return "R_386_TLS_LDM_POP";
		case 32: return "R_386_TLS_LDO_32";
		case 33: return "R_386_TLS_IE_32";
		case 34: return "R_386_TLS_LE_32";
		case 35: return "R_386_TLS_DTPMOD32";
		case 36: return "R_386_TLS_DTPOFF32";
		case 37: return "R_386_TLS_TPOFF32";
		case 38: return "R_386_SIZE32";
		case 39: return "R_386_TLS_GOTDESC";
		case 40: return "R_386_TLS_DESC_CALL";
		case 41: return "R_386_TLS_DESC";
		case 42: return "R_386_IRELATIVE";
		case 43: return "R_386_GOT32X";
		}
		break;
	case EM_AARCH64:
		switch(type) {
		}
		break;
	case EM_ARM:
		switch(type) {
		case 0: return "R_ARM_NONE";
		case 1: return "R_ARM_PC24"; /* Deprecated */
		case 2: return "R_ARM_ABS32";
		case 3: return "R_ARM_REL32";
		case 4: return "R_ARM_LDR_PC_G0"; /* Also R_ARM_PC13 */
		case 5: return "R_ARM_ABS16";
		case 6: return "R_ARM_ABS12";
		case 7: return "R_ARM_THM_ABS5";
		case 8: return "R_ARM_ABS8";
		case 9: return "R_ARM_SBREL32";
		case 10: return "R_ARM_THM_CALL"; /* Also R_ARM_THM_PC22 */
		case 11: return "R_ARM_THM_PC8";
		case 12: return "R_ARM_BREL_ADJ"; /* Also R_ARM_AMP_VCALL9 */
		case 13: return "R_ARM_TLS_DESC"; /* Also R_ARM_SWI24 */
		case 14: return "R_ARM_THM_SWI8"; /* Obsolete */
		case 15: return "R_ARM_XPC25"; /* Obsolete */
		case 16: return "R_ARM_THM_XPC22"; /* Obsolete */
		case 17: return "R_ARM_TLS_DTPMOD32";
		case 18: return "R_ARM_TLS_DTPOFF32";
		case 19: return "R_ARM_TLS_TPOFF32";
		case 20: return "R_ARM_COPY";
		case 21: return "R_ARM_GLOB_DAT";
		case 22: return "R_ARM_JUMP_SLOT";
		case 23: return "R_ARM_RELATIVE";
		case 24: return "R_ARM_GOTOFF32"; /* Also R_ARM_GOTOFF */
		case 25: return "R_ARM_BASE_PREL"; /* GNU R_ARM_GOTPC */
		case 26: return "R_ARM_GOT_BREL"; /* GNU R_ARM_GOT32 */
		case 27: return "R_ARM_PLT32"; /* Deprecated */
		case 28: return "R_ARM_CALL";
		case 29: return "R_ARM_JUMP24";
		case 30: return "R_ARM_THM_JUMP24";
		case 31: return "R_ARM_BASE_ABS";
		case 32: return "R_ARM_ALU_PCREL_7_0"; /* Obsolete */
		case 33: return "R_ARM_ALU_PCREL_15_8"; /* Obsolete */
		case 34: return "R_ARM_ALU_PCREL_23_15"; /* Obsolete */
		case 35: return "R_ARM_LDR_SBREL_11_0_NC"; /* Deprecated */
		case 36: return "R_ARM_ALU_SBREL_19_12_NC"; /* Deprecated */
		case 37: return "R_ARM_ALU_SBREL_27_20_CK"; /* Deprecated */
		case 38: return "R_ARM_TARGET1";
		case 39: return "R_ARM_SBREL31"; /* Deprecated. */
		case 40: return "R_ARM_V4BX";
		case 41: return "R_ARM_TARGET2";
		case 42: return "R_ARM_PREL31";
		case 43: return "R_ARM_MOVW_ABS_NC";
		case 44: return "R_ARM_MOVT_ABS";
		case 45: return "R_ARM_MOVW_PREL_NC";
		case 46: return "R_ARM_MOVT_PREL";
		case 47: return "R_ARM_THM_MOVW_ABS_NC";
		case 48: return "R_ARM_THM_MOVT_ABS";
		case 49: return "R_ARM_THM_MOVW_PREL_NC";
		case 50: return "R_ARM_THM_MOVT_PREL";
		case 51: return "R_ARM_THM_JUMP19";
		case 52: return "R_ARM_THM_JUMP6";
		case 53: return "R_ARM_THM_ALU_PREL_11_0";
		case 54: return "R_ARM_THM_PC12";
		case 55: return "R_ARM_ABS32_NOI";
		case 56: return "R_ARM_REL32_NOI";
		case 57: return "R_ARM_ALU_PC_G0_NC";
		case 58: return "R_ARM_ALU_PC_G0";
		case 59: return "R_ARM_ALU_PC_G1_NC";
		case 60: return "R_ARM_ALU_PC_G1";
		case 61: return "R_ARM_ALU_PC_G2";
		case 62: return "R_ARM_LDR_PC_G1";
		case 63: return "R_ARM_LDR_PC_G2";
		case 64: return "R_ARM_LDRS_PC_G0";
		case 65: return "R_ARM_LDRS_PC_G1";
		case 66: return "R_ARM_LDRS_PC_G2";
		case 67: return "R_ARM_LDC_PC_G0";
		case 68: return "R_ARM_LDC_PC_G1";
		case 69: return "R_ARM_LDC_PC_G2";
		case 70: return "R_ARM_ALU_SB_G0_NC";
		case 71: return "R_ARM_ALU_SB_G0";
		case 72: return "R_ARM_ALU_SB_G1_NC";
		case 73: return "R_ARM_ALU_SB_G1";
		case 74: return "R_ARM_ALU_SB_G2";
		case 75: return "R_ARM_LDR_SB_G0";
		case 76: return "R_ARM_LDR_SB_G1";
		case 77: return "R_ARM_LDR_SB_G2";
		case 78: return "R_ARM_LDRS_SB_G0";
		case 79: return "R_ARM_LDRS_SB_G1";
		case 80: return "R_ARM_LDRS_SB_G2";
		case 81: return "R_ARM_LDC_SB_G0";
		case 82: return "R_ARM_LDC_SB_G1";
		case 83: return "R_ARM_LDC_SB_G2";
		case 84: return "R_ARM_MOVW_BREL_NC";
		case 85: return "R_ARM_MOVT_BREL";
		case 86: return "R_ARM_MOVW_BREL";
		case 87: return "R_ARM_THM_MOVW_BREL_NC";
		case 88: return "R_ARM_THM_MOVT_BREL";
		case 89: return "R_ARM_THM_MOVW_BREL";
		case 90: return "R_ARM_TLS_GOTDESC";
		case 91: return "R_ARM_TLS_CALL";
		case 92: return "R_ARM_TLS_DESCSEQ";
		case 93: return "R_ARM_THM_TLS_CALL";
		case 94: return "R_ARM_PLT32_ABS";
		case 95: return "R_ARM_GOT_ABS";
		case 96: return "R_ARM_GOT_PREL";
		case 97: return "R_ARM_GOT_BREL12";
		case 98: return "R_ARM_GOTOFF12";
		case 99: return "R_ARM_GOTRELAX";
		case 100: return "R_ARM_GNU_VTENTRY";
		case 101: return "R_ARM_GNU_VTINHERIT";
		case 102: return "R_ARM_THM_JUMP11"; /* Also R_ARM_THM_PC11 */
		case 103: return "R_ARM_THM_JUMP8"; /* Also R_ARM_THM_PC9 */
		case 104: return "R_ARM_TLS_GD32";
		case 105: return "R_ARM_TLS_LDM32";
		case 106: return "R_ARM_TLS_LDO32";
		case 107: return "R_ARM_TLS_IE32";
		case 108: return "R_ARM_TLS_LE32";
		case 109: return "R_ARM_TLS_LDO12";
		case 110: return "R_ARM_TLS_LE12";
		case 111: return "R_ARM_TLS_IE12GP";
		/* 112-127 R_ARM_PRIVATE_<n> */
		case 128: return "R_ARM_ME_TOO"; /* Obsolete */
		case 129: return "R_ARM_THM_TLS_DESCSEQ16";
		case 130: return "R_ARM_THM_TLS_DESCSEQ32";
		case 131: return "R_ARM_THM_GOT_BREL12";
		case 132: return "R_ARM_THM_ALU_ABS_G0_NC";
		case 133: return "R_ARM_THM_ALU_ABS_G1_NC";
		case 134: return "R_ARM_THM_ALU_ABS_G2_NC";
		case 135: return "R_ARM_THM_ALU_ABS_G3";
		/* 136-159 Reserved for future allocation. */
		case 160: return "R_ARM_IRELATIVE";
		/* 161-255 Reserved for future allocation. */
		case 249: return "R_ARM_RXPC25";
		case 250: return "R_ARM_RSBREL32";
		case 251: return "R_ARM_THM_RPC22";
		case 252: return "R_ARM_RREL32";
		case 253: return "R_ARM_RABS32";
		case 254: return "R_ARM_RPC24";
		case 255: return "R_ARM_RBASE";
		}
		break;
	case EM_IA_64:
		switch(type) {
		}
		break;
	case EM_MIPS:
		switch(type) {
		}
		break;
	case EM_PPC:
		switch(type) {
		}
		break;
	case EM_PPC64:
		switch(type) {
		}
		break;
	case EM_RISCV:
		switch(type) {
		}
		break;
	case EM_S390:
		switch (type) {
		}
		break;
	case EM_SPARC:
	case EM_SPARCV9:
		switch(type) {
		}
		break;
	case EM_X86_64:
		switch (type) {
		case 0: return "R_X86_64_NONE";
		case 1: return "R_X86_64_64";
		case 2: return "R_X86_64_PC32";
		case 3: return "R_X86_64_GOT32";
		case 4: return "R_X86_64_PLT32";
		case 5: return "R_X86_64_COPY";
		case 6: return "R_X86_64_GLOB_DAT";
		case 7: return "R_X86_64_JUMP_SLOT";
		case 8: return "R_X86_64_RELATIVE";
		case 9: return "R_X86_64_GOTPCREL";
		case 10: return "R_X86_64_32";
		case 11: return "R_X86_64_32S";
		case 12: return "R_X86_64_16";
		case 13: return "R_X86_64_PC16";
		case 14: return "R_X86_64_8";
		case 15: return "R_X86_64_PC8";
		case 16: return "R_X86_64_DTPMOD64";
		case 17: return "R_X86_64_DTPOFF64";
		case 18: return "R_X86_64_TPOFF64";
		case 19: return "R_X86_64_TLSGD";
		case 20: return "R_X86_64_TLSLD";
		case 21: return "R_X86_64_DTPOFF32";
		case 22: return "R_X86_64_GOTTPOFF";
		case 23: return "R_X86_64_TPOFF32";
		case 24: return "R_X86_64_PC64";
		case 25: return "R_X86_64_GOTOFF64";
		case 26: return "R_X86_64_GOTPC32";
		case 27: return "R_X86_64_GOT64";
		case 28: return "R_X86_64_GOTPCREL64";
		case 29: return "R_X86_64_GOTPC64";
		case 30: return "R_X86_64_GOTPLT64";
		case 31: return "R_X86_64_PLTOFF64";
		case 32: return "R_X86_64_SIZE32";
		case 33: return "R_X86_64_SIZE64";
		case 34: return "R_X86_64_GOTPC32_TLSDESC";
		case 35: return "R_X86_64_TLSDESC_CALL";
		case 36: return "R_X86_64_TLSDESC";
		case 37: return "R_X86_64_IRELATIVE";
		case 38: return "R_X86_64_RELATIVE64";
		case 41: return "R_X86_64_GOTPCRELX";
		case 42: return "R_X86_64_REX_GOTPCRELX";
		}
		break;
	}
	return NULL;
}

static void print_rel(ElfW(Rel) *rels, size_t reltab_entries, 
		ElfW(Sym) *symtab, size_t symtab_entries, const char *strtab, int machine)
{
	printf("%-16s %-16s %-24s %-16s %s\n", 
			"r_offset", "r_info", "r_type", "st_value", "st_name");

	for (size_t i = 0; i < reltab_entries; i++) {
		ElfW(Rel) *rel = &rels[i];	
		printf("%16.16jx", (uintmax_t)rel->r_offset);	
		printf(" %16.16jx", (uintmax_t)rel->r_info);
		printf(" %-24.24s", r_type(machine, ELFW(R_TYPE)(rel->r_info)));

		size_t symtabndx = ELFW(R_SYM)(rel->r_info);
		if (symtabndx >= symtab_entries) {
			(void)fprintf(stderr, "Symbol index out of range.\n");	
			continue;
		}
		printf(" %16.16jx", (uintmax_t)symtab[symtabndx].st_value);
		printf(" %s\n", strtab + symtab[symtabndx].st_name);
	}
}

static void print_rela(ElfW(Rela) *relas, size_t reltab_entries, 
		ElfW(Sym) *symtab, size_t symtab_entries, const char *strtab, int machine)
{
	printf("%-16s %-16s %-24s %-16s %s\n", 
			"r_offset", "r_info", "r_type", "st_value", "st_name + r_addend");

	for (size_t i = 0; i < reltab_entries; i++) {
		ElfW(Rela) *rela = &relas[i];	
		printf("%16.16jx", (uintmax_t)rela->r_offset);	
		printf(" %16.16jx", (uintmax_t)rela->r_info);
		printf(" %-24.24s", r_type(machine, ELFW(R_TYPE)(rela->r_info)));

		size_t symtabndx = ELFW(R_SYM)(rela->r_info);
		if (symtabndx >= symtab_entries) {
			(void)fprintf(stderr, "Symbol index out of range.\n");	
			continue;
		}
		printf(" %16.16jx", (uintmax_t)symtab[symtabndx].st_value);
		printf(" %s", strtab + symtab[symtabndx].st_name);
		printf(" + %jx\n", (uintmax_t)rela->r_addend);
	}
}
 
int main(int argc, char *argv[])
{
	int fd;
	char *file_mmbase;
	struct stat file_status;
	size_t fsize;
	ElfW(Ehdr) *ehdr;
	ElfW(Shdr) *shdrs;
	size_t shnum, shstrndx;
	int machine;

//	[ 9] .rela.dyn         RELA            0000000000000560 000560 0000c0 18   A  5   0  8
//	[10] .rela.plt         RELA            0000000000000620 000620 000108 18  AI  5  22  8
 
	if (argc != 2) {
		error(EXIT_FAILURE, 0, "Usage: %s file-name", argv[0]);
	}
 
	if ((fd = open(argv[1], O_RDONLY)) < 0) {
		error(EXIT_FAILURE, errno, "open %s failed", argv[1]);
	}
 
	if (fstat(fd, &file_status) < 0) {
		error(EXIT_FAILURE, errno, "get file %s info err", argv[1]);
	}
	fsize = (size_t)file_status.st_size;
 
	if ((file_mmbase = mmap(NULL, fsize, PROT_READ, 
				MAP_PRIVATE, fd, (off_t)0)) == MAP_FAILED) {
		error(EXIT_FAILURE, errno, "mmap file %s err", argv[1]);
	}
 
	ehdr = (ElfW(Ehdr) *)file_mmbase;
	shdrs = (ElfW(Shdr) *)(file_mmbase + ehdr->e_shoff);
	shnum = ehdr->e_shnum == 0 ? shdrs[0].sh_size : ehdr->e_shnum;
	shstrndx = ehdr->e_shstrndx == SHN_XINDEX ? shdrs[0].sh_link : ehdr->e_shstrndx;
	machine = ehdr->e_machine; 

	for (size_t i = 0; i < shnum; i++) {
		ElfW(Shdr) *shdr = &shdrs[i];	

		const char *shname = file_mmbase + shdrs[shstrndx].sh_offset + shdr->sh_name;

		if (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) {

  			// Get the symbol table information. 
			// for SHT_REL or SHT_RELA:
			// 		sh_link: The section header index 
			// 				of the associated symbol table.
			// 		sh_info: The section header index 
			// 		        of the section to which the relocation applies.
			size_t symtabndx = shdr->sh_link;
			if (shdrs[symtabndx].sh_type != SHT_SYMTAB
					&& shdrs[symtabndx].sh_type != SHT_DYNSYM) {
				(void)fprintf(stderr, "Associated section is not symbol table.\n");	
				continue;
			}
			ElfW(Sym) *symtab = (ElfW(Sym *))(file_mmbase + shdrs[symtabndx].sh_offset);
			size_t symtab_entries = shdrs[symtabndx].sh_size / shdrs[symtabndx].sh_entsize;
			// for SHT_SYMTAB or SHT_DYNSYM
			// 		sh_info: One greater than the symbol table index 
			//				 of the last local symbol (binding STB_LOCAL).
			// 		sh_link: The section header index 
			//				 of the associated string table.
			size_t strtabndx = shdrs[symtabndx].sh_link;
			const char *strtab = file_mmbase + shdrs[strtabndx].sh_offset; 

			size_t reltab_entries = shdr->sh_size / shdr->sh_entsize;

			if (shdr->sh_info != 0) {
				ElfW(Shdr) *destshdr = &shdrs[shdr->sh_info];
				const char *destshdrname = file_mmbase 
                        + shdrs[shstrndx].sh_offset + destshdr->sh_name;
				(void)printf("\nRelocation section [%2zu] '%s' "
					"for section [%2u] '%s' at offset 0x%jx contains %zu entries:\n",
					i, shname, shdr->sh_info, destshdrname,
					(uintmax_t)shdr->sh_offset, reltab_entries);
			} else {
				// The .rel.dyn section does not refer to a specific section	
				(void)printf("\nRelocation section [%2zu] '%s' "
					"at offset 0x%jx contains %zu entries:\n",
					i, shname, (uintmax_t)shdr->sh_offset, reltab_entries);
			}

			if (shdr->sh_type == SHT_REL) {
				ElfW(Rel) *rels = (ElfW(Rel) *)(file_mmbase + shdr->sh_offset);
				print_rel(rels, reltab_entries, 
			        symtab, symtab_entries, strtab, machine);
			}

			if (shdr->sh_type == SHT_RELA) {
				ElfW(Rela) *relas = (ElfW(Rela) *)(file_mmbase + shdr->sh_offset);
				print_rela(relas, reltab_entries, 
			        symtab, symtab_entries, strtab, machine);	
			}
		}
	}

	(void)munmap(file_mmbase, fsize);
	(void)close(fd);
	
	exit(EXIT_SUCCESS);
}

以下是利用libelf库输出所有重定位信息程序: 

#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <fcntl.h>
#include <gelf.h>
#include <stdint.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>

static const char *r_type(unsigned int machine, unsigned int type)
{
	switch (machine) {
	case EM_386:
		switch (type) {
		case 0: return "R_386_NONE";
		case 1: return "R_386_32";
		case 2: return "R_386_PC32";
		case 3: return "R_386_GOT32";
		case 4: return "R_386_PLT32";
		case 5: return "R_386_COPY";
		case 6: return "R_386_GLOB_DAT";
		case 7: return "R_386_JUMP_SLOT";
		case 8: return "R_386_RELATIVE";
		case 9: return "R_386_GOTOFF";
		case 10: return "R_386_GOTPC";
		case 11: return "R_386_32PLT"; /* Not in psabi */
		case 14: return "R_386_TLS_TPOFF";
		case 15: return "R_386_TLS_IE";
		case 16: return "R_386_TLS_GOTIE";
		case 17: return "R_386_TLS_LE";
		case 18: return "R_386_TLS_GD";
		case 19: return "R_386_TLS_LDM";
		case 20: return "R_386_16";
		case 21: return "R_386_PC16";
		case 22: return "R_386_8";
		case 23: return "R_386_PC8";
		case 24: return "R_386_TLS_GD_32";
		case 25: return "R_386_TLS_GD_PUSH";
		case 26: return "R_386_TLS_GD_CALL";
		case 27: return "R_386_TLS_GD_POP";
		case 28: return "R_386_TLS_LDM_32";
		case 29: return "R_386_TLS_LDM_PUSH";
		case 30: return "R_386_TLS_LDM_CALL";
		case 31: return "R_386_TLS_LDM_POP";
		case 32: return "R_386_TLS_LDO_32";
		case 33: return "R_386_TLS_IE_32";
		case 34: return "R_386_TLS_LE_32";
		case 35: return "R_386_TLS_DTPMOD32";
		case 36: return "R_386_TLS_DTPOFF32";
		case 37: return "R_386_TLS_TPOFF32";
		case 38: return "R_386_SIZE32";
		case 39: return "R_386_TLS_GOTDESC";
		case 40: return "R_386_TLS_DESC_CALL";
		case 41: return "R_386_TLS_DESC";
		case 42: return "R_386_IRELATIVE";
		case 43: return "R_386_GOT32X";
		}
		break;
	case EM_AARCH64:
		switch(type) {
		}
		break;
	case EM_ARM:
		switch(type) {
		case 0: return "R_ARM_NONE";
		case 1: return "R_ARM_PC24"; /* Deprecated */
		case 2: return "R_ARM_ABS32";
		case 3: return "R_ARM_REL32";
		case 4: return "R_ARM_LDR_PC_G0"; /* Also R_ARM_PC13 */
		case 5: return "R_ARM_ABS16";
		case 6: return "R_ARM_ABS12";
		case 7: return "R_ARM_THM_ABS5";
		case 8: return "R_ARM_ABS8";
		case 9: return "R_ARM_SBREL32";
		case 10: return "R_ARM_THM_CALL"; /* Also R_ARM_THM_PC22 */
		case 11: return "R_ARM_THM_PC8";
		case 12: return "R_ARM_BREL_ADJ"; /* Also R_ARM_AMP_VCALL9 */
		case 13: return "R_ARM_TLS_DESC"; /* Also R_ARM_SWI24 */
		case 14: return "R_ARM_THM_SWI8"; /* Obsolete */
		case 15: return "R_ARM_XPC25"; /* Obsolete */
		case 16: return "R_ARM_THM_XPC22"; /* Obsolete */
		case 17: return "R_ARM_TLS_DTPMOD32";
		case 18: return "R_ARM_TLS_DTPOFF32";
		case 19: return "R_ARM_TLS_TPOFF32";
		case 20: return "R_ARM_COPY";
		case 21: return "R_ARM_GLOB_DAT";
		case 22: return "R_ARM_JUMP_SLOT";
		case 23: return "R_ARM_RELATIVE";
		case 24: return "R_ARM_GOTOFF32"; /* Also R_ARM_GOTOFF */
		case 25: return "R_ARM_BASE_PREL"; /* GNU R_ARM_GOTPC */
		case 26: return "R_ARM_GOT_BREL"; /* GNU R_ARM_GOT32 */
		case 27: return "R_ARM_PLT32"; /* Deprecated */
		case 28: return "R_ARM_CALL";
		case 29: return "R_ARM_JUMP24";
		case 30: return "R_ARM_THM_JUMP24";
		case 31: return "R_ARM_BASE_ABS";
		case 32: return "R_ARM_ALU_PCREL_7_0"; /* Obsolete */
		case 33: return "R_ARM_ALU_PCREL_15_8"; /* Obsolete */
		case 34: return "R_ARM_ALU_PCREL_23_15"; /* Obsolete */
		case 35: return "R_ARM_LDR_SBREL_11_0_NC"; /* Deprecated */
		case 36: return "R_ARM_ALU_SBREL_19_12_NC"; /* Deprecated */
		case 37: return "R_ARM_ALU_SBREL_27_20_CK"; /* Deprecated */
		case 38: return "R_ARM_TARGET1";
		case 39: return "R_ARM_SBREL31"; /* Deprecated. */
		case 40: return "R_ARM_V4BX";
		case 41: return "R_ARM_TARGET2";
		case 42: return "R_ARM_PREL31";
		case 43: return "R_ARM_MOVW_ABS_NC";
		case 44: return "R_ARM_MOVT_ABS";
		case 45: return "R_ARM_MOVW_PREL_NC";
		case 46: return "R_ARM_MOVT_PREL";
		case 47: return "R_ARM_THM_MOVW_ABS_NC";
		case 48: return "R_ARM_THM_MOVT_ABS";
		case 49: return "R_ARM_THM_MOVW_PREL_NC";
		case 50: return "R_ARM_THM_MOVT_PREL";
		case 51: return "R_ARM_THM_JUMP19";
		case 52: return "R_ARM_THM_JUMP6";
		case 53: return "R_ARM_THM_ALU_PREL_11_0";
		case 54: return "R_ARM_THM_PC12";
		case 55: return "R_ARM_ABS32_NOI";
		case 56: return "R_ARM_REL32_NOI";
		case 57: return "R_ARM_ALU_PC_G0_NC";
		case 58: return "R_ARM_ALU_PC_G0";
		case 59: return "R_ARM_ALU_PC_G1_NC";
		case 60: return "R_ARM_ALU_PC_G1";
		case 61: return "R_ARM_ALU_PC_G2";
		case 62: return "R_ARM_LDR_PC_G1";
		case 63: return "R_ARM_LDR_PC_G2";
		case 64: return "R_ARM_LDRS_PC_G0";
		case 65: return "R_ARM_LDRS_PC_G1";
		case 66: return "R_ARM_LDRS_PC_G2";
		case 67: return "R_ARM_LDC_PC_G0";
		case 68: return "R_ARM_LDC_PC_G1";
		case 69: return "R_ARM_LDC_PC_G2";
		case 70: return "R_ARM_ALU_SB_G0_NC";
		case 71: return "R_ARM_ALU_SB_G0";
		case 72: return "R_ARM_ALU_SB_G1_NC";
		case 73: return "R_ARM_ALU_SB_G1";
		case 74: return "R_ARM_ALU_SB_G2";
		case 75: return "R_ARM_LDR_SB_G0";
		case 76: return "R_ARM_LDR_SB_G1";
		case 77: return "R_ARM_LDR_SB_G2";
		case 78: return "R_ARM_LDRS_SB_G0";
		case 79: return "R_ARM_LDRS_SB_G1";
		case 80: return "R_ARM_LDRS_SB_G2";
		case 81: return "R_ARM_LDC_SB_G0";
		case 82: return "R_ARM_LDC_SB_G1";
		case 83: return "R_ARM_LDC_SB_G2";
		case 84: return "R_ARM_MOVW_BREL_NC";
		case 85: return "R_ARM_MOVT_BREL";
		case 86: return "R_ARM_MOVW_BREL";
		case 87: return "R_ARM_THM_MOVW_BREL_NC";
		case 88: return "R_ARM_THM_MOVT_BREL";
		case 89: return "R_ARM_THM_MOVW_BREL";
		case 90: return "R_ARM_TLS_GOTDESC";
		case 91: return "R_ARM_TLS_CALL";
		case 92: return "R_ARM_TLS_DESCSEQ";
		case 93: return "R_ARM_THM_TLS_CALL";
		case 94: return "R_ARM_PLT32_ABS";
		case 95: return "R_ARM_GOT_ABS";
		case 96: return "R_ARM_GOT_PREL";
		case 97: return "R_ARM_GOT_BREL12";
		case 98: return "R_ARM_GOTOFF12";
		case 99: return "R_ARM_GOTRELAX";
		case 100: return "R_ARM_GNU_VTENTRY";
		case 101: return "R_ARM_GNU_VTINHERIT";
		case 102: return "R_ARM_THM_JUMP11"; /* Also R_ARM_THM_PC11 */
		case 103: return "R_ARM_THM_JUMP8"; /* Also R_ARM_THM_PC9 */
		case 104: return "R_ARM_TLS_GD32";
		case 105: return "R_ARM_TLS_LDM32";
		case 106: return "R_ARM_TLS_LDO32";
		case 107: return "R_ARM_TLS_IE32";
		case 108: return "R_ARM_TLS_LE32";
		case 109: return "R_ARM_TLS_LDO12";
		case 110: return "R_ARM_TLS_LE12";
		case 111: return "R_ARM_TLS_IE12GP";
		/* 112-127 R_ARM_PRIVATE_<n> */
		case 128: return "R_ARM_ME_TOO"; /* Obsolete */
		case 129: return "R_ARM_THM_TLS_DESCSEQ16";
		case 130: return "R_ARM_THM_TLS_DESCSEQ32";
		case 131: return "R_ARM_THM_GOT_BREL12";
		case 132: return "R_ARM_THM_ALU_ABS_G0_NC";
		case 133: return "R_ARM_THM_ALU_ABS_G1_NC";
		case 134: return "R_ARM_THM_ALU_ABS_G2_NC";
		case 135: return "R_ARM_THM_ALU_ABS_G3";
		/* 136-159 Reserved for future allocation. */
		case 160: return "R_ARM_IRELATIVE";
		/* 161-255 Reserved for future allocation. */
		case 249: return "R_ARM_RXPC25";
		case 250: return "R_ARM_RSBREL32";
		case 251: return "R_ARM_THM_RPC22";
		case 252: return "R_ARM_RREL32";
		case 253: return "R_ARM_RABS32";
		case 254: return "R_ARM_RPC24";
		case 255: return "R_ARM_RBASE";
		}
		break;
	case EM_IA_64:
		switch(type) {
		}
		break;
	case EM_MIPS:
		switch(type) {
		}
		break;
	case EM_PPC:
		switch(type) {
		}
		break;
	case EM_PPC64:
		switch(type) {
		}
		break;
	case EM_RISCV:
		switch(type) {
		}
		break;
	case EM_S390:
		switch (type) {
		}
		break;
	case EM_SPARC:
	case EM_SPARCV9:
		switch(type) {
		}
		break;
	case EM_X86_64:
		switch (type) {
		case 0: return "R_X86_64_NONE";
		case 1: return "R_X86_64_64";
		case 2: return "R_X86_64_PC32";
		case 3: return "R_X86_64_GOT32";
		case 4: return "R_X86_64_PLT32";
		case 5: return "R_X86_64_COPY";
		case 6: return "R_X86_64_GLOB_DAT";
		case 7: return "R_X86_64_JUMP_SLOT";
		case 8: return "R_X86_64_RELATIVE";
		case 9: return "R_X86_64_GOTPCREL";
		case 10: return "R_X86_64_32";
		case 11: return "R_X86_64_32S";
		case 12: return "R_X86_64_16";
		case 13: return "R_X86_64_PC16";
		case 14: return "R_X86_64_8";
		case 15: return "R_X86_64_PC8";
		case 16: return "R_X86_64_DTPMOD64";
		case 17: return "R_X86_64_DTPOFF64";
		case 18: return "R_X86_64_TPOFF64";
		case 19: return "R_X86_64_TLSGD";
		case 20: return "R_X86_64_TLSLD";
		case 21: return "R_X86_64_DTPOFF32";
		case 22: return "R_X86_64_GOTTPOFF";
		case 23: return "R_X86_64_TPOFF32";
		case 24: return "R_X86_64_PC64";
		case 25: return "R_X86_64_GOTOFF64";
		case 26: return "R_X86_64_GOTPC32";
		case 27: return "R_X86_64_GOT64";
		case 28: return "R_X86_64_GOTPCREL64";
		case 29: return "R_X86_64_GOTPC64";
		case 30: return "R_X86_64_GOTPLT64";
		case 31: return "R_X86_64_PLTOFF64";
		case 32: return "R_X86_64_SIZE32";
		case 33: return "R_X86_64_SIZE64";
		case 34: return "R_X86_64_GOTPC32_TLSDESC";
		case 35: return "R_X86_64_TLSDESC_CALL";
		case 36: return "R_X86_64_TLSDESC";
		case 37: return "R_X86_64_IRELATIVE";
		case 38: return "R_X86_64_RELATIVE64";
		case 41: return "R_X86_64_GOTPCRELX";
		case 42: return "R_X86_64_REX_GOTPCRELX";
		}
		break;
	}
	return NULL;
}

static const char *get_r_type(Elf *pelf, unsigned int type)
{
	GElf_Ehdr ehdr;
	if (gelf_getehdr(pelf, &ehdr) != &ehdr) {
		errx(EXIT_FAILURE, "gelf_getehdr() failed: %s.", elf_errmsg(-1));	
	}

	return r_type(ehdr.e_machine, type);
}

static const char *get_symbol_name(Elf *pelf, size_t strtabndx, GElf_Sym *sym)
{
	if (GELF_ST_TYPE(sym->st_info) == STT_SECTION) {
		// get shstrndx
		size_t shstrndx;
		if (elf_getshdrstrndx(pelf, &shstrndx) == -1) {
			errx(EXIT_FAILURE, "getshdrstrndx() failed: %s.", elf_errmsg(-1));
		}
	
		Elf_Scn *destscn;
		GElf_Shdr destshdr;
	   
		if ((destscn = elf_getscn(pelf, sym->st_shndx)) == NULL) {
			errx(EXIT_FAILURE, "elf_getscn() failed: %s.", elf_errmsg(-1));
		}
		if (gelf_getshdr(destscn, &destshdr) != &destshdr) {
			errx(EXIT_FAILURE, "gelf_getshdr() failed: %s.", elf_errmsg(-1));
		}
		return elf_strptr(pelf, shstrndx, destshdr.sh_name);
	}
	
	return  elf_strptr(pelf, strtabndx, sym->st_name);
}

static void print_rel(Elf *pelf, Elf_Scn *relscn, GElf_Shdr *relshdr)
{
	size_t sh_entsize = gelf_fsize(pelf, ELF_T_REL, 1, EV_CURRENT);
	size_t entries = relshdr->sh_size / sh_entsize;

	Elf_Data *reldata = elf_getdata(relscn, NULL);
	if (reldata == NULL) return;

	// Get the symbol table information.
	// for SHT_REL or SHT_RELA:
	// 	sh_link: The section header index
	// 			of the associated symbol table.
	// 	sh_info: The section header index
	// 			of the section to which the relocation applies.
	Elf_Scn *symscn = elf_getscn(pelf, relshdr->sh_link);
	if (symscn == NULL) return;

	GElf_Shdr symshdr;
	if (gelf_getshdr(symscn, &symshdr) != &symshdr) return;

	Elf_Data *symdata = elf_getdata(symscn, NULL);
	if (symdata == NULL) return;

	// Get the section header string table index.
	size_t shstrndx;
	if (elf_getshdrstrndx(pelf, &shstrndx) < 0) return;

	if (relshdr->sh_info != 0) {
		// Get the section header of the section the relocations are for.
		Elf_Scn *destscn = elf_getscn(pelf, relshdr->sh_info);	
		GElf_Shdr destshdr;
		if (gelf_getshdr(destscn, &destshdr) != &destshdr) {
			errx(EXIT_FAILURE,
					"gelf_getshdr() failed: %s.", elf_errmsg(-1));	
		}

		(void)printf("\nRelocation section [%2zu] '%s' "
			"for section [%2u] '%s' at offset 0x%jx contains %zu entries:\n",
			elf_ndxscn(relscn),
			elf_strptr(pelf, shstrndx, relshdr->sh_name),
			relshdr->sh_info,
			elf_strptr(pelf, shstrndx, destshdr.sh_name),
			relshdr->sh_offset, entries);
	} else {
		// The .rela.dyn section does not refer to a specific section
		(void)printf("\nRelocation section [%2zu] '%s' "
				"at offset 0x%jx contains %zu entries:\n",
				elf_ndxscn(relscn),
				elf_strptr(pelf, shstrndx, relshdr->sh_name),
				relshdr->sh_offset, entries);	
	}

	printf("%-16s %-16s %-24s %-16s %s\n", 
			"r_offset", "r_info", "r_type", "st_value", "st_name");


	for (size_t i = 0; i < entries; i++) {
		GElf_Rel rel;
		if (gelf_getrel(reldata, i, &rel) != &rel) {
			errx(EXIT_FAILURE, "gelf_getrel() failed: %s.", elf_errmsg(-1));	
		}

		printf("%16.16jx", (uintmax_t)rel.r_offset);	
		printf(" %16.16jx", (uintmax_t)rel.r_info);
		printf(" %-24.24s", get_r_type(pelf, GELF_R_TYPE(rel.r_info))); 

		GElf_Sym sym;
		if (gelf_getsym(symdata, GELF_R_SYM(rel.r_info), &sym) != &sym) {
			errx(EXIT_FAILURE, "gelf_getsym() failed: %s.", elf_errmsg(-1));	
		}
		printf(" %16.16jx", (uintmax_t)sym.st_value);
		// for SHT_SYMTAB or SHT_DYNSYM
		// 	sh_info: One greater than the symbol table index
		// 		of the last local symbol (binding STB_LOCAL) 
		//	sh_link: The section header index
		//		of the associated string table.
		printf(" %s\n", get_symbol_name(pelf, symshdr.sh_link, &sym));
	}
}

static void print_rela(Elf *pelf, Elf_Scn *relascn, GElf_Shdr *relashdr)
{
	size_t sh_entsize = gelf_fsize(pelf, ELF_T_RELA, 1, EV_CURRENT);
	size_t entries = relashdr->sh_size / sh_entsize;

	Elf_Data *reladata = elf_getdata(relascn, NULL);
	if (reladata == NULL) return;

	// Get the symbol table information.
	// for SHT_REL or SHT_RELA:
	// 	sh_link: The section header index
	// 			of the associated symbol table.
	// 	sh_info: The section header index
	// 			of the section to which the relocation applies.
	Elf_Scn *symscn = elf_getscn(pelf, relashdr->sh_link);
	if (symscn == NULL) return;

	GElf_Shdr symshdr;
	if (gelf_getshdr(symscn, &symshdr) != &symshdr) return;

	Elf_Data *symdata = elf_getdata(symscn, NULL);
	if (symdata == NULL) return;

	// Get the section header string table index.
	size_t shstrndx;
	if (elf_getshdrstrndx(pelf, &shstrndx) < 0) return;

	if (relashdr->sh_info != 0) {
		// Get the section header of the section the relocations are for.
		Elf_Scn *destscn = elf_getscn(pelf, relashdr->sh_info);	
		GElf_Shdr destshdr;
		if (gelf_getshdr(destscn, &destshdr) != &destshdr) {
			errx(EXIT_FAILURE,
					"gelf_getshdr() failed: %s.", elf_errmsg(-1));	
		}

		(void)printf("\nRelocation section [%2zu] '%s' "
			"for section [%2u] '%s' at offset 0x%jx contains %zu entries:\n",
			elf_ndxscn(relascn),
			elf_strptr(pelf, shstrndx, relashdr->sh_name),
			relashdr->sh_info,
			elf_strptr(pelf, shstrndx, destshdr.sh_name),
			relashdr->sh_offset, entries);
	} else {
		// The .rela.dyn section does not refer to a specific section
		(void)printf("\nRelocation section [%2zu] '%s' "
				"at offset 0x%jx contains %zu entries:\n",
				elf_ndxscn(relascn),
				elf_strptr(pelf, shstrndx, relashdr->sh_name),
				relashdr->sh_offset, entries);	
	}

	printf("%-16s %-16s %-24s %-16s %s\n", 
			"r_offset", "r_info", "r_type", "st_value", "st_name + r_addend");

	for (size_t i = 0; i < entries; i++) {
		GElf_Rela rela;
		if (gelf_getrela(reladata, i, &rela) != &rela) {
			errx(EXIT_FAILURE, "gelf_getrela() failed: %s.", elf_errmsg(-1));	
		}

		printf("%16.16jx", (uintmax_t)rela.r_offset);	
		printf(" %16.16jx", (uintmax_t)rela.r_info);
		printf(" %-24.24s", get_r_type(pelf, GELF_R_TYPE(rela.r_info))); 

		GElf_Sym sym;
		if (gelf_getsym(symdata, GELF_R_SYM(rela.r_info), &sym) != &sym) {
			errx(EXIT_FAILURE, "gelf_getsym() failed: %s.", elf_errmsg(-1));	
		}
		printf(" %16.16jx", (uintmax_t)sym.st_value);
		// for SHT_SYMTAB or SHT_DYNSYM
		// 	sh_info: One greater than the symbol table index
		// 		of the last local symbol (binding STB_LOCAL) 
		//	sh_link: The section header index
		//		of the associated string table.
		printf(" %s", get_symbol_name(pelf, symshdr.sh_link, &sym));
		printf(" + %jx\n", (uintmax_t)rela.r_addend);
	}
}

int main(int argc, const char *argv[])
{
	int fd, class;
	Elf *pelf = NULL;
	Elf_Scn *scn = NULL;

// [ 9] .rela.dyn         RELA            0000000000000568 000568 0000d8 18   A  5   0  8
// [10] .rela.plt         RELA            0000000000000640 000640 0000f0 18  AI  5  22  8

	if (argc != 2)
		errx(EXIT_FAILURE, "usage: %s file-name", argv[0]);

	if (elf_version(EV_CURRENT) == EV_NONE)
		errx(EXIT_FAILURE, "ELF library initializztion "
			"failed: %s", elf_errmsg(-1));

	if ((fd = open(argv[1], O_RDONLY, 0)) < 0)
		errx(EXIT_FAILURE, "open \"%s\" failed", argv[1]);

	if (!(pelf = elf_begin(fd, ELF_C_READ, NULL)))
		errx(EXIT_FAILURE, "elf_begin() failed: %s", elf_errmsg(-1));

	if (elf_kind(pelf) != ELF_K_ELF)
		errx(EXIT_FAILURE, "\"%s\" is not an ELF object.", argv[1]);

	// get elf class ()
	if ((class = gelf_getclass(pelf)) == ELFCLASSNONE)
		errx(EXIT_FAILURE, "getclass() failed: %s.", elf_errmsg(-1));

	while ((scn = elf_nextscn(pelf, scn)) != NULL) {
		GElf_Shdr shdr;
		if (gelf_getshdr(scn, &shdr) != &shdr) {
			errx(EXIT_FAILURE, "gelf_getshdr() failed: %s.", elf_errmsg(-1));
		}

		if (shdr.sh_type == SHT_REL) {
			print_rel(pelf, scn, &shdr);	
		}

		if (shdr.sh_type == SHT_RELA) {
			print_rela(pelf, scn, &shdr);
		}
	}	

	(void)elf_end(pelf);
	(void)close(fd);

	exit(EXIT_SUCCESS);
}

学习链接:

Relocation》《Relocation Sections

Understanding Symbols Relocation

Relocations, Relocations

Relocation Types

ELF函数重定位问题

Resolving ELF Relocation Name / Symbols

RELRO: RELocation Read-Only

How to Hijack the Global Offset Table with pointers

RELRO分析》《Binary Exploitation Series (7): Full RelRO Bypass

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值