-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathobject_file.cpp
More file actions
373 lines (371 loc) · 17.4 KB
/
object_file.cpp
File metadata and controls
373 lines (371 loc) · 17.4 KB
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
/*
* Auto-added header
* File: object_file.cpp
* Author: camradeling
* Email: camradeling@gmail.com
* 2025
*/
//------------------------------------------------------------------------------------------------------------------------------
#include "memory_helpers.h"
#include "object_file.h"
#include "section.h"
#include "logger.h"
//------------------------------------------------------------------------------------------------------------------------------
const std::string ElfMan::ArchiveObjectFile::name_table_name = std::string("//");
const std::string ElfMan::ArchiveObjectFile::symbol_table_name = std::string("/");
//------------------------------------------------------------------------------------------------------------------------------
std::map<ElfMan::ArchiveObjectFileType, ElfMan::ArchiveObjectFile::FactoryFunc>& ElfMan::ArchiveObjectFile::registry() {
static std::map<ElfMan::ArchiveObjectFileType, ElfMan::ArchiveObjectFile::FactoryFunc> instance;
return instance;
}
//------------------------------------------------------------------------------------------------------------------------------
void ElfMan::ArchiveObjectFile::register_factory(ElfMan::ArchiveObjectFileType type, ElfMan::ArchiveObjectFile::FactoryFunc func) {
registry()[type] = std::move(func);
}
//------------------------------------------------------------------------------------------------------------------------------
std::shared_ptr<ElfMan::ArchiveObjectFile> ElfMan::ArchiveObjectFile::from_bytes(
const uint8_t* buffer,
uint32_t total_sz,
struct ar_hdr hdr,
std::string fname)
{
if (fname == name_table_name || fname == symbol_table_name) {
auto it = registry().find(ArchiveObjectFileType::STRING_TABLE);
if (it != registry().end()) {
return (it->second)(buffer, total_sz, hdr, fname);
}
}
// fallback — binary object
return std::make_shared<ElfMan::ObjectFile>(buffer, total_sz, hdr, fname);
}
//------------------------------------------------------------------------------------------------------------------------------
ElfMan::ObjectFile::ObjectFile(const uint8_t* buffer, uint32_t total_sz, struct ar_hdr hdr, std::string fname)
: ArchiveObjectFile(hdr, fname)
{
// Validation: sanity-check the provided buffer looks like an ELF object file.
// - Ensure we have at least the ELF magic bytes and a minimal header size.
// - Check that section header table offsets/sizes will fit within the provided buffer
// before attempting to read them.
if (total_sz < 4 || buffer == nullptr) {
LOG_ERROR("ObjectFile: buffer is null or too small (size=%u)", total_sz);
throw std::runtime_error("Invalid input buffer");
}
if (!(buffer[0] == 0x7f && buffer[1] == 'E' && buffer[2] == 'L' && buffer[3] == 'F')) {
LOG_ERROR("ObjectFile: invalid ELF magic");
throw std::runtime_error("Not an ELF file");
}
// Basic check: ensure header fits
if (total_sz < (uint32_t)sizeof(Elf32_Ehdr)) {
LOG_ERROR("ObjectFile: buffer smaller than Elf32_Ehdr (size=%u)", total_sz);
throw std::runtime_error("Truncated ELF header");
}
type = ArchiveObjectFileType::ELF_OBJECT;
int index = 0;
// we only need elf_header_stream to read elf_header
ElfMan::Memory::InputMemoryStream elf_header_stream(buffer, total_sz);
elf_header_stream.read(ehdr);
ElfMan::Memory::InputMemoryStream section_header_stream(buffer+ehdr.e_shoff, ehdr.e_shentsize*ehdr.e_shnum);
for(int i = 0; i < ehdr.e_shnum; i++) {
// for every new section - read it's header
Elf32_Shdr shdr;
section_header_stream.read(shdr);
// create a section instance, different section types are handled inside constructor
std::shared_ptr<ElfMan::Section> newsection = ElfMan::Section::from_bytes(&shdr,buffer + shdr.sh_offset, shdr.sh_size, this);
// we save all sections into a map with section offset used as a key
if(newsection->size() && newsection->type() != SHT_NOBITS)
sections.insert(std::pair<uint32_t,std::shared_ptr<ElfMan::Section>>(shdr.sh_offset, newsection));
newsection->index = index++;
// for now we assume there's only one symbol table section
// this is very unlikely to be not true
if (SHT_SYMTAB == shdr.sh_type)
symtab_section = std::dynamic_pointer_cast<SymbolSection>(sections.find(shdr.sh_offset)->second);
// every section needs a smart pointer to it in an indexed vector
sections_by_index.push_back(newsection);
LOG_DEBUG("added section %d, size %d", newsection->index, newsection->size());
}
// symbol table section always have a link to symbol string table section
symbol_strtab_section = std::dynamic_pointer_cast<RawSection>(sections_by_index[symtab_section->link()]);
// and section name string table section is assumed to be always the last
section_strtab_section = std::dynamic_pointer_cast<RawSection>(sections_by_index[sections_by_index.size()-1]);
// relink all relocations
for (auto §ion_pair : sections) {
std::shared_ptr<ElfMan::Section> section = section_pair.second;
if(!section->size() || section->type() != SHT_REL)
continue;
if (auto reltab = std::dynamic_pointer_cast<RelocationSection>(section)) {
for (auto& rel : reltab->relocations)
rel->parent_ptr = section;
}
else {
throw std::runtime_error("dynamic cast to RelocationSection failed");
}
}
// populate symtab section symbol map
// we couldn't do that until all string table sections were added
for (auto &symbol : symtab_section->symbols) {
symtab_section->symbols_by_name.insert(std::pair(symbol->name(), symbol));
}
}
//------------------------------------------------------------------------------------------------------------------------------
std::vector<uint8_t> ElfMan::ObjectFile::serialize()
{
std::vector<uint8_t> object, section_header;
object.resize(sizeof(ehdr));
int max_size = 0;
for (auto secpair : sections)
{
std::shared_ptr<ElfMan::Section> sec = secpair.second;
if(!sec->size() || sec->type() == SHT_NOBITS)
continue;
LOG_DEBUG("processing section %d, size %08X, offset %08X", sec->index, object.size(), sec->offset());
std::vector<uint8_t> section_data = sec->serialize();
// move sections if alignment requires that
//----------------------------------
// additional check for linker generated 0ed pads
if (object.size() != sec->offset()) {// && sec->type() != SHT_REL) {
LOG_DEBUG("section %d: adding zero pad from 0x%08X to 0x%08X", sec->index, object.size(), sec->offset());
std::vector<uint8_t> padvec(sec->offset() - object.size(), 0);
object.insert(object.end(), padvec.begin(), padvec.end());
}
//----------------------------------
if (sec->addralign() > 1)
{
int padding = object.size() % sec->addralign();
if (padding)
{
std::vector<uint8_t> padvec(sec->addralign() - padding, 0);
object.insert(object.end(), padvec.begin(), padvec.end());
}
LOG_DEBUG("section %d: adding alignment pad from 0x%08X to 0x%08lX", sec->index, sec->offset(), object.size());
}
sec->offset(object.size());
object.insert(object.end(), section_data.begin(), section_data.end());
}
// padding for section headers table
int padding = object.size() % sizeof(uint32_t);
if (padding)
{
std::vector<uint8_t> padvec(sizeof(uint32_t) - padding, 0);
object.insert(object.end(), padvec.begin(), padvec.end());
}
// put in elf header, now that we know section header offset
ehdr.e_shoff = object.size();
memcpy((uint8_t*)object.data(), (uint8_t*)&ehdr, sizeof(ehdr));
for (auto section : sections_by_index)
{
LOG_DEBUG("writing section header %d data size %08X, addr %08X, alignment %d\n", section->index, section->size(),
section->offset(), section->addralign());
section_header.insert(section_header.end(), (char*)section->header(), (char*)section->header() + sizeof(Elf32_Shdr));
}
object.insert(object.end(), section_header.begin(), section_header.end());
return object;
}
//------------------------------------------------------------------------------------------------------------------------------
void ElfMan::ObjectFile::move_section_offsets(uint32_t addr, int addend)
{
// move all sections offsets higher than the addr of modified one
for (auto secpair : sections)
{
std::shared_ptr<ElfMan::Section> sec = secpair.second;
if (sec->offset() < addr)
continue;
// adjust alignment
sec->offset(sec->offset() + addend);
LOG_DEBUG("moving section %d from 0x%08X to 0x%08X\n", sec->index, sec->offset(), sec->offset() + addend);
uint32_t offset = sec->offset();
}
}
//------------------------------------------------------------------------------------------------------------------------------
// when we changed one or few symbols visibility (local to global or vice versa), we need to reorder them in a symbol table,
// and after that we need to move all relocations, so that they pointed to correct symbol indexes
// we take symbols by smart pointer, put them into new vector, change their indexes, but their position in original vector is equal to their old index.
// so we always know both new and old indexes, and after correction just swap vectors
void ElfMan::ObjectFile::reorder_symtab_and_relocations()
{
// reorder symbol table, keeping old indexes inside
std::vector<std::shared_ptr<ElfMan::Symbol>> global_symbols, local_symbols, replacement;
for (auto symbol : symtab_section->symbols)
{
if (symbol->bind() == STB_GLOBAL)
global_symbols.push_back(symbol);
else
local_symbols.push_back(symbol);
}
replacement.insert(replacement.end(), local_symbols.begin(), local_symbols.end());
replacement.insert(replacement.end(), global_symbols.begin(), global_symbols.end());
// rewrite all symbols symtab indexes
for (int i = 0; i < replacement.size(); i++)
{
replacement[i]->index = i;
}
// move symtab section first global symbol index
symtab_section->info(local_symbols.size());
// now relink all relocations to new symtab indexes
for (auto section : sections_by_index)
{
if (section->type() != SHT_REL)
continue;
if (auto reltab = std::dynamic_pointer_cast<RelocationSection>(section)) {
for (auto& rel : reltab->relocations)
{
LOG_DEBUG("check relocation %d, offset 0x%08X", rel->index, rel->rhdr.r_offset);
// symtab->symbols is not yet reordered,
// but all symbol instances in it already have indexes from replacement collection
// relocation instances still point to old indexes in symtab
// so we take index from relocation instance, dereference it to a symbol instance
// then check if its new index matches the old one.
// and if not - just rewrite index in relocation instance
std::shared_ptr<ElfMan::Symbol> sym = rel->symbol_to_apply();
if (!sym)
{
LOG_ERROR("failed to find symbol for relocation %d\n", rel->index);
continue;
}
if (sym->index != ELF32_R_SYM(rel->rhdr.r_info))
{
LOG_DEBUG("inconsistent relocation entry for symbol %s, remapping %d to %d\n",
sym->name().c_str(),
ELF32_R_SYM(rel->rhdr.r_info),
sym->index);
rel->rhdr.r_info = ELF32_R_INFO(sym->index,ELF32_R_TYPE(rel->rhdr.r_info));
}
}
}
else {
throw std::runtime_error("dynamic cast to RelocationSection failed");
}
}
std::swap(symtab_section->symbols,replacement);
}
//------------------------------------------------------------------------------------------------------------------------------
// this method rewrite all relocation instancess pointing to a symbol so that they started pointing to another symbol
void ElfMan::ObjectFile::move_relocations(int src_ind, int dest_ind)
{
for (auto section : sections_by_index)
{
if (section->type() != SHT_REL)
continue;
if (auto relsection = std::dynamic_pointer_cast<ElfMan::RelocationSection>(section)) {
for (auto& rel : relsection->relocations) {
LOG_DEBUG("check relocation %d, offset 0x%08X\n", rel->index, rel->rhdr.r_offset);
std::shared_ptr<ElfMan::Symbol> sym = rel->symbol_to_apply();
if (!sym)
{
LOG_ERROR("failed to find symbol for relocation %d\n", rel->index);
continue;
}
if (sym->index == src_ind)
{
LOG_INFO("found relocation entry for symbol %d, remapping to symbol %d, section %d,\n",
src_ind,
dest_ind,
section->index);
rel->rhdr.r_info = ELF32_R_INFO(dest_ind,ELF32_R_TYPE(rel->rhdr.r_info));
}
}
}
else {
throw std::runtime_error("dynamic cast to RelocationSection failed");
}
}
}
//------------------------------------------------------------------------------------------------------------------------------
std::shared_ptr<ElfMan::Symbol> ElfMan::ObjectFile::insert_undefined_global_function(std::string name, bool thumb)
{
// insert new symbol
Elf32_Sym symhdr;
symhdr.st_value = thumb ? 1 : 0;
symhdr.st_size = 0;
symhdr.st_other = STV_DEFAULT;
symhdr.st_shndx = SHN_UNDEF;
// insert new symbol name to strtab
std::shared_ptr<ElfMan::Symbol> newsym(new ElfMan::Symbol(&symhdr, this));
// last index offset in strtab will be current strtab size - we write to the end
newsym->symhdr.st_name = symbol_strtab_section->data.size();
newsym->symhdr.st_info = ELF32_ST_INFO(STB_GLOBAL, STT_FUNC);
symbol_strtab_section->data.resize(symbol_strtab_section->data.size()+name.size()+1);
std::fill(symbol_strtab_section->data.begin()+newsym->symhdr.st_name, symbol_strtab_section->data.end(),0);
// writning new symbol name into last index offset in strtab
memcpy(&symbol_strtab_section->data.data()[newsym->symhdr.st_name], name.data(), name.size());
// now saving new symbol index
newsym->index = symtab_section->symbols.size();
// and saving symbol instance
symtab_section->symbols.push_back(newsym);
symtab_section->symbols_by_name.insert(std::pair(newsym->name(), newsym));
// updating section sizes
int old_symtab_size = symtab_section->size();
symtab_section->size(old_symtab_size + sizeof(Elf32_Sym));
int old_symbol_strtab_size = symbol_strtab_section->size();
symbol_strtab_section->size(old_symbol_strtab_size + name.size()+1);
LOG_DEBUG("%s %08X %08X %08X %02X %02X %08X\n", newsym->name().c_str(),
newsym->symhdr.st_name,
newsym->symhdr.st_value,
newsym->symhdr.st_size,
newsym->symhdr.st_info,
newsym->symhdr.st_other,
newsym->symhdr.st_shndx);
// symtab section changed size - symbol_strtab_section and section_strtab_section should also change offset
symbol_strtab_section->offset(symbol_strtab_section->offset() + sizeof(Elf32_Sym));
section_strtab_section->offset(section_strtab_section->offset() + sizeof(Elf32_Sym) + name.size()+1);
// for all rel sections above the symtab and symbol_strtab sections offsets should be modified.
for (auto section : sections_by_index)
{
if (section->type() != SHT_REL)
continue;
if (auto relsection = std::dynamic_pointer_cast<ElfMan::RelocationSection>(section)) {
if (relsection->offset() >= symtab_section->offset() + old_symtab_size) {
LOG_DEBUG("1) found relocation entry offset %08X, moving to %08X", relsection->offset(), relsection->offset() + sizeof(Elf32_Sym));
relsection->offset(relsection->offset() + sizeof(Elf32_Sym));
}
if (relsection->offset() >= symbol_strtab_section->offset() + old_symbol_strtab_size) {
LOG_DEBUG("2) found relocation entry offset %08X, moving to %08X", relsection->offset(), relsection->offset() + name.size()+1);
relsection->offset(relsection->offset() + name.size()+1);
}
}
}
return std::move(newsym);
}
//------------------------------------------------------------------------------------------------------------------------------
std::shared_ptr<ElfMan::Symbol> ElfMan::ObjectFile::find_symbol(std::string sym_name)
{
if (auto search = symtab_section->symbols_by_name.find(sym_name); search != symtab_section->symbols_by_name.end())
return search->second;
else
return nullptr;
}
//------------------------------------------------------------------------------------------------------------------------------
std::shared_ptr<ElfMan::Symbol> ElfMan::ObjectFile::rename_symbol(std::string old_name, std::string new_name)
{
auto sympair = symtab_section->symbols_by_name.find(old_name);
if (sympair == symtab_section->symbols_by_name.end()) {
LOG_ERROR("symbol %s not found", old_name.c_str());
return nullptr;
}
std::shared_ptr<ElfMan::Symbol> symbol = sympair->second;
if (!symbol) {
LOG_ERROR("symbol %s not found", old_name.c_str());
return nullptr;
}
// change name for symbol
uint8_t* ptr = &symbol_strtab_section->data.data()[symbol->symhdr.st_name];
memset((char *)ptr, 0, old_name.size());
strncpy((char *)ptr, new_name.data(), old_name.size());
return symbol;
}
//------------------------------------------------------------------------------------------------------------------------------
bool ElfMan::StringTable::registered = []{
ArchiveObjectFile::register_factory(ElfMan::ArchiveObjectFileType::STRING_TABLE,
[](const uint8_t* b, uint32_t sz, struct ar_hdr h, std::string f) {
return std::make_shared<ElfMan::StringTable>(b, sz, h, f);
});
return true;
}();
//------------------------------------------------------------------------------------------------------------------------------
bool ElfMan::ObjectFile::registered = []{
ArchiveObjectFile::register_factory(ElfMan::ArchiveObjectFileType::ELF_OBJECT,
[](const uint8_t* b, uint32_t sz, struct ar_hdr h, std::string f) {
return std::make_shared<ElfMan::ObjectFile>(b, sz, h, f);
});
return true;
}();
//------------------------------------------------------------------------------------------------------------------------------