Index: llvm/test/tools/llvm-objcopy/MachO/copy-relocations.s =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/MachO/copy-relocations.s @@ -0,0 +1,30 @@ +# REQUIRES: x86-registered-target + +## Show that llvm-objcopy copies relocation entries where r_extern=0. +# RUN: llvm-mc -assemble -triple x86_64-apple-darwin19 -filetype=obj %s -o %t +# RUN: llvm-objcopy %t %t2 +# RUN: cmp %t %t2 + +## Show that llvm-objcopy updates section indices properly. +# RUN: llvm-objcopy --remove-section=__DATA,__foo %t %t3 +# RUN: llvm-objdump --macho --reloc %t3 | FileCheck %s + +# $ llvm-objdump --macho --reloc copy-relocations.o +# Relocation information (__DATA,__bar) 2 entries +# address pcrel length extern type scattered symbolnum/value +# 00000000 False quad False SUB False 3 (__DATA,__bar) +# 00000000 False quad False UNSIGND False 1 (__TEXT,__text) +.text +L1: + .quad 0 + +.section __DATA,__foo + .quad 0 + +.section __DATA,__bar + .quad L1 - . + +# CHECK: Relocation information (__DATA,__bar) 2 entries +# CHECK-NEXT: address pcrel length extern type scattered symbolnum/value +# CHECK-NEXT: 00000000 False quad False SUB False 2 (__DATA,__bar) +# CHECK-NEXT: 00000000 False quad False UNSIGND False 1 (__TEXT,__text) Index: llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp =================================================================== --- llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp +++ llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp @@ -142,27 +142,27 @@ uint64_t SegOffset = Offset; uint64_t SegFileSize = 0; uint64_t VMSize = 0; - for (auto &Sec : LC.Sections) { + for (std::unique_ptr
&Sec : LC.Sections) { if (IsObjectFile) { - if (Sec.isVirtualSection()) { - Sec.Offset = 0; + if (Sec->isVirtualSection()) { + Sec->Offset = 0; } else { uint64_t PaddingSize = - offsetToAlignment(SegFileSize, Align(1ull << Sec.Align)); - Sec.Offset = SegOffset + SegFileSize + PaddingSize; - Sec.Size = Sec.Content.size(); - SegFileSize += PaddingSize + Sec.Size; + offsetToAlignment(SegFileSize, Align(1ull << Sec->Align)); + Sec->Offset = SegOffset + SegFileSize + PaddingSize; + Sec->Size = Sec->Content.size(); + SegFileSize += PaddingSize + Sec->Size; } - VMSize = std::max(VMSize, Sec.Addr + Sec.Size); + VMSize = std::max(VMSize, Sec->Addr + Sec->Size); } else { - if (Sec.isVirtualSection()) { - Sec.Offset = 0; - VMSize += Sec.Size; + if (Sec->isVirtualSection()) { + Sec->Offset = 0; + VMSize += Sec->Size; } else { - uint32_t SectOffset = Sec.Addr - SegmentVmAddr; - Sec.Offset = SegOffset + SectOffset; - Sec.Size = Sec.Content.size(); - SegFileSize = std::max(SegFileSize, SectOffset + Sec.Size); + uint32_t SectOffset = Sec->Addr - SegmentVmAddr; + Sec->Offset = SegOffset + SectOffset; + Sec->Size = Sec->Content.size(); + SegFileSize = std::max(SegFileSize, SectOffset + Sec->Size); VMSize = std::max(VMSize, SegFileSize); } } @@ -205,10 +205,10 @@ uint64_t MachOLayoutBuilder::layoutRelocations(uint64_t Offset) { for (auto &LC : O.LoadCommands) - for (auto &Sec : LC.Sections) { - Sec.RelOff = Sec.Relocations.empty() ? 0 : Offset; - Sec.NReloc = Sec.Relocations.size(); - Offset += sizeof(MachO::any_relocation_info) * Sec.NReloc; + for (std::unique_ptr
&Sec : LC.Sections) { + Sec->RelOff = Sec->Relocations.empty() ? 0 : Offset; + Sec->NReloc = Sec->Relocations.size(); + Offset += sizeof(MachO::any_relocation_info) * Sec->NReloc; } return Offset; Index: llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp =================================================================== --- llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp +++ llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp @@ -18,21 +18,23 @@ namespace macho { using namespace object; -using SectionPred = std::function; +using SectionPred = std::function &Sec)>; static void removeSections(const CopyConfig &Config, Object &Obj) { - SectionPred RemovePred = [](const Section &) { return false; }; + SectionPred RemovePred = [](const std::unique_ptr
&) { + return false; + }; if (!Config.ToRemove.empty()) { - RemovePred = [&Config, RemovePred](const Section &Sec) { - return Config.ToRemove.matches(Sec.CanonicalName); + RemovePred = [&Config, RemovePred](const std::unique_ptr
&Sec) { + return Config.ToRemove.matches(Sec->CanonicalName); }; } if (Config.StripAll || Config.StripDebug) { // Remove all debug sections. - RemovePred = [RemovePred](const Section &Sec) { - if (Sec.Segname == "__DWARF") + RemovePred = [RemovePred](const std::unique_ptr
&Sec) { + if (Sec->Segname == "__DWARF") return true; return RemovePred(Sec); @@ -41,8 +43,8 @@ if (!Config.OnlySection.empty()) { // Overwrite RemovePred because --only-section takes priority. - RemovePred = [&Config](const Section &Sec) { - return !Config.OnlySection.matches(Sec.CanonicalName); + RemovePred = [&Config](const std::unique_ptr
&Sec) { + return !Config.OnlySection.matches(Sec->CanonicalName); }; } @@ -87,14 +89,14 @@ static Error dumpSectionToFile(StringRef SecName, StringRef Filename, Object &Obj) { for (LoadCommand &LC : Obj.LoadCommands) - for (Section &Sec : LC.Sections) { - if (Sec.CanonicalName == SecName) { + for (std::unique_ptr
&Sec : LC.Sections) { + if (Sec->CanonicalName == SecName) { Expected> BufferOrErr = - FileOutputBuffer::create(Filename, Sec.Content.size()); + FileOutputBuffer::create(Filename, Sec->Content.size()); if (!BufferOrErr) return BufferOrErr.takeError(); std::unique_ptr Buf = std::move(*BufferOrErr); - llvm::copy(Sec.Content, Buf->getBufferStart()); + llvm::copy(Sec->Content, Buf->getBufferStart()); if (Error E = Buf->commit()) return E; @@ -122,7 +124,7 @@ for (LoadCommand &LC : Obj.LoadCommands) { Optional SegName = LC.getSegmentName(); if (SegName && SegName == TargetSegName) { - LC.Sections.push_back(Sec); + LC.Sections.push_back(std::make_unique
(Sec)); return Error::success(); } } @@ -130,7 +132,7 @@ // There's no segment named TargetSegName. Create a new load command and // Insert a new section into it. LoadCommand &NewSegment = Obj.addSegment(TargetSegName); - NewSegment.Sections.push_back(Sec); + NewSegment.Sections.push_back(std::make_unique
(Sec)); return Error::success(); } @@ -187,8 +189,8 @@ if (Config.StripAll) for (LoadCommand &LC : Obj.LoadCommands) - for (Section &Sec : LC.Sections) - Sec.Relocations.clear(); + for (std::unique_ptr
&Sec : LC.Sections) + Sec->Relocations.clear(); for (const StringRef &Flag : Config.DumpSection) { std::pair SecPair = Flag.split("="); Index: llvm/tools/llvm-objcopy/MachO/MachOReader.cpp =================================================================== --- llvm/tools/llvm-objcopy/MachO/MachOReader.cpp +++ llvm/tools/llvm-objcopy/MachO/MachOReader.cpp @@ -59,25 +59,25 @@ // TODO: get rid of reportError and make MachOReader return Expected<> instead. template -std::vector
+std::vector> 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(LoadCmd.Ptr + sizeof(SegmentType)); - std::vector
Sections; + std::vector> Sections; for (; reinterpret_cast(Curr) < End; Curr++) { if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) { SectionType Sec; memcpy((void *)&Sec, Curr, sizeof(SectionType)); MachO::swapStruct(Sec); - Sections.push_back(constructSection(Sec)); + Sections.push_back(std::make_unique
(constructSection(Sec))); } else { - Sections.push_back(constructSection(*Curr)); + Sections.push_back(std::make_unique
(constructSection(*Curr))); } - Section &S = Sections.back(); + std::unique_ptr
&S = Sections.back(); Expected SecRef = MachOObj.getSection(NextSectionIndex++); @@ -86,12 +86,12 @@ if (Expected> E = MachOObj.getSectionContents(SecRef->getRawDataRefImpl())) - S.Content = + S->Content = StringRef(reinterpret_cast(E->data()), E->size()); else reportError(MachOObj.getFileName(), E.takeError()); - S.Relocations.reserve(S.NReloc); + S->Relocations.reserve(S->NReloc); for (auto RI = MachOObj.section_rel_begin(SecRef->getRawDataRefImpl()), RE = MachOObj.section_rel_end(SecRef->getRawDataRefImpl()); RI != RE; ++RI) { @@ -99,10 +99,12 @@ 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); + R.Extern = + R.Scattered ? false : MachOObj.getPlainRelocationExternal(R.Info); + S->Relocations.push_back(R); } - assert(S.NReloc == S.Relocations.size() && + assert(S->NReloc == S->Relocations.size() && "Incorrect number of relocations"); } return Sections; @@ -201,12 +203,24 @@ } void MachOReader::setSymbolInRelocationInfo(Object &O) const { + std::vector Sections; + + for (auto &LC : O.LoadCommands) + for (std::unique_ptr
&Sec : LC.Sections) + Sections.push_back(Sec.get()); + for (auto &LC : O.LoadCommands) for (auto &Sec : LC.Sections) - for (auto &Reloc : Sec.Relocations) + for (auto &Reloc : Sec->Relocations) if (!Reloc.Scattered) { auto *Info = reinterpret_cast(&Reloc.Info); - Reloc.Symbol = O.SymTable.getSymbolByIndex(Info->r_symbolnum); + if (Reloc.Extern) + Reloc.Symbol = O.SymTable.getSymbolByIndex(Info->r_symbolnum); + else { + assert(Info->r_symbolnum <= Sections.size() && + "Invalid section index."); + Reloc.Section = Sections[Info->r_symbolnum - 1]; + } } } Index: llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp =================================================================== --- llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp +++ llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp @@ -112,10 +112,10 @@ // Otherwise, use the last section / reloction. for (const auto &LC : O.LoadCommands) for (const auto &S : LC.Sections) { - Ends.push_back(S.Offset + S.Size); - if (S.RelOff) - Ends.push_back(S.RelOff + - S.NReloc * sizeof(MachO::any_relocation_info)); + Ends.push_back(S->Offset + S->Size); + if (S->RelOff) + Ends.push_back(S->RelOff + + S->NReloc * sizeof(MachO::any_relocation_info)); } if (!Ends.empty()) @@ -157,8 +157,8 @@ memcpy(Begin, &MLC.segment_command_data, sizeof(MachO::segment_command)); Begin += sizeof(MachO::segment_command); - for (const auto &Sec : LC.Sections) - writeSectionInLoadCommand(Sec, Begin); + for (const std::unique_ptr
&Sec : LC.Sections) + writeSectionInLoadCommand(*Sec, Begin); continue; case MachO::LC_SEGMENT_64: if (IsLittleEndian != sys::IsLittleEndianHost) @@ -167,8 +167,8 @@ sizeof(MachO::segment_command_64)); Begin += sizeof(MachO::segment_command_64); - for (const auto &Sec : LC.Sections) - writeSectionInLoadCommand(Sec, Begin); + for (const std::unique_ptr
&Sec : LC.Sections) + writeSectionInLoadCommand(*Sec, Begin); continue; } @@ -229,27 +229,35 @@ } void MachOWriter::writeSections() { + uint32_t SectionIndex = 1; for (const auto &LC : O.LoadCommands) - for (const auto &Sec : LC.Sections) { - if (Sec.isVirtualSection()) + for (const std::unique_ptr
&Sec : LC.Sections) + Sec->Index = SectionIndex++; + + for (const auto &LC : O.LoadCommands) + for (const std::unique_ptr
&Sec : LC.Sections) { + if (Sec->isVirtualSection()) continue; - assert(Sec.Offset && "Section offset can not be zero"); - assert((Sec.Size == Sec.Content.size()) && "Incorrect section size"); - memcpy(B.getBufferStart() + Sec.Offset, Sec.Content.data(), - Sec.Content.size()); - for (size_t Index = 0; Index < Sec.Relocations.size(); ++Index) { - auto RelocInfo = Sec.Relocations[Index]; + assert(Sec->Offset && "Section offset can not be zero"); + assert((Sec->Size == Sec->Content.size()) && "Incorrect section size"); + memcpy(B.getBufferStart() + Sec->Offset, Sec->Content.data(), + Sec->Content.size()); + for (size_t Index = 0; Index < Sec->Relocations.size(); ++Index) { + auto RelocInfo = Sec->Relocations[Index]; if (!RelocInfo.Scattered) { auto *Info = reinterpret_cast(&RelocInfo.Info); - Info->r_symbolnum = RelocInfo.Symbol->Index; + if (RelocInfo.Extern) + Info->r_symbolnum = (*RelocInfo.Symbol)->Index; + else + Info->r_symbolnum = (*RelocInfo.Section)->Index; } if (IsLittleEndian != sys::IsLittleEndianHost) MachO::swapStruct( reinterpret_cast(RelocInfo.Info)); - memcpy(B.getBufferStart() + Sec.RelOff + + memcpy(B.getBufferStart() + Sec->RelOff + Index * sizeof(MachO::any_relocation_info), &RelocInfo.Info, sizeof(RelocInfo.Info)); } Index: llvm/tools/llvm-objcopy/MachO/Object.h =================================================================== --- llvm/tools/llvm-objcopy/MachO/Object.h +++ llvm/tools/llvm-objcopy/MachO/Object.h @@ -37,6 +37,7 @@ struct RelocationInfo; struct Section { + uint32_t Index; std::string Segname; std::string Sectname; // CanonicalName is a string formatted as “,". @@ -83,13 +84,13 @@ // The raw content of the payload of the load command (located right after the // corresponding struct). In some cases it is either empty or can be // copied-over without digging into its structure. - std::vector Payload; + std::vector Payload; // Some load commands can contain (inside the payload) an array of sections, // though the contents of the sections are stored separately. The struct // Section describes only sections' metadata and where to find the // corresponding content inside the binary. - std::vector
Sections; + std::vector> Sections; // Returns the segment name if the load command is a segment command. Optional getSegmentName() const; @@ -157,9 +158,14 @@ }; struct RelocationInfo { - const SymbolEntry *Symbol; + // The referenced symbol entry. Set if !Scattered && Extern. + Optional Symbol; + // The referenced section. Set if !Scattered && !Extern. + Optional Section; // True if Info is a scattered_relocation_info. bool Scattered; + // True if the r_symbolnum points to a section number (i.e. r_extern=0). + bool Extern; MachO::any_relocation_info Info; }; @@ -292,7 +298,8 @@ Object() : NewSectionsContents(Alloc) {} - void removeSections(function_ref ToRemove); + void + removeSections(function_ref &)> ToRemove); void addLoadCommand(LoadCommand LC); /// Creates a new segment load command in the object and returns a reference Index: llvm/tools/llvm-objcopy/MachO/Object.cpp =================================================================== --- llvm/tools/llvm-objcopy/MachO/Object.cpp +++ llvm/tools/llvm-objcopy/MachO/Object.cpp @@ -22,7 +22,8 @@ std::end(Symbols)); } -void Object::removeSections(function_ref ToRemove) { +void Object::removeSections( + function_ref &)> ToRemove) { for (LoadCommand &LC : LoadCommands) LC.Sections.erase(std::remove_if(std::begin(LC.Sections), std::end(LC.Sections), ToRemove), @@ -52,7 +53,7 @@ constructSegment(LC.MachOLoadCommand.segment_command_data, MachO::LC_SEGMENT, SegName); - LoadCommands.push_back(LC); + LoadCommands.push_back(std::move(LC)); return LoadCommands.back(); }