bool ElfConfig::ReadSymbolTable(const MemoryBuffer& in, const ElfSection& symtab_sect, ElfSymtab& symtab, Object& object, const StringTable& strtab, Section* sections[], DiagnosticsEngine& diags) const { ElfSize symsize = symtab_sect.getEntSize(); if (symsize == 0) { diags.Report(SourceLocation(), diag::err_symbol_entity_size_zero); return false; } unsigned long size = symtab_sect.getSize().getUInt(); // Symbol table always starts with null entry symtab.push_back(SymbolRef(0)); ElfSymbolIndex index = 1; for (unsigned long pos=symsize; pos<size; pos += symsize, ++index) { std::auto_ptr<ElfSymbol> elfsym( new ElfSymbol(*this, in, symtab_sect, index, sections, diags)); if (diags.hasErrorOccurred()) return false; SymbolRef sym = elfsym->CreateSymbol(object, strtab); symtab.push_back(sym); if (sym) sym->AddAssocData(elfsym); // Associate symbol data with symbol } return true; }
bool ElfFile::load(ByteArray& data, bool sort) { fileData = data; memcpy(&fileHeader,&fileData[0],sizeof(Elf32_Ehdr)); symTab = NULL; strTab = NULL; // load segments for (int i = 0; i < fileHeader.e_phnum; i++) { int pos = fileHeader.e_phoff+i*fileHeader.e_phentsize; Elf32_Phdr sectionHeader; memcpy(§ionHeader,&fileData[pos],sizeof(Elf32_Phdr)); ByteArray segmentData = fileData.mid(sectionHeader.p_offset,sectionHeader.p_filesz); ElfSegment* segment = new ElfSegment(sectionHeader,segmentData); segments.push_back(segment); } // load sections and assign them to segments for (int i = 0; i < fileHeader.e_shnum; i++) { int pos = fileHeader.e_shoff+i*fileHeader.e_shentsize; Elf32_Shdr sectionHeader; memcpy(§ionHeader,&fileData[pos],sizeof(Elf32_Shdr)); ElfSection* section = new ElfSection(sectionHeader); sections.push_back(section); // check if the section belongs to a segment ElfSegment* owner = NULL; for (int k = 0; k < (int)segments.size(); k++) { if (segments[k]->isSectionPartOf(section)) { owner = segments[k]; break; } } if (owner != NULL) { owner->addSection(section); } else { if (section->getType() != SHT_NOBITS && section->getType() != SHT_NULL) { ByteArray data = fileData.mid(section->getOffset(),section->getSize()); section->setData(data); } switch (section->getType()) { case SHT_SYMTAB: symTab = section; break; case SHT_STRTAB: strTab = section; break; } segmentlessSections.push_back(section); } } determinePartOrder(); loadSectionNames(); if (sort) { std::sort(segmentlessSections.begin(),segmentlessSections.end(),compareSection); for (int i = 0; i < (int)segments.size(); i++) { segments[i]->sortSections(); } } return true; }
ElfRelHackCode_Section(Elf_Shdr &s, Elf &e) : ElfSection(s, NULL, NULL), parent(e) { std::string file(rundir); init = parent.getDynSection()->getSectionForType(DT_INIT); file += "/inject/"; switch (parent.getMachine()) { case EM_386: file += "x86"; break; case EM_X86_64: file += "x86_64"; break; case EM_ARM: file += "arm"; break; default: throw std::runtime_error("unsupported architecture"); } if (init == NULL) file += "-noinit"; file += ".o"; std::ifstream inject(file.c_str(), std::ios::in|std::ios::binary); elf = new Elf(inject); if (elf->getType() != ET_REL) throw std::runtime_error("object for injected code is not ET_REL"); if (elf->getMachine() != parent.getMachine()) throw std::runtime_error("architecture of object for injected code doesn't match"); ElfSymtab_Section *symtab = NULL; // Get all executable sections from the injected code object. // Most of the time, there will only be one for the init function, // but on e.g. x86, there is a separate section for // __i686.get_pc_thunk.$reg // Find the symbol table at the same time. for (ElfSection *section = elf->getSection(1); section != NULL; section = section->getNext()) { if ((section->getType() == SHT_PROGBITS) && (section->getFlags() & SHF_EXECINSTR)) { code.push_back(section); // We need to align this section depending on the greater // alignment required by code sections. if (shdr.sh_addralign < section->getAddrAlign()) shdr.sh_addralign = section->getAddrAlign(); } else if (section->getType() == SHT_SYMTAB) { symtab = (ElfSymtab_Section *) section; } } assert(code.size() != 0); if (symtab == NULL) throw std::runtime_error("Couldn't find a symbol table for the injected code"); // Find the init symbol entry_point = -1; int shndx = 0; for (std::vector<Elf_SymValue>::iterator sym = symtab->syms.begin(); sym != symtab->syms.end(); sym++) { if (strcmp(sym->name, "init") == 0) { entry_point = sym->value.getValue(); shndx = sym->value.getSection()->getIndex(); break; } } if (entry_point == -1) throw std::runtime_error("Couldn't find an 'init' symbol in the injected code"); // Adjust code sections offsets according to their size std::vector<ElfSection *>::iterator c = code.begin(); (*c)->getShdr().sh_addr = 0; for(ElfSection *last = *(c++); c != code.end(); c++) { unsigned int addr = last->getShdr().sh_addr + last->getSize(); if (addr & ((*c)->getAddrAlign() - 1)) addr = (addr | ((*c)->getAddrAlign() - 1)) + 1; (*c)->getShdr().sh_addr = addr; } shdr.sh_size = code.back()->getAddr() + code.back()->getSize(); data = new char[shdr.sh_size]; char *buf = data; for (c = code.begin(); c != code.end(); c++) { memcpy(buf, (*c)->getData(), (*c)->getSize()); buf += (*c)->getSize(); if ((*c)->getIndex() < shndx) entry_point += (*c)->getSize(); } name = elfhack_text; }
bool ElfRelocator::init(const std::wstring& inputName) { relocator = Arch->getElfRelocator(); if (relocator == NULL) { Logger::printError(Logger::Error,L"Object importing not supported for this architecture"); return false; } auto inputFiles = loadArArchive(inputName); if (inputFiles.size() == 0) { Logger::printError(Logger::Error,L"Could not load library"); return false; } for (ArFileEntry& entry: inputFiles) { ElfRelocatorFile file; ElfFile* elf = new ElfFile(); if (elf->load(entry.data,false) == false) { Logger::printError(Logger::Error,L"Could not load object file %s",entry.name); return false; } if (elf->getType() != 1) { Logger::printError(Logger::Error,L"Unexpected ELF type %d in object file %s",elf->getType(),entry.name); return false; } if (elf->getSegmentCount() != 0) { Logger::printError(Logger::Error,L"Unexpected segment count %d in object file %s",elf->getSegmentCount(),entry.name); return false; } // load all relevant sections of this file for (size_t s = 0; s < elf->getSegmentlessSectionCount(); s++) { ElfSection* sec = elf->getSegmentlessSection(s); if (!(sec->getFlags() & SHF_ALLOC)) continue; if (sec->getType() == SHT_PROGBITS || sec->getType() == SHT_NOBITS || sec->getType() == SHT_INIT_ARRAY) { ElfRelocatorSection sectionEntry; sectionEntry.section = sec; sectionEntry.index = s; sectionEntry.relSection = NULL; sectionEntry.label = NULL; // search relocation section for (size_t k = 0; k < elf->getSegmentlessSectionCount(); k++) { ElfSection* relSection = elf->getSegmentlessSection(k); if (relSection->getType() != SHT_REL) continue; if (relSection->getInfo() != s) continue; // got it sectionEntry.relSection = relSection; break; } // keep track of constructor sections if (sec->getName() == ".ctors" || sec->getName() == ".init_array") { ElfRelocatorCtor ctor; ctor.symbolName = Global.symbolTable.getUniqueLabelName(); ctor.size = sec->getSize(); sectionEntry.label = Global.symbolTable.getLabel(ctor.symbolName,-1,-1); sectionEntry.label->setDefined(true); ctors.push_back(ctor); } file.sections.push_back(sectionEntry); } } // init exportable symbols for (int i = 0; i < elf->getSymbolCount(); i++) { Elf32_Sym symbol; bool found = elf->getSymbol(symbol, i); if (ELF32_ST_BIND(symbol.st_info) == STB_GLOBAL && symbol.st_shndx != 0) { ElfRelocatorSymbol symEntry; symEntry.type = ELF32_ST_TYPE(symbol.st_info); symEntry.name = convertUtf8ToWString(elf->getStrTableString(symbol.st_name)); symEntry.relativeAddress = symbol.st_value; symEntry.section = symbol.st_shndx; symEntry.size = symbol.st_size; symEntry.label = NULL; file.symbols.push_back(symEntry); } } file.elf = elf; file.name = entry.name; files.push_back(file); } return true; }
bool ElfRelocator::relocateFile(ElfRelocatorFile& file, u64& relocationAddress) { ElfFile* elf = file.elf; u64 start = relocationAddress; // calculate address for each section std::map<u64,u64> relocationOffsets; for (ElfRelocatorSection& entry: file.sections) { ElfSection* section = entry.section; size_t index = entry.index; int size = section->getSize(); while (relocationAddress % section->getAlignment()) relocationAddress++; if (entry.label != NULL) entry.label->setValue(relocationAddress); relocationOffsets[index] = relocationAddress; relocationAddress += size; } size_t dataStart = outputData.size(); outputData.reserveBytes((size_t)(relocationAddress-start)); // load sections bool error = false; for (ElfRelocatorSection& entry: file.sections) { ElfSection* section = entry.section; size_t index = entry.index; if (section->getType() == SHT_NOBITS) { // reserveBytes initialized the data to 0 already continue; } ByteArray sectionData = section->getData(); // relocate if necessary ElfSection* relSection = entry.relSection; if (relSection != NULL) { for (unsigned int relOffset = 0; relOffset < relSection->getSize(); relOffset += sizeof(Elf32_Rel)) { Elf32_Rel rel; loadRelocation(rel, relSection->getData(), relOffset, elf->isBigEndian()); int pos = rel.r_offset; int symNum = rel.getSymbolNum(); if (symNum <= 0) { Logger::queueError(Logger::Warning,L"Invalid symbol num %06X",symNum); error = true; continue; } Elf32_Sym sym; auto found = elf->getSymbol(sym, symNum); int symSection = sym.st_shndx; RelocationData relData; relData.opcode = sectionData.getDoubleWord(pos, elf->isBigEndian()); relData.opcodeOffset = pos+relocationOffsets[index]; relocator->setSymbolAddress(relData,sym.st_value,sym.st_info & 0xF); // externs? if (relData.targetSymbolType == STT_NOTYPE && sym.st_shndx == 0) { std::wstring symName = toWLowercase(elf->getStrTableString(sym.st_name)); Label* label = Global.symbolTable.getLabel(symName,-1,-1); if (label == NULL) { Logger::queueError(Logger::Error,L"Invalid external symbol %s",symName); error = true; continue; } if (label->isDefined() == false) { Logger::queueError(Logger::Error,L"Undefined external symbol %s in file %s",symName,file.name); error = true; continue; } relData.relocationBase = (unsigned int) label->getValue(); relData.targetSymbolType = label->isData() ? STT_OBJECT : STT_FUNC; relData.targetSymbolInfo = label->getInfo(); } else { relData.relocationBase = relocationOffsets[symSection]+relData.symbolAddress; } if (relocator->relocateOpcode(rel.getType(),relData) == false) { Logger::queueError(Logger::Error,relData.errorMessage); error = true; continue; } sectionData.replaceDoubleWord(pos,relData.opcode, elf->isBigEndian()); } } size_t arrayStart = (size_t) (dataStart+relocationOffsets[index]-start); memcpy(outputData.data(arrayStart),sectionData.data(),sectionData.size()); } // now update symbols for (ElfRelocatorSymbol& sym: file.symbols) { u64 oldAddress = sym.relocatedAddress; switch (sym.section) { case SHN_ABS: // address does not change sym.relocatedAddress = sym.relativeAddress; break; case SHN_COMMON: // needs to be allocated. relativeAddress gives alignment constraint { u64 start = relocationAddress; while (relocationAddress % sym.relativeAddress) relocationAddress++; sym.relocatedAddress = relocationAddress; relocationAddress += sym.size; outputData.reserveBytes((size_t)(relocationAddress-start)); } break; default: // normal relocated symbol sym.relocatedAddress = sym.relativeAddress+relocationOffsets[sym.section]; break; } if (sym.label != NULL) sym.label->setValue(sym.relocatedAddress); if (oldAddress != sym.relocatedAddress) dataChanged = true; } return !error; }