From a87a9ef37372b4380808ec2efa7c762e137668f1 Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Wed, 23 May 2012 11:03:35 +0200 Subject: imxtools: move tools to a new sbtools/ subdirectory Change-Id: I0d8d6831b35037725486f61fc363de87bc8ba92e --- utils/imxtools/sbtools/elf.c | 575 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 575 insertions(+) create mode 100644 utils/imxtools/sbtools/elf.c (limited to 'utils/imxtools/sbtools/elf.c') diff --git a/utils/imxtools/sbtools/elf.c b/utils/imxtools/sbtools/elf.c new file mode 100644 index 0000000000..481ab98dd6 --- /dev/null +++ b/utils/imxtools/sbtools/elf.c @@ -0,0 +1,575 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2011 Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "elf.h" +#include "misc.h" + +/** + * Definitions + * taken from elf.h linux header + * based on ELF specification + * based on ARM ELF specification + */ +typedef uint16_t Elf32_Half; + +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf32_Addr; +typedef uint32_t Elf32_Off; +typedef uint16_t Elf32_Section; + +#define EI_NIDENT 16 + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +}Elf32_Ehdr; + +#define EI_MAG0 0 /* File identification byte 0 index */ +#define ELFMAG0 0x7f /* Magic number byte 0 */ + +#define EI_MAG1 1 /* File identification byte 1 index */ +#define ELFMAG1 'E' /* Magic number byte 1 */ + +#define EI_MAG2 2 /* File identification byte 2 index */ +#define ELFMAG2 'L' /* Magic number byte 2 */ + +#define EI_MAG3 3 /* File identification byte 3 index */ +#define ELFMAG3 'F' /* Magic number byte 3 */ + +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASS32 1 /* 32-bit objects */ + +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ + +#define EI_VERSION 6 /* File version byte index, Value must be EV_CURRENT */ + +#define EI_OSABI 7 /* OS ABI identification */ +#define ELFOSABI_NONE 0 /* UNIX System V ABI */ +#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */ +#define ELFOSABI_ARM 97 /* ARM */ + +#define EI_ABIVERSION 8 /* ABI version */ + +#define EI_PAD 9 /* Byte index of padding bytes */ + +#define ET_EXEC 2 /* Executable file */ + +#define EM_ARM 40 /* ARM */ + +#define EV_CURRENT 1 /* Current version */ + +#define EF_ARM_HASENTRY 0x00000002 + +#define SHN_UNDEF 0 /* Undefined section */ + +typedef struct +{ + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +}Elf32_Shdr; + +#define SHT_NULL 0 /* Section header table entry unused */ +#define SHT_PROGBITS 1 /* Program data */ +#define SHT_SYMTAB 2 /* Symbol table */ +#define SHT_STRTAB 3 /* String table */ +#define SHT_RELA 4 /* Relocation entries with addends */ +#define SHT_HASH 5 /* Symbol hash table */ +#define SHT_DYNAMIC 6 /* Dynamic linking information */ +#define SHT_NOTE 7 /* Notes */ +#define SHT_NOBITS 8 /* Program space with no data (bss) */ +#define SHT_REL 9 /* Relocation entries, no addends */ +#define SHT_SHLIB 10 /* Reserved */ +#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ +#define SHT_INIT_ARRAY 14 /* Array of constructors */ +#define SHT_FINI_ARRAY 15 /* Array of destructors */ +#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ +#define SHT_GROUP 17 /* Section group */ +#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ +#define SHT_NUM 19 /* Number of defined types. */ + +#define SHF_WRITE (1 << 0) /* Writable */ +#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ +#define SHF_EXECINSTR (1 << 2) /* Executable */ +#define SHF_MERGE (1 << 4) /* Might be merged */ +#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ + +typedef struct +{ + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +}Elf32_Phdr; + +#define PT_LOAD 1 /* Loadable program segment */ + +#define PF_X (1 << 0) /* Segment is executable */ +#define PF_W (1 << 1) /* Segment is writable */ +#define PF_R (1 << 2) /* Segment is readable */ + +void elf_init(struct elf_params_t *params) +{ + memset(params, 0, sizeof(struct elf_params_t)); +} + +extern void *xmalloc(size_t s); + +static struct elf_section_t *elf_add_section(struct elf_params_t *params) +{ + struct elf_section_t *sec = xmalloc(sizeof(struct elf_section_t)); + if(params->first_section == NULL) + params->first_section = params->last_section = sec; + else + { + params->last_section->next = sec; + params->last_section = sec; + } + sec->next = NULL; + + return sec; +} + +static struct elf_segment_t *elf_add_segment(struct elf_params_t *params) +{ + struct elf_segment_t *seg = xmalloc(sizeof(struct elf_section_t)); + if(params->first_segment == NULL) + params->first_segment = params->last_segment = seg; + else + { + params->last_segment->next = seg; + params->last_segment = seg; + } + seg->next = NULL; + + return seg; +} + +void elf_add_load_section(struct elf_params_t *params, + uint32_t load_addr, uint32_t size, const void *section) +{ + struct elf_section_t *sec = elf_add_section(params); + + sec->type = EST_LOAD; + sec->addr = load_addr; + sec->size = size; + sec->section = xmalloc(size); + memcpy(sec->section, section, size); +} + +void elf_add_fill_section(struct elf_params_t *params, + uint32_t fill_addr, uint32_t size, uint32_t pattern) +{ + if(pattern != 0x00) + { + printf("oops, non-zero filling, ignore fill section\n"); + return; + } + + struct elf_section_t *sec = elf_add_section(params); + + sec->type = EST_FILL; + sec->addr = fill_addr; + sec->size = size; + sec->pattern = pattern; +} + +void elf_write_file(struct elf_params_t *params, elf_write_fn_t write, + elf_printf_fn_t printf, void *user) +{ + (void) printf; + + Elf32_Ehdr ehdr; + uint32_t phnum = 0; + struct elf_section_t *sec = params->first_section; + uint32_t offset = 0; + Elf32_Phdr phdr; + Elf32_Shdr shdr; + memset(&ehdr, 0, EI_NIDENT); + + while(sec) + { + if(sec->type == EST_LOAD) + { + sec->offset = offset; + offset += sec->size; + } + else + { + sec->offset = 0; + } + + phnum++; + sec = sec->next; + } + + uint32_t strtbl_offset = offset; + + ehdr.e_ident[EI_MAG0] = ELFMAG0; + ehdr.e_ident[EI_MAG1] = ELFMAG1; + ehdr.e_ident[EI_MAG2] = ELFMAG2; + ehdr.e_ident[EI_MAG3] = ELFMAG3; + ehdr.e_ident[EI_CLASS] = ELFCLASS32; + ehdr.e_ident[EI_DATA] = ELFDATA2LSB; + ehdr.e_ident[EI_VERSION] = EV_CURRENT; + ehdr.e_ident[EI_OSABI] = ELFOSABI_NONE; + ehdr.e_ident[EI_ABIVERSION] = 0; + ehdr.e_type = ET_EXEC; + ehdr.e_machine = EM_ARM; + ehdr.e_version = EV_CURRENT; + ehdr.e_entry = params->start_addr; + ehdr.e_flags = 0; + if(params->has_start_addr) + ehdr.e_flags |= EF_ARM_HASENTRY; + ehdr.e_ehsize = sizeof ehdr; + ehdr.e_phentsize = sizeof phdr; + ehdr.e_phnum = phnum; + ehdr.e_shentsize = sizeof shdr; + ehdr.e_shnum = phnum + 2; /* one for section 0 and one for string table */ + ehdr.e_shstrndx = ehdr.e_shnum - 1; + ehdr.e_phoff = ehdr.e_ehsize; + ehdr.e_shoff = ehdr.e_ehsize + ehdr.e_phnum * ehdr.e_phentsize; + + write(user, 0, &ehdr, sizeof ehdr); + + /* allocate enough size to hold any combinaison of .text/.bss in the string table: + * - one empty name ("\0") + * - at most N names of the form ".textXXXX\0" or ".bssXXXX\0" + * - one name ".shstrtab\0" */ + char *strtbl_content = malloc(1 + strlen(".shstrtab") + 1 + + phnum * (strlen(".textXXXX") + 1)); + + strtbl_content[0] = '\0'; + strcpy(&strtbl_content[1], ".shstrtab"); + uint32_t strtbl_index = 1 + strlen(".shstrtab") + 1; + + uint32_t data_offset = ehdr.e_ehsize + ehdr.e_phnum * ehdr.e_phentsize + + ehdr.e_shnum * ehdr.e_shentsize; + + sec = params->first_section; + offset = ehdr.e_phoff; + while(sec) + { + sec->offset += data_offset; + + phdr.p_type = PT_LOAD; + if(sec->type == EST_LOAD) + phdr.p_offset = sec->offset; + else + phdr.p_offset = 0; + phdr.p_paddr = sec->addr; + phdr.p_vaddr = phdr.p_paddr; /* assume identity map ? */ + phdr.p_memsz = sec->size; + if(sec->type == EST_LOAD) + phdr.p_filesz = phdr.p_memsz; + else + phdr.p_filesz = 0; + phdr.p_flags = PF_X | PF_W | PF_R; + phdr.p_align = 0; + + write(user, offset, &phdr, sizeof phdr); + + offset += sizeof(Elf32_Phdr); + sec = sec->next; + } + + sec = params->first_section; + offset = ehdr.e_shoff; + + { + shdr.sh_name = 0; + shdr.sh_type = SHT_NULL; + shdr.sh_flags = 0; + shdr.sh_addr = 0; + shdr.sh_offset = 0; + shdr.sh_size = 0; + shdr.sh_link = SHN_UNDEF; + shdr.sh_info = 0; + shdr.sh_addralign = 0; + shdr.sh_entsize = 0; + + write(user, offset, &shdr, sizeof shdr); + + offset += sizeof(Elf32_Shdr); + } + + uint32_t text_idx = 0; + uint32_t bss_idx = 0; + while(sec) + { + shdr.sh_name = strtbl_index; + if(sec->type == EST_LOAD) + { + strtbl_index += 1 + sprintf(&strtbl_content[strtbl_index], ".text%d", text_idx++); + shdr.sh_type = SHT_PROGBITS; + } + else + { + strtbl_index += 1 + sprintf(&strtbl_content[strtbl_index], ".bss%d", bss_idx++); + shdr.sh_type = SHT_NOBITS; + } + shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR; + shdr.sh_addr = sec->addr; + shdr.sh_offset = sec->offset; + shdr.sh_size = sec->size; + shdr.sh_link = SHN_UNDEF; + shdr.sh_info = 0; + shdr.sh_addralign = 1; + shdr.sh_entsize = 0; + + write(user, offset, &shdr, sizeof shdr); + + offset += sizeof(Elf32_Shdr); + sec = sec->next; + } + + { + shdr.sh_name = 1; + shdr.sh_type = SHT_STRTAB; + shdr.sh_flags = 0; + shdr.sh_addr = 0; + shdr.sh_offset = strtbl_offset + data_offset; + shdr.sh_size = strtbl_index; + shdr.sh_link = SHN_UNDEF; + shdr.sh_info = 0; + shdr.sh_addralign = 1; + shdr.sh_entsize = 0; + + write(user, offset, &shdr, sizeof shdr); + + offset += sizeof(Elf32_Shdr); + } + + sec = params->first_section; + while(sec) + { + if(sec->type == EST_LOAD) + write(user, sec->offset, sec->section, sec->size); + sec = sec->next; + } + + write(user, strtbl_offset + data_offset, strtbl_content, strtbl_index); + free(strtbl_content); +} + +bool elf_read_file(struct elf_params_t *params, elf_read_fn_t read, + elf_printf_fn_t printf, void *user) +{ + #define error_printf(...) ({printf(user, true, __VA_ARGS__); return false;}) + + /* read header */ + Elf32_Ehdr ehdr; + if(!read(user, 0, &ehdr, sizeof(ehdr))) + { + printf(user, true, "error reading elf header\n"); + return false; + } + /* basic checks */ + if(ehdr.e_ident[EI_MAG0] != ELFMAG0 || ehdr.e_ident[EI_MAG1] != ELFMAG1 || + ehdr.e_ident[EI_MAG2] != ELFMAG2 || ehdr.e_ident[EI_MAG3] != ELFMAG3) + error_printf("invalid elf header\n"); + if(ehdr.e_ident[EI_CLASS] != ELFCLASS32) + error_printf("invalid elf class: must be a 32-bit object\n"); + if(ehdr.e_ident[EI_DATA] != ELFDATA2LSB) + error_printf("invalid elf data encoding: must be 32-bit lsb\n"); + if(ehdr.e_ident[EI_VERSION] != EV_CURRENT) + error_printf("invalid elf version\n"); + if(ehdr.e_type != ET_EXEC) + error_printf("invalid elf file: must be an executable file\n"); + if(ehdr.e_machine != EM_ARM) + error_printf("invalid elf file: must target an arm machine\n"); + if(ehdr.e_ehsize != sizeof(ehdr)) + error_printf("invalid elf file: size header mismatch\n"); + if(ehdr.e_phnum > 0 && ehdr.e_phentsize != sizeof(Elf32_Phdr)) + error_printf("invalid elf file: program header size mismatch\n"); + if(ehdr.e_shnum > 0 && ehdr.e_shentsize != sizeof(Elf32_Shdr)) + error_printf("invalid elf file: section header size mismatch\n"); + elf_set_start_addr(params, ehdr.e_entry); + + char *strtab = NULL; + if(ehdr.e_shstrndx != SHN_UNDEF) + { + Elf32_Shdr shstrtab; + if(read(user, ehdr.e_shoff + ehdr.e_shstrndx * ehdr.e_shentsize, + &shstrtab, sizeof(shstrtab))) + { + strtab = xmalloc(shstrtab.sh_size); + if(!read(user, shstrtab.sh_offset, strtab, shstrtab.sh_size)) + { + free(strtab); + strtab = NULL; + } + } + } + /* run through sections */ + printf(user, false, "ELF file:\n"); + for(int i = 1; i < ehdr.e_shnum; i++) + { + uint32_t off = ehdr.e_shoff + i * ehdr.e_shentsize; + Elf32_Shdr shdr; + memset(&shdr, 0, sizeof(shdr)); + if(!read(user, off, &shdr, sizeof(shdr))) + error_printf("error reading elf section header"); + + if(shdr.sh_type == SHT_PROGBITS && shdr.sh_flags & SHF_ALLOC) + { + void *data = xmalloc(shdr.sh_size); + if(!read(user, shdr.sh_offset, data, shdr.sh_size)) + error_printf("error read self section data\n"); + elf_add_load_section(params, shdr.sh_addr, shdr.sh_size, data); + free(data); + + if(strtab) + printf(user, false, "create load segment for %s\n", &strtab[shdr.sh_name]); + } + else if(shdr.sh_type == SHT_NOBITS && shdr.sh_flags & SHF_ALLOC) + { + elf_add_fill_section(params, shdr.sh_addr, shdr.sh_size, 0); + if(strtab) + printf(user, false, "create fill segment for %s\n", &strtab[shdr.sh_name]); + } + else + { + if(strtab) + printf(user, false, "filter out %s\n", &strtab[shdr.sh_name], shdr.sh_type); + } + + } + free(strtab); + /* run through segments */ + for(int i = 1; i < ehdr.e_phnum; i++) + { + uint32_t off = ehdr.e_phoff + i * ehdr.e_phentsize; + Elf32_Phdr phdr; + memset(&phdr, 0, sizeof(phdr)); + if(!read(user, off, &phdr, sizeof(phdr))) + error_printf("error reading elf segment header"); + if(phdr.p_type != PT_LOAD) + continue; + struct elf_segment_t *seg = elf_add_segment(params); + seg->vaddr = phdr.p_vaddr; + seg->paddr = phdr.p_paddr; + seg->vsize = phdr.p_memsz; + seg->psize = phdr.p_filesz; + printf(user, false, "create segment [%#x,+%#x[ -> [%#x,+%#x[\n", + seg->vaddr, seg->vsize, seg->paddr, seg->psize); + } + + return true; +} + +uint32_t elf_translate_virtual_address(struct elf_params_t *params, uint32_t addr) +{ + struct elf_segment_t *seg = params->first_segment; + while(seg) + { + if(seg->vaddr <= addr && addr < seg->vaddr + seg->vsize) + return addr - seg->vaddr + seg->paddr; + seg = seg->next; + } + return addr; +} + +void elf_translate_addresses(struct elf_params_t *params) +{ + struct elf_section_t *sec = params->first_section; + while(sec) + { + sec->addr = elf_translate_virtual_address(params, sec->addr); + sec = sec->next; + } + params->start_addr = elf_translate_virtual_address(params, params->start_addr); +} + +bool elf_is_empty(struct elf_params_t *params) +{ + return params->first_section == NULL; +} + +void elf_set_start_addr(struct elf_params_t *params, uint32_t addr) +{ + params->has_start_addr = true; + params->start_addr = addr; +} + +bool elf_get_start_addr(struct elf_params_t *params, uint32_t *addr) +{ + if(params->has_start_addr && addr != NULL) + *addr = params->start_addr; + return params->has_start_addr; +} + +int elf_get_nr_sections(struct elf_params_t *params) +{ + int nr = 0; + struct elf_section_t *sec = params->first_section; + while(sec) + { + nr++; + sec = sec->next; + } + return nr; +} + +void elf_release(struct elf_params_t *params) +{ + struct elf_section_t *sec = params->first_section; + while(sec) + { + struct elf_section_t *next_sec = sec->next; + if(sec->type == EST_LOAD) + free(sec->section); + free(sec); + sec = next_sec; + } + struct elf_segment_t *seg = params->first_segment; + while(seg) + { + struct elf_segment_t *next_seg = seg->next; + free(seg); + seg = next_seg; + } +} -- cgit v1.2.3