Skip to content

Commit

Permalink
Update internal gotcha to v1.0.4 (#498)
Browse files Browse the repository at this point in the history
  • Loading branch information
daboehme authored Jul 26, 2023
1 parent 5cfa01e commit 36139ee
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 27 deletions.
41 changes: 37 additions & 4 deletions ext/gotcha/src/elf_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,26 @@ static uint32_t gnu_hash_func(const char *str) {
return hash;
}

/* Symbol versioning
*
* https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA.junk/symversion.html
*
* versym[symidx] does only provides an index into the ElfW(Verdef) array
* (DT_VERDEF/SHT_GNU_verdef) and is not the version itself, but SHT_GNU_verdef
* is sorted in ascending order and the entries have a parent relation, thus a
* higher index should always be a higher version. As we only search for the
* latest symbol/highest version it is sufficient to compare the index.
*/

signed long lookup_gnu_hash_symbol(const char *name, ElfW(Sym) * syms,
ElfW(Half) *versym,
char *symnames,
void *sheader) {
uint32_t *buckets, *vals;
uint32_t hash_val;
uint32_t cur_sym, cur_sym_hashval;
signed long latest_sym = -1;
ElfW(Half) latest_sym_ver = 0;
struct gnu_hash_header *header = (struct gnu_hash_header *) (sheader);

buckets = (uint32_t *)(((unsigned char *)(header + 1)) +
Expand All @@ -54,13 +68,22 @@ signed long lookup_gnu_hash_symbol(const char *name, ElfW(Sym) * syms,
cur_sym_hashval = vals[cur_sym - header->symndx];
if (((cur_sym_hashval & ~1) == hash_val) &&
(gotcha_strcmp(name, symnames + syms[cur_sym].st_name) == 0)) {
return (signed long)cur_sym;
if (!versym) {
return (signed long)cur_sym;
}

if ((versym[cur_sym] & 0x7fff) > latest_sym_ver) {
latest_sym = (signed long)cur_sym;
latest_sym_ver = versym[cur_sym] & 0x7fff;
}
}
if (cur_sym_hashval & 1) {
return -1;
break;
}
cur_sym++;
}

return latest_sym;
}

static unsigned long elf_hash(const unsigned char *name) {
Expand All @@ -76,19 +99,29 @@ static unsigned long elf_hash(const unsigned char *name) {
}

signed long lookup_elf_hash_symbol(const char *name, ElfW(Sym) * syms,
ElfW(Half) *versym,
char *symnames, ElfW(Word) * header) {
ElfW(Word) *nbucket = header + 0;
ElfW(Word) *buckets = header + 2;
ElfW(Word) *chains = buckets + *nbucket;
signed long latest_sym = -1;
ElfW(Half) latest_sym_ver = 0;

unsigned int x = elf_hash((const unsigned char *)name);
signed long y = (signed long)buckets[x % *nbucket];
while (y != STN_UNDEF) {
if (gotcha_strcmp(name, symnames + syms[y].st_name) == 0) {
return y;
if (!versym) {
return y;
}

if ((versym[y] & 0x7fff) > latest_sym_ver) {
latest_sym = y;
latest_sym_ver = versym[y] & 0x7fff;
}
}
y = chains[y];
}

return -1;
return latest_sym;
}
39 changes: 32 additions & 7 deletions ext/gotcha/src/elf_ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,38 +35,42 @@ Place, Suite 330, Boston, MA 02111-1307 USA
*
* \fn signed long lookup_gnu_hash_symbol(const char *name,
ElfW(Sym) *syms,
ElfW(Half) *versym,
char *symnames,
void *header);
*
* \brief Looks up the index of a symbol in a symbol table given a symbol name
*
* \param name The name of the function to be looked up
* \param syms The pointer to the symbol table
* \param versym The pointer to the symbol versioning table
* \param symnames A pointer into the string table
* \param header The parameters the underlying GNU Hash function will use
*
******************************************************************************
*/
signed long lookup_gnu_hash_symbol(const char *name, ElfW(Sym) *syms, char *symnames, void *sheader);
signed long lookup_gnu_hash_symbol(const char *name, ElfW(Sym) *syms, ElfW(Half) *versym, char *symnames, void *sheader);

/*!
******************************************************************************
*
* \fn signed long lookup_elf_hash_symbol(const char *name,
ElfW(Sym) *syms,
ElfW(Half) *versym,
char *symnames,
ElfW(Word) *header);
*
* \brief Looks up the index of a symbol in a symbol table given a symbol name
*
* \param name The name of the function to be looked up
* \param syms The pointer to the symbol table
* \param versym The pointer to the symbol versioning table
* \param symnames A pointer into the string table
* \param header The parameters the underlying ELF Hash function will use
*
******************************************************************************
*/
signed long lookup_elf_hash_symbol(const char *name, ElfW(Sym) *syms, char *symnames, ElfW(Word) *header);
signed long lookup_elf_hash_symbol(const char *name, ElfW(Sym) *syms, ElfW(Half) *versym, char *symnames, ElfW(Word) *header);

/*!
******************************************************************************
Expand All @@ -85,6 +89,7 @@ signed long lookup_elf_hash_symbol(const char *name, ElfW(Sym) *syms, char *symn
* \param[out] gnu_hash
* \param[out] got
* \param[out] strtab
* \param[out] versym
*
******************************************************************************
*/
Expand All @@ -96,13 +101,22 @@ signed long lookup_elf_hash_symbol(const char *name, ElfW(Sym) *syms, char *symn
ElfW(Sym) *symtab = NULL; \
ElfW(Addr) gnu_hash = 0x0, elf_hash = 0x0; \
ElfW(Addr) got = 0x0; \
ElfW(Half) *versym = NULL; \
char *strtab = NULL; \
unsigned int rel_size = 0, rel_count = 0, is_rela = 0, i; \
dynsec = lmap->l_ld; \
if (!dynsec) \
return -1; \
for (dentry = dynsec; dentry->d_tag != DT_NULL; dentry++) { \
switch (dentry->d_tag) { \
case DT_REL: { \
rel = (ElfW(Rel) *)dentry->d_un.d_ptr; \
break; \
} \
case DT_RELA: { \
rela = (ElfW(Rela) *) dentry->d_un.d_ptr; \
break; \
} \
case DT_PLTRELSZ: { \
rel_size = (unsigned int) dentry->d_un.d_val; \
break; \
Expand Down Expand Up @@ -135,6 +149,10 @@ signed long lookup_elf_hash_symbol(const char *name, ElfW(Sym) *syms, char *symn
gnu_hash = dentry->d_un.d_val; \
break; \
} \
case DT_VERSYM: { \
versym = (ElfW(Half) *) dentry->d_un.d_ptr; \
break; \
} \
} \
} \
rel_count = rel_size / (is_rela ? sizeof(ElfW(Rela)) : sizeof(ElfW(Rel))); \
Expand All @@ -149,6 +167,7 @@ signed long lookup_elf_hash_symbol(const char *name, ElfW(Sym) *syms, char *symn
(void) rel_size; \
(void) rel_count; \
(void) is_rela; \
(void) versym; \
(void) i;

/*!
Expand Down Expand Up @@ -188,17 +207,23 @@ signed long lookup_elf_hash_symbol(const char *name, ElfW(Sym) *syms, char *symn
*
******************************************************************************
*/
#define FOR_EACH_PLTREL(lmap, op, ...) { \
#define FOR_EACH_PLTREL(lookup_rel, lmap, op, ...) { \
INIT_DYNAMIC(lmap) \
ElfW(Addr) offset = lmap->l_addr; \
(void) offset; \
if (is_rela) { \
rela = (ElfW(Rela) *) jmprel; \
FOR_EACH_PLTREL_INT(rela, op, ## __VA_ARGS__); \
ElfW(Rela) * jmp_rela = (ElfW(Rela) *) jmprel; \
FOR_EACH_PLTREL_INT(jmp_rela, op, ## __VA_ARGS__); \
if (lookup_rel && rela) { \
FOR_EACH_PLTREL_INT(rela, op, ## __VA_ARGS__); \
} \
} \
else { \
rel = (ElfW(Rel) *) jmprel; \
FOR_EACH_PLTREL_INT(rel, op, ## __VA_ARGS__); \
ElfW(Rel) * jmp_rel = (ElfW(Rel) *) jmprel; \
FOR_EACH_PLTREL_INT(jmp_rel, op, ## __VA_ARGS__); \
if (lookup_rel && rel) { \
FOR_EACH_PLTREL_INT(rel, op, ## __VA_ARGS__); \
} \
} \
}

Expand Down
21 changes: 12 additions & 9 deletions ext/gotcha/src/gotcha.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ long lookup_exported_symbol(const char* name, const struct link_map *lib, void**
result = -1;
if (gnu_hash) {
debug_printf(3, "Checking GNU hash for %s in %s\n", name, LIB_NAME(lib));
result = lookup_gnu_hash_symbol(name, symtab, strtab,
result = lookup_gnu_hash_symbol(name, symtab, versym, strtab,
(struct gnu_hash_header *) gnu_hash);
}
if (elf_hash && result == -1) {
debug_printf(3, "Checking ELF hash for %s in %s\n", name, LIB_NAME(lib));
result = lookup_elf_hash_symbol(name, symtab, strtab,
result = lookup_elf_hash_symbol(name, symtab, versym, strtab,
(ElfW(Word) *)elf_hash);
}
if (result == -1) {
Expand Down Expand Up @@ -233,7 +233,7 @@ static int mark_got_writable(struct link_map *lib)
return 0;
}

static int update_library_got(struct link_map *map, hash_table_t *bindingtable)
static int update_library_got(struct link_map *map, hash_table_t *bindingtable, int lookup_rel)
{
struct library_t *lib = get_library(map);
if (!lib) {
Expand All @@ -256,18 +256,18 @@ static int update_library_got(struct link_map *map, hash_table_t *bindingtable)
lib->flags |= LIB_GOT_MARKED_WRITEABLE;
}

FOR_EACH_PLTREL(map, update_lib_bindings, map, bindingtable);
FOR_EACH_PLTREL(lookup_rel, map, update_lib_bindings, map, bindingtable);

lib->generation = current_generation;
return 0;
}

void update_all_library_gots(hash_table_t *bindings)
void update_all_library_gots(hash_table_t *bindings, int lookup_rel)
{
struct link_map *lib_iter;
debug_printf(2, "Searching all callsites for %lu bindings\n", (unsigned long) bindings->entry_count);
for (lib_iter = _r_debug.r_map; lib_iter != 0; lib_iter = lib_iter->l_next) {
update_library_got(lib_iter, bindings);
update_library_got(lib_iter, bindings, lookup_rel);
}
}

Expand Down Expand Up @@ -313,9 +313,12 @@ GOTCHA_EXPORT enum gotcha_error_t gotcha_wrap(struct gotcha_binding_t* user_bind
}

debug_printf(2, "Processing %d bindings\n", num_actions);
int lookup_rel = 0;
for (i = 0; i < num_actions; i++) {
struct internal_binding_t *binding = bindings->internal_bindings + i;

if (gotcha_strcmp(binding->user_binding->name,"main")==0 ||
gotcha_strcmp(binding->user_binding->name,"__libc_start_main")==0)
lookup_rel = 1;
int result = rewrite_wrapper_orders(binding);
if (result & RWO_NEED_LOOKUP) {
debug_printf(2, "Symbol %s needs lookup operation\n", binding->user_binding->name);
Expand All @@ -336,9 +339,9 @@ GOTCHA_EXPORT enum gotcha_error_t gotcha_wrap(struct gotcha_binding_t* user_bind
new_bindings_count++;
}
}

if (new_bindings_count) {
update_all_library_gots(&new_bindings);
update_all_library_gots(&new_bindings, lookup_rel);
destroy_hashtable(&new_bindings);
}

Expand Down
11 changes: 6 additions & 5 deletions ext/gotcha/src/gotcha_auxv.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,15 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

static ElfW(Ehdr) *vdso_ehdr = NULL;
static unsigned int auxv_pagesz = 0;

#define BUFFER_LEN 4096

int parse_auxv_contents()
{
char name[] = "/proc/self/auxv";
int fd, done = 0;
char buffer[4096];
ssize_t buffer_size = 4096, offset = 0, result;
char buffer[BUFFER_LEN];
const ssize_t buffer_size = BUFFER_LEN;
ssize_t offset = 0, result;
ElfW(auxv_t) *auxv, *a;
static int parsed_auxv = 0;

Expand Down Expand Up @@ -223,11 +224,11 @@ struct link_map *get_vdso_from_maps()
{
int maps, hit_eof;
ElfW(Addr) addr_begin, addr_end, dynamic;
char name[4096], line[4096], *line_pos;
char name[BUFFER_LEN], line[BUFFER_LEN], *line_pos;
struct link_map *m;
maps = gotcha_open("/proc/self/maps", O_RDONLY);
for (;;) {
hit_eof = read_line(line, 4096, maps);
hit_eof = read_line(line, BUFFER_LEN, maps);
if (hit_eof) {
gotcha_close(maps);
return NULL;
Expand Down
2 changes: 1 addition & 1 deletion ext/gotcha/src/gotcha_dl.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ static void* dlopen_wrapper(const char* filename, int flags) {
foreach_hash_entry(&notfound_binding_table, NULL, per_binding);

debug_printf(2, "Updating GOT entries for new dlopened libraries\n");
update_all_library_gots(&function_hash_table);
update_all_library_gots(&function_hash_table, 0);

return handle;
}
Expand Down
2 changes: 1 addition & 1 deletion ext/gotcha/src/gotcha_dl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include "tool.h"

void handle_libdl();
extern void update_all_library_gots(hash_table_t *bindings);
extern void update_all_library_gots(hash_table_t *bindings, int lookup_rel);
extern long lookup_exported_symbol(const char* name, const struct link_map *lib, void** symbol);
extern int prepare_symbol(struct internal_binding_t *binding);

Expand Down

0 comments on commit 36139ee

Please sign in to comment.