Skip to content

Instantly share code, notes, and snippets.

@lighth7015
Last active January 30, 2020 00:51
Show Gist options
  • Save lighth7015/d1977846bffc96a121f87c8dd0aa5bd3 to your computer and use it in GitHub Desktop.
Save lighth7015/d1977846bffc96a121f87c8dd0aa5bd3 to your computer and use it in GitHub Desktop.
ELF loader
/**
* 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;
}
/**
* 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;
}
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;
}
#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