#include <stdio.h> #include <string.h> #include "elf_loader.h" #define EI_NIDENT 16 struct elf32_ehdr { unsigned char ident[EI_NIDENT]; /* ident bytes */ unsigned short type; /* file type */ unsigned short machine; /* target machine */ unsigned int version; /* file version */ unsigned int entry; /* start address */ unsigned int phoff; /* phdr file offset */ unsigned int shoff; /* shdr file offset */ unsigned int flags; /* file flags */ unsigned short ehsize; /* sizeof ehdr */ unsigned short phentsize; /* sizeof phdr */ unsigned short phnum; /* number phdrs */ unsigned short shentsize; /* sizeof shdr */ unsigned short shnum; /* number shdrs */ unsigned short shstrndx; /* shdr string index */ } __attribute__((packed)); static const unsigned char elf_magic_header[] = { 0x7f, 0x45, 0x4c, 0x46, /* 0x7f, 'E', 'L', 'F' */ 0x01, /* Only 32-bit objects. */ 0x02, /* Only big-endian. */ 0x01, /* Only ELF version 1. */ }; #define ET_NONE 0 /* Unknown type. */ #define ET_REL 1 /* Relocatable. */ #define ET_EXEC 2 /* Executable. */ #define ET_DYN 3 /* Shared object. */ #define ET_CORE 4 /* Core file. */ #define EM_OR1K 0x005c struct elf32_shdr { unsigned int name; /* section name */ unsigned int type; /* SHT_... */ unsigned int flags; /* SHF_... */ unsigned int addr; /* virtual address */ unsigned int offset; /* file offset */ unsigned int size; /* section size */ unsigned int link; /* misc info */ unsigned int info; /* misc info */ unsigned int addralign; /* memory alignment */ unsigned int entsize; /* entry size if table */ } __attribute__((packed)); struct elf32_name { char name[12]; } __attribute__((packed)); struct elf32_rela { unsigned int offset; /* Location to be relocated. */ unsigned int info; /* Relocation type and symbol index. */ int addend; /* Addend. */ } __attribute__((packed)); #define ELF32_R_SYM(info) ((info) >> 8) #define ELF32_R_TYPE(info) ((unsigned char)(info)) #define R_OR1K_INSN_REL_26 6 struct elf32_sym { unsigned int name; /* String table index of name. */ unsigned int value; /* Symbol value. */ unsigned int size; /* Size of associated object. */ unsigned char info; /* Type and binding information. */ unsigned char other; /* Reserved (not used). */ unsigned short shndx; /* Section index of symbol. */ } __attribute__((packed)); #define SANITIZE_OFFSET_SIZE(offset, size) \ if(offset > 0x10000000) { \ printf("Incorrect offset in ELF data"); \ return 0; \ } \ if((offset + size) > elf_length) { \ printf("Attempted to access past the end of ELF data"); \ return 0; \ } #define GET_POINTER_SAFE(target, target_type, offset) \ SANITIZE_OFFSET_SIZE(offset, sizeof(target_type)); \ target = (target_type *)((char *)elf_data + offset) static void *find_symbol(const struct symbol *symbols, const char *name) { int i; i = 0; while((symbols[i].name != NULL) && (strcmp(symbols[i].name, name) != 0)) i++; return symbols[i].target; } static int fixup(void *dest, int dest_length, struct elf32_rela *rela, void *target) { int type, offset; unsigned int *_dest = dest; unsigned int *_target = target; type = ELF32_R_TYPE(rela->info); offset = rela->offset/4; if(type == R_OR1K_INSN_REL_26) { int val; val = _target - (_dest + offset); _dest[offset] = (_dest[offset] & 0xfc000000) | (val & 0x03ffffff); } else printf("Unsupported relocation type: %d\n", type); return 1; } int load_elf(const struct symbol *symbols, void *elf_data, int elf_length, void *dest, int dest_length) { struct elf32_ehdr *ehdr; struct elf32_shdr *strtable; unsigned int shdrptr; int i; unsigned int textoff, textsize; unsigned int textrelaoff, textrelasize; unsigned int symtaboff, symtabsize; unsigned int strtaboff, strtabsize; /* validate ELF */ GET_POINTER_SAFE(ehdr, struct elf32_ehdr, 0); if(memcmp(ehdr->ident, elf_magic_header, sizeof(elf_magic_header)) != 0) { printf("Incorrect ELF header\n"); return 0; } if(ehdr->type != ET_REL) { printf("ELF is not relocatable\n"); return 0; } if(ehdr->machine != EM_OR1K) { printf("ELF is for a different machine\n"); return 0; } /* extract section info */ GET_POINTER_SAFE(strtable, struct elf32_shdr, ehdr->shoff + ehdr->shentsize*ehdr->shstrndx); textoff = textsize = 0; textrelaoff = textrelasize = 0; symtaboff = symtabsize = 0; strtaboff = strtabsize = 0; shdrptr = ehdr->shoff; for(i=0;i<ehdr->shnum;i++) { struct elf32_shdr *shdr; struct elf32_name *name; GET_POINTER_SAFE(shdr, struct elf32_shdr, shdrptr); GET_POINTER_SAFE(name, struct elf32_name, strtable->offset + shdr->name); if(strncmp(name->name, ".text", 5) == 0) { textoff = shdr->offset; textsize = shdr->size; } else if(strncmp(name->name, ".rela.text", 10) == 0) { textrelaoff = shdr->offset; textrelasize = shdr->size; } else if(strncmp(name->name, ".symtab", 7) == 0) { symtaboff = shdr->offset; symtabsize = shdr->size; } else if(strncmp(name->name, ".strtab", 7) == 0) { strtaboff = shdr->offset; strtabsize = shdr->size; } shdrptr += ehdr->shentsize; } SANITIZE_OFFSET_SIZE(textoff, textsize); SANITIZE_OFFSET_SIZE(textrelaoff, textrelasize); SANITIZE_OFFSET_SIZE(symtaboff, symtabsize); SANITIZE_OFFSET_SIZE(strtaboff, strtabsize); /* load .text section */ if(textsize > dest_length) { printf(".text section is too large\n"); return 0; } memcpy(dest, (char *)elf_data + textoff, textsize); /* process .text relocations */ for(i=0;i<textrelasize;i+=sizeof(struct elf32_rela)) { struct elf32_rela *rela; struct elf32_sym *sym; char *name; GET_POINTER_SAFE(rela, struct elf32_rela, textrelaoff + i); GET_POINTER_SAFE(sym, struct elf32_sym, symtaboff + sizeof(struct elf32_sym)*ELF32_R_SYM(rela->info)); if(sym->name != 0) { void *target; name = (char *)elf_data + strtaboff + sym->name; target = find_symbol(symbols, name); if(target == NULL) { printf("Undefined symbol: %s\n", name); return 0; } if(!fixup(dest, dest_length, rela, target)) return 0; } else { printf("Unsupported relocation\n"); return 0; } } return 1; }