/**************************** elf2mac.cpp ********************************* * Author: Agner Fog * Date created: 2007-01-10 * Last modified: 2012-05-05 * Project: objconv * Module: elf2mac.cpp * Description: * Module for converting ELF file to Mach-O file * * Copyright 2007-2012 GNU General Public License http://www.gnu.org/licenses *****************************************************************************/ #include "stdafx.h" template CELF2MAC::CELF2MAC() { // Constructor memset(this, 0, sizeof(*this)); // Reset everything } template void CELF2MAC::Convert() { // Do the conversion // Some compilers require this-> for accessing members of template base class, // according to the so-called two-phase lookup rule. // Call the subfunctions ToFile.SetFileType(FILETYPE_MACHO_LE); // Set type of new file MakeFileHeader(); // Make file header MakeSectionsIndex(); // Make sections index translation table FindUnusedSymbols(); // Check if symbols used, remove unused symbols MakeSymbolTable(); // Make symbol table and string tables MakeSections(); // Make sections and relocation tables MakeBinaryFile(); // Put sections together *this << ToFile; // Take over new file buffer } template void CELF2MAC::MakeFileHeader() { // Convert subfunction: Make file header and load segment command TMAC_header NewHeader; // new file header NewHeader.magic = (this->WordSize == 32) ? MAC_MAGIC_32 : MAC_MAGIC_64; // Mach magic number identifier NewHeader.cputype = (this->WordSize == 32) ? MAC_CPU_TYPE_I386 : MAC_CPU_TYPE_X86_64; NewHeader.cpusubtype = MAC_CPU_SUBTYPE_I386_ALL; NewHeader.filetype = MAC_OBJECT; NewHeader.ncmds = 3; // Three commands = segment, symbol table, dynsymtab NewHeader.sizeofcmds = 0; // Set this later NewHeader.flags = 0; // No flags needed // put file header in OutFile ToFile.Push(&NewHeader, sizeof(NewHeader)); } template void CELF2MAC::MakeSectionsIndex() { // Make sections index translation table and section offset table. // We must make these tables before the sections, because they are needed for the // symbol tables and relocation tables, and we must make the symbol tables before // the relocation tables, and we must make the relocation tables together with the // sections. uint32 oldsec; // Section number in old file uint32 newsec = 0; // Section number in new file NewSectIndex. SetNum(this->NSections); // Allocate size for section index table NewSectIndex. SetZero(); // Initialize NewSectOffset.SetNum(this->NSections); // Allocate buffer for section offset table NewSectOffset.SetZero(); // Initialize MInt NewVirtualAddress = 0; // Virtual address of new section as specified in Mach-O file // First loop through old sections for (oldsec = 0; oldsec < this->NSections; oldsec++) { NewSectIndex[oldsec] = 0; NewSectOffset[oldsec] = 0; // Get section name const char * sname = ""; uint32 namei = this->SectionHeaders[oldsec].sh_name; if (namei >= this->SecStringTableLen) { err.submit(2112); } else sname = this->SecStringTable + namei; if (cmd.DebugInfo == CMDL_DEBUG_STRIP) { // Check for debug section names if (strncmp(sname, ".note", 5) == 0 || strncmp(sname, ".comment", 8) == 0 || strncmp(sname, ".stab", 5) == 0 || strncmp(sname, ".debug", 6) == 0) { // Remove this section this->SectionHeaders[oldsec].sh_type = SHT_REMOVE_ME; cmd.CountDebugRemoved(); continue; } } if (cmd.ExeptionInfo == CMDL_EXCEPTION_STRIP) { // Check for exception section name if (strncmp(sname, ".eh_frame", 9) == 0) { // Remove this section this->SectionHeaders[oldsec].sh_type = SHT_REMOVE_ME; cmd.CountExceptionRemoved(); continue; } } // Search for program data sections only if (this->SectionHeaders[oldsec].sh_type != SHT_PROGBITS && this->SectionHeaders[oldsec].sh_type != SHT_NOBITS) { // Has no data. Ignore continue; } if (this->SectionHeaders[oldsec].sh_size == 0) { // Remove empty section // The linker has a bug with empty sections continue; } // Section index translation table NewSectIndex[oldsec] = newsec++; // Calculate virtual memory address of section. This address does not have // much to do with the final address, but it is needed in relocation entries. // Alignment int NewAlign = FloorLog2((uint32)this->SectionHeaders[oldsec].sh_addralign); if (NewAlign > 12) NewAlign = 12; // What is the limit for highest alignment? int AlignBy = 1 << NewAlign; // Align memory address NewVirtualAddress = (NewVirtualAddress + AlignBy - 1) & -(MInt)AlignBy; // Virtual memory address of new section NewSectOffset[oldsec] = NewVirtualAddress; // Increment memory address NewVirtualAddress += this->SectionHeaders[oldsec].sh_size; // Fix v. 2.14: Align end of memory address by 4 NewVirtualAddress = (NewVirtualAddress + 3) & MInt(-4); } // Store number of sections in new file NumSectionsNew = newsec; // Calculate file offset of first raw data RawDataOffset = sizeof(TMAC_header) + sizeof(TMAC_segment_command) + NumSectionsNew * sizeof(TMAC_section) + sizeof(MAC_symtab_command) + sizeof(MAC_dysymtab_command); // Align end of memory address by 4 NewVirtualAddress = (NewVirtualAddress + 3) & MInt(-4); // Make segment command TMAC_segment_command NewSegment; memset(&NewSegment, 0, sizeof(NewSegment)); NewSegment.cmd = (this->WordSize == 32) ? MAC_LC_SEGMENT : MAC_LC_SEGMENT_64; NewSegment.cmdsize = sizeof(TMAC_segment_command) + NumSectionsNew * sizeof(TMAC_section); NewSegment.fileoff = RawDataOffset; NewSegment.nsects = NumSectionsNew; NewSegment.maxprot = NewSegment.initprot = 7; // 1=read, 2=write, 4=execute NewSegment.vmsize = NewVirtualAddress; NewSegment.filesize = 0; // Changed later // put segment command in OutFile CommandOffset = ToFile.Push(&NewSegment, sizeof(NewSegment)); } template void CELF2MAC::MakeSymbolTable() { // Convert subfunction: Symbol table and string tables uint32 oldsec; // Section number in old file TELF_SectionHeader OldHeader; // Old section header int FoundSymTab = 0; // Found symbol table int8 * strtab; // Old symbol string table int8 * symtab; // Old symbol table uint32 symtabsize; // Size of old symbol table int8 * symtabend; // End of old symbol table uint32 entrysize; // Size of each entry in old symbol table TELF_Symbol OldSym; // Old symbol table record uint32 OldSymI; // Symbol index in old symbol table const char * symname; // Symbol name int NewSection = 0; // New section index int NewType; // New symbol type int NewDesc; // New symbol reference type MInt Value; // Symbol value uint32 Scope; // 0: Local, 1: Public, 2: External // Loop through old sections to find symbol table for (oldsec = 0; oldsec < this->NSections; oldsec++) { // Search for program data sections only if (this->SectionHeaders[oldsec].sh_type == SHT_SYMTAB || this->SectionHeaders[oldsec].sh_type == SHT_DYNSYM) { FoundSymTab++; // Copy symbol table header for convenience OldHeader = this->SectionHeaders[oldsec]; // Find associated string table if (OldHeader.sh_link >= (uint32)(this->NSections)) { err.submit(2035); OldHeader.sh_link = 0; } strtab = this->Buf() + (uint32)this->SectionHeaders[OldHeader.sh_link].sh_offset; // Find old symbol table entrysize = (uint32)OldHeader.sh_entsize; if (entrysize < sizeof(TELF_Symbol)) {err.submit(2033); entrysize = sizeof(TELF_Symbol);} symtab = this->Buf() + (uint32)OldHeader.sh_offset; symtabsize = (uint32)OldHeader.sh_size; symtabend = symtab + symtabsize; if (NewSymTab[0].GetNumEntries() == 0) { // make empty symbol record for index 0 NewSymTab[0].AddSymbol(0, "", 0, 0, 0, 0); } // Loop through old symbol table for (OldSymI = 0; symtab < symtabend; symtab += entrysize, OldSymI++) { if (OldSymI == 0) continue; // First symbol entry in ELF file is unused // Copy 32 bit symbol table entry or convert 64 bit entry OldSym = *(TELF_Symbol*)symtab; // Old symbol type int type = OldSym.st_type; // Old symbol storage class = binding int binding = OldSym.st_bind; // Get symbol name if (OldSym.st_name < this->SymbolStringTableSize) { symname = strtab + OldSym.st_name; } else { err.submit(2112); // String table corrupt continue; // Ignore } if (symname == 0 || *symname == 0) { // Symbol has no name. Give it a name // Mac linker messes this up if the symbol doesn't have a unique name. char tempbuf[80]; sprintf(tempbuf, "?unnamed%i", OldSymI); int os = UnnamedSymbolsTable.PushString(tempbuf); symname = UnnamedSymbolsTable.Buf() + os; } NewType = NewDesc = 0; // New symbol type // Value = address Value = OldSym.st_value; // Section if (OldSym.st_shndx == SHN_UNDEF) { NewSection = 0; // External } else if ((int16)(OldSym.st_shndx) == SHN_ABS) { NewType |= MAC_N_ABS; // Absolute symbol NewDesc |= MAC_N_NO_DEAD_STRIP; NewSection = 0; } else if ((int16)(OldSym.st_shndx) == SHN_COMMON) { NewType |= MAC_N_ABS; // Common symbol. Translate to abs and make warning NewDesc |= MAC_N_NO_DEAD_STRIP; NewSection = 0; err.submit(1053, symname); // Warning. Common symbol } else if (OldSym.st_shndx >= this->NSections) { err.submit(2036, OldSym.st_shndx); // Special/unknown section index or out of range } else { // Normal section index. // Look up in section index translation table and add 1 because it is 1-based NewSection = NewSectIndex[OldSym.st_shndx] + 1; // Value must be absolute address. Add section address Value += NewSectOffset[OldSym.st_shndx]; } // Convert binding/storage class switch (binding) { case STB_LOCAL: // Local Scope = S_LOCAL; if (!(NewType & MAC_N_ABS)) NewType |= MAC_N_SECT; break; case STB_GLOBAL: if (NewSection || (NewType & MAC_N_ABS)) { // Public Scope = S_PUBLIC; NewType |= MAC_N_EXT; if (!(NewType & MAC_N_ABS)) NewType |= MAC_N_SECT; } else { // External Scope = S_EXTERNAL; NewType |= MAC_N_EXT; } NewDesc |= MAC_REF_FLAG_UNDEFINED_NON_LAZY; break; case STB_WEAK: if (NewSection) { // Weak public Scope = S_PUBLIC; NewType |= MAC_N_EXT | MAC_N_SECT; NewDesc |= MAC_N_WEAK_DEF; if (this->WordSize == 32) { err.submit(1051, symname); // Weak public only allowed in coalesced section of MachO-32 } } else { // Weak external Scope = S_EXTERNAL; NewType |= MAC_N_EXT; NewDesc |= MAC_N_WEAK_REF; } break; default: Scope = S_LOCAL; err.submit(2037, binding); // Other. Not supported } // Make record depending on type switch (type) { case STT_OBJECT: case STT_NOTYPE: // Data object break; case STT_GNU_IFUNC: err.submit(1063); // Warning: Gnu indirect function cannot be converted // continue in next case: case STT_FUNC: // Function break; case STT_FILE: // File name record. Ignore continue; case STT_SECTION: // Section name record. (Has no name) break; case STT_COMMON: default: err.submit(2038, type); // Symbol type not supported continue; } // Discard unused symbols if (Scope != S_PUBLIC && !OldSymbolUsed[OldSymI]) continue; // Store new symbol record in the appropriate table if (Scope > 2) err.submit(9000); NewSymTab[Scope].AddSymbol(OldSymI, symname, NewType, NewDesc, NewSection, Value); // Store scope in OldSymbolScope if (OldSymI < NumOldSymbols) { OldSymbolScope[OldSymI] = Scope; } } // End OldSymI loop } } // End search for symbol table if (FoundSymTab == 0) err.submit(2034); // Symbol table not found if (FoundSymTab > 1) err.submit(1032); // More than one symbol table found } template void CELF2MAC::Elf2MacRelocations(Elf32_Shdr & OldRelHeader, MAC_section_32 & NewHeader, uint32 NewRawDataOffset, uint32 oldsec) { // Convert 32-bit relocations from ELF to MAC // (This function has two template instances, only the 32-bit instance is used) Elf32_Rela OldRelocation; // Old relocation table entry MAC_scattered_relocation_info scat; // Scattered relocation entry memset(&scat, 0, sizeof(scat)); // Get pointer to old relocation table int8 * reltab = this->Buf() + OldRelHeader.sh_offset; int8 * reltabend = reltab + OldRelHeader.sh_size; // Get entry size uint32 entrysize = (uint32)OldRelHeader.sh_entsize; uint32 expectedentrysize = (OldRelHeader.sh_type == SHT_REL) ? sizeof(Elf32_Rel) : sizeof(Elf32_Rela); if (entrysize < expectedentrysize) {err.submit(2033); entrysize = expectedentrysize;} // File pointer to relocations NewHeader.reloff = NewRelocationTab.GetNumEntries()*sizeof(MAC_relocation_info); // Offset to first relocation table added later // Loop through relocation table entries for (; reltab < reltabend; reltab += entrysize) { // Copy relocation entry with or without addend OldRelocation.r_addend = 0; memcpy(&OldRelocation, reltab, entrysize); // Find inline addend uint32 InlinePosition = (uint32)(NewRawDataOffset + OldRelocation.r_offset); // Check that address is valid if (InlinePosition >= this->GetDataSize()) { // Address is invalid err.submit(2032); break; } // Pointer to inline addend int32 * piaddend = (int32*)(NewRawData.Buf() + InlinePosition); // Add old addend if any *piaddend += (int32)OldRelocation.r_addend; // Define relocation parameters uint32 r_address = 0; // section-relative offset to relocation source uint32 r_symbolnum = 0; // symbol index if r_extern == 1 or section ordinal if r_extern == 0 // uint32 r_value = 0; // value of relocation target // int r_scattered = 0; // use scattered relocation int r_pcrel = 0; // self relative int r_length = 2; // size of source: 0=byte, 1=2 bytes, 2=4 bytes, 3=8 bytes int r_extern = 0; // public or external int r_type = 0; // if not 0, machine specific relocation type int Scope = 0; // Symbol scope: 0 = local, 1 = public, 2 = external // source offset r_address = (uint32)OldRelocation.r_offset; // target scope if (OldRelocation.r_sym < NumOldSymbols) { Scope = OldSymbolScope[OldRelocation.r_sym]; } // Get r_extern: 0 = local target referenced by address, // 1 = external symbol referenced by symbol table index switch (Scope) { case S_LOCAL: // Local target must be referenced by address r_extern = 0; break; case S_PUBLIC: // Public target is optionally referenced by index or by address r_extern = 0; // r_extern = 1; is not allowed! break; case S_EXTERNAL: // External target is always referenced by index r_extern = 1; break; } // Get zero-based index into NewSymTab[Scope] int newindex = NewSymTab[Scope].TranslateIndex(OldRelocation.r_sym); if (newindex < 0) { // Symbol not found or wrong type err.submit(2031); break; } if (r_extern) { // r_symbolnum is zero based index into combined symbol tables. // Add number of entries in preceding NewSymTab tables to index // into NewSymTab[Scope] r_symbolnum = newindex + NumSymbols[Scope]; } else { // r_extern = 0. r_symbolnum = target section r_symbolnum = NewSymTab[Scope][newindex].n_sect; // Absolute address of target stored inline in source *piaddend += (uint32)NewSymTab[Scope][newindex].n_value; } // Get relocation type and fix addend switch(OldRelocation.r_type) { case R_386_NONE: // Ignored continue; case R_386_IRELATIVE: err.submit(1063); // Warning: Gnu indirect function cannot be converted // continue in next case?: case R_386_32: // 32-bit absolute virtual address r_type = MAC32_RELOC_VANILLA; break; case R_386_PC32: // 32-bit self-relative r_type = MAC32_RELOC_VANILLA; r_pcrel = 1; // Mach-O 32 bit format requires that self-relative addresses must have // self-relative values already before relocation. Therefore // the source address is subtracted. // (The PC reference point is the end of the source = start // of source + 4, but ELF files have the same offset so no further // correction is needed when converting from ELF file). // !! ToDo: Self-relative relocations plus offset to local symbol in a different section // sometimes causes problems in Mac linker, perhaps because it fails to determine // correctly which section the target is in. Use a relocation with a reference point // instead. This probably occurs only in assembler-coded self-relative 32-bit code. // (Use asmlib A_strtoupper and A_strcspn as test cases - they fail if dummy data // at the end of .data section is removed) *piaddend -= r_address + (uint32)NewHeader.addr; break; case R_UNSUPPORTED_IMAGEREL: // 32-bit image-relative // This occurs only when converting from COFF (via ELF) // Needs scattered relocation entry scat.r_address = r_address; scat.r_length = 2; scat.r_pcrel = 0; scat.r_scattered = 1; scat.r_type = MAC32_RELOC_SECTDIFF; scat.r_value = r_symbolnum; // Store first entry of scattered pair NewRelocationTab.Push(&scat, sizeof(scat)); NewHeader.nreloc++; // Make subtractor record for image base scat.r_type = MAC32_RELOC_PAIR; scat.r_value = GetImagebaseSymbol(); // Store second entry of scattered pair NewRelocationTab.Push(&scat, sizeof(scat)); NewHeader.nreloc++; continue; case R_386_GOT32: case R_386_GLOB_DAT: case R_386_GOTOFF: case R_386_GOTPC: // Global offset table err.submit(2042); // cannot convert position-independent code err.ClearError(2042); // report this error only once r_type = 0; break; case R_386_PLT32: case R_386_JMP_SLOT: // procedure linkage table err.submit(2043); // cannot convert import table err.ClearError(2043); // report this error only once r_type = 0; break; default: // Unknown or unsupported relocation method err.submit(2030, OldRelocation.r_type); r_type = 0; break; } if (!r_pcrel) { // Warn for position dependent code. // This warning is currently turned off in error.cpp. err.submit(1050, this->SymbolName(OldRelocation.r_sym)); // Write this error only once err.ClearError(1050); } // Make relocation entry MAC_relocation_info rel; memset(&rel, 0, sizeof(rel)); // Make non-scattered relocation entry rel.r_address = r_address; rel.r_symbolnum = r_symbolnum; rel.r_pcrel = r_pcrel; rel.r_length = r_length; rel.r_extern = r_extern; rel.r_type = r_type; // Store relocation entry NewRelocationTab.Push(&rel, sizeof(rel)); NewHeader.nreloc++; // Remember that symbol is used // if (SymbolsUsed && OldRelocation.r_type && NewRelocation.r_symbolnum < ?) { // SymbolsUsed[NewRelocation.r_symbolnum]++;} } // End of relocations loop } template void CELF2MAC::Elf2MacRelocations(Elf64_Shdr & OldRelHeader, MAC_section_64 & NewHeader, uint32 NewRawDataOffset, uint32 oldsec) { // Convert 64-bit relocations from ELF to MAC // (This function has two template instances, only the 64-bit instance is used) // Make relocation entry for dummy subtractor MAC_relocation_info relsub; memset(&relsub, 0, sizeof(relsub)); Elf64_Rela OldRelocation; // Old relocation table entry // Get pointer to old relocation table int8 * reltab = this->Buf() + OldRelHeader.sh_offset; int8 * reltabend = reltab + OldRelHeader.sh_size; // Get entry size uint32 entrysize = (uint32)OldRelHeader.sh_entsize; uint32 expectedentrysize = (OldRelHeader.sh_type == SHT_REL) ? sizeof(Elf64_Rel) : sizeof(Elf64_Rela); if (entrysize < expectedentrysize) {err.submit(2033); entrysize = expectedentrysize;} // File pointer to relocations NewHeader.reloff = NewRelocationTab.GetNumEntries()*sizeof(MAC_relocation_info); // Offset to first relocation table added later // Loop through relocation table entries for (; reltab < reltabend; reltab += entrysize) { // Copy relocation entry with or without addend OldRelocation.r_addend = 0; memcpy(&OldRelocation, reltab, entrysize); // Find inline addend uint32 InlinePosition = (uint32)(NewRawDataOffset + OldRelocation.r_offset); // Check that address is valid if (InlinePosition >= this->GetDataSize()) { // Address is invalid err.submit(2032); break; } // Pointer to inline addend int32 * piaddend = (int32*)(NewRawData.Buf() + InlinePosition); // Add old addend if any *piaddend += (uint32)OldRelocation.r_addend; // Define relocation parameters uint32 r_address = 0; // section-relative offset to relocation source uint32 r_symbolnum = 0; // symbol index if r_extern == 1 or section ordinal if r_extern == 0 // uint32 r_value = 0; // value of relocation target // int r_scattered = 0; // scattered relocations not used in 64 bit int r_pcrel = 0; // self relative int r_length = 2; // size of source: 0=byte, 1=2 bytes, 2=4 bytes, 3=8 bytes int r_extern = 0; // public or external int r_type = 0; // if not 0, machine specific relocation type int Scope = 0; // Symbol scope: 0 = local, 1 = public, 2 = external // source offset r_address = (uint32)OldRelocation.r_offset; // target scope if (OldRelocation.r_sym < NumOldSymbols) { Scope = OldSymbolScope[OldRelocation.r_sym]; } // Get r_extern: 0 = local target referenced by address, // 1 = public or external symbol referenced by symbol table index switch (Scope) { case S_LOCAL: // Local target // r_extern = 0; // Local target must be referenced by address // Note: the description in reloc.h says that local targets are addressed // relative to any preceding public target. If there is no preceding label // then referenced by address in the segment. However, the Gnu compiler // uses reference to a local symbol and sets r_extern = 1 to indicate that // it refers to a symbol record, not to an address. I have chosen to use the // latter method because it is simpler, though undocumented. r_extern = 1; break; case S_PUBLIC: // Public target is optionally referenced by index or by address r_extern = 1; break; case S_EXTERNAL: // External target is always referenced by index r_extern = 1; break; } // Get zero-based index into NewSymTab[Scope] int newindex = NewSymTab[Scope].TranslateIndex(OldRelocation.r_sym); if (newindex < 0) { // Symbol not found or wrong type err.submit(2031); break; } // r_symbolnum is zero based index into combined symbol tables. // Add number of entries in preceding NewSymTab tables to index // into NewSymTab[Scope] r_symbolnum = newindex + NumSymbols[Scope]; // Get relocation type and fix addend, 64 bit switch(OldRelocation.r_type) { case R_X86_64_NONE: // Ignored continue; case R_X86_64_64: // 64-bit absolute virtual address r_type = MAC64_RELOC_UNSIGNED; r_length = 3; break; case R_X86_64_IRELATIVE: err.submit(1063); // Warning: Gnu indirect function cannot be converted // continue in next case?: case R_X86_64_32: case R_X86_64_32S: { // 32-bit absolute virtual address // Note: The linker doesn't accept a 32-bit absolute address // Make address relative to the image base, and add the value of the image base to compensate if (cmd.ImageBase == 0) { // Default image base if not specified cmd.ImageBase = 0x400000; } // Make subtractor relocation entry for image base relsub.r_address = r_address; relsub.r_symbolnum = GetImagebaseSymbol(); relsub.r_length = 2; relsub.r_extern = 1; relsub.r_type = MAC64_RELOC_SUBTRACTOR; NewRelocationTab.Push(&relsub, sizeof(relsub)); NewHeader.nreloc++; // Add image base to compensate for subtracted image base *piaddend += cmd.ImageBase; // Now we can add the address we really want: r_type = MAC64_RELOC_UNSIGNED; r_length = 2; // Warn that image base must be set to the specified value char ImageBaseHex[32]; sprintf(ImageBaseHex, "%X", cmd.ImageBase); // write value as hexadecimal err.submit(1300, ImageBaseHex); err.ClearError(1300); break;} case R_X86_64_PC32: // 32-bit self-relative r_type = MAC64_RELOC_BRANCH; // MAC64_RELOC_SIGNED does the same, but the linker complains if external symbol r_length = 2; r_pcrel = 1; // Difference between EIP-relative and self-relative relocation = size of address field // Adjust inline addend for different relocation method: *piaddend += 4; break; case R_UNSUPPORTED_IMAGEREL: // 32-bit image-relative // This occurs only when converting from COFF (via ELF) // Make subtractor relocation entry for image base relsub.r_address = r_address; relsub.r_symbolnum = GetImagebaseSymbol(); relsub.r_length = 2; relsub.r_extern = 1; relsub.r_type = MAC64_RELOC_SUBTRACTOR; NewRelocationTab.Push(&relsub, sizeof(relsub)); NewHeader.nreloc++; // Second record adds the target address r_type = MAC64_RELOC_UNSIGNED; r_length = 2; break; case R_X86_64_GLOB_DAT: case R_X86_64_GOTPCREL: // Create 64-bit GOT entry?? r_type = MAC64_RELOC_GOT; r_length = 2; break; case R_X86_64_GOT32: // 32-bit GOT entry err.submit(2042); // cannot convert 32-bit GOT err.ClearError(2042); // report this error only once r_type = 0; break; case R_X86_64_PLT32: case R_X86_64_JUMP_SLOT: // procedure linkage table err.submit(2043); // cannot convert import table err.ClearError(2043); // report this error only once r_type = 0; break; case R_X86_64_COPY: case R_X86_64_RELATIVE: default: // Unknown or unsupported relocation method err.submit(2030, OldRelocation.r_type); r_type = 0; break; } // Make relocation entry MAC_relocation_info rel; memset(&rel, 0, sizeof(rel)); // Make non-scattered relocation entry rel.r_address = r_address; rel.r_symbolnum = r_symbolnum; rel.r_pcrel = r_pcrel; rel.r_length = r_length; rel.r_extern = r_extern; rel.r_type = r_type; // Store relocation entry NewRelocationTab.Push(&rel, sizeof(rel)); NewHeader.nreloc++; // Remember that symbol is used // if (SymbolsUsed && OldRelocation.r_type && NewRelocation.r_symbolnum < ?) { // SymbolsUsed[NewRelocation.r_symbolnum]++;} } // End of relocations loop } template void CELF2MAC::MakeSections() { // Convert subfunction: Make sections and relocation tables uint32 oldsec; // Section number in old file uint32 relsec; // Relocation section in old file TMAC_section NewHeader; // New section header TELF_SectionHeader OldHeader; // Old section header TELF_SectionHeader OldRelHeader;// Old relocation section header uint32 NewVirtualAddress = 0; // Virtual address of new section uint32 NewRawDataOffset = 0; // Offset into NewRawData of section. // NewRawDataOffset is different from NewVirtualAddress if alignment of sections in // the object file is different from alignment of sections in memory // Count cumulative number of symbols in each scope NumSymbols[0] = 0; NumSymbols[1] = NumSymbols[0] + NewSymTab[0].GetNumEntries(); NumSymbols[2] = NumSymbols[1] + NewSymTab[1].GetNumEntries(); NumSymbols[3] = NumSymbols[2] + NewSymTab[2].GetNumEntries(); if (NumSymbols[3] >= 0x1000000) err.submit(2051); // Too many symbols, max = 2^24 NewSectHeadOffset = ToFile.GetDataSize(); // Second loop through old sections for (oldsec = 0; oldsec < this->NSections; oldsec++) { // Copy old header for convenience OldHeader = this->SectionHeaders[oldsec]; if (OldHeader.sh_size == 0) { // Remove empty section // The linker has a bug with empty sections continue; } // Search for program data sections only if (OldHeader.sh_type == SHT_PROGBITS || OldHeader.sh_type == SHT_NOBITS) { // Reset new section header memset(&NewHeader, 0, sizeof(NewHeader)); // Section name const char * sname = ""; uint32 namei = OldHeader.sh_name; if (namei >= this->SecStringTableLen) err.submit(2112); else sname = this->SecStringTable + namei; // Translate section name and truncate to 16 characters if (!stricmp(sname,".text") || !stricmp(sname,"_text")) { strcpy(NewHeader.sectname, "__text"); strcpy(NewHeader.segname, "__TEXT"); } else if (!stricmp(sname,".data") || !stricmp(sname,"_data")) { strcpy(NewHeader.sectname, "__data"); strcpy(NewHeader.segname, "__DATA"); } else if (!strnicmp(sname+1,"bss", 3)) { strcpy(NewHeader.sectname, "__bss"); strcpy(NewHeader.segname, "__DATA"); } else if (!strnicmp(sname+1,"const", 5) || !strnicmp(sname+1,"rodata", 6)) { strcpy(NewHeader.sectname, "__const"); strcpy(NewHeader.segname, "__DATA"); } else if (!strnicmp(sname, ELF_CONSTRUCTOR_NAME, 5)) { // Constructors strcpy(NewHeader.sectname, MAC_CONSTRUCTOR_NAME); strcpy(NewHeader.segname, "__DATA"); NewHeader.flags = MAC_S_MOD_INIT_FUNC_POINTERS; } else if (OldHeader.sh_flags & SHF_EXECINSTR) { // Other code section if (strlen(NewHeader.sectname) > 16) err.submit(1040, NewHeader.sectname); // Warning: name truncated strncpy(NewHeader.sectname, sname, 16); strcpy(NewHeader.segname, "__TEXT"); } else { // Other data section. Truncate name to 16 characters if (strlen(NewHeader.sectname) > 16) err.submit(1040, NewHeader.sectname); // Warning: name truncated strncpy(NewHeader.sectname, sname, 16); strcpy(NewHeader.segname, "__DATA"); } if (NewHeader.sectname[0] == '.') { // Make sure name begins with '_' NewHeader.sectname[0] = '_'; } // Raw data NewHeader.size = OldHeader.sh_size; // section size in file // File to raw data for section NewHeader.offset = NewRawData.GetDataSize() + RawDataOffset; if (OldHeader.sh_size && OldHeader.sh_type != SHT_NOBITS) { // Not for .bss segment // Copy raw data NewRawDataOffset = NewRawData.Push(this->Buf()+(uint32)OldHeader.sh_offset, (uint32)OldHeader.sh_size); NewRawData.Align(4); } // Section flags if (OldHeader.sh_flags & SHF_EXECINSTR) { NewHeader.flags |= MAC_S_ATTR_PURE_INSTRUCTIONS | MAC_S_ATTR_SOME_INSTRUCTIONS; } else if (OldHeader.sh_type == SHT_NOBITS) { NewHeader.flags |= MAC_S_ZEROFILL; // .bss segment } // Alignment NewHeader.align = FloorLog2((uint32)OldHeader.sh_addralign); if (NewHeader.align > 12) NewHeader.align = 12; // What is the limit for highest alignment? int AlignBy = 1 << NewHeader.align; // Align memory address NewVirtualAddress = (NewVirtualAddress + AlignBy - 1) & -AlignBy; // Virtual memory address of new section NewHeader.addr = NewVirtualAddress; NewVirtualAddress += (uint32)OldHeader.sh_size; // Find relocation table for this section by searching through all sections for (relsec = 1; relsec < this->NSections; relsec++) { // Get section header OldRelHeader = this->SectionHeaders[relsec]; // Check if this is a relocations section referring to oldsec if ((OldRelHeader.sh_type == SHT_REL || OldRelHeader.sh_type == SHT_RELA) // if section is relocation && OldRelHeader.sh_info == oldsec) { // and if section refers to current section Elf2MacRelocations(OldRelHeader, NewHeader, NewRawDataOffset, oldsec); } } // End of search for relocation table // Align raw data for next section NewRawData.Align(4); // Fix v. 2.14: adjust NewVirtualAddress to match above alignment NewVirtualAddress = (NewVirtualAddress + 3) & MInt(-4); // Store section header in file ToFile.Push(&NewHeader, sizeof(NewHeader)); } // End of if section has program data } // End of loop through old sections } // End of function MakeSections template void CELF2MAC::FindUnusedSymbols() { // Check if symbols used, remove unused symbols // Allocate table OldSymbolScope and OldSymbolUsed NumOldSymbols = this->SymbolTableEntries; if (NumOldSymbols > 0 && NumOldSymbols < 0x1000000) { OldSymbolScope.SetNum(NumOldSymbols); OldSymbolScope.SetZero(); OldSymbolUsed.SetNum(NumOldSymbols); OldSymbolUsed.SetZero(); } // Loop through section headers for (uint32 sc = 0; sc < this->NSections; sc++) { uint32 entrysize = uint32(this->SectionHeaders[sc].sh_entsize); // printf("\n%2i Name: %-18s ", sc, SecStringTable + sheader.sh_name); if ((this->SectionHeaders[sc].sh_type==SHT_REL || this->SectionHeaders[sc].sh_type==SHT_RELA)) { // Relocation list int8 * reltab = this->Buf() + uint32(this->SectionHeaders[sc].sh_offset); int8 * reltabend = reltab + uint32(this->SectionHeaders[sc].sh_size); uint32 expectedentrysize = this->SectionHeaders[sc].sh_type == SHT_RELA ? sizeof(TELF_Relocation) : // Elf32_Rela, Elf64_Rela sizeof(TELF_Relocation) - this->WordSize/8; // Elf32_Rel, Elf64_Rel if (entrysize < expectedentrysize) {err.submit(2033); entrysize = expectedentrysize;} // Loop through entries for (; reltab < reltabend; reltab += entrysize) { int isymbol = ((TELF_Relocation*)reltab)->r_sym; // printf("\n>SymbolUsed: %i, Name: %s",isymbol,SymbolName(isymbol)); // Remember symbol used OldSymbolUsed[isymbol]++; } } } } template void CELF2MAC::MakeBinaryFile() { // Convert subfunction: Putting sections together // File header, segment command and section headers have been inserted. int i; // Update segment header for segment size in file ((TMAC_segment_command*)(ToFile.Buf()+CommandOffset))->filesize = NewRawData.GetDataSize(); // Make symbol table command MAC_symtab_command symtab; memset(&symtab, 0, sizeof(symtab)); symtab.cmd = MAC_LC_SYMTAB; symtab.cmdsize = sizeof(symtab); // symoff, nsyms, stroff, strsize inserted later // Store symtab command NewSymtabOffset = ToFile.Push(&symtab, sizeof(symtab)); // Make MAC_dysymtab_command command MAC_dysymtab_command dysymtab; memset(&dysymtab, 0, sizeof(dysymtab)); dysymtab.cmd = MAC_LC_DYSYMTAB; dysymtab.cmdsize = sizeof(dysymtab); dysymtab.ilocalsym = 0; // index to local symbols dysymtab.nlocalsym = NewSymTab[0].GetNumEntries(); // number of local symbols dysymtab.iextdefsym = dysymtab.nlocalsym; // index to externally defined symbols dysymtab.nextdefsym = NewSymTab[1].GetNumEntries(); // number of externally defined symbols dysymtab.iundefsym = dysymtab.iextdefsym + dysymtab.nextdefsym; // index to public symbols dysymtab.nundefsym = NewSymTab[2].GetNumEntries(); // number of public symbols // Store MAC_dysymtab_command command ToFile.Push(&dysymtab, sizeof(dysymtab)); // Store section data uint32 Current = ToFile.Push(NewRawData.Buf(), NewRawData.GetDataSize()); if (Current != RawDataOffset) err.submit(9000); ToFile.Align(4); // Store relocation tables uint32 Reltabs = ToFile.Push(NewRelocationTab.Buf(), NewRelocationTab.GetDataSize()); // Initialize new string table. First string is empty NewStringTable.Push(0, 1); // Store symbol tables and make string table // Tables are not sorted alphabetically yet. This will be done subsequently // by CMAC2MAC uint32 Symtabs = ToFile.GetDataSize(); uint32 NumSyms = 0; for (i = 0; i < 3; i++) { NumSyms += NewSymTab[i].GetNumEntries(); NewSymTab[i].StoreList(&ToFile, &NewStringTable); } // Store string table uint32 StringTab = ToFile.Push(NewStringTable.Buf(), NewStringTable.GetDataSize()); // Set missing values in file header ((TMAC_header*)ToFile.Buf())->sizeofcmds = RawDataOffset - sizeof(TMAC_header); // Adjust relocation offsets in section headers TMAC_section* sectp = (TMAC_section*)(ToFile.Buf() + NewSectHeadOffset); for (i = 0; i < NumSectionsNew; i++, sectp++) { sectp->reloff += Reltabs; } // Set missing symoff, nsyms, stroff, strsize in symtab command MAC_symtab_command * symtabp = (MAC_symtab_command*)(ToFile.Buf() + NewSymtabOffset); symtabp->symoff = Symtabs; symtabp->nsyms = NumSyms; symtabp->stroff = StringTab; symtabp->strsize = NewStringTable.GetDataSize(); } template int CELF2MAC::GetImagebaseSymbol() { // Get symbol table index of __mh_execute_header = image base // Create this symbol table entry if it doesn't exist const char * ImageBaseName = "__mh_execute_header"; static int ImagebaseSymbol = -1; if (ImagebaseSymbol >= 0) { // Found previously return ImagebaseSymbol; } // Search for name among external symbols int index2 = NewSymTab[2].Search(ImageBaseName); if (index2 >= 0) { // found ImagebaseSymbol = index2 + NumSymbols[2]; return ImagebaseSymbol; } // Not found. Create symbol NewSymTab[2].AddSymbol(NumOldSymbols, ImageBaseName, MAC_N_EXT, 0, 0, 0); ImagebaseSymbol = NewSymTab[2].TranslateIndex(NumOldSymbols) + NumSymbols[2]; NumSymbols[3]++; return ImagebaseSymbol; } // Make template instances for 32 and 64 bits template class CELF2MAC; template class CELF2MAC;