Skip to content

Commit 36139ee

Browse files
authored
Update internal gotcha to v1.0.4 (#498)
1 parent 5cfa01e commit 36139ee

File tree

6 files changed

+89
-27
lines changed

6 files changed

+89
-27
lines changed

ext/gotcha/src/elf_ops.c

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,26 @@ static uint32_t gnu_hash_func(const char *str) {
3131
return hash;
3232
}
3333

34+
/* Symbol versioning
35+
*
36+
* https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA.junk/symversion.html
37+
*
38+
* versym[symidx] does only provides an index into the ElfW(Verdef) array
39+
* (DT_VERDEF/SHT_GNU_verdef) and is not the version itself, but SHT_GNU_verdef
40+
* is sorted in ascending order and the entries have a parent relation, thus a
41+
* higher index should always be a higher version. As we only search for the
42+
* latest symbol/highest version it is sufficient to compare the index.
43+
*/
44+
3445
signed long lookup_gnu_hash_symbol(const char *name, ElfW(Sym) * syms,
46+
ElfW(Half) *versym,
3547
char *symnames,
3648
void *sheader) {
3749
uint32_t *buckets, *vals;
3850
uint32_t hash_val;
3951
uint32_t cur_sym, cur_sym_hashval;
52+
signed long latest_sym = -1;
53+
ElfW(Half) latest_sym_ver = 0;
4054
struct gnu_hash_header *header = (struct gnu_hash_header *) (sheader);
4155

4256
buckets = (uint32_t *)(((unsigned char *)(header + 1)) +
@@ -54,13 +68,22 @@ signed long lookup_gnu_hash_symbol(const char *name, ElfW(Sym) * syms,
5468
cur_sym_hashval = vals[cur_sym - header->symndx];
5569
if (((cur_sym_hashval & ~1) == hash_val) &&
5670
(gotcha_strcmp(name, symnames + syms[cur_sym].st_name) == 0)) {
57-
return (signed long)cur_sym;
71+
if (!versym) {
72+
return (signed long)cur_sym;
73+
}
74+
75+
if ((versym[cur_sym] & 0x7fff) > latest_sym_ver) {
76+
latest_sym = (signed long)cur_sym;
77+
latest_sym_ver = versym[cur_sym] & 0x7fff;
78+
}
5879
}
5980
if (cur_sym_hashval & 1) {
60-
return -1;
81+
break;
6182
}
6283
cur_sym++;
6384
}
85+
86+
return latest_sym;
6487
}
6588

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

78101
signed long lookup_elf_hash_symbol(const char *name, ElfW(Sym) * syms,
102+
ElfW(Half) *versym,
79103
char *symnames, ElfW(Word) * header) {
80104
ElfW(Word) *nbucket = header + 0;
81105
ElfW(Word) *buckets = header + 2;
82106
ElfW(Word) *chains = buckets + *nbucket;
107+
signed long latest_sym = -1;
108+
ElfW(Half) latest_sym_ver = 0;
83109

84110
unsigned int x = elf_hash((const unsigned char *)name);
85111
signed long y = (signed long)buckets[x % *nbucket];
86112
while (y != STN_UNDEF) {
87113
if (gotcha_strcmp(name, symnames + syms[y].st_name) == 0) {
88-
return y;
114+
if (!versym) {
115+
return y;
116+
}
117+
118+
if ((versym[y] & 0x7fff) > latest_sym_ver) {
119+
latest_sym = y;
120+
latest_sym_ver = versym[y] & 0x7fff;
121+
}
89122
}
90123
y = chains[y];
91124
}
92125

93-
return -1;
126+
return latest_sym;
94127
}

ext/gotcha/src/elf_ops.h

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,38 +35,42 @@ Place, Suite 330, Boston, MA 02111-1307 USA
3535
*
3636
* \fn signed long lookup_gnu_hash_symbol(const char *name,
3737
ElfW(Sym) *syms,
38+
ElfW(Half) *versym,
3839
char *symnames,
3940
void *header);
4041
*
4142
* \brief Looks up the index of a symbol in a symbol table given a symbol name
4243
*
4344
* \param name The name of the function to be looked up
4445
* \param syms The pointer to the symbol table
46+
* \param versym The pointer to the symbol versioning table
4547
* \param symnames A pointer into the string table
4648
* \param header The parameters the underlying GNU Hash function will use
4749
*
4850
******************************************************************************
4951
*/
50-
signed long lookup_gnu_hash_symbol(const char *name, ElfW(Sym) *syms, char *symnames, void *sheader);
52+
signed long lookup_gnu_hash_symbol(const char *name, ElfW(Sym) *syms, ElfW(Half) *versym, char *symnames, void *sheader);
5153

5254
/*!
5355
******************************************************************************
5456
*
5557
* \fn signed long lookup_elf_hash_symbol(const char *name,
5658
ElfW(Sym) *syms,
59+
ElfW(Half) *versym,
5760
char *symnames,
5861
ElfW(Word) *header);
5962
*
6063
* \brief Looks up the index of a symbol in a symbol table given a symbol name
6164
*
6265
* \param name The name of the function to be looked up
6366
* \param syms The pointer to the symbol table
67+
* \param versym The pointer to the symbol versioning table
6468
* \param symnames A pointer into the string table
6569
* \param header The parameters the underlying ELF Hash function will use
6670
*
6771
******************************************************************************
6872
*/
69-
signed long lookup_elf_hash_symbol(const char *name, ElfW(Sym) *syms, char *symnames, ElfW(Word) *header);
73+
signed long lookup_elf_hash_symbol(const char *name, ElfW(Sym) *syms, ElfW(Half) *versym, char *symnames, ElfW(Word) *header);
7074

7175
/*!
7276
******************************************************************************
@@ -85,6 +89,7 @@ signed long lookup_elf_hash_symbol(const char *name, ElfW(Sym) *syms, char *symn
8589
* \param[out] gnu_hash
8690
* \param[out] got
8791
* \param[out] strtab
92+
* \param[out] versym
8893
*
8994
******************************************************************************
9095
*/
@@ -96,13 +101,22 @@ signed long lookup_elf_hash_symbol(const char *name, ElfW(Sym) *syms, char *symn
96101
ElfW(Sym) *symtab = NULL; \
97102
ElfW(Addr) gnu_hash = 0x0, elf_hash = 0x0; \
98103
ElfW(Addr) got = 0x0; \
104+
ElfW(Half) *versym = NULL; \
99105
char *strtab = NULL; \
100106
unsigned int rel_size = 0, rel_count = 0, is_rela = 0, i; \
101107
dynsec = lmap->l_ld; \
102108
if (!dynsec) \
103109
return -1; \
104110
for (dentry = dynsec; dentry->d_tag != DT_NULL; dentry++) { \
105111
switch (dentry->d_tag) { \
112+
case DT_REL: { \
113+
rel = (ElfW(Rel) *)dentry->d_un.d_ptr; \
114+
break; \
115+
} \
116+
case DT_RELA: { \
117+
rela = (ElfW(Rela) *) dentry->d_un.d_ptr; \
118+
break; \
119+
} \
106120
case DT_PLTRELSZ: { \
107121
rel_size = (unsigned int) dentry->d_un.d_val; \
108122
break; \
@@ -135,6 +149,10 @@ signed long lookup_elf_hash_symbol(const char *name, ElfW(Sym) *syms, char *symn
135149
gnu_hash = dentry->d_un.d_val; \
136150
break; \
137151
} \
152+
case DT_VERSYM: { \
153+
versym = (ElfW(Half) *) dentry->d_un.d_ptr; \
154+
break; \
155+
} \
138156
} \
139157
} \
140158
rel_count = rel_size / (is_rela ? sizeof(ElfW(Rela)) : sizeof(ElfW(Rel))); \
@@ -149,6 +167,7 @@ signed long lookup_elf_hash_symbol(const char *name, ElfW(Sym) *syms, char *symn
149167
(void) rel_size; \
150168
(void) rel_count; \
151169
(void) is_rela; \
170+
(void) versym; \
152171
(void) i;
153172

154173
/*!
@@ -188,17 +207,23 @@ signed long lookup_elf_hash_symbol(const char *name, ElfW(Sym) *syms, char *symn
188207
*
189208
******************************************************************************
190209
*/
191-
#define FOR_EACH_PLTREL(lmap, op, ...) { \
210+
#define FOR_EACH_PLTREL(lookup_rel, lmap, op, ...) { \
192211
INIT_DYNAMIC(lmap) \
193212
ElfW(Addr) offset = lmap->l_addr; \
194213
(void) offset; \
195214
if (is_rela) { \
196-
rela = (ElfW(Rela) *) jmprel; \
197-
FOR_EACH_PLTREL_INT(rela, op, ## __VA_ARGS__); \
215+
ElfW(Rela) * jmp_rela = (ElfW(Rela) *) jmprel; \
216+
FOR_EACH_PLTREL_INT(jmp_rela, op, ## __VA_ARGS__); \
217+
if (lookup_rel && rela) { \
218+
FOR_EACH_PLTREL_INT(rela, op, ## __VA_ARGS__); \
219+
} \
198220
} \
199221
else { \
200-
rel = (ElfW(Rel) *) jmprel; \
201-
FOR_EACH_PLTREL_INT(rel, op, ## __VA_ARGS__); \
222+
ElfW(Rel) * jmp_rel = (ElfW(Rel) *) jmprel; \
223+
FOR_EACH_PLTREL_INT(jmp_rel, op, ## __VA_ARGS__); \
224+
if (lookup_rel && rel) { \
225+
FOR_EACH_PLTREL_INT(rel, op, ## __VA_ARGS__); \
226+
} \
202227
} \
203228
}
204229

ext/gotcha/src/gotcha.c

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,12 @@ long lookup_exported_symbol(const char* name, const struct link_map *lib, void**
6464
result = -1;
6565
if (gnu_hash) {
6666
debug_printf(3, "Checking GNU hash for %s in %s\n", name, LIB_NAME(lib));
67-
result = lookup_gnu_hash_symbol(name, symtab, strtab,
67+
result = lookup_gnu_hash_symbol(name, symtab, versym, strtab,
6868
(struct gnu_hash_header *) gnu_hash);
6969
}
7070
if (elf_hash && result == -1) {
7171
debug_printf(3, "Checking ELF hash for %s in %s\n", name, LIB_NAME(lib));
72-
result = lookup_elf_hash_symbol(name, symtab, strtab,
72+
result = lookup_elf_hash_symbol(name, symtab, versym, strtab,
7373
(ElfW(Word) *)elf_hash);
7474
}
7575
if (result == -1) {
@@ -233,7 +233,7 @@ static int mark_got_writable(struct link_map *lib)
233233
return 0;
234234
}
235235

236-
static int update_library_got(struct link_map *map, hash_table_t *bindingtable)
236+
static int update_library_got(struct link_map *map, hash_table_t *bindingtable, int lookup_rel)
237237
{
238238
struct library_t *lib = get_library(map);
239239
if (!lib) {
@@ -256,18 +256,18 @@ static int update_library_got(struct link_map *map, hash_table_t *bindingtable)
256256
lib->flags |= LIB_GOT_MARKED_WRITEABLE;
257257
}
258258

259-
FOR_EACH_PLTREL(map, update_lib_bindings, map, bindingtable);
259+
FOR_EACH_PLTREL(lookup_rel, map, update_lib_bindings, map, bindingtable);
260260

261261
lib->generation = current_generation;
262262
return 0;
263263
}
264264

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

@@ -313,9 +313,12 @@ GOTCHA_EXPORT enum gotcha_error_t gotcha_wrap(struct gotcha_binding_t* user_bind
313313
}
314314

315315
debug_printf(2, "Processing %d bindings\n", num_actions);
316+
int lookup_rel = 0;
316317
for (i = 0; i < num_actions; i++) {
317318
struct internal_binding_t *binding = bindings->internal_bindings + i;
318-
319+
if (gotcha_strcmp(binding->user_binding->name,"main")==0 ||
320+
gotcha_strcmp(binding->user_binding->name,"__libc_start_main")==0)
321+
lookup_rel = 1;
319322
int result = rewrite_wrapper_orders(binding);
320323
if (result & RWO_NEED_LOOKUP) {
321324
debug_printf(2, "Symbol %s needs lookup operation\n", binding->user_binding->name);
@@ -336,9 +339,9 @@ GOTCHA_EXPORT enum gotcha_error_t gotcha_wrap(struct gotcha_binding_t* user_bind
336339
new_bindings_count++;
337340
}
338341
}
339-
342+
340343
if (new_bindings_count) {
341-
update_all_library_gots(&new_bindings);
344+
update_all_library_gots(&new_bindings, lookup_rel);
342345
destroy_hashtable(&new_bindings);
343346
}
344347

ext/gotcha/src/gotcha_auxv.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,15 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
3131

3232
static ElfW(Ehdr) *vdso_ehdr = NULL;
3333
static unsigned int auxv_pagesz = 0;
34-
34+
#define BUFFER_LEN 4096
3535

3636
int parse_auxv_contents()
3737
{
3838
char name[] = "/proc/self/auxv";
3939
int fd, done = 0;
40-
char buffer[4096];
41-
ssize_t buffer_size = 4096, offset = 0, result;
40+
char buffer[BUFFER_LEN];
41+
const ssize_t buffer_size = BUFFER_LEN;
42+
ssize_t offset = 0, result;
4243
ElfW(auxv_t) *auxv, *a;
4344
static int parsed_auxv = 0;
4445

@@ -223,11 +224,11 @@ struct link_map *get_vdso_from_maps()
223224
{
224225
int maps, hit_eof;
225226
ElfW(Addr) addr_begin, addr_end, dynamic;
226-
char name[4096], line[4096], *line_pos;
227+
char name[BUFFER_LEN], line[BUFFER_LEN], *line_pos;
227228
struct link_map *m;
228229
maps = gotcha_open("/proc/self/maps", O_RDONLY);
229230
for (;;) {
230-
hit_eof = read_line(line, 4096, maps);
231+
hit_eof = read_line(line, BUFFER_LEN, maps);
231232
if (hit_eof) {
232233
gotcha_close(maps);
233234
return NULL;

ext/gotcha/src/gotcha_dl.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ static void* dlopen_wrapper(const char* filename, int flags) {
132132
foreach_hash_entry(&notfound_binding_table, NULL, per_binding);
133133

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

137137
return handle;
138138
}

ext/gotcha/src/gotcha_dl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#include "tool.h"
66

77
void handle_libdl();
8-
extern void update_all_library_gots(hash_table_t *bindings);
8+
extern void update_all_library_gots(hash_table_t *bindings, int lookup_rel);
99
extern long lookup_exported_symbol(const char* name, const struct link_map *lib, void** symbol);
1010
extern int prepare_symbol(struct internal_binding_t *binding);
1111

0 commit comments

Comments
 (0)