kolibrios/programs/develop/objconv/mac2elf.cpp
turbocat 60a4b1c9ef Added objconv port.
git-svn-id: svn://kolibrios.org@9683 a494cfbc-eb01-0410-851d-a64ba20cac60
2022-02-06 11:09:00 +00:00

1268 lines
54 KiB
C++

/**************************** mac2elf.cpp *********************************
* Author: Agner Fog
* Date created: 2008-05-15
* Last modified: 2009-05-19
* Project: objconv
* Module: mac2elf.cpp
* Description:
* Module for converting Mach-O file to ELF file
*
* Copyright 2008 GNU General Public License http://www.gnu.org/licenses
*****************************************************************************/
#include "stdafx.h"
template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::CMAC2ELF () {
// Constructor
memset(this, 0, sizeof(*this)); // Reset everything
}
template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::Convert() {
// Do the conversion
// Some compilers require this-> for accessing members of template base class,
// according to the so-called two-phase lookup rule.
NumSectionsNew = 5; // Number of sections generated so far
// Allocate variable size buffers
MaxSectionsNew = NumSectionsNew + 2 * this->NumSections + 2;// Max number of sections needed
NewSections.SetNum(MaxSectionsNew+1); // Allocate buffers for each section
NewSections.SetZero(); // Initialize
NewSectionHeaders.SetNum(MaxSectionsNew+1); // Allocate array for temporary section headers
NewSectionHeaders.SetZero(); // Initialize
NewSectIndex.SetNum(this->NumSections+1); // Array for translating old section index to new section index
NewSectIndex.SetZero(); // Initialize
SectionSymbols.SetNum(this->MaxSectionsNew+1); // Array of new symbol indices for sections
SectionSymbols.SetZero(); // Initialize
NewSymbolIndex.SetNum(this->SymTabNumber); // Array of new symbol indices
NewSymbolIndex.SetZero(); // Initialize
// Call the subfunctions
ToFile.SetFileType(FILETYPE_ELF); // Set type of to file
MakeSegments(); // Make segment headers and code/data segments
MakeSymbolTable(); // Symbol table and string tables
MakeRelocationTables(this->FileHeader); // Make relocation tables
MakeImportTables(); // Fill import tables
MakeGOT(); // Make fake Global Offset Table
MakeBinaryFile(); // Putting sections together
*this << ToFile; // Take over new file buffer
}
template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::MakeSegments() {
// Convert subfunction: Make segment headers and code/data segments
TELF_SectionHeader NewSecHeader; // New section header
uint32 oldsec; // Section index in old file
uint32 newsec; // Section index in new file
uint32 SecNameIndex; // Section name index into shstrtab
char const * SecName; // Name of new section
const int MAXSECTIONNAMELENGTH = 256;
char RelocationSectionName[MAXSECTIONNAMELENGTH];
const int WordSize = sizeof(MInt) * 8;
// Special segment names
static const char * SpecialSegmentNames[] = {
"Null", ".symtab", ".shstrtab", ".strtab", ".stabstr"
};
// Indexes to these are:
symtab = 1; // Symbol table section number
shstrtab = 2; // Section name string table section number
strtab = 3; // Object name string table section number
stabstr = 4; // Debug string table section number
// Number of special segments = number of names in SpecialSegmentNames:
const uint32 NumSpecialSegments = sizeof(SpecialSegmentNames)/sizeof(SpecialSegmentNames[0]);
// Make first section header string table entry empty
NewSections[shstrtab].PushString("");
// Loop through special sections, except the first Null section:
for (newsec = 0; newsec < NumSpecialSegments; newsec++) {
// Put data into new section header:
// Initialize to zero
memset(&NewSecHeader, 0, sizeof(NewSecHeader));
if (newsec > 0) {
// Put name into section header string table
SecName = SpecialSegmentNames[newsec];
SecNameIndex = NewSections[shstrtab].PushString(SecName);
// Put name into new section header
NewSecHeader.sh_name = SecNameIndex;
}
// Put section header into temporary buffer
NewSectionHeaders[newsec] = NewSecHeader;
}
// Put type, flags, etc. into special segments:
NewSectionHeaders[symtab] .sh_type = SHT_SYMTAB;
NewSectionHeaders[symtab] .sh_entsize = sizeof(TELF_Symbol);
NewSectionHeaders[symtab] .sh_link = strtab;
NewSectionHeaders[shstrtab].sh_type = SHT_STRTAB;
NewSectionHeaders[shstrtab].sh_flags = SHF_STRINGS;
NewSectionHeaders[shstrtab].sh_addralign = 1;
NewSectionHeaders[strtab] .sh_type = SHT_STRTAB;
NewSectionHeaders[strtab] .sh_flags = SHF_STRINGS;
NewSectionHeaders[strtab] .sh_addralign = 1;
NewSectionHeaders[stabstr] .sh_type = SHT_STRTAB;
NewSectionHeaders[stabstr] .sh_flags = SHF_STRINGS;
NewSectionHeaders[stabstr] .sh_addralign = 1;
if (newsec != NumSectionsNew) {
// Check my program for internal consistency
// If you get this error then change the value of NumSectionsNew in
// the constructor to equal the number of entries in
// SpecialSegmentNames, including the Null segment
err.submit(9000);
}
// Find sections in old file
uint32 icmd; // Current load command
uint32 command; // Load command
uint32 cmdsize = 0; // Command size
// Pointer to current position in old file
uint8 * currentp = (uint8*)(this->Buf() + sizeof(TMAC_header));
// Loop through file commands
for (icmd = 1; icmd <= this->FileHeader.ncmds; icmd++, currentp += cmdsize) {
command = ((MAC_load_command*)currentp) -> cmd;
cmdsize = ((MAC_load_command*)currentp) -> cmdsize;
if (command == MAC_LC_SEGMENT || command == MAC_LC_SEGMENT_64) {
// This is the segment command (there should be only one)
if ((command == MAC_LC_SEGMENT) ^ (WordSize == 32)) {
// 32-bit segment in 64-bit file or vice versa
err.submit(2320); return;
}
if (cmdsize < sizeof(TMAC_segment_command)) {
// Zero cmdsize or too small
err.submit(2321); return;
}
// Point to segment command
TMAC_segment_command * sh = (TMAC_segment_command*)currentp;
if (stricmp(sh->segname, MAC_SEG_OBJC) == 0) {
// objective-C runtime segment
err.submit(2021); continue;
}
// Find first section header
TMAC_section * sectp = (TMAC_section*)(currentp + sizeof(TMAC_segment_command));
// Loop through section headers
for (oldsec = 1; oldsec <= this->NumSections; oldsec++, sectp++) {
// Get section name
SecName = sectp->sectname;
// Check for special section names
if (stricmp(SecName,"__eh_frame") == 0) {
// This is an exception handler section
if (cmd.ExeptionInfo == CMDL_EXCEPTION_STRIP) {
// Remove exception handler section
cmd.CountExceptionRemoved();
continue;
}
else if (cmd.InputType != cmd.OutputType) {
err.submit(1030); // Warn that exception information is incompatible
}
}
if (sectp->flags & MAC_S_ATTR_DEBUG) {
// This section has debug information
if (cmd.DebugInfo == CMDL_DEBUG_STRIP) {
// Remove debug info
cmd.CountDebugRemoved();
continue;
}
else if (cmd.InputType != cmd.OutputType) {
err.submit(1029); // Warn that debug information is incompatible
}
}
// Store section index in index translation table
NewSectIndex[oldsec] = newsec;
// Store section data
if (sectp->size > 0 && !((sectp->flags & MAC_SECTION_TYPE) == MAC_S_ZEROFILL || (sectp->flags & MAC_SECTION_TYPE)==MAC_S_GB_ZEROFILL)) {
NewSections[newsec].Push(this->Buf()+sectp->offset, uint32(sectp->size));
}
// Put data into new section header:
// Initialize to zero
memset(&NewSecHeader, 0, sizeof(NewSecHeader));
uint32 type = sectp->flags & MAC_SECTION_TYPE;
uint32 attributes = sectp->flags & MAC_SECTION_ATTRIBUTES;
// Section type
if (type == MAC_S_ZEROFILL || type == MAC_S_GB_ZEROFILL) {
// BSS section
NewSecHeader.sh_type = SHT_NOBITS; // BSS
}
else {
// Normal code or data section
NewSecHeader.sh_type = SHT_PROGBITS; // Program code or data
}
// Section flags
NewSecHeader.sh_flags |= SHF_ALLOC; // Occupies memory during execution
if (attributes & (MAC_S_ATTR_SOME_INSTRUCTIONS | MAC_S_ATTR_PURE_INSTRUCTIONS)) {
// Executable
NewSecHeader.sh_flags |= SHF_EXECINSTR;
}
else {
switch (type) {
case MAC_S_CSTRING_LITERALS:
case MAC_S_4BYTE_LITERALS:
case MAC_S_8BYTE_LITERALS:
case MAC_S_16BYTE_LITERALS:
case MAC_S_LITERAL_POINTERS:
// not writeable
break;
default:
// writeable
NewSecHeader.sh_flags |= SHF_WRITE;
break;
}
}
// Check for special sections
if (strcmp(SecName, MAC_CONSTRUCTOR_NAME) == 0) {
// Constructors segment
SecName = ELF_CONSTRUCTOR_NAME;
NewSecHeader.sh_flags = SHF_WRITE | SHF_ALLOC;
}
// Put name into section header string table
SecNameIndex = NewSections[shstrtab].PushString(SecName);
// Put name into new section header
NewSecHeader.sh_name = SecNameIndex;
// Section virtual memory address
NewSecHeader.sh_addr = sectp->addr;
// Section size in memory
NewSecHeader.sh_size = sectp->size;
// Section alignment
NewSecHeader.sh_addralign = uint32(1 << sectp->align);
// Put section header into temporary buffer
NewSectionHeaders[newsec] = NewSecHeader;
// Increment section number
newsec++;
// Check if section is import table
int SectionType = sectp->flags & MAC_SECTION_TYPE;
int IsImportTable = SectionType >= MAC_S_NON_LAZY_SYMBOL_POINTERS && SectionType <= MAC_S_SYMBOL_STUBS;
if (sectp->nreloc > 0 || IsImportTable) {
// Source section has relocations.
// Make a relocation section in destination file
// Put data into relocation section header:
// Initialize to zero
memset(&NewSecHeader, 0, sizeof(NewSecHeader));
// Name for relocation section = ".rel" or ".rela" + name of section
if (WordSize == 32) {
strcpy(RelocationSectionName, ".rel");
}
else {
strcpy(RelocationSectionName, ".rela");
}
strncat(RelocationSectionName, SecName, MAXSECTIONNAMELENGTH-5);
RelocationSectionName[MAXSECTIONNAMELENGTH-1] = 0;
// Put name into section header string table
uint32 SecNameIndex = NewSections[shstrtab].PushString(RelocationSectionName);
// Put name into new section header
NewSecHeader.sh_name = SecNameIndex;
// Section type
NewSecHeader.sh_type = (WordSize == 32) ? SHT_REL : SHT_RELA; // Relocation section
// Entry size
NewSecHeader.sh_entsize = (WordSize == 32) ? sizeof(Elf32_Rel) : sizeof(Elf64_Rela); // Relocation section
// Section alignment
NewSecHeader.sh_addralign = WordSize / 8;
// Link to the section it relocates for
NewSecHeader.sh_info = newsec - 1;
// Put section header into temporary buffer
NewSectionHeaders[newsec] = NewSecHeader;
// Increment section number
newsec++;
// Check if there are any GOT relocations
// Pointer to old relocation entry
if (sectp->reloff >= this->GetDataSize()) {err.submit(2035); break;}
MAC_relocation_info * relp = (MAC_relocation_info*)(this->Buf() + sectp->reloff);
// Loop through old relocations
for (uint32 oldr = 1; oldr <= sectp->nreloc; oldr++, relp++) {
uint32 RType = relp->r_type; // relocation type
// No scattered relocation in 64-bit mode. GOT only in 64-bit mode
if (WordSize == 64 && (RType == MAC64_RELOC_GOT_LOAD || RType == MAC64_RELOC_GOT)) {
HasGOT++;
}
}
}
}
// Check if GOT needed
if (HasGOT && WordSize == 64) {
// Make a fake Global Offset Table
FakeGOTSection = newsec;
// Put name and data into section header
memset(&NewSecHeader, 0, sizeof(NewSecHeader));
SecNameIndex = NewSections[shstrtab].PushString("_fakeGOT");
NewSecHeader.sh_name = SecNameIndex;
NewSecHeader.sh_type = SHT_PROGBITS; // Type
NewSecHeader.sh_flags = SHF_ALLOC; // Flags
NewSecHeader.sh_addralign = 8; // Alignment
// Put section header into temporary buffer
NewSectionHeaders[newsec++] = NewSecHeader;
// Make relocation section for fake GOT
memset(&NewSecHeader, 0, sizeof(NewSecHeader));
// Put name and data into section header
SecNameIndex = NewSections[shstrtab].PushString("_rela.fakeGOT");
NewSecHeader.sh_name = SecNameIndex;
NewSecHeader.sh_type = SHT_RELA; // Type
NewSecHeader.sh_flags = 0; // Flags
NewSecHeader.sh_addralign = 8; // Alignment
NewSecHeader.sh_entsize = sizeof(Elf64_Rela); // Entry size
NewSecHeader.sh_info = newsec - 1; // Link to the section it relocates for
NewSecHeader.sh_link = symtab; // Link to symbol table
// Put section header into temporary buffer
NewSectionHeaders[newsec++] = NewSecHeader;
}
// Number of sections generated
NumSectionsNew = newsec;
}
}
}
template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::MakeSymbolTable() {
// Convert subfunction: Make symbol table and string tables
uint32 isym; // current old symbol table entry
uint32 OldSectionIndex; // Index into old section table. 1-based
uint32 NewSectionIndex; // Index into new section table. 0-based
const char * name1; // Name of symbol
TELF_Symbol sym; // Temporary symbol table record
uint32 DebugRemoved = 0;// Debug symbols removed
// pointer to old string table
char * oldstringtab = (char*)(this->Buf() + this->StringTabOffset);
// pointer to old symbol table
TMAC_nlist * symp0, *symp;
symp0 = (TMAC_nlist*)(this->Buf() + this->SymTabOffset);
// Check within range
if (this->SymTabOffset + this->SymTabNumber * sizeof(TMAC_nlist) > this->DataSize) {
err.submit(2040); return;
}
// Make the first symbol record empty
NewSections[symtab].Push(0, sizeof(TELF_Symbol));
// Make first string table entries empty
NewSections[strtab] .PushString("");
NewSections[stabstr].PushString("");
// Make symbol records for the start of each section in case they are needed
// by section-relative relocations (r_extern = 0 in MAC_relocation_info)
for (uint32 sec = 1; sec < NumSectionsNew; sec++) {
uint32 type = NewSectionHeaders[sec].sh_type;
if (type == SHT_PROGBITS || type == SHT_NOBITS) {
// Make unnamed symbol table entry for this section
memset(&sym, 0, sizeof(sym));
sym.st_shndx = sec;
sym.st_type = STT_SECTION;
// Put record into new symbol table
NewSections[symtab].Push(&sym, sizeof(sym));
// Insert into section symbol translation table
SectionSymbols[sec] = NewSections[symtab].GetLastIndex();
}
}
// Loop through old symbol table. Local symbols first, global symbols last
for (isym = 0, symp = symp0; isym < this->SymTabNumber; isym++, symp++) {
if ((symp->n_type & MAC_N_STAB) && (cmd.DebugInfo & CMDL_DEBUG_STRIP)) {
// Debug symbol should be removed
DebugRemoved++; continue;
}
// Reset destination entry
memset(&sym, 0, sizeof(sym));
// Get binding
if (isym < this->iextdefsym) {
// Local
sym.st_bind = STB_LOCAL;
}
else if (symp->n_desc & (MAC_N_WEAK_REF | MAC_N_WEAK_DEF)) {
// Weak public or weak external
sym.st_bind = STB_WEAK;
}
else {
// Global (public or external)
sym.st_bind = STB_GLOBAL;
}
// Symbol name
if (symp->n_strx < this->StringTabSize) {
name1 = oldstringtab + symp->n_strx;
}
else {
err.submit(2112); break;
}
// Symbol value
sym.st_value = symp->n_value;
// Get section
OldSectionIndex = symp->n_sect;
if (OldSectionIndex > this->NumSections) {
err.submit(2016); break;
}
// Get new section index
NewSectionIndex = 0;
if (OldSectionIndex > 0) {
// Get new section index from translation table
NewSectionIndex = NewSectIndex[OldSectionIndex];
// Change symbol address to section-relative
// (Also in 64-bit mode)
sym.st_value -= NewSectionHeaders[NewSectionIndex].sh_addr;
}
sym.st_shndx = (uint16)NewSectionIndex;
if (OldSectionIndex && !NewSectionIndex) {
// Section has been removed. Remove symbol also
continue;
}
// Check symbol type
int32 RefType = symp->n_desc & MAC_REF_TYPE;
if (RefType == MAC_REF_FLAG_UNDEFINED_LAZY || RefType == MAC_REF_FLAG_PRIVATE_UNDEFINED_LAZY) {
// Lazy binding
err.submit(1061, name1);
}
else if ((symp->n_type & MAC_N_TYPE) == MAC_N_ABS) {
// Absolute symbol
sym.st_type = STT_NOTYPE;
sym.st_shndx = (uint16)SHN_ABS;
if (sym.st_bind == STB_LOCAL) {
continue; // Remove absolute local symbol (not allowed in COFF)
}
}
else if (sym.st_shndx == 0) { // added by Vladimir 'phcoder' Serbinenko:
// This is an external
sym.st_type = STT_NOTYPE;
}
else {
// This is a data definition record
if (NewSectionHeaders[NewSectionIndex].sh_flags & SHF_EXECINSTR) {
// Code section, assume this is a function
sym.st_type = STT_FUNC;
}
else {
// This is a data object
sym.st_type = STT_OBJECT;
}
if (sym.st_bind == STB_GLOBAL && NewSectionIndex) {
// Symbol is public
// The size is not specified in Mac record,
// so we may give it an arbitrary size:
sym.st_size = 4;
}
}
// Put symbol name into string table
if (name1 && *name1) {
sym.st_name = NewSections[strtab].PushString(name1);
}
// Put record into new symbol table
NewSections[symtab].Push(&sym, sizeof(sym));
// Insert into symbol translation table
NewSymbolIndex[isym] = NewSections[symtab].GetLastIndex();
// Make index to first global symbol
if (isym >= this->iextdefsym && !NewSectionHeaders[symtab].sh_info) {
// This is the first global symbol
NewSectionHeaders[symtab].sh_info = NewSymbolIndex[isym];
}
}
}
template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::MakeRelocationTables(MAC_header_32&) {
// Convert subfunction: Relocation tables, 32-bit version
uint32 oldsec; // Relocated section number in source file
uint32 newsec; // Relocated section number in destination file
uint32 newsecr; // Relocation table section number in destination file
MInt SectAddr; // Section address of relocation source
MInt SourceAddress; // Address of relocation source including section address
MInt TargetAddress; // Target address including section address
uint32 TargetSection; // New section index of relocation target
uint32 TargetOffset; // Section-relative offset of relocation target
uint32 RefAddress; // Reference point address including section address
uint32 RefSection; // New section index of reference point
uint32 RefOffset; // Section-relative offset of reference point
int32 * inlinep = 0; // Pointer to inline addend
//const int WordSize = sizeof(MInt) * 8;
TELF_SectionHeader * NewRelTableSecHeader; // Section header for new relocation table
// Number of symbols
uint32 NumSymbols = NewSections[symtab].GetNumEntries();
// New symbol table
//Elf32_Sym * NewSymbolTable = (Elf32_Sym *)(NewSections[symtab].Buf());
// Find first section header
MAC_section_32 * sectp = (MAC_section_32*)(this->Buf() + this->SectionHeaderOffset);
// Loop through section headers
for (oldsec = 1; oldsec <= this->NumSections; oldsec++, sectp++) {
if (sectp->nreloc > 0) {
// Source section has relocations
// New section index
newsec = NewSectIndex[oldsec];
// Check that section has not been deleted
if (newsec > 0) {
// Section address
SectAddr = NewSectionHeaders[newsec].sh_addr;
// Finc new relocation table section
newsecr = newsec + 1;
if (newsecr >= NewSectionHeaders.GetNumEntries()) {
err.submit(9000); return;}
// New relocation table section header
NewRelTableSecHeader = &NewSectionHeaders[newsecr];
// Check that we have allocated this as a relocation section
if (NewRelTableSecHeader->sh_info != newsec) {
err.submit(9000); return;
}
// Insert header info
NewRelTableSecHeader->sh_type = SHT_REL;
NewRelTableSecHeader->sh_flags = 0;
NewRelTableSecHeader->sh_addralign = 4;
NewRelTableSecHeader->sh_link = symtab; // Point to symbol table
NewRelTableSecHeader->sh_info = newsec; // Point to relocated section
NewRelTableSecHeader->sh_entsize = sizeof(Elf32_Rel); // Entry size:
// Pointer to old relocation entry
if (sectp->reloff >= this->GetDataSize()) {err.submit(2035); break;}
MAC_relocation_info * relp = (MAC_relocation_info*)(this->Buf() + sectp->reloff);
// Loop through old relocations
for (uint32 oldr = 1; oldr <= sectp->nreloc; oldr++, relp++) {
// Make new relocation entry and set to zero
Elf32_Rel NewRelocEntry;
memset(&NewRelocEntry, 0, sizeof(NewRelocEntry));
if (relp->r_address & R_SCATTERED) {
// scattered relocation into
MAC_scattered_relocation_info * scatp = (MAC_scattered_relocation_info*)relp;
// Address of source
NewRelocEntry.r_offset = scatp->r_address;
if (NewRelocEntry.r_offset >= NewSections[newsec].GetDataSize()) {
err.submit(2035); continue; // Out of range
}
// Pointer to inline addend
inlinep = (int32*)(NewSections[newsec].Buf() + NewRelocEntry.r_offset);
if (scatp->r_pcrel) {
// Self-relative scattered
if (scatp->r_type != MAC32_RELOC_VANILLA) {
err.submit(2030, scatp->r_type); continue; // Unexpected type
}
// Scattered, self-relative, vanilla
// Note: I have never seen this relocation method, so I have not
// been able to test it. I don't know for sure how it works and
// the documentation is poor.
SourceAddress = SectAddr + scatp->r_address;
// Target address
TargetAddress = SourceAddress + *inlinep;
TranslateAddress(TargetAddress, TargetSection, TargetOffset);
if (TargetSection == 0) {err.submit(2031); continue;} // not found
NewRelocEntry.r_sym = SectionSymbols[TargetSection];
if (NewRelocEntry.r_sym == 0) {
err.submit(2031); continue; // refers to non-program section
}
// inline contains full relative address
// compensate by subtracting relative address to target section
*inlinep -= int32(NewSectionHeaders[TargetSection].sh_addr - SourceAddress);
// Relocation type
NewRelocEntry.r_type = R_386_PC32;
}
else if (scatp->r_type == MAC32_RELOC_VANILLA) {
// Scattered, absolute
TargetAddress = *inlinep;
TranslateAddress(TargetAddress, TargetSection, TargetOffset);
if (TargetSection == 0) {
err.submit(2031); continue;} // Target not found
NewRelocEntry.r_sym = SectionSymbols[TargetSection];
*inlinep = TargetOffset;
NewRelocEntry.r_type = R_386_32;
if (scatp->r_length != 2) {
err.submit(2030, scatp->r_type); continue; // Only 32-bit supported
}
}
else if (scatp->r_type == MAC32_RELOC_SECTDIFF || scatp->r_type == MAC32_RELOC_LOCAL_SECTDIFF) {
// relative to arbitrary reference point
// check that next record is MAC32_RELOC_PAIR
if (oldr == sectp->nreloc || (scatp+1)->r_type != MAC32_RELOC_PAIR || scatp->r_length != 2) {
err.submit(2050); continue;
}
// Find target address and reference point
RefAddress = (scatp+1)->r_value;
TranslateAddress(RefAddress, RefSection, RefOffset);
TargetAddress = RefAddress + *inlinep;
TranslateAddress(TargetAddress, TargetSection, TargetOffset);
// Check that both points are found
if (RefSection == 0 || TargetSection == 0) {
err.submit(2031); oldr++; relp++; continue;
}
// Address relative to arbitrary reference point can be translated
// to self-relative address if reference point is in same section as source
if (RefSection != newsec) {
err.submit(2044); oldr++; relp++; continue;
}
// Translation is possible
// Get symbol for target section
NewRelocEntry.r_sym = SectionSymbols[TargetSection];
// Make self-relative relocation
NewRelocEntry.r_type = R_386_PC32;
// Calculate compensating addend
*inlinep = TargetOffset + scatp->r_address - RefOffset;
// Linker will add (target section) - (source full address) to *inlinep, which gives
// (target full address) - (reference point full address)
// Advance pointers because we have used two records
oldr++; relp++;
}
else if (scatp->r_type == MAC32_RELOC_PB_LA_PTR) {
// procedure linkage table. Not supported
NewRelocEntry.r_type = R_386_PLT32;
err.submit(2043);
}
else {
// unknown scattered relocation type
err.submit(2030, scatp->r_type); continue;
}
}
else {
// Non scattered relocation info
// Section offset of relocated address
NewRelocEntry.r_offset = relp->r_address;
if (NewRelocEntry.r_offset >= NewSections[newsec].GetDataSize()) {
err.submit(2035); continue; // Out of range
}
// Pointer to inline addend
inlinep = (int32*)(NewSections[newsec].Buf() + NewRelocEntry.r_offset);
if (relp->r_extern) {
// r_extern = 1: target indicated by symbol index
uint32 symold = relp->r_symbolnum;
if (symold >= this->SymTabNumber) {
err.submit(2031); continue; // index out of range
}
NewRelocEntry.r_sym = NewSymbolIndex[symold];
if (relp->r_pcrel) {
// Self-relative.
// Inline contains -(source address)
// Add (source address) to compensate
*inlinep += int32(SectAddr + relp->r_address);
}
}
else {
// r_extern = 0. Target indicated by section + offset
// Old section number
uint32 secold = relp->r_symbolnum;
if (secold > this->NumSections) {
err.submit(2031); continue; // index out of range
}
TargetSection = NewSectIndex[secold];
NewRelocEntry.r_sym = SectionSymbols[TargetSection];
if (NewRelocEntry.r_sym == 0 || NewRelocEntry.r_sym > NumSymbols) {
err.submit(2031); continue; // refers to non-program section
}
if (relp->r_pcrel) {
// Self-relative.
// Inline contains (target address)-(source address)
// Subtract this to compensate
// Target section address
TargetOffset = uint32(NewSectionHeaders[TargetSection].sh_addr);
SourceAddress = SectAddr + relp->r_address;
*inlinep -= int32(TargetOffset - SourceAddress);
}
else {
// Absolute reference
// Inline contains target address, convert to section:offset address
TranslateAddress(*inlinep, TargetSection, TargetOffset);
if (TargetSection == 0) { // Target not found
err.submit(2035); continue;
}
// Translate to section-relative address by subtracting target section address
*inlinep -= int32(NewSectionHeaders[TargetSection].sh_addr);
}
}
// relocation type (32-bit non-scattered)
switch (relp->r_type) {
case MAC32_RELOC_VANILLA:
// Normal relocation
if (relp->r_pcrel) { // self relative
NewRelocEntry.r_type = R_386_PC32;
}
else { // direct
NewRelocEntry.r_type = R_386_32;
}
break;
default:
err.submit(2030, relp->r_type); // unknown type
continue;
}
// size
if (relp->r_length != 2) { // wrong size
err.submit(2030,relp->r_type);
}
}
// Put relocation record into table
NewSections[newsecr].Push(&NewRelocEntry, sizeof(NewRelocEntry));
}
}
}
}
}
template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::MakeRelocationTables(MAC_header_64&) {
// Convert subfunction: Relocation tables, 64-bit version
uint32 oldsec; // Relocated section number in source file
uint32 newsec; // Relocated section number in destination file
uint32 newsecr; // Relocation table section number in destination file
uint32 symold; // Old index of symbol
uint32 TargetSym; // Target symbol
uint32 TargetSection; // New section index of relocation target
uint32 RefSym; // Reference symbol
uint32 RefSection; // New section index of reference point
int64 RefOffset; // Section-relative offset of reference point
int64 SectAddr; // Address of current section
//const int WordSize = sizeof(MInt) * 8; // Word size, 32 or 64 bits
TELF_SectionHeader * NewRelTableSecHeader; // Section header for new relocation table
// Number of symbols
//uint32 NumSymbols = NewSections[symtab].GetNumEntries();
// New symbol table
Elf64_Sym * NewSymbolTable = (Elf64_Sym *)(NewSections[symtab].Buf());
// Find first section header
MAC_section_64 * sectp = (MAC_section_64*)(this->Buf() + this->SectionHeaderOffset);
// Loop through section headers
for (oldsec = 1; oldsec <= this->NumSections; oldsec++, sectp++) {
if (sectp->nreloc > 0) {
// Source section has relocations
// New section index
newsec = NewSectIndex[oldsec];
// Check that section has not been deleted
if (newsec > 0) {
// Section address
SectAddr = NewSectionHeaders[newsec].sh_addr;
// Finc new relocation table section
newsecr = newsec + 1;
if (newsecr > NewSectionHeaders.GetNumEntries()) {
err.submit(9000); return;
}
// New relocation table section header
NewRelTableSecHeader = &NewSectionHeaders[newsecr];
// Check that we have allocated this as a relocation section
if (NewRelTableSecHeader->sh_info != newsec) {
err.submit(9000); return;
}
// Insert header info
NewRelTableSecHeader->sh_type = SHT_RELA;
NewRelTableSecHeader->sh_flags = 0;
NewRelTableSecHeader->sh_addralign = 8;
NewRelTableSecHeader->sh_link = symtab; // Point to symbol table
NewRelTableSecHeader->sh_info = newsec; // Point to relocated section
// Entry size:
NewRelTableSecHeader->sh_entsize = sizeof(Elf64_Rela);
// Pointer to old relocation entry
if (sectp->reloff >= this->GetDataSize()) {err.submit(2035); break;}
MAC_relocation_info * relp = (MAC_relocation_info*)(this->Buf() + sectp->reloff);
// Loop through old relocations
for (uint32 oldr = 1; oldr <= sectp->nreloc; oldr++, relp++) {
// Make new relocation entry and set to zero
Elf64_Rela NewRelocEntry;
memset(&NewRelocEntry, 0, sizeof(NewRelocEntry));
// Pointer to inline addend
int32 * inlinep = 0;
if (relp->r_address & R_SCATTERED) {
// scattered not allowed in 64-bit
err.submit(2030, ((MAC_scattered_relocation_info*)relp)->r_type); continue;
}
else {
// Non scattered relocation info
// Section offset of relocated address
NewRelocEntry.r_offset = relp->r_address;
if (NewRelocEntry.r_offset >= NewSections[newsec].GetDataSize()) {
err.submit(2035); continue; // Out of range
}
// Pointer to inline addend
inlinep = (int32*)(NewSections[newsec].Buf() + NewRelocEntry.r_offset);
// Symbol index of target
symold = relp->r_symbolnum;
if (relp->r_extern) {
if (symold >= this->SymTabNumber) {
err.submit(2031); continue; // index out of range
}
NewRelocEntry.r_sym = NewSymbolIndex[symold];
}
else {
// r_extern = 0, r_symbolnum = section
if (symold > NumSectionsNew) {err.submit(2031); continue;}
TargetSection = NewSectIndex[symold];
NewRelocEntry.r_sym = SectionSymbols[TargetSection];
if (relp->r_pcrel) {
// Self-relative.
// Inline contains (target address)-(source address)
// Subtract this to compensate
// Target section address
uint64 TargetSectAddr = NewSectionHeaders[TargetSection].sh_addr;
uint64 SourceAddress = SectAddr + relp->r_address;
*inlinep -= int32(TargetSectAddr - SourceAddress);
*inlinep += 4; // Compensate for subtracting 4 below
}
}
// Find relocation type
switch (relp->r_type) {
case MAC64_RELOC_UNSIGNED: // absolute address, 32 or 64 bits
if (relp->r_length == 2) {
NewRelocEntry.r_type = R_X86_64_32S; // 32 bit signed
}
else if (relp->r_length == 3) {
NewRelocEntry.r_type = R_X86_64_64; // 64 bit
}
else {
err.submit(2030,relp->r_type); continue;
}
break;
case MAC64_RELOC_SIGNED: // rip-relative, implicit addend = -4
case MAC64_RELOC_BRANCH: // rip-relative, implicit addend = -4
case MAC64_RELOC_SIGNED_1: // implicit addend = -4, not -5
case MAC64_RELOC_SIGNED_2: // implicit addend = -4, not -6
case MAC64_RELOC_SIGNED_4: // implicit addend = -4, not -8
// These are all the same:
// signed 32-bit rip-relative with implicit -4 addend
if (relp->r_length != 2) { // wrong size
err.submit(2030,relp->r_type); continue;
}
NewRelocEntry.r_type = R_X86_64_PC32;
// ELF = self-relative, Mac64 = rip-relative. Compensate for difference
*inlinep -= 4;
break;
case MAC64_RELOC_SUBTRACTOR:
// relative to arbitrary reference point
// must be followed by a X86_64_RELOC_UNSIGNED
// check that next record is MAC64_RELOC_UNSIGNED
if (oldr == sectp->nreloc || (relp+1)->r_type != MAC64_RELOC_UNSIGNED) {
err.submit(2050); continue;
}
// Reference symbol
RefSym = NewRelocEntry.r_sym;
RefSection = NewSymbolTable[RefSym].st_shndx;
RefOffset = NewSymbolTable[RefSym].st_value;
// Target symbol
symold = (relp+1)->r_symbolnum;
if (symold >= this->SymTabNumber) {
err.submit(2031); continue; // index out of range
}
TargetSym = NewSymbolIndex[symold];
NewRelocEntry.r_sym = TargetSym;
// Address relative to arbitrary reference point can be translated
// to self-relative address if reference point is in same section as source
if (RefSection != newsec) {
err.submit(2044); oldr++; relp++; continue;
}
if (relp->r_length == 2) {
*inlinep += int32(NewRelocEntry.r_offset) - int32(RefOffset);
}
else if (relp->r_length == 3) {
*(int64*)inlinep += NewRelocEntry.r_offset - RefOffset;
// there is no 64-bit self-relative relocation in ELF,
// use 32-bit self-relative and hope there is no carry
err.submit(1302); // Warn. This will fail if inline value changes sign
}
else {
err.submit(2044); // wrong size
}
// self-relative type
NewRelocEntry.r_type = R_X86_64_PC32;
// increment counters because we used two records
relp++; oldr++;
break;
case MAC64_RELOC_GOT_LOAD: // a rip-relative load of a GOT entry
*inlinep = -4;
// Continue into next case
case MAC64_RELOC_GOT: // other GOT references
// Make fake GOT entry
//NewRelocEntry.r_addend = MakeGOTEntry(NewRelocEntry.r_sym) - 4;
*inlinep += MakeGOTEntry(NewRelocEntry.r_sym);
NewRelocEntry.r_sym = FakeGOTSymbol;
NewRelocEntry.r_type = R_X86_64_PC32;
break;
}
}
// Put relocation record into table
NewSections[newsecr].Push(&NewRelocEntry, sizeof(NewRelocEntry));
}
}
}
}
}
template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::MakeBinaryFile() {
// Convert subfunction: Make section headers and file header,
// and combine everything into a single memory buffer.
uint32 newsec; // Section index
uint32 SecOffset; // Section offset in file
uint32 SecSize; // Section size in file
uint32 SectionHeaderOffset; // File offset to section headers
const int WordSize = sizeof(MInt) * 8;
// Set file type in ToFile
ToFile.SetFileType(FILETYPE_ELF);
// Make space for file header in ToFile, but don't fill data into it yet
ToFile.Push(0, sizeof(TELF_Header));
// Loop through new section buffers
for (newsec = 0; newsec < NumSectionsNew; newsec++) {
// Size of section
SecSize = NewSections[newsec].GetDataSize();
// Put section into ToFile
SecOffset = ToFile.Push(NewSections[newsec].Buf(), SecSize);
// Put size and offset into section header
NewSectionHeaders[newsec].sh_offset = SecOffset;
if (SecSize) { // Don't set size of BSS sections to zero
NewSectionHeaders[newsec].sh_size = SecSize;
}
// Align before next entry
ToFile.Align(16);
}
// Start offset of section headers
SectionHeaderOffset = ToFile.GetDataSize();
// Loop through new section headers
for (newsec = 0; newsec < NumSectionsNew; newsec++) {
// Put section header into ToFile
ToFile.Push(&NewSectionHeaders[newsec], sizeof(TELF_SectionHeader));
}
// Make file header
TELF_Header FileHeader;
memset(&FileHeader, 0, sizeof(FileHeader)); // Initialize to 0
// Put file type magic number in
strcpy((char*)(FileHeader.e_ident), ELFMAG);
// File class
FileHeader.e_ident[EI_CLASS] = (WordSize == 32) ? ELFCLASS32 : ELFCLASS64;
// Data Endian-ness
FileHeader.e_ident[EI_DATA] = ELFDATA2LSB;
// ELF version
FileHeader.e_ident[EI_VERSION] = EV_CURRENT;
// ABI
FileHeader.e_ident[EI_OSABI] = ELFOSABI_SYSV;
// ABI version
FileHeader.e_ident[EI_ABIVERSION] = 0;
// File type
FileHeader.e_type = ET_REL;
// Machine architecture
FileHeader.e_machine = (WordSize == 32) ? EM_386 : EM_X86_64;
// Version
FileHeader.e_version = EV_CURRENT;
// Flags
FileHeader.e_flags = 0;
// Section header table offset
FileHeader.e_shoff = SectionHeaderOffset;
// File header size
FileHeader.e_ehsize = sizeof(TELF_Header);
// Section header size
FileHeader.e_shentsize = sizeof(TELF_SectionHeader);
// Number of section headers
FileHeader.e_shnum = (uint16)NumSectionsNew;
// Section header string table index
FileHeader.e_shstrndx = (uint16)shstrtab;
// Put file header into beginning of ToFile where we made space for it
memcpy(ToFile.Buf(), &FileHeader, sizeof(FileHeader));
}
template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::MakeImportTables() {
// Convert subfunction: Fill import tables
uint32 oldsec; // Old section number
uint32 Type; // Old section type
uint32 NumEntries; // Number of entries in import table
uint32 EntrySize; // Entry size of import table
uint32 NewSec1; // New section number of import table
uint32 NewSec2; // New section number of relocation for import table
uint32 Offset; // Offset of relocation source
uint32 OldSymbol; // Old symbol number of import
uint32 i; // Loop counter
uint32 * IndSymTab; // Pointer to indirect symbol table
uint32 IndSymi; // Index into indirect symbol table
uint32 IndSymNum; // Number of entries in indirect symbol table
TELF_Relocation NewRelocEntry; // New relocation entry
const int WordSize = sizeof(MInt) * 8;
// Machine code of jmp instruction
static const int8 JmpInstruction[5] = {int8(0xE9), int8(0xFC), int8(0xFF), int8(0xFF), int8(0xFF)};
// Number of indirect symbols
IndSymNum = this->IndirectSymTabNumber;
if (IndSymNum == 0) {
return; // No indirect symbols
}
// Find indirect symbol table
IndSymTab = (uint32*)(this->Buf() + this->IndirectSymTabOffset);
// Find first section header
TMAC_section * sectp = (TMAC_section*)(this->Buf() + this->SectionHeaderOffset);
// Loop through section headers
for (oldsec = 1; oldsec <= this->NumSections; oldsec++, sectp++) {
// Search for import tables
Type = sectp->flags & MAC_SECTION_TYPE;
if (Type >= MAC_S_NON_LAZY_SYMBOL_POINTERS && Type <= MAC_S_SYMBOL_STUBS) {
// This is an import table
// Indirect symbol table first entry
IndSymi = sectp->reserved1;
// Entry size:
EntrySize = sectp->reserved2;
if (EntrySize == 0) EntrySize = WordSize / 8;
// Find new section
NewSec1 = NewSectIndex[oldsec];
NumEntries = uint32(NewSectionHeaders[NewSec1].sh_size) / EntrySize;
// Find new relocation section
NewSec2 = NewSec1 + 1;
if (NewSectionHeaders[NewSec2].sh_type != SHT_REL && NewSectionHeaders[NewSec2].sh_type != SHT_RELA) {
err.submit(9000); // This should be a relocation section
}
NewSectionHeaders[NewSec2].sh_link = symtab; // Point to symbol table
// Offset of first relocation
Offset = EntrySize & 1; // 1 if EntrySize = 5, otherwise 0
// Loop through entries
for (i = 0; i < NumEntries; i++, Offset += EntrySize) {
// Find symbol
if (IndSymi >= IndSymNum) {
err.submit(1303); break; // Import symbol table exhausted
}
OldSymbol = IndSymTab[IndSymi];
if (OldSymbol >= this->SymTabNumber) {
err.submit(1052); break;
}
// Increment pointer to import symbol table
IndSymi++;
// Make relocation record
memset(&NewRelocEntry, 0, sizeof(NewRelocEntry));
NewRelocEntry.r_offset = Offset;
if (WordSize == 32) {
if (EntrySize == 4) {
NewRelocEntry.r_type = R_386_32;
}
else if (EntrySize == 5) {
NewRelocEntry.r_type = R_386_PC32;
}
else {
err.submit(2045);
}
}
else { // 64 bit
if (EntrySize == 8) {
NewRelocEntry.r_type = R_X86_64_64;
}
else if (EntrySize == 5) {
NewRelocEntry.r_type = R_X86_64_PC32;
}
else {
err.submit(2045);
}
}
NewRelocEntry.r_sym = NewSymbolIndex[OldSymbol];
// Store relocation record
NewSections[NewSec2].Push(&NewRelocEntry, (WordSize==32) ? sizeof(Elf32_Rel) : sizeof(Elf64_Rela));
// Insert jmp instruction if EntrySize = 5
if (EntrySize == 5) {
if (Offset -1 + EntrySize > NewSections[NewSec1].GetDataSize()) {
err.submit(9000); // Outside section
}
memcpy(NewSections[NewSec1].Buf()+Offset-1, JmpInstruction, 5);
}
}
}
}
}
template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::TranslateAddress(MInt addr, uint32 & section, uint32 & offset) {
// Translate 32-bit address to section + offset
// (Sections are not necessarily ordered by address)
uint32 sec;
MInt secstart;
for (sec = 1; sec < NumSectionsNew; sec++) {
secstart = NewSectionHeaders[sec].sh_addr;
if (addr >= secstart && addr < secstart + MInt(NewSectionHeaders[sec].sh_size)) {
// Section found
section = sec;
offset = uint32(addr - secstart);
return;
}
}
// Not found
section = offset = 0;
}
template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
uint32 CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::MakeGOTEntry(int symbol) {
// Make entry in fake GOT for symbol
uint32 NumGOTEntries = GOTSymbols.GetNumEntries();
uint32 symi; // Symbol index
const int WordSize = sizeof(MInt) * 8;
// Get symbol for start of GOT
FakeGOTSymbol = SectionSymbols[FakeGOTSection];
// Search for symbol in previous entries
for (symi = 0; symi < NumGOTEntries; symi++) {
if (GOTSymbols[symi] == symbol) break;
}
if (symi == NumGOTEntries) {
// Not found. Make new entry
GOTSymbols.Push(symbol);
}
return symi * (WordSize / 8);
}
template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::MakeGOT() {
// Make fake Global Offset Table
const int WordSize = sizeof(MInt) * 8;
if (!HasGOT) return;
uint32 NumEntries = GOTSymbols.GetNumEntries();
NewSections[FakeGOTSection].Push(0, NumEntries*(WordSize/8));
// Make relocations for GOT
Elf64_Rela NewRelocEntry;
memset(&NewRelocEntry, 0, sizeof(NewRelocEntry));
for (uint32 i = 0; i < NumEntries; i++) {
NewRelocEntry.r_offset = i * (WordSize/8);
NewRelocEntry.r_sym = GOTSymbols[i];
NewRelocEntry.r_type = R_X86_64_64;
NewSections[FakeGOTSection+1].Push(&NewRelocEntry, sizeof(NewRelocEntry));
}
}
// Make template instances for 32 and 64 bits
template class CMAC2ELF<MAC32STRUCTURES,ELF32STRUCTURES>;
template class CMAC2ELF<MAC64STRUCTURES,ELF64STRUCTURES>;