Last active
January 30, 2020 00:51
-
-
Save lighth7015/d1977846bffc96a121f87c8dd0aa5bd3 to your computer and use it in GitHub Desktop.
ELF loader
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* ELF loadable modules | |
* | |
* Architecture-dependend functions for x86. | |
* | |
* 12 Feb 2011, Yury Ossadchy | |
**/ | |
#include "elf-module-private.h" | |
int elf_module_check_machine(elf_module_t *elf) | |
{ | |
return elf->header->e_machine == EM_386 | |
|| elf->header->e_machine == EM_486; | |
} | |
int elf_module_reloc_section(elf_module_t *elf, Elf_Shdr *shdr) { | |
int n = shdr->sh_size / sizeof(Elf_Rel), status = -EME_UNSUPPORTED; | |
Elf_Sym *symtab = (Elf_Sym *) elf->header + elf->symtab->sh_offset; | |
for ( Elf_Rel *rel = (Elf_Rel *) elf->header + shdr->sh_offset | |
, *end = (Elf_Rel *) &rel[n] | |
; rel < end | |
; rel++) | |
{ | |
Elf_Addr *ptr = (Elf_Addr *) ((intptr_t *) (((intptr_t *) elf->start) + | |
((intptr_t) elf->sections[shdr->sh_info].sh_addr) + | |
((intptr_t) rel->r_offset))); | |
Elf_Sym *sym = &symtab[ELF_REL_SYM(rel->r_info)]; | |
switch (ELF_REL_TYPE(rel->r_info)) { | |
case R_386_32: | |
*ptr += sym->st_value; | |
break; | |
case R_386_PC32: | |
*ptr += sym->st_value - (Elf_Addr) ((intptr_t) ptr); | |
break; | |
default: | |
goto elf_reloc_finished; | |
} | |
} | |
status = 0; | |
elf_reloc_finished: | |
return status; | |
} | |
int elf_module_reloca_section(elf_module_t *elf, Elf_Shdr *shdr) { | |
return -EME_UNSUPPORTED; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* ELF loadable modules | |
* | |
* 12 Feb 2011, Yury Ossadchy | |
*/ | |
#include <stdio.h> | |
#include <string.h> | |
#include "elf-module-private.h" | |
static char *elf_module_sym_name(elf_module_t *elf, int offs) { | |
return elf->names + offs; | |
} | |
static void elf_module_layout(elf_module_t *elf) | |
{ | |
Elf_Shdr *shdr; | |
int i = 1; | |
for ( shdr = &elf->sections[i]; | |
i < elf->header->e_shnum; | |
i++, shdr = &elf->sections[i]) | |
{ | |
if (shdr->sh_flags & SHF_ALLOC) { | |
shdr->sh_addr = shdr->sh_addralign? | |
(elf->size + shdr->sh_addralign - 1) & ~(shdr->sh_addralign - 1): | |
elf->size; | |
elf->size = shdr->sh_addr + shdr->sh_size; | |
} | |
} | |
} | |
static void *elf_module_get_ptr(elf_module_t *elf, Elf_Addr addr){ | |
return (void *) (((intptr_t) elf->start) + ((intptr_t) addr)); | |
} | |
static void *elf_module_sec_ptr(elf_module_t *elf, Elf_Shdr *shdr) { | |
return (void *)(((intptr_t) elf->header) + (intptr_t) shdr->sh_offset); | |
} | |
static int elf_module_reloc(elf_module_t *elf) { | |
int i = 0; | |
Elf_Shdr *shdr; | |
for ( i = 0, shdr = &elf->sections[i]; | |
i < elf->header->e_shnum; | |
i++, | |
shdr = &elf->sections[i]) | |
{ | |
switch (shdr->sh_type) { | |
case SHT_REL: | |
elf_module_reloc_section(elf, shdr); | |
break; | |
case SHT_RELA: | |
elf_module_reloca_section(elf, shdr); | |
break; | |
} | |
shdr++; | |
} | |
return 0; | |
} | |
static int elf_module_link(elf_module_t *elf, elf_module_link_cbs_t *cbs) | |
{ | |
int result = -EME_NOEXEC; | |
int n = elf->symtab->sh_size / sizeof(Elf_Sym); | |
Elf_Sym *symtab = elf_module_sec_ptr(elf, elf->symtab); | |
for ( Elf_Sym *sym = &symtab[1] | |
, *end = &symtab[n] | |
; sym < end | |
; sym++) | |
{ | |
switch (sym->st_shndx) | |
{ | |
case SHN_COMMON: | |
goto finished; | |
case SHN_ABS: | |
break; | |
/* resolve external symbol */ | |
case SHN_UNDEF: | |
sym->st_value = (Elf_Addr) (intptr_t) | |
cbs->resolve( elf, elf_module_sym_name(elf, sym->st_name)); | |
if (!sym->st_value) { | |
result = -EME_UNDEFINED_REFERENCE; | |
goto finished; | |
} | |
break; | |
/* bind to physical section location and define as accessible symbol */ | |
default: | |
sym->st_value += (Elf_Addr) (intptr_t) | |
elf_module_get_ptr( elf, elf->sections[sym->st_shndx].sh_addr); | |
if (ELF_SYM_TYPE(sym->st_info) != STT_SECTION) { | |
result = cbs->define(elf, elf_module_sym_name(elf, sym->st_name), | |
(void *) (intptr_t) sym->st_value); | |
if (result < 0) | |
goto finished; | |
} | |
} | |
} | |
finished: | |
return result; | |
} | |
int elf_module_init(elf_module_t *elf, void *data, size_t size) | |
{ | |
int i = 1; | |
elf->header = data; | |
Elf_Shdr *shdr; | |
if (memcmp(elf->header->e_ident, ELF_MAGIC, sizeof(ELF_MAGIC) - 1) | |
|| !elf_module_check_machine(elf)) { | |
return -EME_NOEXEC; | |
} | |
elf->sections = (void *) (((intptr_t) data) + ((intptr_t) elf->header->e_shoff)); | |
elf->strings = (void *) (((intptr_t) data) + ((intptr_t) elf->sections[elf->header->e_shstrndx].sh_offset)); | |
elf->size = 0; | |
/* section 0 is reserved */ | |
for ( shdr = &elf->sections[i]; | |
i < elf->header->e_shnum; | |
i++, | |
shdr = &elf->sections[i]) | |
{ | |
if (shdr->sh_type == SHT_SYMTAB) { | |
elf->symtab = &elf->sections[i]; | |
elf->strtab = &elf->sections[elf->sections[i].sh_link]; | |
elf->names = (void *) | |
(((intptr_t) data) + ((intptr_t) elf->strtab->sh_offset)); | |
} | |
} | |
elf_module_layout(elf); | |
return 0; | |
} | |
size_t elf_module_get_size(elf_module_t *elf) { | |
return elf->size; | |
} | |
void elf_module_set_data(elf_module_t *elf, void *data) { | |
elf->data = data; | |
} | |
void *elf_module_get_data(elf_module_t *elf) { | |
return elf->data; | |
} | |
int elf_module_load(elf_module_t *elf, void *dest, elf_module_link_cbs_t *cbs) { | |
int i = 1, res = 0; | |
elf->start = dest; | |
for ( Elf_Shdr *shdr = &elf->sections[i]; | |
i < elf->header->e_shnum; | |
i++, | |
shdr = &elf->sections[i]) | |
{ | |
if (shdr->sh_flags & SHF_ALLOC) { | |
void* address = (void *) (((intptr_t) elf->header) + ((intptr_t) shdr->sh_offset)); | |
memcpy(elf_module_get_ptr(elf, shdr->sh_addr), address, shdr->sh_size); | |
} | |
} | |
if ((res = elf_module_link(elf, cbs)) < 0) | |
goto finished; | |
res = elf_module_reloc(elf); | |
finished: | |
return res; | |
} | |
void *elf_module_lookup_symbol(elf_module_t *elf, char *name) { | |
int n = elf->symtab->sh_size / sizeof(Elf_Sym); | |
Elf_Sym *sym = (void *) (((intptr_t) elf->header) + | |
((intptr_t) elf->symtab->sh_offset) + | |
((intptr_t) sizeof(Elf_Sym))); | |
for (int i = 1; i < n; i++, sym++) { | |
switch (sym->st_shndx) { | |
case SHN_ABS: | |
break; | |
case SHN_UNDEF: | |
break; | |
default: | |
if (!strcmp(elf_module_sym_name(elf, sym->st_name), name)) | |
return (void *) (intptr_t) sym->st_value; | |
} | |
} | |
return NULL; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extern int mod2__var; | |
extern int mod1__main(void); | |
extern char *mod2__get_name(void); | |
static void call_mod2(void) { | |
printf("calling function from mod2....\n"); | |
printf("mod2__var: %i\n", mod2__var); | |
printf("response: %s\n", mod2__get_name()); | |
} | |
int mod1__main(void) { | |
printf ("Module 1 selftest: "); | |
for (int i = 0; i < 10; i++) | |
printf("%i ", i); | |
printf ("... module 1 is ok\n"); | |
call_mod2(); | |
return 1025; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdarg.h> | |
#include <stdint.h> | |
#include <string.h> | |
#ifndef _WIN32 | |
#include <sys/mman.h> | |
#endif | |
#include <elf-module.h> | |
#pragma clang diagnostic push | |
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" | |
#ifdef DEBUGGING | |
# define DPRINTF(module, fmt, ...) debug_print(module, fmt, 0, ## __VA_ARGS__) | |
# define ABORT_MSG(module, fmt, ...) debug_print(module, fmt, 1, ## __VA_ARGS__) | |
#else | |
# define ABORT_MSG(module, fmt, ...) debug_print(module, fmt, 1, ## __VA_ARGS__) | |
# define DPRINTF(x, ...) do {} while (0) | |
#endif | |
#pragma clang diagnostic pop | |
static void* nullptr = NULL; | |
__attribute__((section("_GLOBAL_OFFSET_TABLE_"))) | |
intptr_t global_offset_table; | |
/* Module table entry */ | |
typedef struct module { | |
struct module *next; | |
char *name; | |
} module_t; | |
/* Symbol table entry */ | |
typedef struct symbol { | |
struct symbol *next; | |
module_t *module; | |
void *addr; | |
char *name; | |
} symbol_t; | |
/* Symbol table */ | |
static symbol_t *symtab = NULL; | |
/* Module table */ | |
static module_t core_module = { | |
.name = "core" | |
}; | |
static module_t *modtab = &core_module; | |
static module_t* loader = &core_module; | |
static void | |
debug_print(module_t* module, char *fmt, int code, ...) { | |
char *buffer, *line; | |
va_list args; | |
va_start(args, code); | |
int length = vsnprintf(nullptr, 0, fmt, args); | |
buffer = calloc(sizeof(char), length); | |
vsprintf(buffer, fmt, args); | |
const char* format = code > 0 | |
? "%-24s: ERROR: %s" | |
: "%-24s: %s"; | |
va_end(args); | |
length = snprintf( nullptr, 0, format, module->name, buffer ); | |
line = calloc(sizeof(char), ++length ); | |
snprintf( line, length, format, module->name, buffer ); | |
free(buffer); | |
puts(line); | |
free(line); | |
if (code > 0) { | |
exit(1); | |
} | |
} | |
static void *load_file(char *file, size_t *size) | |
{ | |
FILE *f = fopen(file, "rb"); | |
void *result; | |
if (!f) | |
return NULL; | |
fseek(f, 0, SEEK_END); | |
*size = ftell(f); | |
fseek(f, 0, SEEK_SET); | |
result = malloc(*size); | |
if (!result) | |
goto out; | |
if (fread(result, 1, *size, f) < *size) { | |
free(result); | |
result = NULL; | |
goto out; | |
} | |
out: | |
fclose(f); | |
return result; | |
} | |
/* | |
* Define new symbol. If elf is NULL, it's core API function. | |
*/ | |
static int | |
define_symbol(elf_module_t *elf, char *name, void *addr) { | |
module_t *module = elf | |
? elf_module_get_data(elf) | |
: &core_module; | |
if (name[0] != '.' | |
&& (strcmp(name, "_GLOBAL_OFFSET_TABLE_") != 0)) { | |
DPRINTF(module, "Exporting symbol '%s' (defined in module %s)", name, module->name); | |
} | |
symbol_t *symbol, | |
*current = nullptr; | |
for ( symbol = symtab | |
; symbol | |
; symbol = symbol->next) | |
{ | |
if (symbol->name[0] != '.' && name[0] != '.' | |
&& (strcmp(symbol->name, "_GLOBAL_OFFSET_TABLE_") != 0)) { | |
if (!strcmp(symbol->name, name)) { | |
ABORT_MSG(module, "Duplicate definition of `%s'.", name); | |
} | |
else { | |
printf("Symbol: (%s@%s)\n", symbol->name, name); | |
} | |
} | |
} | |
current = (symbol_t *) | |
malloc(sizeof(*symbol) + strlen(name) + 1); | |
current->name = strdup(name); | |
current->addr = addr; | |
current->module = module; | |
current->next = symtab; | |
symtab = current; | |
return 0; | |
} | |
static symbol_t *lookup_symbol(char *name) | |
{ | |
symbol_t *symbol = nullptr; | |
for ( symbol_t *handle = symtab | |
; handle && !symbol | |
; handle = handle->next) | |
{ | |
if (handle->name[0] != '.' && name[0] != '.' | |
&& (strcmp(handle->name, "_GLOBAL_OFFSET_TABLE_") != 0) | |
&& (strcmp(name, "_GLOBAL_OFFSET_TABLE_") != 0)) { | |
DPRINTF( loader, "Symbol name: (%s, %s)", name, handle->name); | |
} | |
if ( strcmp(handle->name, name) == 0 | |
&& ( handle->name[0] != '.' )) { | |
symbol = handle; | |
} | |
} | |
sym_finished: | |
return symbol; | |
} | |
static module_t *lookup_module(char *name) | |
{ | |
module_t *module = nullptr; | |
for ( module_t *handle = modtab | |
; handle && !module | |
; handle = handle->next) | |
{ | |
if (strcmp(handle->name, name) == 0) { | |
DPRINTF(loader, "Module found: %s", handle->name); | |
module = handle; | |
} | |
} | |
mod_finished: | |
return module; | |
} | |
static void load_module(char *module); | |
/* | |
* Resolve symbol callback. | |
* | |
* If symbol is external(starts with 'MODULENAME__'), calls | |
* load_module, if needed. | |
*/ | |
static void *resolve_symbol(elf_module_t *elf, char *name) { | |
char *ptr, *modname; | |
symbol_t *sym; | |
/* load module, if needed */ | |
if (name[0] != '.' | |
&& (strcmp(name, "_GLOBAL_OFFSET_TABLE_") != 0)) { | |
DPRINTF( loader, "resolve symbol: %s", name); | |
} | |
if ((ptr = strstr(name, "__")) != NULL) { | |
modname = malloc(ptr - name + 1); | |
memset( modname, '\0', ptr - name + 1); | |
strncpy(modname, name, ptr - name); | |
DPRINTF( loader, "Locating external symbol [%s@%s].", name, modname); | |
if (strcmp(name, modname) && !lookup_module(modname)) { | |
load_module(modname); | |
} | |
} | |
if (( sym = lookup_symbol( name ))) { | |
if (strcmp( name, "_GLOBAL_OFFSET_TABLE_" ) != 0) { | |
DPRINTF( loader, "%s found in %s", name, sym->module->name); | |
} | |
return sym->addr; | |
} | |
else { | |
DPRINTF( loader, "Undefined symbol: %s (0x%08x)", name, sym ); | |
} | |
return NULL; | |
} | |
static void *allocate(size_t size) { | |
#if _WIN32 | |
return malloc(size); | |
#else | |
return mmap(NULL, size, | |
PROT_READ | PROT_WRITE | PROT_EXEC, | |
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
#endif | |
} | |
void hexdump(const void* data, size_t size) { | |
char ascii[17]; | |
size_t i, j; | |
ascii[16] = '\0'; | |
printf(" %08x ", 0); | |
for (i = 0; i < size; ++i) { | |
printf("%02X ", ((unsigned char*)data)[i]); | |
if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') { | |
ascii[i % 16] = ((unsigned char*)data)[i]; | |
} else { | |
ascii[i % 16] = '.'; | |
} | |
if ((i+1) % 8 == 0 || i+1 == size) { | |
printf(" "); | |
if ((i+1) % 16 == 0) { | |
printf(" | %s |\n %08x ", ascii, i + 1); | |
} else if (i+1 == size) { | |
ascii[(i+1) % 16] = '\0'; | |
if ((i+1) % 16 <= 8) { | |
printf(" "); | |
} | |
for (j = (i+1) % 16; j < 16; ++j) { | |
printf(" "); | |
} | |
printf(" | %s |\n X", ascii); | |
} | |
} | |
} | |
puts(""); | |
} | |
static void load_module(char *module) { | |
int err; | |
size_t size; | |
void *core; | |
char fullname[128]; | |
elf_module_t elf; | |
module_t *mod = | |
(module_t *) calloc(1, sizeof(module_t)); | |
elf_module_link_cbs_t link = { | |
.define = define_symbol, | |
.resolve = resolve_symbol, | |
}; | |
void *data; | |
DPRINTF( loader, "Loading module %s.gz", module); | |
mod->name = module; | |
sprintf(fullname, "%s.mod", module); | |
uint32_t result = 0; | |
if (!( data = load_file( fullname, &size ))) { | |
ABORT_MSG( loader, "Error loading module '%s'", module); | |
} | |
if (elf_module_init(&elf, data, size) < 0) { | |
hexdump(data, size); | |
ABORT_MSG( loader, "Error loading module '%s': Not an executable.", fullname); | |
} | |
elf_module_set_data(&elf, mod); | |
if (!( core = allocate( size ))) { | |
ABORT_MSG( loader, "Error loading module '%s': Out of memory.", fullname); | |
} | |
if (( result = elf_module_load( &elf, core, &link )) < 0) { | |
ABORT_MSG( loader, "Error loading module '%s': Error %d", result ); | |
} | |
free(data); | |
mod->next = modtab; | |
modtab = mod; | |
} | |
typedef void (*module_init)(void); | |
int main(int argc, char **argv) { | |
module_init module_main; | |
if (argc < 2) { | |
DPRINTF( loader, ""); | |
DPRINTF( loader, "ELF Module Loader"); | |
DPRINTF( loader, ""); | |
DPRINTF( loader, "Usage: <module__function> [ options ] [ mod1 mod2 mod3 ... ]"); | |
} | |
else { | |
/* standard definitions(core API) */ | |
define_symbol(NULL, "_GLOBAL_OFFSET_TABLE_", ((void *) global_offset_table)); | |
define_symbol(NULL, "printf", ((void *) printf)); | |
/* load and link modules and set options, if any */ | |
for ( int i = 2; i < argc; i++) { | |
if (strcmp(argv[i], "-v") != 0) { | |
load_module(argv[i]); | |
} | |
} | |
/* call module_main */ | |
if ((( module_main = (module_init) resolve_symbol( nullptr, argv[1] )))) { | |
DPRINTF( loader, "Executing module proc. at %p", module_main ); | |
//module_main(); | |
} | |
else { | |
DPRINTF( loader, "Unable to load method (%s),", argv[1] ); | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment