-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathelfdlmap.c
311 lines (267 loc) · 7.57 KB
/
elfdlmap.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
#define _GNU_SOURCE
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_SYS_FEATURES_H
#include <sys/features.h>
#else
#include <features.h>
#endif
#include <stdlib.h>
#include <elfdlmap.h>
#if defined(HAVE_LINK_H) && defined(HAVE_DL_ITERATE_PHDR)
#include <stdio.h>
#include <link.h>
/* 1: basic info
* 2: dt_tag info
* 3: individual hash bucket info
*/
#define DLMAP_DEBUG 0
typedef struct Elf_GnuHashHdr {
Elf32_Word nbuckets;
Elf32_Word symndx;
Elf32_Word maskwords;
Elf32_Word shift2;
} Elf_GnuHashHdr;
#define MSK_SYMTAB (1<<0)
#define MSK_STRTAB (1<<1)
#define MSK_HASH (1<<2)
#define MSK_GNUHASH (1<<3)
static int
cb(struct dl_phdr_info *info, size_t info_len, void *closure)
{
CexpLinkMap **tailpp = closure;
CexpLinkMap map;
ElfW(Dyn) *dyn, *dyns;
ElfW(Addr) off = info->dlpi_addr;
ElfW(Addr) uoff;
ElfW(Sym) *symtab = 0; /* silence compiler warning */
const char *strtab = 0; /* silence compiler warning */
void *hash = 0; /* silence compiler warning */
void *gnu_hash = 0; /* silence compiler warning */
Elf_GnuHashHdr hhdr;
unsigned long ndsyms;
int i;
unsigned msk;
#if DLMAP_DEBUG > 0
printf("Object %s @ %p\n", info->dlpi_name, (void*)info->dlpi_addr);
#endif
/* Find dynamic section */
dyn = 0;
for ( i = 0; i < info->dlpi_phnum; i++ ) {
if ( PT_DYNAMIC == info->dlpi_phdr[i].p_type ) {
dyn = (ElfW(Dyn)*)(info->dlpi_phdr[i].p_vaddr + off);
#if DLMAP_DEBUG > 0
printf("Dynamic section @ %p\n", (void*)dyn);
#endif
break;
}
}
if ( ! dyn ) {
fprintf(stderr,"cexpLinkMapBuild() - No dynamic section found for '%s'\n", info->dlpi_name);
return -1;
}
msk = 0;
dyns = 0;
/* Now scan the dynamic section for things we need... */
while ( DT_NULL != dyn->d_tag ) {
switch ( dyn->d_tag ) {
case DT_SYMTAB:
symtab = (void*)(dyn->d_un.d_ptr);
/* remember dynamic symtab entry */
#if DLMAP_DEBUG > 1
printf("Symtab found @%p\n", (void*)symtab);
#endif
dyns = dyn;
msk |= MSK_SYMTAB;
break;
case DT_STRTAB:
strtab = (const char*)(dyn->d_un.d_ptr);
#if DLMAP_DEBUG > 1
printf("Strtab found @%p\n", (void*)strtab);
#endif
msk |= MSK_STRTAB;
break;
case DT_GNU_HASH:
gnu_hash = (void*)(dyn->d_un.d_ptr);
#if DLMAP_DEBUG > 1
printf("GNU HASH found @%p\n", (void*)gnu_hash);
#endif
msk |= MSK_GNUHASH;
break;
case DT_HASH:
hash = (void*)(dyn->d_un.d_ptr);
#if DLMAP_DEBUG > 1
printf("HASH found @%p\n", (void*)hash);
#endif
msk |= MSK_HASH;
break;
default:
break;
}
dyn++;
}
/* Check if we have what we need */
if ( ! (MSK_SYMTAB & msk) ) {
fprintf(stderr,"cexpLinkMapBuild() - No dynamic symbols found for '%s'\n", info->dlpi_name);
return -1;
}
if ( ! (MSK_STRTAB & msk) ) {
fprintf(stderr,"cexpLinkMapBuild() - No dynamic stringtab found for '%s'\n", info->dlpi_name);
return -1;
}
if ( ! ((MSK_HASH | MSK_GNUHASH) & msk) ) {
/* unable to determine symbol count w/o section headers nor hash tables
* and section header may not be mapped.
*/
fprintf(stderr,"cexpLinkMapBuild() - No hash table found for '%s'\n", info->dlpi_name);
return -1;
}
/* Here comes some uglyness - it seems glibc's dynamic linker
* already fixed up pointers in the DYNAMIC section to point
* to real virtual addresses. EXCEPT in the case of a vdso
* object which it has NOT fixed up (probably can't write there
* anyways).
* uClibc-0.9.33 OTOH doesn't seem to ever fix up the DYNAMIC
* section.
*/
#ifdef __UCLIBC__
uoff = off;
#if DLMAP_DEBUG > 0
printf("UCLIBC fixup 0x%lx\n", uoff);
#endif
#else
uoff = 0;
/* Use ugly heuristics to guess if the linker has 'fixed up' the pointers
* in the dynamic section; glibc seems to do so, uclibc does not. Also,
* glibc does not fix up the 'vdso's dynamic section...
*/
for ( i = 0; i < info->dlpi_phnum; i++) {
if ( PT_LOAD != info->dlpi_phdr[i].p_type )
continue;
if ( info->dlpi_phdr[i].p_vaddr <= dyns->d_un.d_ptr
&& dyns->d_un.d_ptr + sizeof(ElfW(Sym)) < info->dlpi_phdr[i].p_vaddr + info->dlpi_phdr[i].p_memsz ) {
/* It's pointing into one of the segments; could still have been fixed up,
* theoretically - should we do some more tests ? ...
*/
#if DLMAP_DEBUG > 0
printf("GLIBC fixup hack due to PHDR #%i (vaddr %p - %p)\n",
i,
(void*)info->dlpi_phdr[i].p_vaddr,
(void*)(info->dlpi_phdr[i].p_vaddr + info->dlpi_phdr[i].p_memsz)
);
#endif
uoff = off;
break;
}
}
#if DLMAP_DEBUG > 0
printf("GLIBC fixup 0x%lx\n", uoff);
#endif
#endif
symtab = ((void*)symtab) + uoff;
strtab = ((void*)strtab) + uoff;
if ( (MSK_HASH & msk) ) {
hash = ((void*)hash) + uoff;
}
if ( (MSK_GNUHASH & msk) ) {
gnu_hash = ((void*)gnu_hash) + uoff;
}
/* Now we have to go through some pains to find the number of symbols.
* Unfortunately this info is not contained in the dynamic section :-(
* and other ELF info (such as the section headers) may not be mapped
* into the program's address space so we can't use section headers.
*
* It is, however, possible to extract the number of symbols from
* the hash table info...
*
* Note that both, GNU_HASH and HASH may be present. Check for
* GNU_HASH first to make sure hhdr is valid when computing first_sym.
*/
if ( (MSK_GNUHASH & msk) ) {
/* read the header */
hhdr = *(Elf_GnuHashHdr*)gnu_hash;
#if DLMAP_DEBUG > 0
printf("GNU Hash Hdr %u buckets\n", hhdr.nbuckets);
printf("GNU Hash Hdr %u symndx\n", hhdr.symndx);
#endif
gnu_hash += sizeof(hhdr);
/* skip filter */
gnu_hash += hhdr.maskwords * sizeof(ElfW(Addr));
#if DLMAP_DEBUG > 2
for ( i=0; i<hhdr.nbuckets; i++ ) {
printf("GNU Bucket %u: %u\n", i, ((Elf32_Word*)gnu_hash)[i]);
}
#endif
ndsyms = 0;
/* find the last chain and follow it to the end */
for ( i = hhdr.nbuckets - 1; i >=0 && 0 == (ndsyms = ((Elf32_Word*)gnu_hash)[i]); i--)
/* nothing else to do */;
/* skip the bucket area */
gnu_hash += hhdr.nbuckets * sizeof(Elf32_Word);
if ( ndsyms >= hhdr.symndx ) {
i = ndsyms - hhdr.symndx;
while ( 0 == (((Elf32_Word*)gnu_hash)[i] & 1 ) )
i++;
/* found last index; number of symbols is one more */
i++;
} else {
i=0;
}
ndsyms = i+hhdr.symndx;
#if DLMAP_DEBUG > 0
printf("%lu dynamic symbols from GNU_HASH\n", ndsyms);
#endif
} else if ( (MSK_HASH & msk) ) {
/* This is easy; the number of chains equals the number of symbols */
ndsyms = ((Elf32_Word*)hash)[1];
#if DLMAP_DEBUG > 0
printf("%lu dynamic symbols from HASH\n", ndsyms);
#endif
} else {
fprintf(stderr, "cexpLinkMapBuild() - internal error; should not get here (%s)\n", info->dlpi_name);
return -1;
}
map = calloc(1, sizeof(*map));
map->flags = CEXP_LINK_MAP_STATIC_STRINGS;
map->elfsyms = symtab;
map->strtab = strtab;
map->name = info->dlpi_name;
map->nsyms = ndsyms;
map->offset = off;
/* if there is a gnu hashtable then */
/* all undefined or local symbols are first */
map->firstsym = (MSK_GNUHASH & msk) ? hhdr.symndx : 0;
#if DLMAP_DEBUG > 0
printf("Firstsym: %lu\n", map->firstsym);
#endif
/* enqueue at tail */
**tailpp = map;
*tailpp = &map->next;
return 0;
}
#endif
CexpLinkMap
cexpLinkMapBuild(const char *name, void *parm)
{
CexpLinkMap rval = 0;
#if defined(HAVE_LINK_H) && defined(HAVE_DL_ITERATE_PHDR)
CexpLinkMap *tailp = &rval;
if ( dl_iterate_phdr( cb, &tailp ) ) {
/* something went wrong */
cexpLinkMapFree(rval);
rval = 0;
}
#endif
return rval;
}
void
cexpLinkMapFree(CexpLinkMap m)
{
CexpLinkMap mn;
while ( m ) {
mn = m->next;
free( m );
m = mn;
}
}