Amaury Pouly | 9d7df9a | 2011-09-15 16:10:31 +0000 | [diff] [blame] | 1 | /*************************************************************************** |
| 2 | * __________ __ ___. |
| 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| 7 | * \/ \/ \/ \/ \/ |
| 8 | * $Id$ |
| 9 | * |
| 10 | * Copyright (C) 2011 Amaury Pouly |
| 11 | * |
| 12 | * This program is free software; you can redistribute it and/or |
| 13 | * modify it under the terms of the GNU General Public License |
| 14 | * as published by the Free Software Foundation; either version 2 |
| 15 | * of the License, or (at your option) any later version. |
| 16 | * |
| 17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| 18 | * KIND, either express or implied. |
| 19 | * |
| 20 | ****************************************************************************/ |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 21 | #include "elf.h" |
Amaury Pouly | 4cab9f2 | 2011-10-29 22:27:53 +0000 | [diff] [blame] | 22 | #include "misc.h" |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 23 | |
Amaury Pouly | c4cb4cc | 2011-04-17 15:49:58 +0000 | [diff] [blame] | 24 | /** |
| 25 | * Definitions |
| 26 | * taken from elf.h linux header |
| 27 | * based on ELF specification |
| 28 | * based on ARM ELF specification |
| 29 | */ |
| 30 | typedef uint16_t Elf32_Half; |
| 31 | |
| 32 | typedef uint32_t Elf32_Word; |
| 33 | typedef int32_t Elf32_Sword; |
| 34 | typedef uint32_t Elf32_Addr; |
| 35 | typedef uint32_t Elf32_Off; |
| 36 | typedef uint16_t Elf32_Section; |
| 37 | |
| 38 | #define EI_NIDENT 16 |
| 39 | |
| 40 | typedef struct |
| 41 | { |
| 42 | unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ |
| 43 | Elf32_Half e_type; /* Object file type */ |
| 44 | Elf32_Half e_machine; /* Architecture */ |
| 45 | Elf32_Word e_version; /* Object file version */ |
| 46 | Elf32_Addr e_entry; /* Entry point virtual address */ |
| 47 | Elf32_Off e_phoff; /* Program header table file offset */ |
| 48 | Elf32_Off e_shoff; /* Section header table file offset */ |
| 49 | Elf32_Word e_flags; /* Processor-specific flags */ |
| 50 | Elf32_Half e_ehsize; /* ELF header size in bytes */ |
| 51 | Elf32_Half e_phentsize; /* Program header table entry size */ |
| 52 | Elf32_Half e_phnum; /* Program header table entry count */ |
| 53 | Elf32_Half e_shentsize; /* Section header table entry size */ |
| 54 | Elf32_Half e_shnum; /* Section header table entry count */ |
| 55 | Elf32_Half e_shstrndx; /* Section header string table index */ |
| 56 | }Elf32_Ehdr; |
| 57 | |
| 58 | #define EI_MAG0 0 /* File identification byte 0 index */ |
| 59 | #define ELFMAG0 0x7f /* Magic number byte 0 */ |
| 60 | |
| 61 | #define EI_MAG1 1 /* File identification byte 1 index */ |
| 62 | #define ELFMAG1 'E' /* Magic number byte 1 */ |
| 63 | |
| 64 | #define EI_MAG2 2 /* File identification byte 2 index */ |
| 65 | #define ELFMAG2 'L' /* Magic number byte 2 */ |
| 66 | |
| 67 | #define EI_MAG3 3 /* File identification byte 3 index */ |
| 68 | #define ELFMAG3 'F' /* Magic number byte 3 */ |
| 69 | |
| 70 | #define EI_CLASS 4 /* File class byte index */ |
| 71 | #define ELFCLASS32 1 /* 32-bit objects */ |
| 72 | |
| 73 | #define EI_DATA 5 /* Data encoding byte index */ |
| 74 | #define ELFDATA2LSB 1 /* 2's complement, little endian */ |
| 75 | |
| 76 | #define EI_VERSION 6 /* File version byte index, Value must be EV_CURRENT */ |
| 77 | |
| 78 | #define EI_OSABI 7 /* OS ABI identification */ |
| 79 | #define ELFOSABI_NONE 0 /* UNIX System V ABI */ |
| 80 | #define ELFOSABI_ARM_AEABI 64 /* ARM EABI */ |
| 81 | #define ELFOSABI_ARM 97 /* ARM */ |
| 82 | |
| 83 | #define EI_ABIVERSION 8 /* ABI version */ |
| 84 | |
| 85 | #define EI_PAD 9 /* Byte index of padding bytes */ |
| 86 | |
| 87 | #define ET_EXEC 2 /* Executable file */ |
| 88 | |
| 89 | #define EM_ARM 40 /* ARM */ |
| 90 | |
| 91 | #define EV_CURRENT 1 /* Current version */ |
| 92 | |
| 93 | #define EF_ARM_HASENTRY 0x00000002 |
| 94 | |
| 95 | #define SHN_UNDEF 0 /* Undefined section */ |
| 96 | |
| 97 | typedef struct |
| 98 | { |
| 99 | Elf32_Word sh_name; /* Section name (string tbl index) */ |
| 100 | Elf32_Word sh_type; /* Section type */ |
| 101 | Elf32_Word sh_flags; /* Section flags */ |
| 102 | Elf32_Addr sh_addr; /* Section virtual addr at execution */ |
| 103 | Elf32_Off sh_offset; /* Section file offset */ |
| 104 | Elf32_Word sh_size; /* Section size in bytes */ |
| 105 | Elf32_Word sh_link; /* Link to another section */ |
| 106 | Elf32_Word sh_info; /* Additional section information */ |
| 107 | Elf32_Word sh_addralign; /* Section alignment */ |
| 108 | Elf32_Word sh_entsize; /* Entry size if section holds table */ |
| 109 | }Elf32_Shdr; |
| 110 | |
| 111 | #define SHT_NULL 0 /* Section header table entry unused */ |
| 112 | #define SHT_PROGBITS 1 /* Program data */ |
| 113 | #define SHT_SYMTAB 2 /* Symbol table */ |
| 114 | #define SHT_STRTAB 3 /* String table */ |
| 115 | #define SHT_RELA 4 /* Relocation entries with addends */ |
| 116 | #define SHT_HASH 5 /* Symbol hash table */ |
| 117 | #define SHT_DYNAMIC 6 /* Dynamic linking information */ |
| 118 | #define SHT_NOTE 7 /* Notes */ |
| 119 | #define SHT_NOBITS 8 /* Program space with no data (bss) */ |
| 120 | #define SHT_REL 9 /* Relocation entries, no addends */ |
| 121 | #define SHT_SHLIB 10 /* Reserved */ |
| 122 | #define SHT_DYNSYM 11 /* Dynamic linker symbol table */ |
| 123 | #define SHT_INIT_ARRAY 14 /* Array of constructors */ |
| 124 | #define SHT_FINI_ARRAY 15 /* Array of destructors */ |
| 125 | #define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ |
| 126 | #define SHT_GROUP 17 /* Section group */ |
| 127 | #define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ |
| 128 | #define SHT_NUM 19 /* Number of defined types. */ |
| 129 | |
| 130 | #define SHF_WRITE (1 << 0) /* Writable */ |
| 131 | #define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ |
| 132 | #define SHF_EXECINSTR (1 << 2) /* Executable */ |
| 133 | #define SHF_MERGE (1 << 4) /* Might be merged */ |
| 134 | #define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ |
| 135 | |
| 136 | typedef struct |
| 137 | { |
| 138 | Elf32_Word p_type; /* Segment type */ |
| 139 | Elf32_Off p_offset; /* Segment file offset */ |
| 140 | Elf32_Addr p_vaddr; /* Segment virtual address */ |
| 141 | Elf32_Addr p_paddr; /* Segment physical address */ |
| 142 | Elf32_Word p_filesz; /* Segment size in file */ |
| 143 | Elf32_Word p_memsz; /* Segment size in memory */ |
| 144 | Elf32_Word p_flags; /* Segment flags */ |
| 145 | Elf32_Word p_align; /* Segment alignment */ |
| 146 | }Elf32_Phdr; |
| 147 | |
| 148 | #define PT_LOAD 1 /* Loadable program segment */ |
| 149 | |
| 150 | #define PF_X (1 << 0) /* Segment is executable */ |
| 151 | #define PF_W (1 << 1) /* Segment is writable */ |
| 152 | #define PF_R (1 << 2) /* Segment is readable */ |
| 153 | |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 154 | void elf_init(struct elf_params_t *params) |
| 155 | { |
Amaury Pouly | 5827937 | 2011-11-06 01:49:13 +0000 | [diff] [blame] | 156 | memset(params, 0, sizeof(struct elf_params_t)); |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 157 | } |
| 158 | |
| 159 | extern void *xmalloc(size_t s); |
| 160 | |
| 161 | static struct elf_section_t *elf_add_section(struct elf_params_t *params) |
| 162 | { |
| 163 | struct elf_section_t *sec = xmalloc(sizeof(struct elf_section_t)); |
| 164 | if(params->first_section == NULL) |
| 165 | params->first_section = params->last_section = sec; |
| 166 | else |
| 167 | { |
| 168 | params->last_section->next = sec; |
| 169 | params->last_section = sec; |
| 170 | } |
| 171 | sec->next = NULL; |
| 172 | |
| 173 | return sec; |
| 174 | } |
| 175 | |
Amaury Pouly | 64b4672 | 2011-09-15 14:36:58 +0000 | [diff] [blame] | 176 | static struct elf_segment_t *elf_add_segment(struct elf_params_t *params) |
| 177 | { |
| 178 | struct elf_segment_t *seg = xmalloc(sizeof(struct elf_section_t)); |
| 179 | if(params->first_segment == NULL) |
| 180 | params->first_segment = params->last_segment = seg; |
| 181 | else |
| 182 | { |
| 183 | params->last_segment->next = seg; |
| 184 | params->last_segment = seg; |
| 185 | } |
| 186 | seg->next = NULL; |
| 187 | |
| 188 | return seg; |
| 189 | } |
| 190 | |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 191 | void elf_add_load_section(struct elf_params_t *params, |
| 192 | uint32_t load_addr, uint32_t size, const void *section) |
| 193 | { |
| 194 | struct elf_section_t *sec = elf_add_section(params); |
| 195 | |
| 196 | sec->type = EST_LOAD; |
| 197 | sec->addr = load_addr; |
| 198 | sec->size = size; |
| 199 | sec->section = xmalloc(size); |
| 200 | memcpy(sec->section, section, size); |
| 201 | } |
| 202 | |
| 203 | void elf_add_fill_section(struct elf_params_t *params, |
| 204 | uint32_t fill_addr, uint32_t size, uint32_t pattern) |
| 205 | { |
| 206 | if(pattern != 0x00) |
| 207 | { |
| 208 | printf("oops, non-zero filling, ignore fill section\n"); |
| 209 | return; |
| 210 | } |
| 211 | |
| 212 | struct elf_section_t *sec = elf_add_section(params); |
| 213 | |
| 214 | sec->type = EST_FILL; |
| 215 | sec->addr = fill_addr; |
| 216 | sec->size = size; |
| 217 | sec->pattern = pattern; |
| 218 | } |
| 219 | |
Amaury Pouly | 4cab9f2 | 2011-10-29 22:27:53 +0000 | [diff] [blame] | 220 | void elf_write_file(struct elf_params_t *params, elf_write_fn_t write, |
| 221 | elf_printf_fn_t printf, void *user) |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 222 | { |
Amaury Pouly | e36471d | 2011-11-01 11:23:43 +0000 | [diff] [blame] | 223 | (void) printf; |
| 224 | |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 225 | Elf32_Ehdr ehdr; |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 226 | uint32_t phnum = 0; |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 227 | struct elf_section_t *sec = params->first_section; |
| 228 | uint32_t offset = 0; |
| 229 | Elf32_Phdr phdr; |
Amaury Pouly | f85c044 | 2011-03-03 09:22:32 +0000 | [diff] [blame] | 230 | Elf32_Shdr shdr; |
| 231 | memset(&ehdr, 0, EI_NIDENT); |
| 232 | |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 233 | while(sec) |
| 234 | { |
| 235 | if(sec->type == EST_LOAD) |
| 236 | { |
| 237 | sec->offset = offset; |
| 238 | offset += sec->size; |
| 239 | } |
Amaury Pouly | 4cab9f2 | 2011-10-29 22:27:53 +0000 | [diff] [blame] | 240 | else |
| 241 | { |
| 242 | sec->offset = 0; |
| 243 | } |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 244 | |
| 245 | phnum++; |
| 246 | sec = sec->next; |
| 247 | } |
| 248 | |
Amaury Pouly | f85c044 | 2011-03-03 09:22:32 +0000 | [diff] [blame] | 249 | uint32_t strtbl_offset = offset; |
| 250 | |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 251 | ehdr.e_ident[EI_MAG0] = ELFMAG0; |
| 252 | ehdr.e_ident[EI_MAG1] = ELFMAG1; |
| 253 | ehdr.e_ident[EI_MAG2] = ELFMAG2; |
| 254 | ehdr.e_ident[EI_MAG3] = ELFMAG3; |
| 255 | ehdr.e_ident[EI_CLASS] = ELFCLASS32; |
| 256 | ehdr.e_ident[EI_DATA] = ELFDATA2LSB; |
| 257 | ehdr.e_ident[EI_VERSION] = EV_CURRENT; |
| 258 | ehdr.e_ident[EI_OSABI] = ELFOSABI_NONE; |
| 259 | ehdr.e_ident[EI_ABIVERSION] = 0; |
| 260 | ehdr.e_type = ET_EXEC; |
| 261 | ehdr.e_machine = EM_ARM; |
| 262 | ehdr.e_version = EV_CURRENT; |
| 263 | ehdr.e_entry = params->start_addr; |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 264 | ehdr.e_flags = 0; |
| 265 | if(params->has_start_addr) |
| 266 | ehdr.e_flags |= EF_ARM_HASENTRY; |
| 267 | ehdr.e_ehsize = sizeof ehdr; |
Amaury Pouly | f85c044 | 2011-03-03 09:22:32 +0000 | [diff] [blame] | 268 | ehdr.e_phentsize = sizeof phdr; |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 269 | ehdr.e_phnum = phnum; |
Amaury Pouly | f85c044 | 2011-03-03 09:22:32 +0000 | [diff] [blame] | 270 | ehdr.e_shentsize = sizeof shdr; |
Amaury Pouly | 80889ee | 2011-04-15 17:05:10 +0000 | [diff] [blame] | 271 | ehdr.e_shnum = phnum + 2; /* one for section 0 and one for string table */ |
Amaury Pouly | f85c044 | 2011-03-03 09:22:32 +0000 | [diff] [blame] | 272 | ehdr.e_shstrndx = ehdr.e_shnum - 1; |
| 273 | ehdr.e_phoff = ehdr.e_ehsize; |
| 274 | ehdr.e_shoff = ehdr.e_ehsize + ehdr.e_phnum * ehdr.e_phentsize; |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 275 | |
| 276 | write(user, 0, &ehdr, sizeof ehdr); |
| 277 | |
Amaury Pouly | 8fa0e13 | 2011-05-02 22:52:52 +0000 | [diff] [blame] | 278 | /* allocate enough size to hold any combinaison of .text/.bss in the string table: |
| 279 | * - one empty name ("\0") |
Amaury Pouly | f4a1501 | 2011-10-20 10:14:50 +0000 | [diff] [blame] | 280 | * - at most N names of the form ".textXXXX\0" or ".bssXXXX\0" |
Amaury Pouly | 8fa0e13 | 2011-05-02 22:52:52 +0000 | [diff] [blame] | 281 | * - one name ".shstrtab\0" */ |
| 282 | char *strtbl_content = malloc(1 + strlen(".shstrtab") + 1 + |
Amaury Pouly | f4a1501 | 2011-10-20 10:14:50 +0000 | [diff] [blame] | 283 | phnum * (strlen(".textXXXX") + 1)); |
Amaury Pouly | 8fa0e13 | 2011-05-02 22:52:52 +0000 | [diff] [blame] | 284 | |
| 285 | strtbl_content[0] = '\0'; |
| 286 | strcpy(&strtbl_content[1], ".shstrtab"); |
| 287 | uint32_t strtbl_index = 1 + strlen(".shstrtab") + 1; |
| 288 | |
Amaury Pouly | f85c044 | 2011-03-03 09:22:32 +0000 | [diff] [blame] | 289 | uint32_t data_offset = ehdr.e_ehsize + ehdr.e_phnum * ehdr.e_phentsize + |
| 290 | ehdr.e_shnum * ehdr.e_shentsize; |
| 291 | |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 292 | sec = params->first_section; |
Amaury Pouly | f85c044 | 2011-03-03 09:22:32 +0000 | [diff] [blame] | 293 | offset = ehdr.e_phoff; |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 294 | while(sec) |
| 295 | { |
Amaury Pouly | f85c044 | 2011-03-03 09:22:32 +0000 | [diff] [blame] | 296 | sec->offset += data_offset; |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 297 | |
| 298 | phdr.p_type = PT_LOAD; |
| 299 | if(sec->type == EST_LOAD) |
| 300 | phdr.p_offset = sec->offset; |
| 301 | else |
| 302 | phdr.p_offset = 0; |
| 303 | phdr.p_paddr = sec->addr; |
| 304 | phdr.p_vaddr = phdr.p_paddr; /* assume identity map ? */ |
| 305 | phdr.p_memsz = sec->size; |
| 306 | if(sec->type == EST_LOAD) |
| 307 | phdr.p_filesz = phdr.p_memsz; |
| 308 | else |
| 309 | phdr.p_filesz = 0; |
| 310 | phdr.p_flags = PF_X | PF_W | PF_R; |
| 311 | phdr.p_align = 0; |
| 312 | |
| 313 | write(user, offset, &phdr, sizeof phdr); |
| 314 | |
| 315 | offset += sizeof(Elf32_Phdr); |
| 316 | sec = sec->next; |
| 317 | } |
| 318 | |
| 319 | sec = params->first_section; |
Amaury Pouly | f85c044 | 2011-03-03 09:22:32 +0000 | [diff] [blame] | 320 | offset = ehdr.e_shoff; |
Amaury Pouly | 80889ee | 2011-04-15 17:05:10 +0000 | [diff] [blame] | 321 | |
| 322 | { |
| 323 | shdr.sh_name = 0; |
| 324 | shdr.sh_type = SHT_NULL; |
| 325 | shdr.sh_flags = 0; |
| 326 | shdr.sh_addr = 0; |
| 327 | shdr.sh_offset = 0; |
| 328 | shdr.sh_size = 0; |
| 329 | shdr.sh_link = SHN_UNDEF; |
| 330 | shdr.sh_info = 0; |
| 331 | shdr.sh_addralign = 0; |
| 332 | shdr.sh_entsize = 0; |
| 333 | |
| 334 | write(user, offset, &shdr, sizeof shdr); |
| 335 | |
| 336 | offset += sizeof(Elf32_Shdr); |
| 337 | } |
Amaury Pouly | 8fa0e13 | 2011-05-02 22:52:52 +0000 | [diff] [blame] | 338 | |
| 339 | uint32_t text_idx = 0; |
| 340 | uint32_t bss_idx = 0; |
Amaury Pouly | f85c044 | 2011-03-03 09:22:32 +0000 | [diff] [blame] | 341 | while(sec) |
| 342 | { |
Amaury Pouly | 8fa0e13 | 2011-05-02 22:52:52 +0000 | [diff] [blame] | 343 | shdr.sh_name = strtbl_index; |
Amaury Pouly | f85c044 | 2011-03-03 09:22:32 +0000 | [diff] [blame] | 344 | if(sec->type == EST_LOAD) |
Amaury Pouly | 8fa0e13 | 2011-05-02 22:52:52 +0000 | [diff] [blame] | 345 | { |
| 346 | strtbl_index += 1 + sprintf(&strtbl_content[strtbl_index], ".text%d", text_idx++); |
Amaury Pouly | f85c044 | 2011-03-03 09:22:32 +0000 | [diff] [blame] | 347 | shdr.sh_type = SHT_PROGBITS; |
Amaury Pouly | 8fa0e13 | 2011-05-02 22:52:52 +0000 | [diff] [blame] | 348 | } |
Amaury Pouly | f85c044 | 2011-03-03 09:22:32 +0000 | [diff] [blame] | 349 | else |
Amaury Pouly | 8fa0e13 | 2011-05-02 22:52:52 +0000 | [diff] [blame] | 350 | { |
| 351 | strtbl_index += 1 + sprintf(&strtbl_content[strtbl_index], ".bss%d", bss_idx++); |
Amaury Pouly | f85c044 | 2011-03-03 09:22:32 +0000 | [diff] [blame] | 352 | shdr.sh_type = SHT_NOBITS; |
Amaury Pouly | 8fa0e13 | 2011-05-02 22:52:52 +0000 | [diff] [blame] | 353 | } |
Amaury Pouly | f85c044 | 2011-03-03 09:22:32 +0000 | [diff] [blame] | 354 | shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR; |
| 355 | shdr.sh_addr = sec->addr; |
| 356 | shdr.sh_offset = sec->offset; |
| 357 | shdr.sh_size = sec->size; |
| 358 | shdr.sh_link = SHN_UNDEF; |
| 359 | shdr.sh_info = 0; |
| 360 | shdr.sh_addralign = 1; |
| 361 | shdr.sh_entsize = 0; |
| 362 | |
| 363 | write(user, offset, &shdr, sizeof shdr); |
| 364 | |
| 365 | offset += sizeof(Elf32_Shdr); |
| 366 | sec = sec->next; |
| 367 | } |
| 368 | |
| 369 | { |
Amaury Pouly | 8fa0e13 | 2011-05-02 22:52:52 +0000 | [diff] [blame] | 370 | shdr.sh_name = 1; |
Amaury Pouly | 80889ee | 2011-04-15 17:05:10 +0000 | [diff] [blame] | 371 | shdr.sh_type = SHT_STRTAB; |
Amaury Pouly | f85c044 | 2011-03-03 09:22:32 +0000 | [diff] [blame] | 372 | shdr.sh_flags = 0; |
| 373 | shdr.sh_addr = 0; |
| 374 | shdr.sh_offset = strtbl_offset + data_offset; |
Amaury Pouly | 8fa0e13 | 2011-05-02 22:52:52 +0000 | [diff] [blame] | 375 | shdr.sh_size = strtbl_index; |
Amaury Pouly | f85c044 | 2011-03-03 09:22:32 +0000 | [diff] [blame] | 376 | shdr.sh_link = SHN_UNDEF; |
| 377 | shdr.sh_info = 0; |
| 378 | shdr.sh_addralign = 1; |
| 379 | shdr.sh_entsize = 0; |
| 380 | |
| 381 | write(user, offset, &shdr, sizeof shdr); |
| 382 | |
| 383 | offset += sizeof(Elf32_Shdr); |
| 384 | } |
| 385 | |
| 386 | sec = params->first_section; |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 387 | while(sec) |
| 388 | { |
| 389 | if(sec->type == EST_LOAD) |
| 390 | write(user, sec->offset, sec->section, sec->size); |
| 391 | sec = sec->next; |
| 392 | } |
Amaury Pouly | f85c044 | 2011-03-03 09:22:32 +0000 | [diff] [blame] | 393 | |
Amaury Pouly | 8fa0e13 | 2011-05-02 22:52:52 +0000 | [diff] [blame] | 394 | write(user, strtbl_offset + data_offset, strtbl_content, strtbl_index); |
| 395 | free(strtbl_content); |
Amaury Pouly | c4cb4cc | 2011-04-17 15:49:58 +0000 | [diff] [blame] | 396 | } |
| 397 | |
| 398 | bool elf_read_file(struct elf_params_t *params, elf_read_fn_t read, |
| 399 | elf_printf_fn_t printf, void *user) |
| 400 | { |
| 401 | #define error_printf(...) ({printf(user, true, __VA_ARGS__); return false;}) |
Amaury Pouly | a29ef61 | 2011-05-13 12:13:02 +0000 | [diff] [blame] | 402 | |
Amaury Pouly | c4cb4cc | 2011-04-17 15:49:58 +0000 | [diff] [blame] | 403 | /* read header */ |
| 404 | Elf32_Ehdr ehdr; |
| 405 | if(!read(user, 0, &ehdr, sizeof(ehdr))) |
| 406 | { |
| 407 | printf(user, true, "error reading elf header\n"); |
| 408 | return false; |
| 409 | } |
| 410 | /* basic checks */ |
| 411 | if(ehdr.e_ident[EI_MAG0] != ELFMAG0 || ehdr.e_ident[EI_MAG1] != ELFMAG1 || |
| 412 | ehdr.e_ident[EI_MAG2] != ELFMAG2 || ehdr.e_ident[EI_MAG3] != ELFMAG3) |
| 413 | error_printf("invalid elf header\n"); |
| 414 | if(ehdr.e_ident[EI_CLASS] != ELFCLASS32) |
| 415 | error_printf("invalid elf class: must be a 32-bit object\n"); |
| 416 | if(ehdr.e_ident[EI_DATA] != ELFDATA2LSB) |
| 417 | error_printf("invalid elf data encoding: must be 32-bit lsb\n"); |
| 418 | if(ehdr.e_ident[EI_VERSION] != EV_CURRENT) |
Amaury Pouly | 8da7237 | 2011-05-01 12:44:57 +0000 | [diff] [blame] | 419 | error_printf("invalid elf version\n"); |
Amaury Pouly | c4cb4cc | 2011-04-17 15:49:58 +0000 | [diff] [blame] | 420 | if(ehdr.e_type != ET_EXEC) |
| 421 | error_printf("invalid elf file: must be an executable file\n"); |
| 422 | if(ehdr.e_machine != EM_ARM) |
| 423 | error_printf("invalid elf file: must target an arm machine\n"); |
| 424 | if(ehdr.e_ehsize != sizeof(ehdr)) |
| 425 | error_printf("invalid elf file: size header mismatch\n"); |
Amaury Pouly | 79c6aca | 2011-04-18 20:27:23 +0000 | [diff] [blame] | 426 | if(ehdr.e_phnum > 0 && ehdr.e_phentsize != sizeof(Elf32_Phdr)) |
Amaury Pouly | c4cb4cc | 2011-04-17 15:49:58 +0000 | [diff] [blame] | 427 | error_printf("invalid elf file: program header size mismatch\n"); |
Amaury Pouly | 79c6aca | 2011-04-18 20:27:23 +0000 | [diff] [blame] | 428 | if(ehdr.e_shnum > 0 && ehdr.e_shentsize != sizeof(Elf32_Shdr)) |
Amaury Pouly | c4cb4cc | 2011-04-17 15:49:58 +0000 | [diff] [blame] | 429 | error_printf("invalid elf file: section header size mismatch\n"); |
Amaury Pouly | 8da7237 | 2011-05-01 12:44:57 +0000 | [diff] [blame] | 430 | elf_set_start_addr(params, ehdr.e_entry); |
Amaury Pouly | c4cb4cc | 2011-04-17 15:49:58 +0000 | [diff] [blame] | 431 | |
| 432 | char *strtab = NULL; |
Amaury Pouly | 79c6aca | 2011-04-18 20:27:23 +0000 | [diff] [blame] | 433 | if(ehdr.e_shstrndx != SHN_UNDEF) |
Amaury Pouly | c4cb4cc | 2011-04-17 15:49:58 +0000 | [diff] [blame] | 434 | { |
| 435 | Elf32_Shdr shstrtab; |
| 436 | if(read(user, ehdr.e_shoff + ehdr.e_shstrndx * ehdr.e_shentsize, |
| 437 | &shstrtab, sizeof(shstrtab))) |
| 438 | { |
| 439 | strtab = xmalloc(shstrtab.sh_size); |
| 440 | if(!read(user, shstrtab.sh_offset, strtab, shstrtab.sh_size)) |
| 441 | { |
| 442 | free(strtab); |
| 443 | strtab = NULL; |
| 444 | } |
| 445 | } |
| 446 | } |
| 447 | /* run through sections */ |
| 448 | printf(user, false, "ELF file:\n"); |
Amaury Pouly | 64b4672 | 2011-09-15 14:36:58 +0000 | [diff] [blame] | 449 | for(int i = 1; i < ehdr.e_shnum; i++) |
Amaury Pouly | c4cb4cc | 2011-04-17 15:49:58 +0000 | [diff] [blame] | 450 | { |
| 451 | uint32_t off = ehdr.e_shoff + i * ehdr.e_shentsize; |
| 452 | Elf32_Shdr shdr; |
| 453 | memset(&shdr, 0, sizeof(shdr)); |
| 454 | if(!read(user, off, &shdr, sizeof(shdr))) |
| 455 | error_printf("error reading elf section header"); |
| 456 | |
| 457 | if(shdr.sh_type == SHT_PROGBITS && shdr.sh_flags & SHF_ALLOC) |
| 458 | { |
| 459 | void *data = xmalloc(shdr.sh_size); |
| 460 | if(!read(user, shdr.sh_offset, data, shdr.sh_size)) |
Amaury Pouly | 8da7237 | 2011-05-01 12:44:57 +0000 | [diff] [blame] | 461 | error_printf("error read self section data\n"); |
Amaury Pouly | c4cb4cc | 2011-04-17 15:49:58 +0000 | [diff] [blame] | 462 | elf_add_load_section(params, shdr.sh_addr, shdr.sh_size, data); |
Amaury Pouly | 5827937 | 2011-11-06 01:49:13 +0000 | [diff] [blame] | 463 | free(data); |
Amaury Pouly | c4cb4cc | 2011-04-17 15:49:58 +0000 | [diff] [blame] | 464 | |
Amaury Pouly | 79c6aca | 2011-04-18 20:27:23 +0000 | [diff] [blame] | 465 | if(strtab) |
| 466 | printf(user, false, "create load segment for %s\n", &strtab[shdr.sh_name]); |
Amaury Pouly | c4cb4cc | 2011-04-17 15:49:58 +0000 | [diff] [blame] | 467 | } |
| 468 | else if(shdr.sh_type == SHT_NOBITS && shdr.sh_flags & SHF_ALLOC) |
| 469 | { |
| 470 | elf_add_fill_section(params, shdr.sh_addr, shdr.sh_size, 0); |
Amaury Pouly | 79c6aca | 2011-04-18 20:27:23 +0000 | [diff] [blame] | 471 | if(strtab) |
| 472 | printf(user, false, "create fill segment for %s\n", &strtab[shdr.sh_name]); |
Amaury Pouly | c4cb4cc | 2011-04-17 15:49:58 +0000 | [diff] [blame] | 473 | } |
| 474 | else |
Amaury Pouly | 79c6aca | 2011-04-18 20:27:23 +0000 | [diff] [blame] | 475 | { |
| 476 | if(strtab) |
| 477 | printf(user, false, "filter out %s\n", &strtab[shdr.sh_name], shdr.sh_type); |
| 478 | } |
Amaury Pouly | c4cb4cc | 2011-04-17 15:49:58 +0000 | [diff] [blame] | 479 | |
| 480 | } |
Amaury Pouly | 5827937 | 2011-11-06 01:49:13 +0000 | [diff] [blame] | 481 | free(strtab); |
Amaury Pouly | 64b4672 | 2011-09-15 14:36:58 +0000 | [diff] [blame] | 482 | /* run through segments */ |
| 483 | for(int i = 1; i < ehdr.e_phnum; i++) |
| 484 | { |
| 485 | uint32_t off = ehdr.e_phoff + i * ehdr.e_phentsize; |
| 486 | Elf32_Phdr phdr; |
| 487 | memset(&phdr, 0, sizeof(phdr)); |
| 488 | if(!read(user, off, &phdr, sizeof(phdr))) |
| 489 | error_printf("error reading elf segment header"); |
| 490 | if(phdr.p_type != PT_LOAD) |
| 491 | continue; |
| 492 | struct elf_segment_t *seg = elf_add_segment(params); |
| 493 | seg->vaddr = phdr.p_vaddr; |
| 494 | seg->paddr = phdr.p_paddr; |
| 495 | seg->vsize = phdr.p_memsz; |
| 496 | seg->psize = phdr.p_filesz; |
| 497 | printf(user, false, "create segment [%#x,+%#x[ -> [%#x,+%#x[\n", |
| 498 | seg->vaddr, seg->vsize, seg->paddr, seg->psize); |
| 499 | } |
| 500 | |
Amaury Pouly | c4cb4cc | 2011-04-17 15:49:58 +0000 | [diff] [blame] | 501 | return true; |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 502 | } |
| 503 | |
Amaury Pouly | 64b4672 | 2011-09-15 14:36:58 +0000 | [diff] [blame] | 504 | uint32_t elf_translate_virtual_address(struct elf_params_t *params, uint32_t addr) |
| 505 | { |
| 506 | struct elf_segment_t *seg = params->first_segment; |
| 507 | while(seg) |
| 508 | { |
| 509 | if(seg->vaddr <= addr && addr < seg->vaddr + seg->vsize) |
| 510 | return addr - seg->vaddr + seg->paddr; |
| 511 | seg = seg->next; |
| 512 | } |
| 513 | return addr; |
| 514 | } |
| 515 | |
| 516 | void elf_translate_addresses(struct elf_params_t *params) |
| 517 | { |
| 518 | struct elf_section_t *sec = params->first_section; |
| 519 | while(sec) |
| 520 | { |
| 521 | sec->addr = elf_translate_virtual_address(params, sec->addr); |
| 522 | sec = sec->next; |
| 523 | } |
| 524 | params->start_addr = elf_translate_virtual_address(params, params->start_addr); |
| 525 | } |
| 526 | |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 527 | bool elf_is_empty(struct elf_params_t *params) |
| 528 | { |
| 529 | return params->first_section == NULL; |
| 530 | } |
| 531 | |
| 532 | void elf_set_start_addr(struct elf_params_t *params, uint32_t addr) |
| 533 | { |
| 534 | params->has_start_addr = true; |
| 535 | params->start_addr = addr; |
| 536 | } |
| 537 | |
Amaury Pouly | c4cb4cc | 2011-04-17 15:49:58 +0000 | [diff] [blame] | 538 | bool elf_get_start_addr(struct elf_params_t *params, uint32_t *addr) |
| 539 | { |
| 540 | if(params->has_start_addr && addr != NULL) |
| 541 | *addr = params->start_addr; |
| 542 | return params->has_start_addr; |
| 543 | } |
| 544 | |
| 545 | int elf_get_nr_sections(struct elf_params_t *params) |
| 546 | { |
| 547 | int nr = 0; |
| 548 | struct elf_section_t *sec = params->first_section; |
| 549 | while(sec) |
| 550 | { |
| 551 | nr++; |
| 552 | sec = sec->next; |
| 553 | } |
| 554 | return nr; |
| 555 | } |
| 556 | |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 557 | void elf_release(struct elf_params_t *params) |
| 558 | { |
Amaury Pouly | 5827937 | 2011-11-06 01:49:13 +0000 | [diff] [blame] | 559 | struct elf_section_t *sec = params->first_section; |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 560 | while(sec) |
| 561 | { |
Amaury Pouly | 5827937 | 2011-11-06 01:49:13 +0000 | [diff] [blame] | 562 | struct elf_section_t *next_sec = sec->next; |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 563 | if(sec->type == EST_LOAD) |
| 564 | free(sec->section); |
| 565 | free(sec); |
| 566 | sec = next_sec; |
| 567 | } |
Amaury Pouly | 5827937 | 2011-11-06 01:49:13 +0000 | [diff] [blame] | 568 | struct elf_segment_t *seg = params->first_segment; |
| 569 | while(seg) |
| 570 | { |
| 571 | struct elf_segment_t *next_seg = seg->next; |
| 572 | free(seg); |
| 573 | seg = next_seg; |
| 574 | } |
Amaury Pouly | 1583b1c | 2010-12-01 16:17:11 +0000 | [diff] [blame] | 575 | } |