void ElfFile::loadProgramHeader(Elf32_Phdr& header, ByteArray& data, int pos) { bool bigEndian = isBigEndian(); header.p_type = data.getDoubleWord(pos + 0x00, bigEndian); header.p_offset = data.getDoubleWord(pos + 0x04, bigEndian); header.p_vaddr = data.getDoubleWord(pos + 0x08, bigEndian); header.p_paddr = data.getDoubleWord(pos + 0x0C, bigEndian); header.p_filesz = data.getDoubleWord(pos + 0x10, bigEndian); header.p_memsz = data.getDoubleWord(pos + 0x14, bigEndian); header.p_flags = data.getDoubleWord(pos + 0x18, bigEndian); header.p_align = data.getDoubleWord(pos + 0x1C, bigEndian); }
void ElfFile::loadSectionHeader(Elf32_Shdr& header, ByteArray& data, int pos) { bool bigEndian = isBigEndian(); header.sh_name = data.getDoubleWord(pos + 0x00, bigEndian); header.sh_type = data.getDoubleWord(pos + 0x04, bigEndian); header.sh_flags = data.getDoubleWord(pos + 0x08, bigEndian); header.sh_addr = data.getDoubleWord(pos + 0x0C, bigEndian); header.sh_offset = data.getDoubleWord(pos + 0x10, bigEndian); header.sh_size = data.getDoubleWord(pos + 0x14, bigEndian); header.sh_link = data.getDoubleWord(pos + 0x18, bigEndian); header.sh_info = data.getDoubleWord(pos + 0x1C, bigEndian); header.sh_addralign = data.getDoubleWord(pos + 0x20, bigEndian); header.sh_entsize = data.getDoubleWord(pos + 0x24, bigEndian); }
std::vector<PsxLibEntry> loadPsxLibrary(const std::wstring& inputName) { ByteArray input = ByteArray::fromFile(inputName); std::vector<PsxLibEntry> result; if (input.size() == 0) return result; if (memcmp(input.data(),psxObjectFileMagicNum,sizeof(psxObjectFileMagicNum)) == 0) { PsxLibEntry entry; entry.name = getFileNameFromPath(inputName); entry.data = input; result.push_back(entry); return result; } if (memcmp(input.data(),"LIB\x01",4) != 0) return result; int pos = 4; while (pos < input.size()) { PsxLibEntry entry; for (int i = 0; i < 16 && input[pos+i] != ' '; i++) { entry.name += input[pos+i]; } int size = input.getDoubleWord(pos+16); int skip = 20; while (input[pos+skip] != 0) { skip += input[pos+skip++]; } skip++; entry.data = input.mid(pos+skip,size-skip); pos += size; result.push_back(entry); } return result; }
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; }
void ElfRelocator::loadRelocation(Elf32_Rel& rel, ByteArray& data, int offset, bool bigEndian) { rel.r_offset = data.getDoubleWord(offset + 0x00, bigEndian); rel.r_info = data.getDoubleWord(offset + 0x04, bigEndian); }
bool PsxRelocator::parseObject(ByteArray data, PsxRelocatorFile& dest) { if (memcmp(data.data(),psxObjectFileMagicNum,sizeof(psxObjectFileMagicNum)) != 0) return false; int pos = 6; std::vector<PsxSegment>& segments = dest.segments; std::vector<PsxSymbol>& syms = dest.symbols; int activeSegment = -1; int lastSegmentPartStart = -1; while (pos < data.size()) { switch (data[pos]) { case 0x10: // segment definition { PsxSegment seg; seg.id = data.getDoubleWord(pos+1); segments.push_back(seg); pos += 5; if (data[pos] != 8) return false; int nameLen = data[pos+1]; std::wstring& name = segments[segments.size()-1].name; pos += 1 + loadString(data,pos+1,name); } break; case 0x14: // group? pos += data[pos+4]+5; break; case 0x1C: // source file name pos += data[pos+3]+4; break; case 0x06: // set segment id { int id = data.getWord(pos+1); pos += 3; int num = -1; for (size_t i = 0; i < segments.size(); i++) { if (segments[i].id == id) { num = i; break; } } activeSegment = num; } break; case 0x02: // append to data segment { int size = data.getWord(pos+1); pos += 3; ByteArray d = data.mid(pos,size); pos += size; lastSegmentPartStart = segments[activeSegment].data.size(); segments[activeSegment].data.append(d); } break; case 0x08: // append zeroes data segment { int size = data.getWord(pos+1); pos += 3; ByteArray d; d.reserveBytes(size); segments[activeSegment].data.append(d); } break; case 0x0A: // relocation data { int type = data[pos+1]; pos += 2; PsxRelocation rel; rel.relativeOffset = 0; rel.filePos = pos-2; switch (type) { case 0x10: // 32 bit word rel.type = PsxRelocationType::WordLiteral; rel.segmentOffset = data.getWord(pos); pos += 2; break; case 0x4A: // jal rel.type = PsxRelocationType::FunctionCall; rel.segmentOffset = data.getWord(pos); pos += 2; break; case 0x52: // upper immerdiate rel.type = PsxRelocationType::UpperImmediate; rel.segmentOffset = data.getWord(pos); pos += 2; break; case 0x54: // lower immediate (add) rel.type = PsxRelocationType::LowerImmediate; rel.segmentOffset = data.getWord(pos); pos += 2; break; default: return false; } rel.segmentOffset += lastSegmentPartStart; checkothertype: int otherType = data[pos++]; switch (otherType) { case 0x02: // reference to symbol with id num rel.refType = PsxRelocationRefType::SymblId; rel.referenceId = data.getWord(pos); pos += 2; break; case 0x2C: // ref to other segment? rel.refType = PsxRelocationRefType::SegmentOffset; switch (data[pos++]) { case 0x00: rel.relativeOffset = data.getDoubleWord(pos); pos += 4; goto checkothertype; case 0x04: rel.referenceId = data.getWord(pos); // segment id pos += 2; if (data[pos++] != 0x00) { return false; } rel.referencePos = data.getDoubleWord(pos); pos += 4; break; default: return false; } break; case 0x2E: // negative ref? rel.refType = PsxRelocationRefType::SegmentOffset; switch (data[pos++]) { case 0x00: rel.relativeOffset = -data.getDoubleWord(pos); pos += 4; goto checkothertype; default: return false; } break; default: return false; } segments[activeSegment].relocations.push_back(rel); } break; case 0x12: // internal symbol { PsxSymbol sym; sym.type = PsxSymbolType::Internal; sym.segment = data.getWord(pos+1); sym.offset = data.getDoubleWord(pos+3); pos += 7 + loadString(data,pos+7,sym.name); syms.push_back(sym); } break; case 0x0E: // external symbol { PsxSymbol sym; sym.type = PsxSymbolType::External; sym.id = data.getWord(pos+1); pos += 3 + loadString(data,pos+3,sym.name); syms.push_back(sym); } break; case 0x30: // bss symbol? { PsxSymbol sym; sym.type = PsxSymbolType::BSS; sym.id = data.getWord(pos+1); sym.segment = data.getWord(pos+3); sym.size = data.getDoubleWord(pos+5); pos += 9 + loadString(data,pos+9,sym.name); syms.push_back(sym); } break; case 0x0C: // internal with id { PsxSymbol sym; sym.type = PsxSymbolType::InternalID; sym.id = data.getWord(pos+1); sym.segment = data.getWord(pos+3); sym.offset = data.getDoubleWord(pos+5); pos += 9 + loadString(data,pos+9,sym.name); syms.push_back(sym); } break; case 0x4A: // function { PsxSymbol sym; sym.type = PsxSymbolType::Function; sym.segment = data.getWord(pos+1); sym.offset = data.getDoubleWord(pos+3); pos += 0x1D + loadString(data,pos+0x1D,sym.name); syms.push_back(sym); } break; case 0x4C: // function size pos += 11; break; case 0x3C: // ?? pos += 3; break; case 0x00: // ?? pos++; break; case 0x32: // ?? pos += 3; break; case 0x3A: // ?? pos += 9; break; default: return false; } } return true; }
bool PsxRelocator::relocateFile(PsxRelocatorFile& file, int& relocationAddress) { std::map<int,int> relocationOffsets; std::map<int,int> symbolOffsets; int start = relocationAddress; // assign addresses to segments for (PsxSegment& seg: file.segments) { int index = seg.id; int size = seg.data.size(); relocationOffsets[index] = relocationAddress; relocationAddress += size; while (relocationAddress % 4) relocationAddress++; } // parse/add/relocate symbols bool error = false; for (PsxSymbol& sym: file.symbols) { int pos; switch (sym.type) { case PsxSymbolType::Internal: case PsxSymbolType::Function: sym.label->setValue(relocationOffsets[sym.segment]+sym.offset); sym.label->setDefined(true); break; case PsxSymbolType::InternalID: pos = relocationOffsets[sym.segment]+sym.offset; sym.label->setValue(pos); sym.label->setDefined(true); symbolOffsets[sym.id] = pos; break; case PsxSymbolType::BSS: sym.label->setValue(relocationAddress); sym.label->setDefined(true); symbolOffsets[sym.id] = relocationAddress; relocationAddress += sym.size; while (relocationAddress % 4) relocationAddress++; break; case PsxSymbolType::External: if (sym.label->isDefined() == false) { Logger::queueError(Logger::Error,L"Undefined external symbol %s in file %s",sym.name,file.name); error = true; continue; } symbolOffsets[sym.id] = sym.label->getValue(); break; } } if (error) return false; int dataStart = outputData.size(); outputData.reserveBytes(relocationAddress-start); // load code and data for (PsxSegment& seg: file.segments) { // relocate ByteArray sectionData = seg.data; for (PsxRelocation& rel: seg.relocations) { RelocationData relData; int pos = rel.segmentOffset; relData.opcode = sectionData.getDoubleWord(pos); switch (rel.refType) { case PsxRelocationRefType::SymblId: relData.relocationBase = symbolOffsets[rel.referenceId]+rel.relativeOffset; break; case PsxRelocationRefType::SegmentOffset: relData.relocationBase = relocationOffsets[rel.referenceId] + rel.referencePos+rel.relativeOffset; break; } switch (rel.type) { case PsxRelocationType::WordLiteral: reloc->relocateOpcode(R_MIPS_32,relData); break; case PsxRelocationType::UpperImmediate: reloc->relocateOpcode(R_MIPS_HI16,relData); break; case PsxRelocationType::LowerImmediate: reloc->relocateOpcode(R_MIPS_LO16,relData); break; case PsxRelocationType::FunctionCall: reloc->relocateOpcode(R_MIPS_26,relData); break; } sectionData.replaceDoubleWord(pos,relData.opcode); } int arrayStart = dataStart+relocationOffsets[seg.id]-start; memcpy(outputData.data(arrayStart),sectionData.data(),sectionData.size()); } return true; }