-
Notifications
You must be signed in to change notification settings - Fork 0
/
orc.c
400 lines (336 loc) · 14.9 KB
/
orc.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
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
#include <errno.h>
#include <string.h>
#include <endian.h>
#include <stdlib.h>
#include "orc.h"
enum ORCError find_dynamic_tag(FILE *handle, Elf32_Off dyn_seg_offset, Elf32_Word dyn_seg_size, Elf32_Sword tag, Elf32_Dyn *dynamic_tag) {
if (fseek(handle, dyn_seg_offset, SEEK_SET) == -1) {
fprintf(stderr, "Failed to seek to DYNAMIC segment at offset %u: %s\n", dyn_seg_offset, strerror(errno));
return ORC_CRITICIAL;
}
for (Elf32_Word i = 0; i < dyn_seg_size; i += sizeof(Elf32_Dyn)) {
if (fread(dynamic_tag, sizeof(Elf32_Dyn), 1, handle) != 1)
{
if (ferror(handle)) {
fprintf(stderr, "Failed to read dynamic tag %hu\n", i);
return ORC_FILE_IO_ERR;
}
fprintf(stderr, "Invalid dynamic tags\n");
return ORC_INVALID_ELF;
}
dynamic_tag->d_tag = be32toh(dynamic_tag->d_tag);
if (dynamic_tag->d_tag == tag)
return ORC_SUCCESS;
}
fprintf(stderr, "Dynamic tag %u not found\n", tag);
return ORC_DYN_TAG_NOT_FOUND;
}
enum ORCError count_mips_jump_slot_relocs(FILE *handle, Elf32_Off rel_plt_offset, Elf32_Word rel_plt_size, Elf32_Word *count) {
Elf32_Rel rel;
if (fseek(handle, rel_plt_offset, SEEK_SET) == -1) {
fprintf(stderr, "Failed to seek to .rel.plt section at offset %u: %s\n", rel_plt_offset, strerror(errno));
return ORC_CRITICIAL;
}
fprintf(stderr, "Reading relocation in .rel.plt at offset 0x%x\n", rel_plt_offset);
*count = 0;
for (Elf32_Word i = 0; i < rel_plt_size; i += sizeof(Elf32_Rel)) {
if (fread(&rel, sizeof(Elf32_Rel), 1, handle) != 1) {
if (ferror(handle)) {
fprintf(stderr, "Failed to read .rel.plt relocation %u\n", i/4);
return ORC_FILE_IO_ERR;
}
fprintf(stderr, "Invalid .rel.plt section\n");
return ORC_INVALID_ELF;
}
if (ELF32_R_TYPE(be32toh(rel.r_info)) == R_MIPS_JUMP_SLOT)
*count += 1;
}
fprintf(stderr, "Found %u MIPS_JUMP_SLOT relocations in .rel.plt at 0x%x\n", *count, rel_plt_offset);
return ORC_SUCCESS;
}
enum ORCError find_program_headers(FILE *handle, Elf32_Off ph_off, Elf32_Half ph_num, Elf32_Word seg_type, Elf32_Phdr **phdrs, Elf32_Half *count) {
Elf32_Phdr phdr;
if (fseek(handle, ph_off, SEEK_SET) == -1) {
fprintf(stderr, "Failed to seek to program headers at offset 0x%x: %s\n", ph_off, strerror(errno));
return ORC_CRITICIAL;
}
*count = 0;
for (Elf32_Half i = 0; i < ph_num; i++) {
if (fread(&phdr, sizeof(Elf32_Phdr), 1, handle) != 1)
{
if (ferror(handle)) {
fprintf(stderr, "Failed to read program header %hu\n", i);
return ORC_FILE_IO_ERR;
}
fprintf(stderr, "Invalid program headers\n");
return ORC_INVALID_ELF;
}
if (be32toh(phdr.p_type) == seg_type) {
if (!(*phdrs = reallocarray(*phdrs, *count + 1, sizeof(Elf32_Phdr)))) {
fprintf(stderr, "Failed to allocate memory %hu program headers: %s\n", *count, strerror(errno));
return ORC_CRITICIAL;
}
memcpy(*phdrs + *count, &phdr, sizeof(Elf32_Phdr));
(*count)++;
}
}
if (!*count) {
fprintf(stderr, "Failed to find program headers of type %u\n", seg_type);
return ORC_PHDR_NOT_FOUND;
}
fprintf(stderr, "Found %hu program headers of type %u\n", *count, seg_type);
return ORC_SUCCESS;
}
enum ORCError find_vaddr_segment(Elf32_Phdr *loadable_segs, Elf32_Half num_segs, Elf32_Addr vaddr, Elf32_Half *segment_index) {
for (Elf32_Half i = 0; i < num_segs; i++) {
if (be32toh(loadable_segs[i].p_vaddr) < vaddr && vaddr < be32toh(loadable_segs[i].p_vaddr) + be32toh(loadable_segs[i].p_memsz)) {
*segment_index = i;
return ORC_SUCCESS;
}
}
fprintf(stderr, "Failed to find loadable segment containing vaddr 0x%x\n", vaddr);
return ORC_INVALID_ELF;
}
enum ORCError calculate_file_offset(Elf32_Phdr *loadable_segs, Elf32_Half num_segs, Elf32_Addr vaddr, Elf32_Off *file_off) {
enum ORCError err;
Elf32_Half i;
if ((err = find_vaddr_segment(loadable_segs, num_segs, vaddr, &i)) == ORC_SUCCESS)
*file_off = htobe32((vaddr - be32toh(loadable_segs[i].p_vaddr)) + be32toh(loadable_segs[i].p_offset));
return err;
}
enum ORCError get_mips_stub_info(
FILE *handle,
Elf32_Word mips_external_gotno,
Elf32_Off got_off,
Elf32_Off dynsym_off,
Elf32_Word got_entsize,
Elf32_Word dynsym_entsize,
Elf32_Word *stub_count,
Elf32_Addr *stub_base_addr
) {
Elf32_Sym sym;
Elf32_Addr got_entry;
*stub_base_addr = 0xffffffff;
*stub_count = 0;
for (Elf32_Word i = 0; i < mips_external_gotno; i++) {
if (fseek(handle, dynsym_off + (i * dynsym_entsize), SEEK_SET) == -1) {
fprintf(stderr, "Failed to seek to dynsym at offset 0x%x: %s\n", dynsym_off + (i * dynsym_entsize), strerror(errno));
return ORC_FILE_IO_ERR;
}
if (fread(&sym, sizeof(Elf32_Sym), 1, handle) != 1)
{
if (ferror(handle)) {
fprintf(stderr, "Failed to read dynamic symbol at offset 0x%x\n", dynsym_off + (i * dynsym_entsize));
return ORC_FILE_IO_ERR;
}
fprintf(stderr, "Invalid dynsyms\n");
return ORC_INVALID_ELF;
}
if (
ELF32_ST_TYPE(sym.st_info) & STT_FUNC &&
be16toh(sym.st_shndx) == SHN_UNDEF &&
be32toh(sym.st_value) != 0
) {
if (fseek(handle, got_off + (i * got_entsize), SEEK_SET) == -1) {
fprintf(stderr, "Failed to seek to GOT at offset 0x%x: %s\n", got_off + (i * got_entsize), strerror(errno));
return ORC_FILE_IO_ERR;
}
if (fread(&got_entry, sizeof(Elf32_Addr), 1, handle) != 1)
{
if (ferror(handle)) {
fprintf(stderr, "Failed to read GOT entry at offset 0x%x\n", got_off + (i * got_entsize));
return ORC_FILE_IO_ERR;
}
fprintf(stderr, "Invalid GOT\n");
return ORC_INVALID_ELF;
}
if (sym.st_value == got_entry) {
(*stub_count)++;
if (be32toh(sym.st_value) < be32toh(*stub_base_addr))
*stub_base_addr = sym.st_value;
}
}
}
return ORC_SUCCESS;
}
/* https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-48031.html */
enum ORCError calculate_hash_size(FILE *handle, Elf32_Shdr *hash_section) {
Elf32_Word nbucket, nchain;
if (fseek(handle, be32toh(hash_section->sh_offset), SEEK_SET) == -1) {
fprintf(stderr, "Failed to seek to hash at offset 0x%x: %s\n", be32toh(hash_section->sh_offset), strerror(errno));
return ORC_FILE_IO_ERR;
}
if (fread(&nbucket, sizeof(Elf32_Word), 1, handle) != 1) {
if (ferror(handle)) {
fprintf(stderr, "Failed to read hash nbucket at offset 0x%x\n", be32toh(hash_section->sh_offset));
return ORC_FILE_IO_ERR;
}
fprintf(stderr, "Invalid hash\n");
return ORC_INVALID_ELF;
}
nbucket = be32toh(nbucket);
fprintf(stderr, "hash nbucket: %u\n", nbucket);
if (fread(&nchain, sizeof(Elf32_Word), 1, handle) != 1) {
if (ferror(handle)) {
fprintf(stderr, "Failed to read hash nchain at offset 0x%x\n", be32toh(hash_section->sh_offset) + 4);
return ORC_FILE_IO_ERR;
}
fprintf(stderr, "Invalid hash\n");
return ORC_INVALID_ELF;
}
nchain = be32toh(nchain);
fprintf(stderr, "hash nchain: %u\n", nchain);
hash_section->sh_size = htobe32(sizeof(Elf32_Word) * (2 + nchain + nbucket));
return ORC_SUCCESS;
}
enum ORCError read_dynstr_table(FILE *handle, Elf32_Shdr *dynstr_sh, char **dynstr_table) {
enum ORCError err;
Elf32_Off dynstr_off = be32toh(dynstr_sh->sh_offset);
Elf32_Word dynstr_size = be32toh(dynstr_sh->sh_size);
if (!(*dynstr_table = malloc(dynstr_size))) {
fprintf(stderr, "Failed to allocate %u bytes for dynamic string array: %s\n", dynstr_size, strerror(errno));
return ORC_CRITICIAL;
}
if (fseek(handle, dynstr_off, SEEK_SET) == -1) {
fprintf(stderr, "Failed to seek to dynamic string table at 0x%x: %s\n", dynstr_off, strerror(errno));
return ORC_FILE_IO_ERR;
}
fprintf(stderr, "Attempting to read %u bytes of dynamic string table...\n", dynstr_size);
if (fread(*dynstr_table, 1, dynstr_size, handle) != dynstr_size)
{
if (ferror(handle)) {
fprintf(stderr, "Failed to read %u bytes of dynamic string table at 0x%x\n", dynstr_size, dynstr_off);
return ORC_FILE_IO_ERR;
}
fprintf(stderr, "Invalid dynamic string table at 0x%x\n", dynstr_off);
return ORC_INVALID_ELF;
}
fprintf(stderr, "Read %u bytes of dynamic string table\n", dynstr_size);
return ORC_SUCCESS;
}
enum ORCError find_dynamic_symbol(FILE *handle, const char *sym_name, const int sym_type, const char *dynstr_table, const Elf32_Shdr *dynsym, Elf32_Sym *sym, Elf32_Word *sym_idx) {
if (fseek(handle, be32toh(dynsym->sh_offset), SEEK_SET) == -1) {
fprintf(stderr, "Failed to seek to dynamic symbol table at 0x%x: %s\n", be32toh(dynsym->sh_offset), strerror(errno));
return ORC_CRITICIAL;
}
for (Elf32_Word i = 0; i < be32toh(dynsym->sh_size); i += be32toh(dynsym->sh_entsize)) {
if (fread(sym, be32toh(dynsym->sh_entsize), 1, handle) != 1)
{
if (ferror(handle)) {
fprintf(stderr, "Failed to read symbol at index %hu\n", i);
return ORC_FILE_IO_ERR;
}
fprintf(stderr, "Invalid dynamic symbol table\n");
return ORC_INVALID_ELF;
}
if ((ELF32_ST_TYPE(sym->st_info) & sym_type) != sym_type)
continue;
if (!strcmp(sym_name, dynstr_table + be32toh(sym->st_name))) {
*sym_idx = i / be32toh(dynsym->sh_entsize);
fprintf(stderr, "Found dynamic symbol %s at index %u\n", sym_name, *sym_idx);
return ORC_SUCCESS;
}
}
fprintf(stderr, "Dynamic symbol %s not found\n", sym_name);
return ORC_SYM_NOT_FOUND;
}
enum ORCError parse_rel_plt_from_dyn_seg(FILE *handle, Elf32_Off dyn_seg_offset, Elf32_Word dyn_seg_size, Elf32_Shdr *rel_plt) {
Elf32_Dyn dynamic_tag;
enum ORCError err;
switch ((err = find_dynamic_tag(handle, dyn_seg_offset, dyn_seg_size, DT_PLTREL, &dynamic_tag))) {
case ORC_SUCCESS:
if (be32toh(dynamic_tag.d_un.d_val) != DT_REL) {
fprintf(stderr, "DT_PLTREL has invalid value: %u; MIPS non-PIC ABI expect DT_REL (%u)\n", be32toh(dynamic_tag.d_un.d_val), DT_REL);
return ORC_DYN_VALUE_INVALID;
}
break;
case ORC_DYN_TAG_NOT_FOUND:
fprintf(stderr, "Failed to find DT_PLTREL dynamic tag\n");
default:
return err;
}
switch ((err = find_dynamic_tag(handle, dyn_seg_offset, dyn_seg_size, DT_RELENT, &dynamic_tag))) {
case ORC_SUCCESS:
rel_plt->sh_entsize = dynamic_tag.d_un.d_val;
fprintf(stderr, "Found DT_RELENT: %u\n", be32toh(rel_plt->sh_entsize));
break;
case ORC_DYN_TAG_NOT_FOUND:
/*
Evidently some MIPS executables will not have a DT_RELENT tag
if they don't have a DT_REL section
*/
rel_plt->sh_entsize = htobe32(sizeof(Elf32_Rel));
fprintf(stderr, "Failed to find DT_RELENT dynamic tag, defaulting to sizeof(Elf32_rel) == %lu\n", sizeof(Elf32_Rel));
break;
default:
return err;
}
switch ((err = find_dynamic_tag(handle, dyn_seg_offset, dyn_seg_size, DT_JMPREL, &dynamic_tag))) {
case ORC_SUCCESS:
rel_plt->sh_addr = dynamic_tag.d_un.d_ptr;
fprintf(stderr, "Found DT_JMPREL: 0x%x\n", be32toh(rel_plt->sh_addr));
break;
case ORC_DYN_TAG_NOT_FOUND:
fprintf(stderr, "Failed to find DT_JMPREL dynamic tag\n");
default:
return err;
}
switch ((err = find_dynamic_tag(handle, dyn_seg_offset, dyn_seg_size, DT_PLTRELSZ, &dynamic_tag))) {
case ORC_SUCCESS:
rel_plt->sh_size = dynamic_tag.d_un.d_val;
fprintf(stderr, "Found DT_PLTRELSZ: %u\n", be32toh(rel_plt->sh_size));
break;
case ORC_DYN_TAG_NOT_FOUND:
fprintf(stderr, "Failed to find DT_PLTRELSZ dynamic tag\n");
default:
return err;
}
return ORC_SUCCESS;
}
enum ORCError find_r_mips_jump_slot_rel(FILE *handle, Elf32_Shdr *rel_plt, Elf32_Word sym_idx, Elf32_Rel *rel, Elf32_Word *rel_idx) {
if (fseek(handle, rel_plt->sh_offset, SEEK_SET) == -1) {
fprintf(stderr, "Failed to seek to .rel.plt section at offset %u: %s\n", rel_plt->sh_offset, strerror(errno));
return ORC_CRITICIAL;
}
fprintf(stderr, "Reading relocation in .rel.plt at offset 0x%x\n", rel_plt->sh_offset);
for (Elf32_Word i = 0; i < rel_plt->sh_size; i += rel_plt->sh_entsize) {
if (fread(rel, rel_plt->sh_entsize, 1, handle) != 1) {
if (ferror(handle)) {
fprintf(stderr, "Failed to read .rel.plt relocation %u\n", i/rel_plt->sh_entsize);
return ORC_FILE_IO_ERR;
}
fprintf(stderr, "Invalid .rel.plt section\n");
return ORC_INVALID_ELF;
}
if (ELF32_R_TYPE(be32toh(rel->r_info)) == R_MIPS_JUMP_SLOT && ELF32_R_SYM(be32toh(rel->r_info)) == sym_idx) {
*rel_idx = i / rel_plt->sh_entsize;
fprintf(stderr, "Found MIPS_JUMP_SLOT relocation %u for dynamic symbol %u\n", *rel_idx, sym_idx);
return ORC_SUCCESS;
}
}
return ORC_REL_NOT_FOUND;
}
/* https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA.junk/symversion.html */
enum ORCError parse_gnu_version_requirements_size(FILE *handle, Elf32_Off offset, Elf32_Word verneednum, Elf32_Word *size) {
Elf32_Verneed verneed;
*size = verneednum * sizeof(Elf32_Verneed);
while (verneednum--)
{
if (fseek(handle, offset, SEEK_SET) == -1) {
fprintf(stderr, "Failed to seek to next verneed in .gnu.version_r section at offset %u: %s\n", offset, strerror(errno));
return ORC_CRITICIAL;
}
if (fread(&verneed, sizeof(Elf32_Verneed), 1, handle) != 1) {
if (ferror(handle)) {
fprintf(stderr, "Failed to verneed in .gnu.version_r at offset\n");
return ORC_FILE_IO_ERR;
}
fprintf(stderr, "Invalid .gnu.version_r section\n");
return ORC_INVALID_ELF;
}
*size += be16toh(verneed.vn_cnt) * sizeof(Elf32_Vernaux);
offset = offset + be32toh(verneed.vn_next);
}
*size = htobe32(*size);
return ORC_SUCCESS;
}