artiq/soc/runtime/elf_loader.c

241 lines
7.7 KiB
C
Raw Normal View History

2014-07-04 23:49:08 +08:00
#include <string.h>
2014-10-19 23:51:49 +08:00
#include "comm.h"
2014-07-04 23:49:08 +08:00
#include "elf_loader.h"
#define EI_NIDENT 16
struct elf32_ehdr {
2014-09-05 12:03:22 +08:00
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 */
2014-07-04 23:49:08 +08:00
} __attribute__((packed));
static const unsigned char elf_magic_header[] = {
2014-09-05 12:03:22 +08:00
0x7f, 0x45, 0x4c, 0x46, /* 0x7f, 'E', 'L', 'F' */
0x01, /* Only 32-bit objects. */
0x02, /* Only big-endian. */
0x01, /* Only ELF version 1. */
2014-07-04 23:49:08 +08:00
};
#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 {
2014-09-05 12:03:22 +08:00
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 */
2014-07-04 23:49:08 +08:00
} __attribute__((packed));
struct elf32_name {
2014-09-05 12:03:22 +08:00
char name[12];
2014-07-04 23:49:08 +08:00
} __attribute__((packed));
struct elf32_rela {
2014-09-05 12:03:22 +08:00
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 {
2014-09-05 12:03:22 +08:00
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 STT_NOTYPE 0
#define STT_OBJECT 1
#define STT_FUNC 2
#define STT_SECTION 3
#define STT_FILE 4
#define ELF32_ST_TYPE(info) ((info) & 0x0f)
2014-07-04 23:49:08 +08:00
#define SANITIZE_OFFSET_SIZE(offset, size) \
2014-09-05 12:03:22 +08:00
if(offset > 0x10000000) { \
2014-10-19 23:51:49 +08:00
comm_log("Incorrect offset in ELF data"); \
return 0; \
2014-09-05 12:03:22 +08:00
} \
if((offset + size) > elf_length) { \
2014-10-19 23:51:49 +08:00
comm_log("Attempted to access past the end of ELF data"); \
return 0; \
2014-09-05 12:03:22 +08:00
}
2014-07-04 23:49:08 +08:00
#define GET_POINTER_SAFE(target, target_type, offset) \
2014-09-05 12:03:22 +08:00
SANITIZE_OFFSET_SIZE(offset, sizeof(target_type)); \
target = (target_type *)((char *)elf_data + offset)
2014-07-04 23:49:08 +08:00
2014-08-28 16:56:48 +08:00
void *find_symbol(const struct symbol *symbols, const char *name)
{
2014-09-05 12:03:22 +08:00
int i;
2014-09-05 12:03:22 +08:00
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)
{
2014-09-05 12:03:22 +08:00
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
2014-10-19 23:51:49 +08:00
comm_log("Unsupported relocation type: %d", type);
2014-09-05 12:03:22 +08:00
return 1;
}
int load_elf(symbol_resolver resolver, symbol_callback callback, void *elf_data, int elf_length, void *dest, int dest_length)
2014-07-04 23:49:08 +08:00
{
2014-09-05 12:03:22 +08:00
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) {
2014-10-19 23:51:49 +08:00
comm_log("Incorrect ELF header");
return 0;
2014-09-05 12:03:22 +08:00
}
if(ehdr->type != ET_REL) {
2014-10-19 23:51:49 +08:00
comm_log("ELF is not relocatable");
return 0;
2014-09-05 12:03:22 +08:00
}
if(ehdr->machine != EM_OR1K) {
2014-10-19 23:51:49 +08:00
comm_log("ELF is for a different machine");
return 0;
2014-09-05 12:03:22 +08:00
}
/* 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) {
2014-10-19 23:51:49 +08:00
comm_log(".text section is too large");
return 0;
2014-09-05 12:03:22 +08:00
}
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;
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) {
char *name;
2014-09-05 12:03:22 +08:00
void *target;
name = (char *)elf_data + strtaboff + sym->name;
target = resolver(name);
if(target == NULL) {
2014-10-19 23:51:49 +08:00
comm_log("Undefined symbol: %s", name);
return 0;
2014-09-05 12:03:22 +08:00
}
if(!fixup(dest, dest_length, rela, target))
return 0;
2014-09-05 12:03:22 +08:00
} else {
2014-10-19 23:51:49 +08:00
comm_log("Unsupported relocation");
return 0;
2014-09-05 12:03:22 +08:00
}
}
/* list provided functions via callback */
for(i=0;i<symtabsize;i+=sizeof(struct elf32_sym)) {
struct elf32_sym *sym;
GET_POINTER_SAFE(sym, struct elf32_sym, symtaboff + i);
if((ELF32_ST_TYPE(sym->info) == STT_FUNC) && (sym->name != 0)) {
char *name;
void *target;
name = (char *)elf_data + strtaboff + sym->name;
target = (char *)dest + sym->value;
if(!callback(name, target))
return 0;
}
}
return 1;
2014-07-04 23:49:08 +08:00
}