/*
This file is part of Libelfpp.
Libelfpp is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Libelfpp is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Libelfpp. If not, see
.
Copyright (c) Alexandre Becoulet
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace elfpp
{
template
void elfn_access::load_symtab(object &obj)
{
section &table = obj.get_section(".symtab");
if (table.get_type() != SHT_SYMTAB)
throw std::runtime_error("not a symbol table section");
// get symbol table content
elf_sym_t *symtab = (elf_sym_t*)table.get_content();
size_t count = table.get_size() / sizeof (elf_sym_t);
// get symbol string section
section &strtab = *table.get_link();
const char *symstr = (char*)strtab.get_content();
// create symbol objects from table
obj.symidx_.resize(count);
for (unsigned int i = 1; i < count; i++)
{
unsigned int idx = swap(symtab[i].st_shndx);
symbol *sym = new symbol(symstr + swap(symtab[i].st_name));
obj.symidx_[i] = sym;
sym->set_value(swap(symtab[i].st_value));
sym->set_size(swap(symtab[i].st_size));
sym->set_info(symtab[i].st_info);
sym->set_other(symtab[i].st_other);
if (idx > 0 && idx < SHN_LORESERVE)
{
section *symsec = obj.secidx_.at(idx);
if (!symsec)
continue;
sym->set_section(*symsec);
symsec->add_symbol(*sym);
if (obj.type_ != ET_REL)
sym->set_value(sym->get_value() - symsec->get_vaddr());
if (symsec && sym->get_size() > 0 && symsec->get_type() != SHT_NOBITS)
{
if (symsec->get_size() < sym->get_value())
std::cerr << "elfpp: symbol value above section size " << sym->get_name()
<< ":" << std::hex << sym->get_value() << std::endl;
// else
// sym->set_content(symsec->get_content() + sym->get_value());
}
}
else
{
sym->set_section_ndx(idx);
obj.add_symbol(*sym);
}
}
// discard symbol table sections
delete &table;
delete &strtab;
}
template
void elfn_access::load_addend(const object &obj, reloc &r, const elf_rel_t *reltab,
size_t count, int index, uint8_t *data)
{
// r->set_addend(rel_read(obj, type, sec->get_content() + ro));
uint64_t offset = swap(reltab[index].r_offset);
r.set_offset(offset);
switch (obj.machine_)
{
case EM_MIPS:
switch (r.get_type())
{
case R_MIPS_26:
r.set_addend((swap(*(uint32_t*)(data + offset)) & 0x3ffffff) << 2);
return;
case R_MIPS_32:
r.set_addend(swap(*(uint32_t*)(data + offset)));
return;
case R_MIPS_HI16: {
int32_t value = (swap(*(uint32_t*)(data + offset)) & 0xffff) << 16;
// get next LO16 relocation
for (index++; index < (int)count && rel_type(swap(reltab[index].r_info)) != R_MIPS_LO16; index++)
;
if (index != (int)count)
{
offset = swap(reltab[index].r_offset);
value += (int16_t)swap(*(uint32_t*)(data + offset));
r.set_addend(value);
}
else
{
std::cerr << "elfpp: can not find matching LO16 relocation" << std::endl;
}
return;
}
case R_MIPS_LO16: {
int32_t value = (int16_t)swap(*(uint32_t*)(data + offset));
// get prev HI16 relocation if any
if (index > 0 && rel_type(swap(reltab[index - 1].r_info)) == R_MIPS_HI16)
{
offset = swap(reltab[index - 1].r_offset);
value += (swap(*(uint32_t*)(data + offset)) & 0xffff) << 16;
}
r.set_addend(value);
return;
}
default: ;
}
break;
case EM_ARM:
switch (r.get_type())
{
case R_ARM_PC24:
r.set_addend(((swap(*(uint32_t*)(data + offset)) & 0xffffff) << 2) + 8);
return;
case R_ARM_ABS32:
r.set_addend(swap(*(uint32_t*)(data + offset)));
return;
default: ;
}
break;
default: ;
}
std::cerr << "elfpp: unhandled relocation (machine=" << obj.machine_
<< ", type=" << r.get_type() << ")" << std::endl;
return;
}
template
void elfn_access::rel_write(const object &obj, enum reloc_e type, void *data, int64_t value)
{
uint32_t mask;
switch (obj.machine_)
{
case EM_MIPS:
switch (type)
{
case R_MIPS_26:
mask = 0x3ffffff;
if (value & 0x3)
std::cerr << "elfpp: R_MIPS_26 relocation with 2 LSB set" << std::endl;
value >>= 2;
goto done;
case R_MIPS_32:
mask = 0xffffffff;
goto done;
case R_MIPS_HI16:
mask = 0xffff;
value >>= 16;
goto done;
case R_MIPS_LO16:
mask = 0xffff;
goto done;
default: ;
}
break;
case EM_ARM:
switch (type)
{
case R_ARM_PC24:
mask = 0x00ffffff;
value = (value - 8) >> 2;
goto done;
case R_ARM_ABS32:
mask = 0xffffffff;
goto done;
default: ;
}
break;
default: ;
}
std::cerr << "elfpp: unhandled relocation (machine=" << obj.machine_ << ", type=" << type << ")" << std::endl;
return;
done:
*(uint32_t*)data = swap(mask & value) | (*(uint32_t*)data & swap(~mask));
}
template
void elfn_access::load_reltab(object &obj)
{
// load all reloaction table sections
if (obj.type_ != ET_REL)
return;
FOREACH2(relsec, obj.get_section_table())
{
switch (relsec->get_type())
{
case SHT_REL: {
elf_rel_t *reltab = (elf_rel_t*)relsec->get_content();
size_t count = relsec->get_size() / sizeof (elf_rel_t);
obj.rel_with_addend_ = false;
for (unsigned int i = 0; i < count; i++)
{
if (!rel_sym(swap(reltab[i].r_info)))
continue;
reloc *r = new reloc();
section *sec = relsec->get_info();
symbol *sym = obj.symidx_.at(rel_sym(swap(reltab[i].r_info)));
assert(sym);
r->set_symbol(sym);
r->set_section(sec);
reloc_e type = rel_type(swap(reltab[i].r_info));
r->set_type(type);
load_addend(obj, *r, reltab, count, i, sec->get_content());
}
// std::cerr << "elfpp: relocation " << i << " offset " << ro << " out of section" << std::endl;
delete &*relsec;
break;
}
case SHT_RELA: {
elf_rela_t *reltab = (elf_rela_t*)relsec->get_content();
size_t count = relsec->get_size() / sizeof (elf_rela_t);
obj.rel_with_addend_ = true;
for (unsigned int i = 0; i < count; i++)
{
if (!rel_sym(swap(reltab[i].r_info)))
continue;
reloc *r = new reloc();
section *sec = relsec->get_info();
symbol *sym = obj.symidx_[rel_sym(swap(reltab[i].r_info))];
assert(sym);
r->set_symbol(sym);
r->set_section(sec);
r->set_type(rel_type(swap(reltab[i].r_info)));
uint32_t ro = swap(reltab[i].r_offset);
r->set_offset(ro);
r->set_addend(swap(reltab[i].r_addend));
}
delete &*relsec;
break;
}
default:
break;
}
}
obj.generate_symtab_ = true;
}
static bool sort_predicate(const symbol *a, const symbol *b)
{
return a->get_info() < b->get_info();
}
template
void elfn_access::save_symtab(object &obj)
{
size_t shnum = 1;
// renumber current section table
FOREACH(s, obj.get_section_table())
s->index_ = shnum++;
// create symbol and associated string sections
section *symtab = new section(obj, SHT_SYMTAB);
section *strtab = new section(obj, SHT_STRTAB);
std::string str(1, '\0');
symtab->set_name(".symtab");
symtab->set_entsize(sizeof (elf_sym_t));
symtab->set_link(strtab);
strtab->set_name(".strtab");
strtab->set_flags(SHF_STRINGS);
obj.add_section(*symtab);
obj.add_section(*strtab);
// prepare sorted symbol array
unsigned int i = 0;
std::vector table;
table.resize(obj.get_symbol_table().size());
FOREACH(sym, obj.get_symbol_table())
table[i++] = sym->second;
FOREACH(sec, obj.get_section_table())
{
table.resize(i + sec->get_symbol_table().size());
FOREACH(sym, sec->get_symbol_table())
table[i++] = sym->second;
}
sort(table.begin(), table.end(), sort_predicate);
for (i = 0; i < table.size(); i++)
{
if (ELF_ST_BIND(table[i]->get_info()) != STB_LOCAL)
break;
}
symtab->set_info_last(i + 1);
// generate symbol table section content
std::vector reloclist[shnum];
elf_sym_t symdata[table.size() + 1];
i = 1;
std::memset(symdata, 0, sizeof(elf_sym_t));
FOREACH(sym_, table)
{
elf_sym_t &ent = symdata[i];
symbol *sym = *sym_;
swap(&ent.st_name, str.size());
str.append(sym->get_name().c_str(), sym->get_name().size() + 1);
swap(&ent.st_value, sym->get_value());
swap(&ent.st_size, sym->get_size());
ent.st_info = sym->get_info();
ent.st_other = sym->get_other();
FOREACH(rel, sym->reloc_tab_)
reloclist[rel->get_section()->index_].push_back(&*rel);
if (sym->get_section())
swap(&ent.st_shndx, sym->get_section()->index_);
else
swap(&ent.st_shndx, sym->get_section_ndx());
sym->index_ = i++;
}
strtab->set_size(str.size());
std::memcpy(strtab->get_content(), str.data(), str.size());
symtab->set_size(sizeof(symdata));
std::memcpy(symtab->get_content(), symdata, sizeof(symdata));
// generate relocation table
FOREACH(sec, obj.get_section_table())
{
if (!sec->index_)
continue;
std::vector &rl = reloclist[sec->index_];
if (rl.empty())
continue;
section *reltab = new section(obj, obj.rel_with_addend_ ? SHT_RELA : SHT_REL);
reltab->set_entsize(sizeof (elf_rel_t));
reltab->set_link(symtab);
reltab->set_info(&*sec);
obj.add_section(*reltab);
if (obj.rel_with_addend_)
{
elf_rela_t reldata[rl.size()];
reltab->set_name(".rela" + sec->get_name());
i = 0;
FOREACH(rel_, rl)
{
reloc *rel = *rel_;
if (symbol *ms = rel->get_mangled_symbol())
swap(&reldata[i].r_offset, ms->get_value() + rel->get_offset());
else
swap(&reldata[i].r_offset, rel->get_offset());
swap(&reldata[i].r_info, rel_info(rel->get_symbol()->index_, rel->get_type()));
swap(&reldata[i].r_addend, rel->get_addend());
i++;
}
reltab->set_size(sizeof(reldata));
std::memcpy(reltab->get_content(), reldata, sizeof(reldata));
}
else
{
elf_rel_t reldata[rl.size()];
reltab->set_name(".rel" + sec->get_name());
i = 0;
FOREACH(rel_, rl)
{
reloc *rel = *rel_;
uint64_t ro = rel->get_offset();
if (symbol *ms = rel->get_mangled_symbol())
{
// relocation offset is relative to mangled symbol value
if (ro < ms->get_size())
rel_write(obj, rel->get_type(), ms->get_content() + ro, rel->get_addend());
else
std::cerr << "elfpp: relocation offset above mangled symbol size" << std::endl;
ro += ms->get_value();
}
else
{
// relocation offset is relative to mangled section
if (ro < sec->get_size())
rel_write(obj, rel->get_type(), sec->get_content() + ro, rel->get_addend());
else
std::cerr << "elfpp: relocation offset above section size" << std::endl;
}
swap(&reldata[i].r_offset, ro);
swap(&reldata[i].r_info, rel_info(rel->get_symbol()->index_, rel->get_type()));
i++;
}
reltab->set_size(sizeof(reldata));
std::memcpy(reltab->get_content(), reldata, sizeof(reldata));
}
}
}
template
void elfn_access::read(object &obj, FILE *file)
{
// read file header
elf_hdr_t head_;
fseek(file, 0, SEEK_SET);
if (fread(&head_, sizeof (head_), 1, file) != 1)
throw std::runtime_error("unable to read file");
obj.word_width_ = (ei_class_e)head_.e_ident[EI_CLASS];
obj.byteorder_ = byteorder_ = (ei_data_e)head_.e_ident[EI_DATA];
obj.os_abi_ = (ei_osabi_e)head_.e_ident[EI_OSABI];
obj.abi_ver_ = head_.e_ident[EI_ABIVERSION];
obj.type_ = (e_type_e)swap(head_.e_type);
obj.machine_ = (e_machine_e)swap(head_.e_machine);
obj.entry_ = swap(head_.e_entry);
obj.flags_ = swap(head_.e_flags);
// read segment table
size_t phnum = swap(head_.e_phnum);
elf_phdr_t phdr[phnum];
fseek(file, swap(head_.e_phoff), SEEK_SET);
if (fread(phdr, sizeof (elf_phdr_t), phnum, file) != phnum)
throw std::runtime_error("unable to read file");
// create segment objects
for (unsigned int i = 0; i < phnum; i++)
{
segment *s = new segment(obj);
s->set_type((p_type_e)swap(phdr[i].p_type));
s->set_file_offset(swap(phdr[i].p_offset));
s->set_vaddr(swap(phdr[i].p_vaddr));
s->set_paddr(swap(phdr[i].p_paddr));
s->set_mem_size(swap(phdr[i].p_memsz));
s->set_file_size(swap(phdr[i].p_filesz));
s->set_flags((p_flags_e)swap(phdr[i].p_flags));
s->set_align(swap(phdr[i].p_align));
obj.add_segment(*s);
}
// read section table
size_t shnum = swap(head_.e_shnum);
elf_shdr_t shdr[shnum];
obj.secidx_.resize(shnum);
fseek(file, swap(head_.e_shoff), SEEK_SET);
if (fread(shdr, sizeof (elf_shdr_t), shnum, file) != shnum)
throw std::runtime_error("unable to read file");
// create section objects
for (unsigned int i = 1; i < shnum; i++)
{
sh_type_e type = (sh_type_e)swap(shdr[i].sh_type);
section *s = new section(obj, type);
off_t offset = swap(shdr[i].sh_offset);
obj.secidx_[i] = s;
s->index_ = i;
s->set_flags((sh_flags_e)swap(shdr[i].sh_flags));
s->set_vaddr(swap(shdr[i].sh_addr));
s->set_file_offset(offset);
s->set_size(swap(shdr[i].sh_size));
s->set_align(swap(shdr[i].sh_addralign));
s->set_entsize(swap(shdr[i].sh_entsize));
if (type != SHT_NOBITS && s->get_size())
{
fseek(file, offset, SEEK_SET);
if (fread(s->get_content(), s->get_size(), 1, file) != 1)
throw std::runtime_error("unable to read file");
}
obj.add_section(*s);
}
// update sections links
for (unsigned int i = 1; i < shnum; i++)
{
section &s = *obj.secidx_[i];
unsigned int link = swap(shdr[i].sh_link);
if (link)
s.set_link(obj.secidx_.at(link));
unsigned int info = swap(shdr[i].sh_info);
switch (s.type_)
{
case SHT_REL:
case SHT_RELA:
if (info)
s.set_info(obj.secidx_.at(info));
break;
case SHT_DYNSYM:
case SHT_SYMTAB:
s.set_info_last(info);
break;
default:
;
}
}
// set sections names
unsigned int shstrndx = swap(head_.e_shstrndx);
if (shstrndx != SHN_UNDEF)
{
section &shstr = *obj.secidx_.at(shstrndx);
const char *str = (char*)shstr.get_content();
for (unsigned int i = 1; i < shnum; i++)
obj.secidx_[i]->set_name(str + swap(shdr[i].sh_name));
delete &shstr;
}
}
template
void elfn_access::create_segments(object &obj)
{
p_flags_e flag;
FOREACH(sec, obj.get_section_table())
{
segment *s = new segment(obj);
if(!(sec->get_flags() & elfpp::SHF_ALLOC))
{
s->set_type(PT_NULL);
continue;
}
s->set_type(PT_LOAD);
s->set_file_offset(0);//done while writing section in file
#ifdef MEMO_DEBUG
std::cout << "seg building (" << std::hex << sec->get_vaddr() << ") for: " << *sec << std::endl;
#endif
s->set_vaddr(sec->get_vaddr());
s->set_paddr(sec->get_vaddr());
s->set_mem_size(sec->get_size());
s->set_file_size(sec->get_size());
flag = PF_R ;//ALLOC ==> read ?
flag = flag | (p_flags_e)(sec->get_flags() & SHF_WRITE)? PF_W : PF_R ;
flag = flag | (p_flags_e)(sec->get_flags() & SHF_EXECINSTR)? PF_X : PF_R ;
s->set_flags(flag);
s->set_align(0);//FIXME!
#ifdef MEMO_DEBUG
std::cout <<"New segment " << s->get_file_size() << std::endl;
#endif
obj.add_segment(*s);
}
}
template
void elfn_access::write(object &obj, FILE *file)
{
byteorder_ = obj.byteorder_;
if (obj.generate_symtab_)
save_symtab(obj);
// create all segments
create_segments(obj);
// write symbol contents
FOREACH(sec, obj.get_section_table())
{
if (sec->get_type() == SHT_NOBITS)
continue;
FOREACH(sym_, sec->get_symbol_table())
{
symbol *sym = sym_->second;
if (!sym->get_content())
continue;
if (sec->get_size() < sym->get_value() + sym->get_size())
sec->set_size(sym->get_value() + sym->get_size());
std::memcpy(sec->get_content() + sym->get_value(), sym->get_content(), sym->get_size());
}
}
elf_hdr_t head;
// create a new section name section
section *shstrtab = new section(obj, SHT_STRTAB);
shstrtab->set_name(".shstrtab");
shstrtab->set_flags(SHF_STRINGS);
obj.add_section(*shstrtab);
size_t shnum = 1;
FOREACH(s, obj.get_section_table())
s->index_ = shnum++;
std::string strtab(1, '\0');
unsigned int stridx[shnum];
stridx[0] = 0;
FOREACH(s, obj.get_section_table())
{
stridx[s->index_] = strtab.size();
strtab.append(s->get_name().c_str(), s->get_name().size() + 1);
}
shstrtab->set_size(strtab.size());
std::memcpy(shstrtab->get_content(), strtab.data(), strtab.size());
size_t phnum = shnum;//FIXME!:only relevent section
// update header
std::memset(head.e_ident, 0, sizeof(head.e_ident));
std::memcpy(head.e_ident, ELFMAG, 4);
head.e_ident[EI_CLASS] = obj.word_width_;
head.e_ident[EI_DATA] = byteorder_;
head.e_ident[EI_VERSION] = EV_CURRENT;
head.e_ident[EI_OSABI] = obj.os_abi_;
head.e_ident[EI_ABIVERSION] = obj.abi_ver_;
swap(&head.e_type, obj.type_);
swap(&head.e_machine, obj.machine_);
swap(&head.e_version, EV_CURRENT);
swap(&head.e_entry, obj.entry_);
swap(&head.e_phoff, sizeof (elf_hdr_t));
swap(&head.e_shoff, sizeof (elf_hdr_t)+ (sizeof(elf_phdr_t)*phnum));//!
swap(&head.e_flags, obj.flags_);
swap(&head.e_ehsize, sizeof (elf_hdr_t));
swap(&head.e_phentsize, sizeof (elf_phdr_t));
swap(&head.e_phnum, phnum);
swap(&head.e_shentsize, sizeof (elf_shdr_t));
swap(&head.e_shnum, shnum);
swap(&head.e_shstrndx, shstrtab->index_);
// update section table
elf_shdr_t shdr[shnum];
std::memset(shdr, 0, sizeof(shdr));
FOREACH(s, obj.get_section_table())
{
unsigned int i = s->index_;
swap(&shdr[i].sh_name, stridx[i]);
swap(&shdr[i].sh_type, s->get_type());
swap(&shdr[i].sh_flags, s->get_flags());
swap(&shdr[i].sh_addr, s->get_vaddr());
swap(&shdr[i].sh_size, s->get_size());
if (s->get_link())
swap(&shdr[i].sh_link, s->get_link()->index_);
switch (s->get_type())
{
case SHT_REL:
case SHT_RELA:
assert(s->get_info());
swap(&shdr[i].sh_info, s->get_info()->index_);
break;
case SHT_DYNSYM:
case SHT_SYMTAB:
swap(&shdr[i].sh_info, s->get_info_last());
default:
break;
}
swap(&shdr[i].sh_addralign, s->get_align());
swap(&shdr[i].sh_entsize, s->get_entsize());
}
// update program header
elf_phdr_t phdr[phnum];
std::memset(phdr, 0, sizeof(phdr));
unsigned int i = 1;//to keep the same order as sections
FOREACH(s, obj.get_segment_table())
{
swap(&phdr[i].p_type, s->get_type());
swap(&phdr[i].p_flags, s->get_flags());
swap(&phdr[i].p_vaddr, s->get_vaddr());
swap(&phdr[i].p_paddr, s->get_paddr());
swap(&phdr[i].p_filesz, s->get_file_size());
swap(&phdr[i].p_memsz, s->get_mem_size());
swap(&phdr[i].p_align, s->get_align());
//note: offset done while writing sections
i++;
}
//assert((i == phnum)and (i == shnum));
// write header to file
fseek(file, 0, SEEK_SET);
if (fwrite(&head, sizeof(head), 1, file) != 1)
throw std::runtime_error("unable to write header to file");
fseek(file, sizeof(phdr)+sizeof(shdr), SEEK_CUR);
// write sections to file
FOREACH(s, obj.get_section_table())
{
if (s->get_type() == SHT_NOBITS)
continue;
#ifdef MEMO_DEBUG
std::cout << "offset: " << ftell(file) << " for " << *s << std::endl;
std::cout << std::hex << "paddr: " << phdr[s->index_].p_paddr << ", sect vaddr: " << s->get_vaddr() << "/" <index_].sh_addr << std::endl;
#endif
swap(&shdr[s->index_].sh_offset, ftell(file));
swap(&phdr[s->index_].p_offset, ftell(file));
if (s->get_size() && fwrite(s->get_content(), s->get_size(), 1, file) != 1)
throw std::runtime_error("unable to write section to file");
}
// write program header to file
fseek(file, sizeof(head), SEEK_SET);
if (fwrite(phdr, sizeof(phdr), 1, file) != 1)
throw std::runtime_error("unable to write section table to file");
// write section table to file
//toDO:assert(cur_seek == phnum*sizeof(ph_t)+...)
//fseek(file, sizeof(phdr)+sizeof(head), SEEK_CUR);
if (fwrite(shdr, sizeof(shdr), 1, file) != 1)
throw std::runtime_error("unable to write section table to file");
}
template class elfn_access;
template class elfn_access;
}