forked from asherkin/vtable
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.cpp
355 lines (299 loc) · 11.4 KB
/
main.cpp
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
#define __LIBELF_INTERNAL__ 1
#include "libelf.h"
#include "gelf.h"
#define R_386_32 1
#ifdef EMSCRIPTEN
#include <emscripten/val.h>
#include <emscripten/bind.h>
#endif
#include <vector>
#include <string>
#include <memory>
#include <fstream>
#include <iostream>
#include <cstdio>
#include <cstring>
struct LargeNumber {
operator unsigned long long() const {
return ((unsigned long long)high << 32) | low;
}
LargeNumber operator=(unsigned long long i) {
high = i >> 32;
low = i & 0xFFFFFFFF;
isUnsigned = true;
return *this;
}
unsigned int high;
unsigned int low;
bool isUnsigned;
};
struct RodataChunk {
#ifdef EMSCRIPTEN
RodataChunk(): data(emscripten::val::null()) {}
#endif
LargeNumber offset;
#ifdef EMSCRIPTEN
emscripten::val data;
#else
std::vector<unsigned char> data;
#endif
};
struct SymbolInfo {
unsigned int section;
LargeNumber address;
LargeNumber size;
std::string name;
};
struct RelocationInfo {
LargeNumber address;
LargeNumber target;
};
struct ProgramInfo {
std::string error;
int addressSize;
unsigned int rodataIndex;
LargeNumber rodataStart;
std::vector<RodataChunk> rodataChunks;
unsigned int relRodataIndex;
LargeNumber relRodataStart;
std::vector<RodataChunk> relRodataChunks;
std::vector<SymbolInfo> symbols;
std::vector<RelocationInfo> relocations;
};
ProgramInfo process(std::string image) {
ProgramInfo programInfo = {};
if (elf_version(EV_CURRENT) == EV_NONE) {
programInfo.error = "Failed to init libelf.";
return programInfo;
}
Elf *elf = elf_memory(&image[0], image.size());
if (!elf) {
programInfo.error = "elf_begin failed. (" + std::string(elf_errmsg(-1)) + ")";
return programInfo;
}
Elf_Kind elfKind = elf_kind(elf);
if (elfKind != ELF_K_ELF) {
programInfo.error = "Input is not an ELF object. (" + std::to_string(elfKind) + ")";
return programInfo;
}
GElf_Ehdr elfHeader;
if (gelf_getehdr(elf, &elfHeader) != &elfHeader) {
programInfo.error = "Failed to get ELF header. (" + std::string(elf_errmsg(-1)) + ")";
return programInfo;
}
switch (elfHeader.e_machine) {
case EM_386:
programInfo.addressSize = 4;
break;
case EM_X86_64:
programInfo.addressSize = 8;
break;
default:
programInfo.error = "Unsupported architecture. (" + std::to_string(elfHeader.e_machine) + ")";
return programInfo;
}
size_t numberOfSections;
if (elf_getshdrnum(elf, &numberOfSections) != 0) {
programInfo.error = "Failed to get number of ELF sections. (" + std::string(elf_errmsg(-1)) + ")";
return programInfo;
}
size_t sectionNameStringTableIndex;
if (elf_getshdrstrndx(elf, §ionNameStringTableIndex) != 0) {
programInfo.error = "Failed to get ELF section names. (" + std::string(elf_errmsg(-1)) + ")";
return programInfo;
}
Elf_Scn *relocationTableScn = nullptr;
Elf_Scn *dynamicSymbolTableScn = nullptr;
Elf_Scn *symbolTableScn = nullptr;
size_t stringTableIndex = SHN_UNDEF;
Elf_Scn *stringTableScn = nullptr;
size_t rodataIndex = SHN_UNDEF;
Elf64_Addr rodataOffset;
Elf_Scn *rodataScn = nullptr;
size_t relRodataIndex = SHN_UNDEF;
Elf64_Addr relRodataOffset;
Elf_Scn *relRodataScn = nullptr;
for (size_t elfSectionIndex = 0; elfSectionIndex < numberOfSections; ++elfSectionIndex) {
Elf_Scn *elfScn = elf_getscn(elf, elfSectionIndex);
if (!elfScn) {
programInfo.error = "Failed to get section " + std::to_string(elfSectionIndex) + ". (" + std::string(elf_errmsg(-1)) + ")";
continue;
}
GElf_Shdr elfSectionHeader;
if (gelf_getshdr(elfScn, &elfSectionHeader) != &elfSectionHeader) {
programInfo.error = "Failed to get header for section " + std::to_string(elfSectionIndex) + ". (" + std::string(elf_errmsg(-1)) + ")";
continue;
}
const char *name = elf_strptr(elf, sectionNameStringTableIndex, elfSectionHeader.sh_name);
if (!name) {
programInfo.error = "Failed to get name of section " + std::to_string(elfSectionIndex) + ". (" + std::string(elf_errmsg(-1)) + ")";
continue;
}
if (elfSectionHeader.sh_type == SHT_REL && strcmp(name, ".rel.dyn") == 0) {
relocationTableScn = elfScn;
} else if (elfSectionHeader.sh_type == SHT_DYNSYM && strcmp(name, ".dynsym") == 0) {
dynamicSymbolTableScn = elfScn;
} else if (elfSectionHeader.sh_type == SHT_SYMTAB && strcmp(name, ".symtab") == 0) {
symbolTableScn = elfScn;
} else if (elfSectionHeader.sh_type == SHT_STRTAB && strcmp(name, ".strtab") == 0) {
stringTableIndex = elfSectionIndex;
stringTableScn = elfScn;
} else if (elfSectionHeader.sh_type == SHT_PROGBITS && strcmp(name, ".rodata") == 0) {
rodataIndex = elfSectionIndex;
rodataOffset = elfSectionHeader.sh_addr;
rodataScn = elfScn;
} else if (elfSectionHeader.sh_type == SHT_PROGBITS && strcmp(name, ".data.rel.ro") == 0) {
relRodataIndex = elfSectionIndex;
relRodataOffset = elfSectionHeader.sh_addr;
relRodataScn = elfScn;
}
if (relocationTableScn && dynamicSymbolTableScn && symbolTableScn && stringTableScn && rodataScn && relRodataScn) {
break;
}
}
if (!symbolTableScn || !stringTableScn || !rodataScn) {
programInfo.error = "Failed to find all required ELF sections.";
return programInfo;
}
programInfo.rodataStart = rodataOffset;
programInfo.rodataIndex = rodataIndex;
if (relocationTableScn && dynamicSymbolTableScn) {
Elf_Data *relocationData = nullptr;
while ((relocationData = elf_getdata(relocationTableScn, relocationData)) != nullptr) {
size_t relocationIndex = 0;
GElf_Rel relocation;
while (gelf_getrel(relocationData, relocationIndex++, &relocation) == &relocation) {
size_t type = GELF_R_TYPE(relocation.r_info);
if (type != R_386_32) {
continue;
}
Elf_Data *symbolData = nullptr;
while ((symbolData = elf_getdata(dynamicSymbolTableScn, symbolData)) != nullptr) {
GElf_Sym symbol;
size_t symbolIndex = GELF_R_SYM(relocation.r_info);
if (gelf_getsym(symbolData, symbolIndex, &symbol) != &symbol) {
continue;
}
RelocationInfo relocationInfo;
relocationInfo.address = relocation.r_offset;
relocationInfo.target = symbol.st_value;
programInfo.relocations.push_back(std::move(relocationInfo));
break;
}
}
}
}
Elf_Data *rodata = nullptr;
while ((rodata = elf_getdata(rodataScn, rodata)) != nullptr) {
RodataChunk rodataChunk;
rodataChunk.offset = rodata->d_off;
#ifdef EMSCRIPTEN
// memory_view doesn't do a copy, but we want JS to manage this once embind is done.
rodataChunk.data = emscripten::val(emscripten::memory_view<unsigned char>(rodata->d_size, (unsigned char *)rodata->d_buf)).call<emscripten::val>("slice");
#else
rodataChunk.data = std::move(std::vector<unsigned char>((char *)rodata->d_buf, (char *)rodata->d_buf + rodata->d_size));
#endif
programInfo.rodataChunks.push_back(std::move(rodataChunk));
}
if (relRodataScn) {
programInfo.relRodataStart = relRodataOffset;
programInfo.relRodataIndex = relRodataIndex;
Elf_Data *relRodata = nullptr;
while ((relRodata = elf_getdata(relRodataScn, relRodata)) != nullptr) {
RodataChunk relRodataChunk;
relRodataChunk.offset = relRodata->d_off;
#ifdef EMSCRIPTEN
// memory_view doesn't do a copy, but we want JS to manage this once embind is done.
relRodataChunk.data = emscripten::val(emscripten::memory_view<unsigned char>(relRodata->d_size, (unsigned char *)relRodata->d_buf)).call<emscripten::val>("slice");
#else
relRodataChunk.data = std::move(std::vector<unsigned char>((char *)relRodata->d_buf, (char *)relRodata->d_buf + relRodata->d_size));
#endif
programInfo.relRodataChunks.push_back(std::move(relRodataChunk));
}
}
Elf_Data *symbolData = nullptr;
while ((symbolData = elf_getdata(symbolTableScn, symbolData)) != nullptr) {
size_t symbolIndex = 0;
GElf_Sym symbol;
while (gelf_getsym(symbolData, symbolIndex++, &symbol) == &symbol) {
const char *name = elf_strptr(elf, stringTableIndex, symbol.st_name);
if (!name) {
std::cerr << "Failed to symbol name for " + std::to_string(symbolIndex) + ". (" + std::string(elf_errmsg(-1)) + ")" << std::endl;
continue;
}
SymbolInfo symbolInfo;
symbolInfo.section = symbol.st_shndx;
symbolInfo.address = symbol.st_value;
symbolInfo.size = symbol.st_size;
symbolInfo.name = name;
programInfo.symbols.push_back(std::move(symbolInfo));
}
}
elf_end(elf);
return programInfo;
}
#ifdef EMSCRIPTEN
EMSCRIPTEN_BINDINGS(vtable) {
emscripten::value_object<LargeNumber>("LargeNumber")
.field("high", &LargeNumber::high)
.field("low", &LargeNumber::low)
.field("unsigned", &LargeNumber::isUnsigned);
emscripten::value_object<ProgramInfo>("ProgramInfo")
.field("error", &ProgramInfo::error)
.field("addressSize", &ProgramInfo::addressSize)
.field("rodataStart", &ProgramInfo::rodataStart)
.field("rodataIndex", &ProgramInfo::rodataIndex)
.field("rodataChunks", &ProgramInfo::rodataChunks)
.field("relRodataStart", &ProgramInfo::relRodataStart)
.field("relRodataIndex", &ProgramInfo::relRodataIndex)
.field("relRodataChunks", &ProgramInfo::relRodataChunks)
.field("symbols", &ProgramInfo::symbols)
.field("relocations", &ProgramInfo::relocations);
emscripten::value_object<RodataChunk>("RodataChunk")
.field("offset", &RodataChunk::offset)
.field("data", &RodataChunk::data);
emscripten::value_object<SymbolInfo>("SymbolInfo")
.field("section", &SymbolInfo::section)
.field("address", &SymbolInfo::address)
.field("size", &SymbolInfo::size)
.field("name", &SymbolInfo::name);
emscripten::value_object<RelocationInfo>("RelocationInfo")
.field("address", &RelocationInfo::address)
.field("target", &RelocationInfo::target);
emscripten::register_vector<RodataChunk>("VectorRodataChunk");
emscripten::register_vector<SymbolInfo>("VectorSymbolInfo");
emscripten::register_vector<RelocationInfo>("VectorRelocationInfo");
emscripten::function("process", &process);
}
#else
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <file-name>\n", argv[0]);
return 1;
}
std::ifstream file(argv[1], std::ios::binary);
std::string image((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
ProgramInfo programInfo = process(image);
if (!programInfo.error.empty()) {
fprintf(stderr, "Failed to process input file '%s': %s.\n", argv[1], programInfo.error.c_str());
return 1;
}
fprintf(stdout, "address size: %d\n", programInfo.addressSize);
fprintf(stdout, "rodata start: %08lx\n", programInfo.rodataStart);
fprintf(stdout, "rodata chunks: %lu\n", programInfo.rodataChunks.size());
for (const auto &chunk : programInfo.rodataChunks) {
fprintf(stdout, " offset: %08lx\n", chunk.offset);
fprintf(stdout, " size: %lu\n", chunk.data.size());
}
fprintf(stdout, "symbols: %lu\n", programInfo.symbols.size());
for (const auto &symbol : programInfo.symbols) {
if (symbol.address == 0 || symbol.size == 0 || symbol.name.empty()) {
continue;
}
fprintf(stdout, " offset: %08lx\n", symbol.address);
fprintf(stdout, " size: %lu\n", symbol.size);
fprintf(stdout, " name: %s\n", symbol.name.c_str());
}
return 0;
}
#endif