1 /* 2 ** Copyright 2002-2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 ** Distributed under the terms of the Haiku License. 4 */ 5 6 7 #include "elf.h" 8 9 #include <boot/platform.h> 10 #include <boot/stage2.h> 11 #include <elf32.h> 12 #include <kernel.h> 13 14 #include <unistd.h> 15 #include <string.h> 16 #include <stdlib.h> 17 18 //#define TRACE_ELF 19 #ifdef TRACE_ELF 20 # define TRACE(x) dprintf x 21 #else 22 # define TRACE(x) ; 23 #endif 24 25 26 static status_t 27 verify_elf_header(struct Elf32_Ehdr &header) 28 { 29 if (memcmp(header.e_ident, ELF_MAGIC, 4) != 0 30 || header.e_ident[4] != ELFCLASS32 31 || header.e_phoff == 0 32 || !header.IsHostEndian() 33 || header.e_phentsize != sizeof(struct Elf32_Phdr)) 34 return B_BAD_TYPE; 35 36 return B_OK; 37 } 38 39 40 static status_t 41 load_elf_symbol_table(int fd, preloaded_image *image) 42 { 43 struct Elf32_Ehdr &elfHeader = image->elf_header; 44 Elf32_Sym *symbolTable = NULL; 45 Elf32_Shdr *stringHeader = NULL; 46 uint32 numSymbols = 0; 47 char *stringTable; 48 status_t status; 49 50 // get section headers 51 52 ssize_t size = elfHeader.e_shnum * elfHeader.e_shentsize; 53 Elf32_Shdr *sectionHeaders = (struct Elf32_Shdr *)malloc(size); 54 if (sectionHeaders == NULL) { 55 dprintf("error allocating space for section headers\n"); 56 return B_NO_MEMORY; 57 } 58 59 ssize_t length = read_pos(fd, elfHeader.e_shoff, sectionHeaders, size); 60 if (length < size) { 61 TRACE(("error reading in program headers\n")); 62 status = B_ERROR; 63 goto error1; 64 } 65 66 // find symbol table in section headers 67 68 for (int32 i = 0; i < elfHeader.e_shnum; i++) { 69 if (sectionHeaders[i].sh_type == SHT_SYMTAB) { 70 stringHeader = §ionHeaders[sectionHeaders[i].sh_link]; 71 72 if (stringHeader->sh_type != SHT_STRTAB) { 73 TRACE(("doesn't link to string table\n")); 74 status = B_BAD_DATA; 75 goto error1; 76 } 77 78 // read in symbol table 79 symbolTable = (Elf32_Sym *)kernel_args_malloc(size = sectionHeaders[i].sh_size); 80 if (symbolTable == NULL) { 81 status = B_NO_MEMORY; 82 goto error1; 83 } 84 85 length = read_pos(fd, sectionHeaders[i].sh_offset, symbolTable, size); 86 if (length < size) { 87 TRACE(("error reading in symbol table\n")); 88 status = B_ERROR; 89 goto error1; 90 } 91 92 numSymbols = size / sizeof(Elf32_Sym); 93 break; 94 } 95 } 96 97 if (symbolTable == NULL) { 98 TRACE(("no symbol table\n")); 99 status = B_BAD_VALUE; 100 goto error1; 101 } 102 103 // read in string table 104 105 stringTable = (char *)kernel_args_malloc(size = stringHeader->sh_size); 106 if (stringTable == NULL) { 107 status = B_NO_MEMORY; 108 goto error2; 109 } 110 111 length = read_pos(fd, stringHeader->sh_offset, stringTable, size); 112 if (length < size) { 113 TRACE(("error reading in string table\n")); 114 status = B_ERROR; 115 goto error3; 116 } 117 118 TRACE(("loaded debug %ld symbols\n", numSymbols)); 119 120 // insert tables into image 121 image->debug_symbols = symbolTable; 122 image->num_debug_symbols = numSymbols; 123 image->debug_string_table = stringTable; 124 image->debug_string_table_size = size; 125 126 free(sectionHeaders); 127 return B_OK; 128 129 error3: 130 kernel_args_free(stringTable); 131 error2: 132 kernel_args_free(symbolTable); 133 error1: 134 free(sectionHeaders); 135 136 return status; 137 } 138 139 140 status_t 141 elf_load_image(int fd, preloaded_image *image) 142 { 143 size_t totalSize; 144 status_t status; 145 146 TRACE(("elf_load_image(fd = %d, image = %p)\n", fd, image)); 147 148 struct Elf32_Ehdr &elfHeader = image->elf_header; 149 150 ssize_t length = read_pos(fd, 0, &elfHeader, sizeof(Elf32_Ehdr)); 151 if (length < (ssize_t)sizeof(Elf32_Ehdr)) 152 return B_BAD_TYPE; 153 154 status = verify_elf_header(elfHeader); 155 if (status < B_OK) 156 return status; 157 158 ssize_t size = elfHeader.e_phnum * elfHeader.e_phentsize; 159 Elf32_Phdr *programHeaders = (struct Elf32_Phdr *)malloc(size); 160 if (programHeaders == NULL) { 161 dprintf("error allocating space for program headers\n"); 162 return B_NO_MEMORY; 163 } 164 165 length = read_pos(fd, elfHeader.e_phoff, programHeaders, size); 166 if (length < size) { 167 TRACE(("error reading in program headers\n")); 168 status = B_ERROR; 169 goto error1; 170 } 171 172 // create an area large enough to hold the image 173 174 image->data_region.size = 0; 175 image->text_region.size = 0; 176 177 for (int32 i = 0; i < elfHeader.e_phnum; i++) { 178 Elf32_Phdr &header = programHeaders[i]; 179 180 switch (header.p_type) { 181 case PT_LOAD: 182 break; 183 case PT_DYNAMIC: 184 image->dynamic_section.start = header.p_vaddr; 185 image->dynamic_section.size = header.p_memsz; 186 continue; 187 default: 188 dprintf("unhandled pheader type 0x%lx\n", header.p_type); 189 continue; 190 } 191 192 elf_region *region; 193 if (header.IsReadWrite()) { 194 if (image->data_region.size != 0) { 195 dprintf("elf: rw already handled!\n"); 196 continue; 197 } 198 region = &image->data_region; 199 } else if (header.IsExecutable()) { 200 if (image->text_region.size != 0) { 201 dprintf("elf: ro already handled!\n"); 202 continue; 203 } 204 region = &image->text_region; 205 } else 206 continue; 207 208 region->start = ROUNDOWN(header.p_vaddr, B_PAGE_SIZE); 209 region->size = ROUNDUP(header.p_memsz + (header.p_vaddr % B_PAGE_SIZE), B_PAGE_SIZE); 210 region->delta = -region->start; 211 212 TRACE(("segment %d: start = %p, size = %lu, delta = %lx\n", i, 213 region->start, region->size, region->delta)); 214 } 215 216 // get the segment order 217 elf_region *firstRegion; 218 elf_region *secondRegion; 219 if (image->text_region.start < image->data_region.start) { 220 firstRegion = &image->text_region; 221 secondRegion = &image->data_region; 222 } else { 223 firstRegion = &image->data_region; 224 secondRegion = &image->text_region; 225 } 226 227 // Check whether the segments have an unreasonable amount of unused space 228 // inbetween. 229 totalSize = secondRegion->start + secondRegion->size - firstRegion->start; 230 if (totalSize > image->text_region.size + image->data_region.size 231 + 8 * 1024) { 232 status = B_BAD_DATA; 233 goto error1; 234 } 235 236 // if image->text_region.start == NULL (image is relocatable), 237 // platform_allocate_region() automatically allocates an address 238 if (platform_allocate_region((void **)&firstRegion->start, totalSize, 239 B_READ_AREA | B_WRITE_AREA) < B_OK) { 240 status = B_NO_MEMORY; 241 goto error1; 242 } 243 244 // initialize the region pointers to the allocated region 245 secondRegion->start += firstRegion->start + firstRegion->delta; 246 247 image->data_region.delta += image->data_region.start; 248 image->text_region.delta += image->text_region.start; 249 250 // load program data 251 252 for (int i = 0; i < elfHeader.e_phnum; i++) { 253 Elf32_Phdr &header = programHeaders[i]; 254 255 if (header.p_type != PT_LOAD) 256 continue; 257 258 elf_region *region; 259 if (header.IsReadWrite()) 260 region = &image->data_region; 261 else if (header.IsExecutable()) 262 region = &image->text_region; 263 else 264 continue; 265 266 TRACE(("load segment %d (%ld bytes)...\n", i, header.p_filesz)); 267 268 length = read_pos(fd, header.p_offset, 269 (void *)(region->start + (header.p_vaddr % B_PAGE_SIZE)), header.p_filesz); 270 if (length < (ssize_t)header.p_filesz) { 271 status = B_BAD_DATA; 272 dprintf("error reading in seg %d\n", i); 273 goto error2; 274 } 275 276 // clear anything above the file size (that may also contain the BSS area) 277 278 uint32 offset = (header.p_vaddr % B_PAGE_SIZE) + header.p_filesz; 279 if (offset < region->size) 280 memset((void *)(region->start + offset), 0, region->size - offset); 281 } 282 283 // modify the dynamic section by the delta of the regions 284 image->dynamic_section.start += image->text_region.delta; 285 286 image->num_debug_symbols = 0; 287 image->debug_symbols = NULL; 288 image->debug_string_table = NULL; 289 290 // ToDo: this should be enabled by kernel settings! 291 if (1) 292 load_elf_symbol_table(fd, image); 293 294 free(programHeaders); 295 296 return B_OK; 297 298 error2: 299 if (image->text_region.start != NULL) 300 platform_free_region((void *)image->text_region.start, totalSize); 301 error1: 302 free(programHeaders); 303 304 return status; 305 } 306 307 308 status_t 309 elf_load_image(Directory *directory, const char *path) 310 { 311 preloaded_image *image; 312 313 TRACE(("elf_load_image(directory = %p, \"%s\")\n", directory, path)); 314 315 int fd = open_from(directory, path, O_RDONLY); 316 if (fd < 0) 317 return fd; 318 319 // check if this file has already been loaded 320 321 struct stat stat; 322 fstat(fd, &stat); 323 324 image = gKernelArgs.preloaded_images; 325 for (; image != NULL; image = image->next) { 326 if (image->inode == stat.st_ino) { 327 // file has already been loaded, no need to load it twice! 328 close(fd); 329 return B_OK; 330 } 331 } 332 333 // we still need to load it, so do it 334 335 image = (preloaded_image *)kernel_args_malloc(sizeof(preloaded_image)); 336 if (image == NULL) { 337 close(fd); 338 return B_NO_MEMORY; 339 } 340 341 status_t status = elf_load_image(fd, image); 342 if (status == B_OK) { 343 image->name = kernel_args_strdup(path); 344 image->inode = stat.st_ino; 345 346 // insert to kernel args 347 image->next = gKernelArgs.preloaded_images; 348 gKernelArgs.preloaded_images = image; 349 } else 350 kernel_args_free(image); 351 352 close(fd); 353 return status; 354 } 355 356