diff --git a/llvm/test/tools/llvm-objcopy/MachO/add-section.test b/llvm/test/tools/llvm-objcopy/MachO/add-section.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/MachO/add-section.test @@ -0,0 +1,105 @@ +## Show that if llvm-objcopy adds a new section into the object if +## --add-section is given. + +# RUN: yaml2obj %s > %t +# RUN: echo -n abcdefg > %t.data + +# RUN: not llvm-objcopy --add-section __text=%t.data %t %t.invalid-section-name 2>&1 \ +# RUN: | FileCheck %s -DINPUT=%t --check-prefix=INVALID-SECTION-NAME +# INVALID-SECTION-NAME: error: '[[INPUT]]': invalid section name (should be formatted as ',
') + +## Case 1: Add a new section into an existing segment. +# RUN: llvm-objcopy --add-section __TEXT,__text=%t.data %t %t.out1 +# RUN: llvm-readobj --sections --section-data %t.out1 \ +# RUN: | FileCheck %s --check-prefixes=COMMON,CASE1 + +## Case 2: Add a new section into an nonexistent segment. +# RUN: llvm-objcopy --add-section __FOO,__bar=%t.data %t %t.out2 +# RUN: llvm-readobj --sections --section-data --macho-segment %t.out2 \ +# RUN: | FileCheck %s --check-prefixes=COMMON,CASE2 + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x00000003 + filetype: 0x00000001 + ncmds: 1 + sizeofcmds: 152 + flags: 0x00002000 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 152 + segname: '' + vmaddr: 0 + vmsize: 4 + fileoff: 184 + filesize: 4 + maxprot: 7 + initprot: 7 + nsects: 1 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000000000000 + content: 'AABBCCDD' + size: 4 + offset: 184 + align: 0 + reloff: 0x00000000 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + +# COMMON: Index: 1 +# CASE1-NEXT: Name: __TEXT,__text (5F 5F 54 45 58 54 2C 5F 5F 74 65 78 74 00 00 00) +# CASE1-NEXT: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00) +# CASE2-NEXT: Name: __FOO,__bar (5F 5F 46 4F 4F 2C 5F 5F 62 61 72 00 00 00 00 00) +# CASE2-NEXT: Segment: __FOO (5F 5F 46 4F 4F 00 00 00 00 00 00 00 00 00 00 00) +# COMMON-NEXT: Address: 0x0 +# COMMON-NEXT: Size: 0x7 +# COMMON-NEXT: Offset: 340 +# COMMON-NEXT: Alignment: 0 +# COMMON-NEXT: RelocationOffset: 0x0 +# COMMON-NEXT: RelocationCount: 0 +# COMMON-NEXT: Type: Regular (0x0) +# COMMON-NEXT: Attributes [ (0x0) +# COMMON-NEXT: ] +# COMMON-NEXT: Reserved1: 0x0 +# COMMON-NEXT: Reserved2: 0x0 +# COMMON-NEXT: Reserved3: 0x0 +# COMMON-NEXT: SectionData ( +# COMMON-NEXT: 0000: 61626364 656667 |abcdefg| +# COMMON-NEXT: ) + +## Make sure that new segment (__FOO) is created. +# CASE2: Segment { +# CASE2-NEXT: Cmd: LC_SEGMENT_64 +# CASE2-NEXT: Name: +# CASE2-NEXT: Size: 152 +# CASE2-NEXT: vmaddr: 0x0 +# CASE2-NEXT: vmsize: 0x4 +# CASE2-NEXT: fileoff: 336 +# CASE2-NEXT: filesize: 4 +# CASE2-NEXT: maxprot: rwx +# CASE2-NEXT: initprot: rwx +# CASE2-NEXT: nsects: 1 +# CASE2-NEXT: flags: 0x0 +# CASE2-NEXT: } +# CASE2-NEXT: Segment { +# CASE2-NEXT: Cmd: LC_SEGMENT_64 +# CASE2-NEXT: Name: __FOO +# CASE2-NEXT: Size: 152 +# CASE2-NEXT: vmaddr: 0x0 +# CASE2-NEXT: vmsize: 0x7 +# CASE2-NEXT: fileoff: 340 +# CASE2-NEXT: filesize: 7 +# CASE2-NEXT: maxprot: --- +# CASE2-NEXT: initprot: --- +# CASE2-NEXT: nsects: 1 +# CASE2-NEXT: flags: 0x0 +# CASE2-NEXT: } \ No newline at end of file diff --git a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp --- a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp @@ -65,6 +65,39 @@ Obj.SymTable.removeSymbols(RemovePred); } +static Error addSection(StringRef SecName, StringRef Filename, Object &Obj) { + ErrorOr> BufOrErr = + MemoryBuffer::getFile(Filename); + if (!BufOrErr) + return createFileError(Filename, errorCodeToError(BufOrErr.getError())); + std::unique_ptr Buf = std::move(*BufOrErr); + ArrayRef Content( + reinterpret_cast(Buf->getBufferStart()), + Buf->getBufferSize()); + + std::pair Pair = SecName.split(','); + StringRef TargetSegName = Pair.first; + Section Sec(TargetSegName, SecName); + Sec.setOwnedContentData(Content); + + for (LoadCommand &LC : Obj.LoadCommands) { + Optional SegName = LC.getSegmentName(); + if (SegName && SegName == TargetSegName) { + LC.Sections.push_back(Sec); + return Error::success(); + } + } + + // There's no segment named TargetSegName. Create a new load command and + // Insert a new section into it. + // TODO: 32-bit + ErrorOr NewSegment = Obj.addSegment64(TargetSegName); + if (!NewSegment) + return createFileError(Filename, errorCodeToError(BufOrErr.getError())); + NewSegment->Sections.push_back(Sec); + return Error::success(); +} + static Error validateOptions(const CopyConfig &Config) { // TODO: Support section renaming in GNU objcopy for compatibility (see // http://lists.llvm.org/pipermail/llvm-dev/2019-May/132570.html). @@ -88,12 +121,11 @@ if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() || Config.BuildIdLinkInput || Config.BuildIdLinkOutput || !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() || - !Config.AllocSectionsPrefix.empty() || !Config.AddSection.empty() || - !Config.DumpSection.empty() || !Config.KeepSection.empty() || - !Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() || - !Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() || - !Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() || - !Config.SymbolsToRename.empty() || + !Config.AllocSectionsPrefix.empty() || !Config.DumpSection.empty() || + !Config.KeepSection.empty() || !Config.SymbolsToGlobalize.empty() || + !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() || + !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || + !Config.SectionsToRename.empty() || !Config.SymbolsToRename.empty() || !Config.UnneededSymbolsToRemove.empty() || !Config.SetSectionFlags.empty() || Config.ExtractDWO || Config.KeepFileSymbols || Config.LocalizeHidden || Config.PreserveDates || @@ -113,6 +145,16 @@ markSymbols(Config, Obj); removeSymbols(Config, Obj); + for (const auto &Flag : Config.AddSection) { + std::pair SecPair = Flag.split("="); + StringRef SecName = SecPair.first; + StringRef File = SecPair.second; + if (Error E = isValidMachOCannonicalName(SecName)) + return E; + if (Error E = addSection(SecName, File, Obj)) + return E; + } + if (Config.StripAll) for (LoadCommand &LC : Obj.LoadCommands) for (Section &Sec : LC.Sections) diff --git a/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp b/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp --- a/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp @@ -29,13 +29,9 @@ template 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.CannonicalName = (Twine(S.Segname) + "," + S.Sectname).str(); + StringRef Sectname(Sec.sectname, strnlen(Sec.sectname, sizeof(Sec.sectname))); + StringRef Segname(Sec.segname, strnlen(Sec.segname, sizeof(Sec.sectname))); + Section S(Segname, Sectname); S.Addr = Sec.addr; S.Size = Sec.size; S.Offset = Sec.offset; diff --git a/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp b/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp --- a/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp @@ -233,9 +233,9 @@ 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()); + assert((Sec.Size == Sec.getContent().size()) && "Incorrect section size"); + memcpy(B.getBufferStart() + Sec.Offset, Sec.getContent().data(), + Sec.getContent().size()); for (size_t Index = 0; Index < Sec.Relocations.size(); ++Index) { auto RelocInfo = Sec.Relocations[Index]; if (!RelocInfo.Scattered) { diff --git a/llvm/tools/llvm-objcopy/MachO/Object.h b/llvm/tools/llvm-objcopy/MachO/Object.h --- a/llvm/tools/llvm-objcopy/MachO/Object.h +++ b/llvm/tools/llvm-objcopy/MachO/Object.h @@ -35,25 +35,51 @@ }; struct RelocationInfo; -struct Section { +class Section { +private: + std::vector OwnedContentData; + +public: std::string Sectname; std::string Segname; // CannonicalName is a string formatted as “,". std::string CannonicalName; - uint64_t Addr; - uint64_t Size; - uint32_t Offset; - uint32_t Align; - uint32_t RelOff; - uint32_t NReloc; - uint32_t Flags; - uint32_t Reserved1; - uint32_t Reserved2; - uint32_t Reserved3; - + uint64_t Addr = 0; + uint64_t Size = 0; + uint32_t Offset = 0; + uint32_t Align = 0; + uint32_t RelOff = 0; + uint32_t NReloc = 0; + uint32_t Flags = 0; + uint32_t Reserved1 = 0; + uint32_t Reserved2 = 0; + uint32_t Reserved3 = 0; StringRef Content; + std::vector Relocations; + Section(StringRef SegName, StringRef SectName) + : Sectname(SectName), Segname(SegName), + CannonicalName((Twine(SegName) + Twine(',') + SectName).str()) {} + + Section(StringRef SegName, StringRef SectName, StringRef Content) + : Sectname(SectName), Segname(SegName), + CannonicalName((Twine(SegName) + Twine(',') + SectName).str()), + Content(Content) {} + + void setOwnedContentData(ArrayRef Data) { + OwnedContentData.assign(Data.begin(), Data.end()); + Content = StringRef(reinterpret_cast(OwnedContentData.data()), + OwnedContentData.size()); + } + + StringRef getContent() const { + if (OwnedContentData.size() > 0) + return StringRef(reinterpret_cast(OwnedContentData.data()), + OwnedContentData.size()); + return Content; + } + MachO::SectionType getType() const { return static_cast(Flags & MachO::SECTION_TYPE); } @@ -81,6 +107,9 @@ // Section describes only sections' metadata and where to find the // corresponding content inside the binary. std::vector
Sections; + + // Returns the segment name if the load command is a segment command. + Optional getSegmentName() const; }; // A symbol information. Fields which starts with "n_" are same as them in the @@ -268,6 +297,10 @@ Optional FunctionStartsCommandIndex; void removeSections(function_ref ToRemove); + + /// Creates a new segment_command_64 in the object and returns a reference + /// to the newly created load command. + LoadCommand &addSegment64(StringRef SegName); }; } // end namespace macho diff --git a/llvm/tools/llvm-objcopy/MachO/Object.cpp b/llvm/tools/llvm-objcopy/MachO/Object.cpp --- a/llvm/tools/llvm-objcopy/MachO/Object.cpp +++ b/llvm/tools/llvm-objcopy/MachO/Object.cpp @@ -1,5 +1,7 @@ #include "Object.h" #include "../llvm-objcopy.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorOr.h" namespace llvm { namespace objcopy { @@ -24,6 +26,35 @@ std::end(LC.Sections)); } +LoadCommand &Object::addSegment64(StringRef SegName) { + LoadCommand LC; + MachO::segment_command_64 &Seg = LC.MachOLoadCommand.segment_command_64_data; + memset(&Seg, 0, sizeof(MachO::segment_command_64)); + Seg.cmd = MachO::LC_SEGMENT_64; + assert(SegName.size() <= sizeof(Seg.segname) && "too long segment name"); + strncpy(Seg.segname, SegName.data(), SegName.size()); + LoadCommands.push_back(LC); + return LoadCommands.back(); +} + +Optional LoadCommand::getSegmentName() const { + const MachO::macho_load_command &MLC = MachOLoadCommand; + switch (MLC.load_command_data.cmd) { + case MachO::LC_SEGMENT: + return StringRef(MLC.segment_command_data.segname, + strnlen(MLC.segment_command_data.segname, + sizeof(MLC.segment_command_data.segname))); + break; + case MachO::LC_SEGMENT_64: + return StringRef(MLC.segment_command_64_data.segname, + strnlen(MLC.segment_command_64_data.segname, + sizeof(MLC.segment_command_64_data.segname))); + break; + default: + return None; + } +} + } // end namespace macho } // end namespace objcopy } // end namespace llvm