| //===- MachOReader.cpp ------------------------------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "MachOReader.h" |
| #include "../llvm-objcopy.h" |
| #include "Object.h" |
| #include "llvm/BinaryFormat/MachO.h" |
| #include "llvm/Object/MachO.h" |
| #include <memory> |
| |
| namespace llvm { |
| namespace objcopy { |
| namespace macho { |
| |
| void MachOReader::readHeader(Object &O) const { |
| O.Header.Magic = MachOObj.getHeader().magic; |
| O.Header.CPUType = MachOObj.getHeader().cputype; |
| O.Header.CPUSubType = MachOObj.getHeader().cpusubtype; |
| O.Header.FileType = MachOObj.getHeader().filetype; |
| O.Header.NCmds = MachOObj.getHeader().ncmds; |
| O.Header.SizeOfCmds = MachOObj.getHeader().sizeofcmds; |
| O.Header.Flags = MachOObj.getHeader().flags; |
| } |
| |
| template <typename SectionType> |
| Section constructSectionCommon(SectionType Sec) { |
| Section S; |
| S.Sectname = |
| StringRef(Sec.sectname, strnlen(Sec.sectname, sizeof(Sec.sectname))) |
| .str(); |
| S.Segname = |
| StringRef(Sec.segname, strnlen(Sec.segname, sizeof(Sec.sectname))).str(); |
| S.Addr = Sec.addr; |
| S.Size = Sec.size; |
| S.Offset = Sec.offset; |
| S.Align = Sec.align; |
| S.RelOff = Sec.reloff; |
| S.NReloc = Sec.nreloc; |
| S.Flags = Sec.flags; |
| S.Reserved1 = Sec.reserved1; |
| S.Reserved2 = Sec.reserved2; |
| S.Reserved3 = 0; |
| return S; |
| } |
| |
| template <typename SectionType> Section constructSection(SectionType Sec); |
| |
| template <> Section constructSection(MachO::section Sec) { |
| return constructSectionCommon(Sec); |
| } |
| |
| template <> Section constructSection(MachO::section_64 Sec) { |
| Section S = constructSectionCommon(Sec); |
| S.Reserved3 = Sec.reserved3; |
| return S; |
| } |
| |
| // TODO: get rid of reportError and make MachOReader return Expected<> instead. |
| template <typename SectionType, typename SegmentType> |
| std::vector<Section> |
| extractSections(const object::MachOObjectFile::LoadCommandInfo &LoadCmd, |
| const object::MachOObjectFile &MachOObj, |
| size_t &NextSectionIndex) { |
| auto End = LoadCmd.Ptr + LoadCmd.C.cmdsize; |
| const SectionType *Curr = |
| reinterpret_cast<const SectionType *>(LoadCmd.Ptr + sizeof(SegmentType)); |
| std::vector<Section> Sections; |
| for (; reinterpret_cast<const void *>(Curr) < End; Curr++) { |
| if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) { |
| SectionType Sec; |
| memcpy((void *)&Sec, Curr, sizeof(SectionType)); |
| MachO::swapStruct(Sec); |
| Sections.push_back(constructSection(Sec)); |
| } else { |
| Sections.push_back(constructSection(*Curr)); |
| } |
| |
| Section &S = Sections.back(); |
| |
| Expected<object::SectionRef> SecRef = |
| MachOObj.getSection(NextSectionIndex++); |
| if (!SecRef) |
| reportError(MachOObj.getFileName(), SecRef.takeError()); |
| |
| if (Expected<ArrayRef<uint8_t>> E = |
| MachOObj.getSectionContents(SecRef->getRawDataRefImpl())) |
| S.Content = |
| StringRef(reinterpret_cast<const char *>(E->data()), E->size()); |
| else |
| reportError(MachOObj.getFileName(), E.takeError()); |
| |
| S.Relocations.reserve(S.NReloc); |
| for (auto RI = MachOObj.section_rel_begin(SecRef->getRawDataRefImpl()), |
| RE = MachOObj.section_rel_end(SecRef->getRawDataRefImpl()); |
| RI != RE; ++RI) { |
| RelocationInfo R; |
| R.Symbol = nullptr; // We'll fill this field later. |
| R.Info = MachOObj.getRelocation(RI->getRawDataRefImpl()); |
| R.Scattered = MachOObj.isRelocationScattered(R.Info); |
| S.Relocations.push_back(R); |
| } |
| |
| assert(S.NReloc == S.Relocations.size() && |
| "Incorrect number of relocations"); |
| } |
| return Sections; |
| } |
| |
| void MachOReader::readLoadCommands(Object &O) const { |
| // For MachO sections indices start from 1. |
| size_t NextSectionIndex = 1; |
| for (auto LoadCmd : MachOObj.load_commands()) { |
| LoadCommand LC; |
| switch (LoadCmd.C.cmd) { |
| case MachO::LC_SEGMENT: |
| LC.Sections = extractSections<MachO::section, MachO::segment_command>( |
| LoadCmd, MachOObj, NextSectionIndex); |
| break; |
| case MachO::LC_SEGMENT_64: |
| LC.Sections = |
| extractSections<MachO::section_64, MachO::segment_command_64>( |
| LoadCmd, MachOObj, NextSectionIndex); |
| break; |
| case MachO::LC_SYMTAB: |
| O.SymTabCommandIndex = O.LoadCommands.size(); |
| break; |
| case MachO::LC_DYSYMTAB: |
| O.DySymTabCommandIndex = O.LoadCommands.size(); |
| break; |
| case MachO::LC_DYLD_INFO: |
| case MachO::LC_DYLD_INFO_ONLY: |
| O.DyLdInfoCommandIndex = O.LoadCommands.size(); |
| break; |
| case MachO::LC_DATA_IN_CODE: |
| O.DataInCodeCommandIndex = O.LoadCommands.size(); |
| break; |
| case MachO::LC_FUNCTION_STARTS: |
| O.FunctionStartsCommandIndex = O.LoadCommands.size(); |
| break; |
| } |
| #define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ |
| case MachO::LCName: \ |
| memcpy((void *)&(LC.MachOLoadCommand.LCStruct##_data), LoadCmd.Ptr, \ |
| sizeof(MachO::LCStruct)); \ |
| if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) \ |
| MachO::swapStruct(LC.MachOLoadCommand.LCStruct##_data); \ |
| LC.Payload = ArrayRef<uint8_t>( \ |
| reinterpret_cast<uint8_t *>(const_cast<char *>(LoadCmd.Ptr)) + \ |
| sizeof(MachO::LCStruct), \ |
| LoadCmd.C.cmdsize - sizeof(MachO::LCStruct)); \ |
| break; |
| |
| switch (LoadCmd.C.cmd) { |
| default: |
| memcpy((void *)&(LC.MachOLoadCommand.load_command_data), LoadCmd.Ptr, |
| sizeof(MachO::load_command)); |
| if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) |
| MachO::swapStruct(LC.MachOLoadCommand.load_command_data); |
| LC.Payload = ArrayRef<uint8_t>( |
| reinterpret_cast<uint8_t *>(const_cast<char *>(LoadCmd.Ptr)) + |
| sizeof(MachO::load_command), |
| LoadCmd.C.cmdsize - sizeof(MachO::load_command)); |
| break; |
| #include "llvm/BinaryFormat/MachO.def" |
| } |
| O.LoadCommands.push_back(std::move(LC)); |
| } |
| } |
| |
| template <typename nlist_t> |
| SymbolEntry constructSymbolEntry(StringRef StrTable, const nlist_t &nlist) { |
| assert(nlist.n_strx < StrTable.size() && |
| "n_strx exceeds the size of the string table"); |
| SymbolEntry SE; |
| SE.Name = StringRef(StrTable.data() + nlist.n_strx).str(); |
| SE.n_type = nlist.n_type; |
| SE.n_sect = nlist.n_sect; |
| SE.n_desc = nlist.n_desc; |
| SE.n_value = nlist.n_value; |
| return SE; |
| } |
| |
| void MachOReader::readSymbolTable(Object &O) const { |
| StringRef StrTable = MachOObj.getStringTableData(); |
| for (auto Symbol : MachOObj.symbols()) { |
| SymbolEntry SE = |
| (MachOObj.is64Bit() |
| ? constructSymbolEntry( |
| StrTable, |
| MachOObj.getSymbol64TableEntry(Symbol.getRawDataRefImpl())) |
| : constructSymbolEntry( |
| StrTable, |
| MachOObj.getSymbolTableEntry(Symbol.getRawDataRefImpl()))); |
| |
| O.SymTable.Symbols.push_back(std::make_unique<SymbolEntry>(SE)); |
| } |
| } |
| |
| void MachOReader::setSymbolInRelocationInfo(Object &O) const { |
| for (auto &LC : O.LoadCommands) |
| for (auto &Sec : LC.Sections) |
| for (auto &Reloc : Sec.Relocations) |
| if (!Reloc.Scattered) { |
| auto *Info = reinterpret_cast<MachO::relocation_info *>(&Reloc.Info); |
| Reloc.Symbol = O.SymTable.getSymbolByIndex(Info->r_symbolnum); |
| } |
| } |
| |
| void MachOReader::readRebaseInfo(Object &O) const { |
| O.Rebases.Opcodes = MachOObj.getDyldInfoRebaseOpcodes(); |
| } |
| |
| void MachOReader::readBindInfo(Object &O) const { |
| O.Binds.Opcodes = MachOObj.getDyldInfoBindOpcodes(); |
| } |
| |
| void MachOReader::readWeakBindInfo(Object &O) const { |
| O.WeakBinds.Opcodes = MachOObj.getDyldInfoWeakBindOpcodes(); |
| } |
| |
| void MachOReader::readLazyBindInfo(Object &O) const { |
| O.LazyBinds.Opcodes = MachOObj.getDyldInfoLazyBindOpcodes(); |
| } |
| |
| void MachOReader::readExportInfo(Object &O) const { |
| O.Exports.Trie = MachOObj.getDyldInfoExportsTrie(); |
| } |
| |
| void MachOReader::readDataInCodeData(Object &O) const { |
| if (!O.DataInCodeCommandIndex) |
| return; |
| const MachO::linkedit_data_command &LDC = |
| O.LoadCommands[*O.DataInCodeCommandIndex] |
| .MachOLoadCommand.linkedit_data_command_data; |
| |
| O.DataInCode.Data = arrayRefFromStringRef( |
| MachOObj.getData().substr(LDC.dataoff, LDC.datasize)); |
| } |
| |
| void MachOReader::readFunctionStartsData(Object &O) const { |
| if (!O.FunctionStartsCommandIndex) |
| return; |
| const MachO::linkedit_data_command &LDC = |
| O.LoadCommands[*O.FunctionStartsCommandIndex] |
| .MachOLoadCommand.linkedit_data_command_data; |
| |
| O.FunctionStarts.Data = arrayRefFromStringRef( |
| MachOObj.getData().substr(LDC.dataoff, LDC.datasize)); |
| } |
| |
| void MachOReader::readIndirectSymbolTable(Object &O) const { |
| MachO::dysymtab_command DySymTab = MachOObj.getDysymtabLoadCommand(); |
| for (uint32_t i = 0; i < DySymTab.nindirectsyms; ++i) |
| O.IndirectSymTable.Symbols.push_back( |
| MachOObj.getIndirectSymbolTableEntry(DySymTab, i)); |
| } |
| |
| std::unique_ptr<Object> MachOReader::create() const { |
| auto Obj = std::make_unique<Object>(); |
| readHeader(*Obj); |
| readLoadCommands(*Obj); |
| readSymbolTable(*Obj); |
| setSymbolInRelocationInfo(*Obj); |
| readRebaseInfo(*Obj); |
| readBindInfo(*Obj); |
| readWeakBindInfo(*Obj); |
| readLazyBindInfo(*Obj); |
| readExportInfo(*Obj); |
| readDataInCodeData(*Obj); |
| readFunctionStartsData(*Obj); |
| readIndirectSymbolTable(*Obj); |
| return Obj; |
| } |
| |
| } // end namespace macho |
| } // end namespace objcopy |
| } // end namespace llvm |