This example that demonstrates how to use libelf operations to relocate addresses within the ELF file.
#include <stdlib.h>
#include "libelf/libelf.h"
/*********************************************************************
Structure used to keep track of information within ELF
*********************************************************************/
typedef unsigned long long int uint64;
typedef signed long long int int64;
typedef char bool;
/* ELF symbol details
*/
typedef struct ElfSymbol_s {
char* es_name; /* ELF symbol: name */
uint64 es_value; /* value */
uint64 es_size; /* size */
unsigned char es_type; /* type */
unsigned char es_bind; /* bind */
unsigned char es_other; /* other */
int64 es_shndx; /* ELF section index */
} *ElfSymbol;
/* ELF file details
*/
typedef struct ElfDetails_s {
Elf* ed_elf; /* ->ELF instance for CU */
bool ed_is_64bit; /* 64-bit: true */
/* 32-bit: false */
/* ELF Section details */
Elf_Scn** ed_elf_scns; /* List of ->ELF scn objects */
char** ed_scn_names; /* List of ELF section names */
int64* ed_infos; /* List of section sh_info values */
char** ed_datas; /* List of ->section data buffer */
uint64* ed_data_sizes; /* List of length of section data */
int64 ed_n_elf_scns; /* Number of ELF sections */
int64 ed_text_idx; /* .text section index */
int64 ed_symtab_idx; /* .symtab section index */
int64 ed_strtab_idx; /* .strtab section index */
int64 ed_shstrtab_idx; /* .shstrtab section index */
/* ELF Symbol details */
ElfSymbol ed_symbols; /* List of ->ELF symbol info */
uint64 ed_n_symbols; /* Number of ELF symbols */
} *ElfDetails;
/*--< Local Routines >-----------------------------------------------------*/
/* Examine ELF descriptor and find out all information necessary
for relocating the .dbg.
All information are stored in 'ret_details'.
'ret_details' is deallocated with _load_elf_term
*/
static int
_load_elf_file_details(
Elf* elf, /* ->ELF instance for CU I*/
ElfDetails* ret_details); /* ->returned ELF file details O*/
/* Terminate ELF loader processing, release resources
*/
static int
_load_elf_term(
ElfDetails details); /* ELF file details I*/
/* Load 64-bit ELF symbol table
*/
static int
_load_elf64_symbol_table(
ElfDetails details); /* ELF file details I*/
/* Load 32-bit ELF symbol table
*/
static int
_load_elf32_symbol_table(
ElfDetails details); /* ELF file details I*/
/* Given the 16 byte raw MD5 signature, verify that it matches the loaded
.dbg file
*/
static int
_validate_MD5_signature(
ElfDetails details, /* ELF file details I*/
unsigned char digest[16]); /* PPA2 MD5 signature I*/
/* Relocate the ELF sections based on the information in 'reloc_adj'
*/
static int
_relocate_elf_sections(
ElfDetails details, /* ELF file details I*/
int64* reloc_adj); /* Adjustment array I*/
/*--< Relocation main routines >-------------------------------------------*/
int
relocate_elf_load_cu(
Elf* elf, /* ->ELF instance for CU IO*/
unsigned char md5_sig[16]); /* MD5 signature I*/
/*--< FUNCTION IMPLEMENTATION >--------------------------------------------*/
int main ()
{
Elf* elf;
FILE *fp;
int rc;
unsigned char md5[16] = { 0x3C, 0x2C, 0x96, 0x82,
0x22, 0xFF, 0xB7, 0x24,
0x2B, 0x52, 0x53, 0x00,
0x65, 0x01, 0xF6, 0x0F };
elf_version (EV_CURRENT);
fp = fopen ("mytest.dbg", "rb");
elf = elf_begin_b (fp, ELF_C_READ, NULL);
rc = relocate_elf_load_cu (elf, md5);
printf ("rc should be zero: %d\n", rc);
elf_end(elf);
}
#pragma convert ("ISO8859-1")
/* Load ELF file and relocate .text to given address(es)
*/
int
relocate_elf_load_cu(
Elf* elf, /* ->ELF instance for CU IO*/
unsigned char md5_sig[16]) { /* MD5 signature I*/
ElfDetails details;
int64* reloc_adj; /* An array keeping track of
address adjustment needed for
each .text symid */
int i;
int rc;
/* Load ELF file section and symbol tables */
rc = _load_elf_file_details(elf, &details;);
if (rc) return rc;
/* Validate MD5 signature */
rc = _validate_MD5_signature(details, md5_sig);
if (rc) return rc;
/* TO BE FILLED IN: create reloc_adj array */
/* This will relocate 0x37b8 to 0xDEADBEEF */
reloc_adj = (int64*) calloc (sizeof(int64), details->ed_n_symbols);
reloc_adj[2] = 0xDEADBEEF;
/* Relocate the ELF sections based on the current section origins */
rc = _relocate_elf_sections(details, reloc_adj);
if (rc) return rc;
/* Processing complete. Remove temporary tables */
rc = _load_elf_term(details);
if (rc) return rc;
/* Terminate reloc_adj */
free (reloc_adj);
return 0;
}
/* Load ELF file section and symbol tables
*/
static int
_load_elf_file_details(
Elf* elf, /* ->ELF instance for CU I*/
ElfDetails* ret_details) { /* ->returned ELF file details O*/
ElfDetails details;
char* ehdr_ident;
Elf32_Ehdr* ehdr32;
Elf64_Ehdr* ehdr64;
Elf32_Shdr* shdr32;
Elf64_Shdr* shdr64;
Elf_Scn* scn;
Elf_Data* data;
char* scn_name;
Elf_Scn** section_list;
char** name_list;
char** data_list;
uint64* data_size_list;
int64* info_list;
int64 scn_idx,
n_elf_scns,
shstrtab_idx;
int rc,
is_64bit,
elf_machine;
/* Determine if 64-bit or 32-bit ELF file */
if ((ehdr_ident = elf_getident(elf, NULL)) == NULL) {
return -1; /* ERROR */
}
is_64bit = (ehdr_ident[EI_CLASS] == ELFCLASS64);
/* Access the ELF file header */
if (is_64bit) {
if ((ehdr64 = elf64_getehdr(elf)) == NULL) {
return -1; /* ERROR */
}
elf_machine = ehdr64->e_machine;
n_elf_scns = ehdr64->e_shnum + 1; /* Allow for section 0 */
shstrtab_idx = ehdr64->e_shstrndx;
}
else {
if ((ehdr32 = elf32_getehdr(elf)) == NULL) {
return -1; /* ERROR */
}
elf_machine = ehdr32->e_machine;
n_elf_scns = ehdr32->e_shnum + 1; /* Allow for section 0 */
shstrtab_idx = ehdr32->e_shstrndx;
}
/* Validate machine type */
if (elf_machine != EM_S390) {
return -1; /* ERROR */
}
/* Allocate the new ElfDetails object */
if (n_elf_scns == 0) {
return -1; /* ERROR */
}
details = (ElfDetails) calloc (sizeof(struct ElfDetails_s), 1);
if (details == NULL) {
return -2; /* out of memory */
}
/* Initialize the new object */
details->ed_elf = elf;
details->ed_n_elf_scns = n_elf_scns;
details->ed_shstrtab_idx = shstrtab_idx;
if (is_64bit) {
details->ed_is_64bit = 1;
}
/* Allocate list object (array of Dwarf_Ptr) for the ELF sections */
section_list = (Elf_Scn**) calloc (sizeof(Elf_Scn*), n_elf_scns);
if (section_list == NULL) {
return -2; /* out of memory */
}
details->ed_elf_scns = section_list;
/* Allocate list object (array of char*) for the ELF section names */
name_list = (char**) calloc (sizeof(char*), n_elf_scns);
if (name_list == NULL) {
return -2; /* out of memory */
}
details->ed_scn_names = name_list;
/* Allocate list object (array of Dwarf_Ptr) for section data addrs */
data_list = (char**) calloc (sizeof(char*), n_elf_scns);
if (data_list == NULL) {
return -2; /* out of memory */
}
details->ed_datas = data_list;
/* Allocate addr object (array of uint64) for section data lengths */
data_size_list = (uint64*) calloc (sizeof(uint64), n_elf_scns);
if (data_size_list == NULL) {
return -2; /* out of memory */
}
details->ed_data_sizes = data_size_list;
/* Allocate addr object (array of int64) for section sh_info */
info_list = (int64*) calloc (sizeof(int64), n_elf_scns);
if (info_list == NULL) {
return -2; /* out of memory */
}
details->ed_infos = info_list;
/* Populate the ELF section lists */
scn_idx = 0;
scn = NULL;
while ((scn = elf_nextscn(elf,scn)) != NULL) {
/* Save ELF section for section symbol lookup */
scn_idx = elf_ndxscn(scn);
if (scn_idx < n_elf_scns) {
section_list[scn_idx] = scn;
}
else {
return -1; /* ERROR */
}
/* Process ELF section header */
if (is_64bit) {
if ((shdr64 = elf64_getshdr(scn)) == NULL) {
return -1; /* ERROR */
}
/* Get section name */
if ((scn_name = elf_strptr(elf,
shstrtab_idx,
shdr64->sh_name)) == NULL) {
return -1; /* ERROR */
}
info_list[scn_idx] = shdr64->sh_info;
}
else {
if ((shdr32 = elf32_getshdr(scn)) == NULL) {
return -1; /* ERROR */
}
/* Get section name */
if ((scn_name = elf_strptr(elf,
shstrtab_idx,
shdr32->sh_name)) == NULL) {
return -1; /* ERROR */
}
info_list[scn_idx] = shdr32->sh_info;
}
/* Note ELF Section names */
name_list[scn_idx] = scn_name;
/* Note index of ELF .text, .symtab, .strtab and .shstrtab sections */
if (strcmp(scn_name,".text") == 0) {
/* Validate .text is z/OS DWARF in ELF packing */
if (is_64bit) {
if (shdr64->sh_type != SHT_NOBITS) {
return -1; /* ERROR */
}
}
else {
if (shdr32->sh_type != SHT_NOBITS) {
return -1; /* ERROR */
}
}
/* Validate there is only 1 .text section */
if (details->ed_text_idx != 0) {
return -1; /* ERROR */
}
details->ed_text_idx = scn_idx;
}
else if (strcmp(scn_name,".symtab") == 0) {
/* Validate there is only 1 .symtab section */
if (details->ed_symtab_idx != 0) {
return -1; /* ERROR */
}
details->ed_symtab_idx = scn_idx;
}
else if (strcmp(scn_name,".strtab") == 0) {
/* Validate there is only 1 .strtab section */
if (details->ed_strtab_idx != 0) {
return -1; /* ERROR */
}
details->ed_strtab_idx = scn_idx;
}
else if (strcmp(scn_name,".shstrtab") == 0) {
/* Validate there is only 1 .shstrtab section */
if (details->ed_shstrtab_idx != scn_idx) {
return -1; /* ERROR */
}
}
/* Prepare to read ELF section Data */
if ((data = elf_getdata(scn, 0)) != NULL) {
data_list[scn_idx] = data->d_buf;
data_size_list[scn_idx] = data->d_size;
}
}
/* Ensure the file has all required sections */
if ((details->ed_text_idx == 0) ||
(details->ed_symtab_idx == 0) ||
(details->ed_strtab_idx == 0) ||
(details->ed_shstrtab_idx == 0)) {
return -1; /* ERROR */
}
/* Create the symbol table from the ELF .symtab section */
if (details->ed_is_64bit) {
rc = _load_elf64_symbol_table(details);
}
else {
rc = _load_elf32_symbol_table(details);
}
if (rc) return rc;
/* Return the ElfDetails object to the caller */
*ret_details = details;
return 0;
}
/* Terminate ELF loader processing, release resources
*/
static int
_load_elf_term(
ElfDetails details) { /* ELF file details I*/
/* Delete the resources for this ElfDetails object */
if (details->ed_elf_scns != NULL) {
free (details->ed_elf_scns);
}
if (details->ed_datas != NULL) {
free (details->ed_datas);
}
if (details->ed_data_sizes != NULL) {
free (details->ed_data_sizes);
}
if (details->ed_symbols != NULL) {
free (details->ed_symbols);
}
free (details);
return 0;
}
/* Load 64-bit ELF symbol table
*/
static int
_load_elf64_symbol_table(
ElfDetails details) { /* ELF file details I*/
Elf* elf;
Elf64_Shdr* shdr64;
Elf64_Sym* symtab;
ElfSymbol symbols,
cur_sym;
uint64 link,
shstrtab_idx;
uint64 n_symbols,
i;
elf = details->ed_elf;
if (elf == NULL) {
return -1; /* ERROR */
}
shstrtab_idx = details->ed_shstrtab_idx;
/* Allocate the array of ElfSymbol objects */
n_symbols = (details->ed_data_sizes[details->ed_symtab_idx]) /
sizeof(Elf64_Sym);
if (n_symbols == 0) {
return -1; /* ERROR */
}
symbols = (ElfSymbol) calloc (sizeof(struct ElfSymbol_s), n_symbols);
if (symbols == NULL) {
return -2; /* Out of memory */
}
details->ed_symbols = symbols;
details->ed_n_symbols = n_symbols;
/* Process the 64-bit .symtab section */
cur_sym = symbols;
symtab = (Elf64_Sym*)(details->ed_datas[details->ed_symtab_idx]);
link = details->ed_strtab_idx;
for (i = 0;
i < n_symbols;
i++, cur_sym++, symtab++) {
cur_sym->es_value = symtab->st_value;
cur_sym->es_size = symtab->st_size;
cur_sym->es_type = ELF64_ST_TYPE(symtab->st_info);
cur_sym->es_bind = ELF64_ST_BIND(symtab->st_info);
cur_sym->es_other = symtab->st_other;
cur_sym->es_shndx = symtab->st_shndx;
if (symtab->st_name == 0) {
if (cur_sym->es_type == STT_SECTION) {
if (cur_sym->es_shndx == SHN_UNDEF) {
cur_sym->es_name = "undef";
}
else if (cur_sym->es_shndx == SHN_ABS) {
cur_sym->es_name = "abs";
}
else if (cur_sym->es_shndx == SHN_COMMON) {
cur_sym->es_name = "common";
}
else if (cur_sym->es_shndx < details->ed_n_elf_scns) {
/* Get ELF section header */
shdr64 = elf64_getshdr(details->ed_elf_scns[cur_sym->es_shndx]);
if (shdr64 == NULL) {
return -1; /* ERROR */
}
/* Get ELF section name */
cur_sym->es_name = elf_strptr(details->ed_elf,
shstrtab_idx,
shdr64->sh_name);
}
else {
cur_sym->es_name = "<Unknown section="">";
}
}
else {
/* Not section... note NULL */
cur_sym->es_name = "<NULL>";
}
}
else {
cur_sym->es_name = elf_strptr(details->ed_elf,
link,
symtab->st_name);
}
if (cur_sym->es_name == NULL) {
return -1; /* ERROR */
}
}
return 0;
}
/* Load 32-bit ELF symbol table
*/
static int
_load_elf32_symbol_table(
ElfDetails details) { /* ELF file details I*/
Elf* elf;
Elf32_Shdr* shdr32;
Elf32_Sym* symtab;
ElfSymbol symbols,
cur_sym;
uint64 link,
shstrtab_idx;
uint64 n_symbols,
i;
elf = details->ed_elf;
if (elf == NULL) {
return -1; /* ERROR */
}
shstrtab_idx = details->ed_shstrtab_idx;
/* Allocate the array of ElfSymbol objects */
n_symbols = (details->ed_data_sizes[details->ed_symtab_idx]) /
sizeof(Elf32_Sym);
if (n_symbols == 0) {
return -1; /* ERROR */
}
symbols = (ElfSymbol) calloc (sizeof(struct ElfSymbol_s), n_symbols);
if (symbols == NULL) {
return -2; /* Out of memory */
}
details->ed_symbols = symbols;
details->ed_n_symbols = n_symbols;
/* Process the 32-bit .symtab section */
cur_sym = symbols;
symtab = (Elf32_Sym*)(details->ed_datas[details->ed_symtab_idx]);
link = details->ed_strtab_idx;
for (i = 0;
i < n_symbols;
i++, cur_sym++, symtab++) {
cur_sym->es_value = symtab->st_value;
cur_sym->es_size = symtab->st_size;
cur_sym->es_type = ELF32_ST_TYPE(symtab->st_info);
cur_sym->es_bind = ELF32_ST_BIND(symtab->st_info);
cur_sym->es_other = symtab->st_other;
cur_sym->es_shndx = symtab->st_shndx;
if (symtab->st_name == 0) {
if (cur_sym->es_type == STT_SECTION) {
if (cur_sym->es_shndx == SHN_UNDEF) {
cur_sym->es_name = "undef";
}
else if (cur_sym->es_shndx == SHN_ABS) {
cur_sym->es_name = "abs";
}
else if (cur_sym->es_shndx == SHN_COMMON) {
cur_sym->es_name = "common";
}
else if (cur_sym->es_shndx < details->ed_n_elf_scns) {
/* Get ELF section header */
shdr32 = elf32_getshdr(details->ed_elf_scns[cur_sym->es_shndx]);
if (shdr32 == NULL) {
return -1; /* ERROR */
}
/* Get ELF section name */
cur_sym->es_name = elf_strptr(details->ed_elf,
shstrtab_idx,
shdr32->sh_name);
}
else {
cur_sym->es_name = "<Unknown section="">";
}
}
else {
/* Not section... note NULL */
cur_sym->es_name = "<NULL>";
}
}
else {
cur_sym->es_name = elf_strptr(details->ed_elf,
link,
symtab->st_name);
}
if (cur_sym->es_name == NULL) {
return -1; /* ERROR */
}
}
return 0;
}
/* Given the 16 byte raw MD5 signature, verify that it matches the loaded
.dbg file
*/
static int
_validate_MD5_signature(
ElfDetails details, /* ELF file details I*/
unsigned char digest[16]) { /* PPA2 MD5 signature I*/
ElfSymbol symbols,
cur_sym;
unsigned char md5_chars[32+1];
unsigned char* sym_name;
uint64 n_symbols,
i,
pos;
symbols = details->ed_symbols;
n_symbols = details->ed_n_symbols;
if ((symbols == NULL) ||
(n_symbols == 0)) {
return -1; /* ERROR */
}
/* Generate text for MD5 signature portion of symbol */
for (i = 0, pos = 0; i < 16; i++ ) {
const char * convstring = "0123456789ABCDEF";
char top_nibble,
bottom_nibble;
top_nibble = digest[i] >> 4;
bottom_nibble = digest[i] & 0x0F;
md5_chars[pos] = convstring[top_nibble];
pos++;
md5_chars[pos] = convstring[bottom_nibble];
pos++;
}
md5_chars[pos] = 0x00;
/* Scan the symbol table for the first symobl in .text that resemble MD5 signature */
for (i = 0, cur_sym = symbols;
i < n_symbols;
i++, cur_sym++) {
const int sym_name_len = strlen(cur_sym->es_name);
sym_name = cur_sym->es_name;
if (cur_sym->es_shndx == details->ed_text_idx &&
sym_name_len >= 32 &&
!strcmp(sym_name+sym_name_len-32, md5_chars)) {
/* matching MD5 signature found */
return 0;
}
}
/* MD5 signature not found */
return -1;
}
/* Relocate the ELF sections based on the relocation adjustments array
'reloc_adj' is an array containing adjustments that needs to be
made to each corresponding relocation entry.
For example:
Typical .symtab entries:
Sym 2: value= 0x000, ..., name= .MD5_3FD489E1D88CB743682E3A44875A1765
Sym 3: value= 0x010, ..., name= func1
Sym 4: value= 0x020, ..., name= func2
Sym 5: value= 0x050, ..., name= func3
If all relocation base on sym 2, and it needs to adjust to 0xDEADBEEF, then
'reloc_adj' would contain:
{ 0, 0, 0xDEADBEEF, 0, 0, 0 }
^-- index 2 correspond to sym 2
*/
static int
_relocate_elf_sections(
ElfDetails details, /* ELF file details I*/
int64* reloc_adj) { /* .text relocation adjustments I*/
ElfSymbol symbols,
cur_sym;
uint64 reloc_offset;
uint64 reloc_sym;
int64 reloc_scn;
char** scn_names;
int64* infos;
unsigned int reloc_type;
char* scn_name,
* relscn_name,
* sym_name,
* reloc_scn_name,
* reloc_name;
char** datas;
uint64* data_sizes;
int64 relscn_idx;
char* reloc_data;
char* relscn_data;
uint64 reloc_data_size,
reloc_data_off,
relscn_data_size;
int64 n_elf_scns,
change;
uint64 n_symbols,
i;
n_elf_scns = details->ed_n_elf_scns;
n_symbols = details->ed_n_symbols;
scn_names = details->ed_scn_names;
symbols = details->ed_symbols;
datas = details->ed_datas;
data_sizes = details->ed_data_sizes;
infos = details->ed_infos;
if ((n_symbols == 0) ||
(n_elf_scns == 0) ||
(scn_names == NULL) ||
(symbols == NULL) ||
(datas == NULL) ||
(data_sizes == NULL)) {
return -1; /* ERROR */
}
/* Scan section lists, processing SHT_REL-format relocation sections */
for (i = 1;
i < n_elf_scns;
i++) {
/* Check for ELF SHT_REL-format section */
scn_name = scn_names[i];
if (strncmp(scn_name, ".rel.",5) == 0) {
/* Access relocation section info */
reloc_data = datas[i];
reloc_data_size = data_sizes[i];
/* Access data section info */
relscn_idx = infos[i];
relscn_name = scn_names[relscn_idx];
relscn_data = datas[relscn_idx];
relscn_data_size = data_sizes[relscn_idx];
if (details->ed_is_64bit) {
/* Relocate all R_390_64 type relocation entries */
for (reloc_data_off = 0;
reloc_data_off < reloc_data_size;
reloc_data_off += sizeof(Elf64_Rel)) {
Elf64_Rel* p = (Elf64_Rel*)(reloc_data + reloc_data_off);
reloc_offset = p->r_offset;
reloc_sym = ELF64_R_SYM(p->r_info);
if (reloc_sym >= n_symbols) {
return -1; /* ERROR */
}
cur_sym = symbols + reloc_sym;
reloc_scn = cur_sym->es_shndx;
if (reloc_scn >= n_elf_scns) {
return -1; /* ERROR */
}
reloc_type = ELF64_R_TYPE(p->r_info);
switch (reloc_type) {
case R_390_NONE :
/* No adjustment required... likely DWARF info */
break;
case R_390_32 : {
/* Check for relocation adjustment */
signed int* relscn_ptr;
signed int reloc_item;
change = reloc_adj[reloc_sym];
if (change != 0) {
relscn_ptr = (signed int*)(relscn_data + reloc_offset);
reloc_item = *relscn_ptr;
*relscn_ptr = reloc_item + change;
}
}
break;
case R_390_64 : {
/* Check for relocation adjustment */
int64* relscn_ptr;
int64 reloc_item;
change = reloc_adj[reloc_sym];
if (change != 0) {
relscn_ptr = (int64*)(relscn_data + reloc_offset);
reloc_item = *relscn_ptr;
*relscn_ptr = reloc_item + change;
}
}
break;
default :
return -1; /* ERROR */
}
}
}
else {
/* Relocate all R_390_32 type relocation entries */
for (reloc_data_off = 0;
reloc_data_off < reloc_data_size;
reloc_data_off += sizeof(Elf32_Rel)) {
Elf32_Rel* p = (Elf32_Rel*)(reloc_data + reloc_data_off);
reloc_offset = p->r_offset;
reloc_sym = ELF32_R_SYM(p->r_info);
if (reloc_sym >= n_symbols) {
return -1; /* ERROR */
}
cur_sym = symbols + reloc_sym;
fflush(NULL);
reloc_scn = cur_sym->es_shndx;
if (reloc_scn >= n_elf_scns) {
return -1; /* ERROR */
}
reloc_type = ELF32_R_TYPE(p->r_info);
switch (reloc_type) {
case R_390_NONE :
/* No adjustment required... likely DWARF info */
break;
case R_390_32 : {
/* Check for relocation adjustment */
signed int* relscn_ptr;
signed int reloc_item;
change = reloc_adj[reloc_sym];
if (change != 0) {
relscn_ptr = (signed int*)(relscn_data + reloc_offset);
reloc_item = *relscn_ptr;
*relscn_ptr = reloc_item + change;
}
}
break;
case R_390_64 : {
/* Check for relocation adjustment */
int64* relscn_ptr;
int64 reloc_item;
change = reloc_adj[reloc_sym];
if (change != 0) {
relscn_ptr = (int64*)(relscn_data + reloc_offset);
reloc_item = *relscn_ptr;
*relscn_ptr = reloc_item + change;
}
}
break;
default :
return -1; /* ERROR */
}
}
}
}
}
return 0;
}
#pragma convert (0)