-
Accessing NULL in Elf
I'm trying to retrieve the size of some callbacks because I have to manually duplicate them for the sake of passing specific lua calls relative to the glut window they were assigned. I got this far but the app segfaults at the start of the function:
Code:
#if __INTPTR_WIDTH__ == 32
#define ELF_T( NAME ) Elf32_##NAME
#elif __INTPTR_WIDTH__ == 64
#define ELF_T( NAME ) Elf64_##NAME
#else
#define ELF_T( NAME ) Elf_##NAME
#endif
void get_cb_sizes( foo_cb_info_t *callbacks ) {
#ifdef __ELF__
ELF_T(Ehdr) const *elf_header = NULL;
ELF_T(Phdr) const *program_headers =
(ELF_T(Phdr) const *)elf_header->e_phoff,
*program_header = NULL;
ELF_T(Shdr) const *section_headers =
(ELF_T(Shdr) const *)elf_header->e_shoff,
*section_header = NULL;
ELF_T(Sym) const *symbols = NULL, *symbol = NULL;
ELF_T(Half) section_index, symbol_index, symbol_count = 0;
foo_cb_id_t foo_cb_id;
if ( section_headers ) {
for (
section_index = 0;
section_index < elf_header->e_shnum;
++section_index
)
{
section_header = &(section_headers[section_index]);
if ( section_header->sh_type == SHT_SYMTAB )
break;
}
if ( section_index == elf_header->e_shnum ) {
puts("Section '.symtab' missing!");
exit(EXIT_FAILURE);
}
symbols = (ELF_T(Sym) const *)(section_header->sh_offset);
symbol_count = section_header->sh_size / sizeof(ELF_T(Sym));
}
else if ( program_headers ) {
program_header = &(program_headers[0]);
puts("Program headers not supported yet!");
exit(EXIT_FAILURE);
}
if ( symbols && symbol_count ) {
for (
symbol_index = 0;
symbol_index < symbol_count;
++symbol_index
)
{
symbol = &(symbols[symbol_index]);
for (
foo_cb_id = 0;
foo_cb_id < foo_cb_id_count;
++foo_cb_id
)
{
if ( symbol->st_value ==
(ELF_T(Off))callbacks[foo_cb_id].foo_cb ) {
callbacks[foo_cb_id].size = symbol->st_size;
}
}
}
}
#else
puts("Must be compiled as an elf!");
exit(EXIT_FAILURE);
#endif
}
Any help in getting this to work would be appreciated
Edit: Forgot to copy the ELF_T define, added it in now
-
I ended up doing it this way:
Code:
#if __INTPTR_WIDTH__ == 32
#define ELF_T( NAME ) Elf32_##NAME
#elif __INTPTR_WIDTH__ == 64
#define ELF_T( NAME ) Elf64_##NAME
#else
#define ELF_T( NAME ) Elf_##NAME
#endif
void get_cb_sizes( char *name ) {
#ifdef __ELF__
uchar *buff = calloc(BUFSIZ,1), *temp = NULL;
char *text;
if ( !buff ) exit(ENOMEM);
FILE *file = fopen(name,"r");
if ( !file ) {
free(buff);
exit(errno);
}
fread( buff, BUFSIZ, 1, file );
ELF_T(Ehdr) *elf_header = (ELF_T(Ehdr) *)buff;
ELF_T(Shdr) *sec_headers = NULL, *sec_header = NULL;
ELF_T(Sym) *syms = NULL, *sym = NULL;
size_t prev = BUFSIZ, need = 0, info = 0, symtabi = 0,
symtabs_used = 0, strtabs_used = 0, strtabs_upto = 10;
ELF_T(Half) sectioni, symi, sym_count = 0,
symtabs[10] = {0}, strtabs[10] = {0};
foo_cb_id_t foo_cb_id;
sec_headers = (ELF_T(Shdr)*)elf_header->e_shoff;
if ( elf_header->e_phoff > elf_header->e_shoff )
need = elf_header->e_phoff +
(elf_header->e_phnum * sizeof(ELF_T(Phdr)));
else
need = elf_header->e_shoff +
(elf_header->e_shnum * sizeof(ELF_T(Shdr)));
if ( elf_header->e_shstrndx + BUFSIZ > need )
need = elf_header->e_shstrndx + BUFSIZ;
if ( elf_header->e_entry > need )
need = elf_header->e_entry;
if ( need > prev ) {
temp = realloc( buff, need );
if ( !temp ) goto failed;
buff = temp;
fseek( file, 0, SEEK_SET );
fread( buff, need, 1, file );
prev = need;
elf_header = (ELF_T(Ehdr)*)buff;
sec_headers = (ELF_T(Shdr)*)&(buff[elf_header->e_shoff]);
}
printf("e_shoff %zu...\n", elf_header->e_shoff );
if ( sec_headers ) {
for (
sectioni = 0;
sectioni < elf_header->e_shnum;
++sectioni
)
{
sec_header = &(sec_headers[sectioni]);
if ( (sec_header->sh_type == SHT_STRTAB ||
sectioni == elf_header->e_shstrndx)
&& strtabs_used < strtabs_upto ) {
strtabs[strtabs_used++] = sectioni;
info = sec_header->sh_offset + sec_header->sh_size;
if ( info > need ) need = info;
}
if ( (sec_header->sh_type == SHT_SYMTAB
|| sec_header->sh_type == SHT_DYNSYM)
&& symtabs_used < strtabs_upto ) {
symtabs[symtabs_used++] = sectioni;
info = sec_header->sh_offset + sec_header->sh_size;
if ( info > need ) need = info;
}
}
if ( symtabs_used == 0 ) {
puts("Section '.symtab' missing!");
exit(EXIT_FAILURE);
}
if ( need > prev ) {
temp = realloc( buff, need );
if ( !temp ) goto failed;
buff = temp;
fseek( file, 0, SEEK_SET );
fread( buff, need, 1, file );
prev = need;
elf_header = (ELF_T(Ehdr)*)buff;
sec_headers = (ELF_T(Shdr)*)&(buff[elf_header->e_shoff]);
}
}
if ( symtabs_used ) {
for ( symtabi = 0; symtabi < symtabs_used; ++symtabi )
{
printf("Entering symbol list %zu...\n", symtabi );
sec_header = &(sec_headers[symtabs[symtabi]]);
syms = (ELF_T(Sym)*)&(buff[sec_header->sh_offset]);
sym_count = sec_header->sh_size / sizeof(ELF_T(Sym));
sec_header = &(sec_headers[sec_header->sh_link]);
text = (char*)&(buff[sec_header->sh_offset]);
for ( symi = 0; symi < sym_count; ++symi )
{
sym = &(syms[symi]);
name = &(text[sym->st_name]);
//printf("Checking symbol %u '%s'...\n", symi, name );
for (
foo_cb_id = 0;
foo_cb_id < foo_cb_id_count;
++foo_cb_id
)
{
if ( strcmp(name,foo_callbacks[foo_cb_id].name) ==0) {
foo_callbacks[foo_cb_id].size = sym->st_size;
}
}
}
}
}
if ( buff ) free(buff);
if ( file ) fclose(file);
return;
failed:
if ( buff ) free(buff);
if ( file ) fclose(file);
exit(errno);
#else
puts("Must be compiled as an elf!");
exit(EXIT_FAILURE);
#endif
}
int main( int argc, char *argv[] ) {
foo_cb_info_t callbacks[foo_cb_id_count] = {
FOO_CB_INFO(foo_cb_close),
FOO_CB_INFO(foo_cb_display),
FOO_CB_INFO(foo_cb_idle),
FOO_CB_INFO(foo_cb_keyboard),
FOO_CB_INFO(foo_cb_reshape),
FOO_CB_INFO(foo_cb_special),
};
g_argc = argc;
g_argv = argv;
atexit(foo_atexit);
memcpy( foo_callbacks, callbacks, sizeof(foo_cb_info_t) * foo_cb_id_count );
get_cb_sizes(argv[0]);
...
Now I'm getting this issue:
Code:
malloc(): invalid size (unsorted)
So I've still gotta figure out if I'm duplicating it properly but I do get as far as the call to glutCreateWindow() so no longer having issues finding the size of them
-
I'm gonna take a break for now, if anyone wants to help this is currently how I'm duplicating the functions and why I need to do so:
Code:
typedef struct foo_cb_info {
size_t pos;
size_t size;
char *name;
void *cb;
} foo_cb_info_t;
#define FOO_CB_INFO(NAME) {0,0,#NAME,(void*)NAME}
typedef enum foo_cb_id {
foo_cb_id_close,
foo_cb_id_display,
foo_cb_id_idle,
foo_cb_id_keyboard,
foo_cb_id_reshape,
foo_cb_id_special,
foo_cb_id_count,
} foo_cb_id_t;
foo_cb_info_t foo_callbacks[foo_cb_id_count] = {{0}};
typedef struct foo_window {
int window_id;
int parent_id;
int lua_cb[foo_cb_id_count];
void *foo_cb[foo_cb_id_count];
} foo_window_t;
int g_windows_used = 0;
int g_windows_upto = 0;
foo_window_t **g_windows = NULL;
int g_current_window = 0;
void foo_cb_close() {
volatile int *lua_cb = (int*)(((uintptr_t)(&lua_cb)
- foo_callbacks[foo_cb_id_close].pos)
+ (sizeof(int) * 2) + (sizeof(int) * foo_cb_id_close));
if ( *lua_cb == 0 ) return;
...
int foo_need_window() {
uchar *mem;
size_t pos, i, need;
foo_cb_info_t *info;
foo_window_t **windows = g_windows, *window;
if ( g_windows_upto < 0 ) g_windows_upto = 0;
if ( g_windows_used >= g_windows_upto ) {
windows = foo_alloc( NULL, windows,
g_windows_upto * sizeof(foo_window_t*),
(g_windows_used + 100) * sizeof(foo_window_t*) );
if ( !windows ) return errno;
g_windows = windows;
g_windows_upto = g_windows_used + 100;
}
g_current_window = g_windows_used++;
pos = sizeof(foo_window_t);
for ( i = 0; i < foo_cb_id_count; ++i ) {
info = &(foo_callbacks[i]);
if ( !info->size ) {
puts("info->size was 0!");
exit(ERANGE);
}
info->pos = pos;
need = info->size + (sizeof(int) - (info->size % sizeof(int)));
pos += need;
}
window = g_windows[g_current_window] = calloc( pos, 1 );
if ( !window ) return errno;
mem = (uchar*)window;
for ( i = 0; i < foo_cb_id_count; ++i ) {
info = &(foo_callbacks[i]);
window->foo_cb[i] = &mem[pos];
(void)memcpy(
&(mem[pos]),
info->cb,
info->size );
}
return EXIT_SUCCESS;
}
-
Well I came back to it and did some research, learned that I couldn't use malloc for this purpose and had to write a whole function for executable memory explicitly, somehow segfaulting after I call it though, the line where I can catch the segfault is at the bottom of the below code, the top half is the code I use before the segfault:
Code:
typedef struct foo_addr {
size_t cap;
uchar *foo, *mem;
struct foo_addr *nxt, *prv;
} foo_addr_t;
typedef struct foo_page {
size_t cap;
uchar *mem;
struct foo_addr * one;
} foo_page_t;
size_t pages_size = 0, pages_want = 0;
size_t pages_used = 0, pages_upto = 0, pages_node = 0;
foo_page_t* pages;
int foo_size_exemem( void **dst, size_t want ) {
void *tmp = NULL;
foo_page_t *page = NULL;
foo_addr_t *addr = NULL, *prev;
size_t i = 0;
if ( !dst ) return EDESTADDRREQ;
if ( !want ) {
if ( *dst ) {
for ( i = 0; i < pages_used; ++i ) {
page = &(pages[i]);
for ( addr = page->one;
addr->nxt; addr = addr->nxt ) {
if ( addr->mem == *dst ) {
*dst = NULL;
addr->mem = NULL;
return EXIT_SUCCESS;
}
}
}
return EINVAL;
}
return EXIT_SUCCESS;
}
if ( *dst ) return EINVAL;
if ( pages_used == pages_upto ) {
pages_want = sizeof(foo_page_t) * (pages_used + 100);
tmp = realloc((void*)pages, pages_want);
if ( !tmp ) return ENOMEM;
pages = (foo_page_t*)tmp;
pages_upto += 100;
pages_size = pages_want;
}
for ( i = 0; i < pages_used; ++i ) {
page = &(pages[i]);
if ( page->cap >= want ) {
for ( addr = page->one;
addr->nxt; addr = addr->nxt ) {
if ( !addr->mem && addr->cap >= want ) {
addr->mem = (uchar*)(addr + sizeof(foo_addr_t));
*dst = addr->mem;
return EXIT_SUCCESS;
}
}
addr->cap = want;
addr->foo = page->mem;
addr->mem = (uchar*)(addr + sizeof(foo_addr_t));
prev = addr;
addr = (foo_addr_t*)(addr->mem + want);
prev->nxt = addr;
page->cap -= want;
*dst = prev->mem;
return EXIT_SUCCESS;
}
}
page = &(pages[i]);
page->cap = sysconf(_SC_PAGESIZE);
page->mem = (uchar*)mmap( NULL, page->cap,
PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, 0, 0);
if ( !(page->mem) ) return errno;
++pages_used;
addr = page->one = (foo_addr_t*)page->mem;
addr->cap = want;
addr->foo = page->mem;
addr->mem = (uchar*)(addr + sizeof(foo_addr_t));
prev = addr;
addr = (foo_addr_t*)(addr->mem + want);
prev->nxt = addr;
page->cap -= want;
*dst = prev->mem;
return EXIT_SUCCESS;
}
void foo_free_exemem() {
size_t i;
for ( i = 0; i < pages_used; ++i )
munmap( pages[i].mem, sysconf(_SC_PAGESIZE) );
free(pages);
pages = NULL;
pages_used = pages_upto = 0;
pages_size = pages_want = 0;
}
int foo_need_window() {
uchar *mem;
size_t pos, i, need;
foo_cb_info_t *info;
foo_window_t **windows = g_windows, *window;
if ( g_windows_upto < 0 ) g_windows_upto = 0;
if ( g_windows_used >= g_windows_upto ) {
windows = foo_alloc( NULL, windows,
g_windows_upto * sizeof(foo_window_t*),
(g_windows_used + 100) * sizeof(foo_window_t*) );
if ( !windows ) return errno;
g_windows = windows;
g_windows_upto = g_windows_used + 100;
}
g_current_window = g_windows_used++;
pos = sizeof(foo_window_t);
for ( i = 0; i < foo_cb_id_count; ++i ) {
info = &(foo_callbacks[i]);
if ( !info->size ) {
puts("info->size was 0!");
exit(ERANGE);
}
info->pos = pos;
need = info->size + (sizeof(int) - (info->size % sizeof(int)));
pos += need;
}
errno = foo_size_exemem( (void**)(&window), pos );
if ( !window ) return errno;
(void)memset( window, 0, pos );
g_windows[g_current_window] = window;
mem = (uchar*)window;
for ( i = 0; i < foo_cb_id_count; ++i ) {
info = &(foo_callbacks[i]);
window->foo_cb[i] = &mem[info->pos];
(void)memcpy(
&(mem[info->pos]),
info->cb,
info->size );
}
return EXIT_SUCCESS;
}
...
char const* foo_getstring( lua_State *L, int pos ) {
if ( lua_isstring(L,pos) ) // Segfault inside this call
return lua_tostring(L,pos);
return NULL;
}
...
int foo_glutCreateWindow( lua_State *L ) {
int ret = foo_need_window();
char const *title = foo_getstring( L, -1 );
...
-
Well now I know for sure there's some problem with my allocation method, I tried modifying my code to support realloc like behaviour and then modified my foo_alloc() function (which lua relies on) to use that instead, this is the code with an added comment at where it segfaults:
Code:
typedef struct foo_addr {
size_t size;
void *page, *data;
struct foo_addr *next, *prev;
} foo_addr_t;
typedef struct foo_page {
size_t cap;
uchar *mem;
struct foo_addr * one;
} foo_page_t;
size_t pages_size = 0, pages_want = 0;
size_t pages_used = 0, pages_upto = 0, pages_node = 0;
foo_page_t* pages;
int foo_find_addr_in_pages( void *ptr, foo_addr_t **dst ) {
size_t i = 0;
foo_page_t *page;
foo_addr_t *addr;
if ( !dst ) return EDESTADDRREQ;
if ( ptr ) {
for ( i = 0; i < pages_used; ++i ) {
page = &(pages[i]);
for ( addr = page->one;
addr->next; addr = addr->next ) {
if ( addr->data == ptr ) {
*dst = addr;
return EXIT_SUCCESS;
}
}
}
return EINVAL;
}
return EXIT_FAILURE;
}
void foo_new_page_addr(
foo_page_t *page, foo_addr_t *addr, foo_addr_t *next, size_t want ) {
addr->size = want;
addr->page = page->mem;
addr->data = (addr + sizeof(foo_addr_t));
if ( next ) addr->next = next;
next = (foo_addr_t*)((uintptr_t)(addr->data) + want);
if ( addr->next ) next->next = addr->next;
addr->next = next;
next->prev = addr;
next->size = sysconf(_SC_PAGESIZE) -
(uintptr_t)(next + sizeof(foo_addr_t));
}
int foo_find_memory( foo_page_t **base,
foo_addr_t **dst, foo_addr_t **nxt, size_t want ) {
foo_page_t *page = NULL;
foo_addr_t *next = NULL, *addr = NULL;
size_t p, size = 0;
for ( p = 0; p < pages_used; ++p ) {
page = &(pages[p]);
addr = page->one;
while ( addr ) {
if ( addr->data ) continue;
next = addr;
size = 0;
while ( size < want ) {
if ( next->data ) break;
size += next->size;
next = next->next;
if ( !next ) break;
}
if ( size >= want ) {
*base = page;
*dst = addr;
*nxt = next;
return EXIT_SUCCESS;
}
if ( !next ) break;
addr = next->next;
}
}
if ( pages_used == pages_upto ) {
pages_want = sizeof(foo_page_t) * (pages_used + 100);
page = (foo_page_t*)realloc((void*)pages, pages_want);
if ( !page ) return ENOMEM;
pages = page;
pages_upto += 100;
pages_size = pages_want;
}
page = &(pages[p]);
page->cap = sysconf(_SC_PAGESIZE);
page->mem = (uchar*)mmap( NULL, page->cap,
PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, -1, -1);
if ( !(page->mem) ) return errno;
++pages_used;
addr = page->one = (foo_addr_t*)page->mem;
addr->size = sysconf(_SC_PAGESIZE) - sizeof(foo_addr_t); // here is where it segfaults
*base = page;
*dst = addr;
*nxt = NULL;
return EXIT_FAILURE;
}
int foo_size_exemem( void **dst, size_t want ) {
foo_page_t *page = NULL;
foo_addr_t *addr = NULL, *next, *current;
size_t i = 0;
if ( !dst ) return EDESTADDRREQ;
if ( !want ) {
if ((errno = foo_find_addr_in_pages( *dst, &addr ))
!= EXIT_SUCCESS)
return errno;
*dst = NULL;
addr->data = NULL;
return EXIT_SUCCESS;
}
if ( *dst ) {
if ((errno = foo_find_addr_in_pages(*dst,¤t))
!= EXIT_SUCCESS )
return errno;
if ( want < current->size ) {
foo_new_page_addr( page, current, NULL, want );
return EXIT_SUCCESS;
}
if ( want == current->size ) return EXIT_SUCCESS;
i = 0;
addr = current;
while ( i < want ) {
if ( !addr || addr->data ) break;
i += addr->size;
addr = addr->next;
if ( !addr ) break;
}
if ( i >= want ) {
foo_new_page_addr( page, current, addr, want );
return EXIT_SUCCESS;
}
if ( (errno = foo_find_memory( &page, &addr, &next, want ))
!= EXIT_SUCCESS )
return errno;
foo_new_page_addr( page, addr, next, want );
memcpy( current->data, addr->data, current->size );
*dst = addr->data;
current->data = NULL;
return EXIT_SUCCESS;
}
if ( (errno = foo_find_memory( &page, &addr, &next, want ))
!= EXIT_SUCCESS )
return errno;
foo_new_page_addr( page, addr, next, want );
*dst = addr->data;
return EXIT_SUCCESS;
}
void foo_free_exemem() {
size_t i;
for ( i = 0; i < pages_used; ++i )
munmap( pages[i].mem, sysconf(_SC_PAGESIZE) );
free(pages);
pages = NULL;
pages_used = pages_upto = 0;
pages_size = pages_want = 0;
}
void *foo_alloc( void *ud, void *ptr, size_t size, size_t want ) {
uchar *tmp = NULL;
(void)ud;
errno = EXIT_SUCCESS;
if ( want == size ) return ptr;
if ( !want ) {
if ( ptr ) foo_size_exemem( &ptr, 0 );
return NULL;
}
tmp = ptr;
if ( (errno = foo_size_exemem( (void**)(&tmp), want ))
!= EXIT_SUCCESS )
goto nomem;
if ( !ptr ) memset( tmp, 0, want );
if ( size < want ) memset( &tmp[size], 0, want - size );
return tmp;
nomem:
if ( errno == EXIT_SUCCESS ) errno = ENOMEM;
return NULL;
}
-
Found the main issue, was return EXIT_FAILURE instead of EXIT_SUCCESS after retrieving the page, now I got an infinite loop somewhere so time for more debugging :(