64bit elf support

enable parsing 64bit elf files

Change-Id: I7981f4769cf1b822f288fe2e32166254e4394bab
This commit is contained in:
Johann 2011-02-23 17:07:35 -05:00
parent e6948bf0f9
commit ddd260eb62

View File

@ -218,7 +218,7 @@ bail:
return EXIT_FAILURE; return EXIT_FAILURE;
} }
#else #elif defined(__ELF__)
#include "elf.h" #include "elf.h"
#define COPY_STRUCT(dst, buf, ofst, sz) do {\ #define COPY_STRUCT(dst, buf, ofst, sz) do {\
@ -237,212 +237,420 @@ bail:
typedef struct typedef struct
{ {
uint8_t *buf; /* Buffer containing ELF data */ uint8_t *buf; /* Buffer containing ELF data */
size_t sz; /* Buffer size */ size_t sz; /* Buffer size */
int le_data; /* Data is little-endian */ int le_data; /* Data is little-endian */
Elf32_Ehdr hdr; unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
int bits; /* 32 or 64 */
Elf32_Ehdr hdr32;
Elf64_Ehdr hdr64;
} elf_obj_t; } elf_obj_t;
int parse_elf32_header(elf_obj_t *elf) int parse_elf_header(elf_obj_t *elf)
{ {
int res; int res;
/* Verify ELF32 header */ /* Verify ELF Magic numbers */
COPY_STRUCT(&elf->hdr, elf->buf, 0, elf->sz); COPY_STRUCT(&elf->e_ident, elf->buf, 0, elf->sz);
res = elf->hdr.e_ident[EI_MAG0] == ELFMAG0; res = elf->e_ident[EI_MAG0] == ELFMAG0;
res &= elf->hdr.e_ident[EI_MAG1] == ELFMAG1; res &= elf->e_ident[EI_MAG1] == ELFMAG1;
res &= elf->hdr.e_ident[EI_MAG2] == ELFMAG2; res &= elf->e_ident[EI_MAG2] == ELFMAG2;
res &= elf->hdr.e_ident[EI_MAG3] == ELFMAG3; res &= elf->e_ident[EI_MAG3] == ELFMAG3;
res &= elf->hdr.e_ident[EI_CLASS] == ELFCLASS32; res &= elf->e_ident[EI_CLASS] == ELFCLASS32
res &= elf->hdr.e_ident[EI_DATA] == ELFDATA2LSB || elf->e_ident[EI_CLASS] == ELFCLASS64;
|| elf->hdr.e_ident[EI_DATA] == ELFDATA2MSB; res &= elf->e_ident[EI_DATA] == ELFDATA2LSB;
if (!res) goto bail; if (!res) goto bail;
elf->le_data = elf->hdr.e_ident[EI_DATA] == ELFDATA2LSB; elf->le_data = elf->e_ident[EI_DATA] == ELFDATA2LSB;
ENDIAN_ASSIGN_IN_PLACE(elf->hdr.e_type); /* Read in relevant values */
ENDIAN_ASSIGN_IN_PLACE(elf->hdr.e_machine); if (elf->e_ident[EI_CLASS] == ELFCLASS32)
ENDIAN_ASSIGN_IN_PLACE(elf->hdr.e_version);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr.e_entry);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr.e_phoff);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr.e_shoff);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr.e_flags);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr.e_ehsize);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr.e_phentsize);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr.e_phnum);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr.e_shentsize);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr.e_shnum);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr.e_shstrndx);
return 0;
bail:
return 1;
}
int parse_elf32_section(elf_obj_t *elf, int idx, Elf32_Shdr *hdr)
{
if (idx >= elf->hdr.e_shnum)
goto bail;
COPY_STRUCT(hdr, elf->buf, elf->hdr.e_shoff + idx * elf->hdr.e_shentsize,
elf->sz);
ENDIAN_ASSIGN_IN_PLACE(hdr->sh_name);
ENDIAN_ASSIGN_IN_PLACE(hdr->sh_type);
ENDIAN_ASSIGN_IN_PLACE(hdr->sh_flags);
ENDIAN_ASSIGN_IN_PLACE(hdr->sh_addr);
ENDIAN_ASSIGN_IN_PLACE(hdr->sh_offset);
ENDIAN_ASSIGN_IN_PLACE(hdr->sh_size);
ENDIAN_ASSIGN_IN_PLACE(hdr->sh_link);
ENDIAN_ASSIGN_IN_PLACE(hdr->sh_info);
ENDIAN_ASSIGN_IN_PLACE(hdr->sh_addralign);
ENDIAN_ASSIGN_IN_PLACE(hdr->sh_entsize);
return 0;
bail:
return 1;
}
char *parse_elf32_string_table(elf_obj_t *elf, int s_idx, int idx)
{
Elf32_Shdr shdr;
if (parse_elf32_section(elf, s_idx, &shdr))
{ {
log_msg("Failed to parse ELF string table: section %d, index %d\n", elf->bits = 32;
s_idx, idx); COPY_STRUCT(&elf->hdr32, elf->buf, 0, elf->sz);
return "";
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_type);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_machine);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_version);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_entry);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_phoff);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_shoff);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_flags);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_ehsize);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_phentsize);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_phnum);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_shentsize);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_shnum);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_shstrndx);
}
else /* if (elf->e_ident[EI_CLASS] == ELFCLASS64) */
{
elf->bits = 64;
COPY_STRUCT(&elf->hdr64, elf->buf, 0, elf->sz);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_type);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_machine);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_version);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_entry);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_phoff);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_shoff);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_flags);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_ehsize);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_phentsize);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_phnum);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_shentsize);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_shnum);
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_shstrndx);
} }
return (char *)(elf->buf + shdr.sh_offset + idx); return 0;
bail:
log_msg("Failed to parse ELF file header");
return 1;
} }
int parse_elf32_symbol(elf_obj_t *elf, unsigned int ofst, Elf32_Sym *sym) int parse_elf_section(elf_obj_t *elf, int idx, Elf32_Shdr *hdr32, Elf64_Shdr *hdr64)
{ {
COPY_STRUCT(sym, elf->buf, ofst, elf->sz); if (hdr32)
ENDIAN_ASSIGN_IN_PLACE(sym->st_name); {
ENDIAN_ASSIGN_IN_PLACE(sym->st_value); if (idx >= elf->hdr32.e_shnum)
ENDIAN_ASSIGN_IN_PLACE(sym->st_size); goto bail;
ENDIAN_ASSIGN_IN_PLACE(sym->st_info);
ENDIAN_ASSIGN_IN_PLACE(sym->st_other); COPY_STRUCT(hdr32, elf->buf, elf->hdr32.e_shoff + idx * elf->hdr32.e_shentsize,
ENDIAN_ASSIGN_IN_PLACE(sym->st_shndx); elf->sz);
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_name);
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_type);
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_flags);
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_addr);
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_offset);
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_size);
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_link);
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_info);
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_addralign);
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_entsize);
}
else /* if (hdr64) */
{
if (idx >= elf->hdr64.e_shnum)
goto bail;
COPY_STRUCT(hdr64, elf->buf, elf->hdr64.e_shoff + idx * elf->hdr64.e_shentsize,
elf->sz);
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_name);
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_type);
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_flags);
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_addr);
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_offset);
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_size);
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_link);
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_info);
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_addralign);
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_entsize);
}
return 0; return 0;
bail: bail:
return 1; return 1;
} }
int parse_elf32(uint8_t *buf, size_t sz, output_fmt_t mode) char *parse_elf_string_table(elf_obj_t *elf, int s_idx, int idx)
{ {
elf_obj_t elf; if (elf->bits == 32)
Elf32_Shdr shdr; {
Elf32_Shdr shdr;
if (parse_elf_section(elf, s_idx, &shdr, NULL))
{
log_msg("Failed to parse ELF string table: section %d, index %d\n",
s_idx, idx);
return "";
}
return (char *)(elf->buf + shdr.sh_offset + idx);
}
else /* if (elf->bits == 64) */
{
Elf64_Shdr shdr;
if (parse_elf_section(elf, s_idx, NULL, &shdr))
{
log_msg("Failed to parse ELF string table: section %d, index %d\n",
s_idx, idx);
return "";
}
return (char *)(elf->buf + shdr.sh_offset + idx);
}
}
int parse_elf_symbol(elf_obj_t *elf, unsigned int ofst, Elf32_Sym *sym32, Elf64_Sym *sym64)
{
if (sym32)
{
COPY_STRUCT(sym32, elf->buf, ofst, elf->sz);
ENDIAN_ASSIGN_IN_PLACE(sym32->st_name);
ENDIAN_ASSIGN_IN_PLACE(sym32->st_value);
ENDIAN_ASSIGN_IN_PLACE(sym32->st_size);
ENDIAN_ASSIGN_IN_PLACE(sym32->st_info);
ENDIAN_ASSIGN_IN_PLACE(sym32->st_other);
ENDIAN_ASSIGN_IN_PLACE(sym32->st_shndx);
}
else /* if (sym64) */
{
COPY_STRUCT(sym64, elf->buf, ofst, elf->sz);
ENDIAN_ASSIGN_IN_PLACE(sym64->st_name);
ENDIAN_ASSIGN_IN_PLACE(sym64->st_value);
ENDIAN_ASSIGN_IN_PLACE(sym64->st_size);
ENDIAN_ASSIGN_IN_PLACE(sym64->st_info);
ENDIAN_ASSIGN_IN_PLACE(sym64->st_other);
ENDIAN_ASSIGN_IN_PLACE(sym64->st_shndx);
}
return 0;
bail:
return 1;
}
int parse_elf(uint8_t *buf, size_t sz, output_fmt_t mode)
{
elf_obj_t elf;
unsigned int ofst; unsigned int ofst;
int i; int i;
Elf32_Off strtab_off; /* save String Table offset for later use */ Elf32_Off strtab_off32;
Elf64_Off strtab_off64; /* save String Table offset for later use */
memset(&elf, 0, sizeof(elf)); memset(&elf, 0, sizeof(elf));
elf.buf = buf; elf.buf = buf;
elf.sz = sz; elf.sz = sz;
/* Parse Header */ /* Parse Header */
if (parse_elf32_header(&elf)) if (parse_elf_header(&elf))
{ goto bail;
log_msg("Parse error: File does not appear to be valid ELF32\n");
return 1;
}
for (i = 0; i < elf.hdr.e_shnum; i++) if (elf.bits == 32)
{ {
parse_elf32_section(&elf, i, &shdr); Elf32_Shdr shdr;
for (i = 0; i < elf.hdr32.e_shnum; i++)
if (shdr.sh_type == SHT_STRTAB)
{ {
char strtsb_name[128]; parse_elf_section(&elf, i, &shdr, NULL);
strcpy(strtsb_name, (char *)(elf.buf + shdr.sh_offset + shdr.sh_name)); if (shdr.sh_type == SHT_STRTAB)
if (!(strcmp(strtsb_name, ".shstrtab")))
{ {
log_msg("found section: %s\n", strtsb_name); char strtsb_name[128];
strtab_off = shdr.sh_offset;
break; strcpy(strtsb_name, (char *)(elf.buf + shdr.sh_offset + shdr.sh_name));
if (!(strcmp(strtsb_name, ".shstrtab")))
{
/* log_msg("found section: %s\n", strtsb_name); */
strtab_off32 = shdr.sh_offset;
break;
}
}
}
}
else /* if (elf.bits == 64) */
{
Elf64_Shdr shdr;
for (i = 0; i < elf.hdr64.e_shnum; i++)
{
parse_elf_section(&elf, i, NULL, &shdr);
if (shdr.sh_type == SHT_STRTAB)
{
char strtsb_name[128];
strcpy(strtsb_name, (char *)(elf.buf + shdr.sh_offset + shdr.sh_name));
if (!(strcmp(strtsb_name, ".shstrtab")))
{
/* log_msg("found section: %s\n", strtsb_name); */
strtab_off64 = shdr.sh_offset;
break;
}
} }
} }
} }
/* Parse all Symbol Tables */ /* Parse all Symbol Tables */
for (i = 0; i < elf.hdr.e_shnum; i++) if (elf.bits == 32)
{ {
Elf32_Shdr shdr;
parse_elf32_section(&elf, i, &shdr); for (i = 0; i < elf.hdr32.e_shnum; i++)
if (shdr.sh_type == SHT_SYMTAB)
{ {
for (ofst = shdr.sh_offset; parse_elf_section(&elf, i, &shdr, NULL);
ofst < shdr.sh_offset + shdr.sh_size;
ofst += shdr.sh_entsize) if (shdr.sh_type == SHT_SYMTAB)
{ {
Elf32_Sym sym; for (ofst = shdr.sh_offset;
ofst < shdr.sh_offset + shdr.sh_size;
parse_elf32_symbol(&elf, ofst, &sym); ofst += shdr.sh_entsize)
/* For all OBJECTS (data objects), extract the value from the
* proper data segment.
*/
if (ELF32_ST_TYPE(sym.st_info) == STT_OBJECT && sym.st_name)
log_msg("found data object %s\n",
parse_elf32_string_table(&elf,
shdr.sh_link,
sym.st_name));
if (ELF32_ST_TYPE(sym.st_info) == STT_OBJECT
&& sym.st_size == 4)
{ {
Elf32_Shdr dhdr; Elf32_Sym sym;
int32_t val;
char section_name[128];
parse_elf32_section(&elf, sym.st_shndx, &dhdr); parse_elf_symbol(&elf, ofst, &sym, NULL);
/* For explanition - refer to _MSC_VER version of code */ /* For all OBJECTS (data objects), extract the value from the
strcpy(section_name, (char *)(elf.buf + strtab_off + dhdr.sh_name)); * proper data segment.
log_msg("Section_name: %s, Section_type: %d\n", section_name, dhdr.sh_type); */
/* if (ELF32_ST_TYPE(sym.st_info) == STT_OBJECT && sym.st_name)
log_msg("found data object %s\n",
parse_elf_string_table(&elf,
shdr.sh_link,
sym.st_name));
*/
if (!(strcmp(section_name, ".bss"))) if (ELF32_ST_TYPE(sym.st_info) == STT_OBJECT
&& sym.st_size == 4)
{ {
val = 0; Elf32_Shdr dhdr;
} int val = 0;
else char section_name[128];
{
memcpy(&val, parse_elf_section(&elf, sym.st_shndx, &dhdr, NULL);
elf.buf + dhdr.sh_offset + sym.st_value,
sizeof(val)); /* For explanition - refer to _MSC_VER version of code */
strcpy(section_name, (char *)(elf.buf + strtab_off32 + dhdr.sh_name));
/* log_msg("Section_name: %s, Section_type: %d\n", section_name, dhdr.sh_type); */
if (strcmp(section_name, ".bss"))
{
if (sizeof(val) != sym.st_size)
{
/* The target value is declared as an int in
* asm_*_offsets.c, which is 4 bytes on all
* targets we currently use. Complain loudly if
* this is not true.
*/
log_msg("Symbol size is wrong\n");
goto bail;
}
memcpy(&val,
elf.buf + dhdr.sh_offset + sym.st_value,
sym.st_size);
}
if (!elf.le_data)
{
log_msg("Big Endian data not supported yet!\n");
goto bail;
}
switch (mode)
{
case OUTPUT_FMT_RVDS:
printf("%-40s EQU %5d\n",
parse_elf_string_table(&elf,
shdr.sh_link,
sym.st_name),
val);
break;
case OUTPUT_FMT_GAS:
printf(".equ %-40s, %5d\n",
parse_elf_string_table(&elf,
shdr.sh_link,
sym.st_name),
val);
break;
default:
printf("%s = %d\n",
parse_elf_string_table(&elf,
shdr.sh_link,
sym.st_name),
val);
}
} }
}
}
}
}
else /* if (elf.bits == 64) */
{
Elf64_Shdr shdr;
for (i = 0; i < elf.hdr64.e_shnum; i++)
{
parse_elf_section(&elf, i, NULL, &shdr);
if (!elf.le_data) if (shdr.sh_type == SHT_SYMTAB)
{ {
log_msg("Big Endian data not supported yet!\n"); for (ofst = shdr.sh_offset;
goto bail; ofst < shdr.sh_offset + shdr.sh_size;
}\ ofst += shdr.sh_entsize)
{
Elf64_Sym sym;
switch (mode) parse_elf_symbol(&elf, ofst, NULL, &sym);
/* For all OBJECTS (data objects), extract the value from the
* proper data segment.
*/
/* if (ELF64_ST_TYPE(sym.st_info) == STT_OBJECT && sym.st_name)
log_msg("found data object %s\n",
parse_elf_string_table(&elf,
shdr.sh_link,
sym.st_name));
*/
if (ELF64_ST_TYPE(sym.st_info) == STT_OBJECT
&& sym.st_size == 4)
{ {
case OUTPUT_FMT_RVDS: Elf64_Shdr dhdr;
printf("%-40s EQU %5d\n", int val = 0;
parse_elf32_string_table(&elf, char section_name[128];
shdr.sh_link,
sym.st_name), parse_elf_section(&elf, sym.st_shndx, NULL, &dhdr);
val);
break; /* For explanition - refer to _MSC_VER version of code */
case OUTPUT_FMT_GAS: strcpy(section_name, (char *)(elf.buf + strtab_off64 + dhdr.sh_name));
printf(".equ %-40s, %5d\n", /* log_msg("Section_name: %s, Section_type: %d\n", section_name, dhdr.sh_type); */
parse_elf32_string_table(&elf,
shdr.sh_link, if ((strcmp(section_name, ".bss")))
sym.st_name), {
val); if (sizeof(val) != sym.st_size)
break; {
default: /* The target value is declared as an int in
printf("%s = %d\n", * asm_*_offsets.c, which is 4 bytes on all
parse_elf32_string_table(&elf, * targets we currently use. Complain loudly if
shdr.sh_link, * this is not true.
sym.st_name), */
val); log_msg("Symbol size is wrong\n");
goto bail;
}
memcpy(&val,
elf.buf + dhdr.sh_offset + sym.st_value,
sym.st_size);
}
if (!elf.le_data)
{
log_msg("Big Endian data not supported yet!\n");
goto bail;
}
switch (mode)
{
case OUTPUT_FMT_RVDS:
printf("%-40s EQU %5d\n",
parse_elf_string_table(&elf,
shdr.sh_link,
sym.st_name),
val);
break;
case OUTPUT_FMT_GAS:
printf(".equ %-40s, %5d\n",
parse_elf_string_table(&elf,
shdr.sh_link,
sym.st_name),
val);
break;
default:
printf("%s = %d\n",
parse_elf_string_table(&elf,
shdr.sh_link,
sym.st_name),
val);
}
} }
} }
} }
@ -454,7 +662,7 @@ int parse_elf32(uint8_t *buf, size_t sz, output_fmt_t mode)
return 0; return 0;
bail: bail:
log_msg("Parse error: File does not appear to be valid ELF32\n"); log_msg("Parse error: File does not appear to be valid ELF32 or ELF64\n");
return 1; return 1;
} }
@ -521,8 +729,7 @@ int main(int argc, char **argv)
goto bail; goto bail;
} }
res = parse_elf32(file_buf, stat_buf.st_size, mode); res = parse_elf(file_buf, stat_buf.st_size, mode);
//res = parse_coff(file_buf, stat_buf.st_size);
free(file_buf); free(file_buf);
if (!res) if (!res)