Index: llvm/docs/CommandGuide/llvm-objcopy.rst =================================================================== --- llvm/docs/CommandGuide/llvm-objcopy.rst +++ llvm/docs/CommandGuide/llvm-objcopy.rst @@ -43,6 +43,9 @@ starts with ".note". Otherwise, it will have type `SHT_PROGBITS`. Can be specified multiple times to add multiple sections. + For MachO objects, ``
`` must be formatted as + ``,
``. + .. option:: --binary-architecture , -B Ignored for compatibility. Index: llvm/test/tools/llvm-objcopy/MachO/add-section.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/MachO/add-section.test @@ -0,0 +1,123 @@ +## Show that llvm-objcopy adds a new section into the object if +## --add-section is given. + +# RUN: yaml2obj %s > %t +# RUN: echo -n abcdefg > %t.data + +## Error case 1: Nonexistent input file is specified by --add-section. +# RUN: not llvm-objcopy --add-section __TEXT,__text=%t.missing %t %t.nonexistent-file 2>&1 \ +# RUN: | FileCheck %s -DINPUT=%t -DSECTION_DATA_FILE=%t.missing --check-prefix=NONEXSITENT-FILE +# NONEXSITENT-FILE: error: '[[INPUT]]': '[[SECTION_DATA_FILE]]': {{[Nn]}}o such file or directory + +## Error case 2: Too long segment name. +# RUN: not llvm-objcopy --add-section __TOOOOOOOOO_LONG,__text=%t.data %t %t.too-long-seg-name 2>&1 \ +# RUN: | FileCheck %s -DINPUT=%t --check-prefix=TOO-LONG-SEG-NAME +# TOO-LONG-SEG-NAME: error: '[[INPUT]]': too long segment name: '__TOOOOOOOOO_LONG' + +## 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 a 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 + +## Case 3: Add a new section with /dev/null. +# RUN: llvm-objcopy --add-section __TEXT,__text=/dev/null %t %t.devnull +# RUN: llvm-readobj --sections --section-data %t.devnull \ +# RUN: | FileCheck %s --check-prefixes=COMMON,DEVNULL + +--- !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: 0 +# COMMON-NEXT: __text (5F 5F 74 65 78 74 00 00 00 00 00 00 00 00 00 00) +# COMMON-NEXT: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00) +# COMMON: Index: 1 +# CASE1-NEXT: __text (5F 5F 74 65 78 74 00 00 00 00 00 00 00 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: __bar (5F 5F 62 61 72 00 00 00 00 00 00 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) +# DEVNULL-NEXT: __text (5F 5F 74 65 78 74 00 00 00 00 00 00 00 00 00 00) +# DEVNULL-NEXT: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00) +# COMMON-NEXT: Address: 0x0 +# CASE1-NEXT: Size: 0x7 +# CASE2-NEXT: Size: 0x7 +# DEVNULL-NEXT: Size: 0x0 +# 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 ( +# CASE1-NEXT: 0000: 61626364 656667 |abcdefg| +# CASE2-NEXT: 0000: 61626364 656667 |abcdefg| +# COMMON-NEXT: ) + +# 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 Index: llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp =================================================================== --- llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp +++ llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp @@ -106,15 +106,68 @@ SecName.str().c_str()); } +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, Pair.second); + Sec.setOwnedContents(Content); + + // Add the a section into an existing segment. + 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. + LoadCommand &NewSegment = Obj.addSegment(TargetSegName); + NewSegment.Sections.push_back(Sec); + return Error::success(); +} + +// isValidMachOCannonicalName returns success if Name is a MachO cannonical name +// (",
") and lengths of both segment and section names are +// valid. +Error isValidMachOCannonicalName(StringRef Name) { + if (Name.count(',') != 1) + return createStringError(errc::invalid_argument, + "invalid section name '%s' (should be formatted " + "as ',
')", + Name.str().c_str()); + + std::pair Pair = Name.split(','); + if (Pair.first.size() > 16) + return createStringError(errc::invalid_argument, + "too long segment name: '%s'", + Pair.first.str().c_str()); + if (Pair.second.size() > 16) + return createStringError(errc::invalid_argument, + "too long section name: '%s'", + Pair.second.str().c_str()); + return Error::success(); +} + static Error handleArgs(const CopyConfig &Config, Object &Obj) { if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() || Config.BuildIdLinkInput || Config.BuildIdLinkOutput || !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() || - !Config.AllocSectionsPrefix.empty() || !Config.AddSection.empty() || - !Config.KeepSection.empty() || Config.NewSymbolVisibility || - !Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() || - !Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() || - !Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() || + !Config.AllocSectionsPrefix.empty() || !Config.KeepSection.empty() || + Config.NewSymbolVisibility || !Config.SymbolsToGlobalize.empty() || + !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() || + !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || + !Config.SectionsToRename.empty() || !Config.UnneededSymbolsToRemove.empty() || !Config.SetSectionAlignment.empty() || !Config.SetSectionFlags.empty() || Config.ExtractDWO || Config.KeepFileSymbols || Config.LocalizeHidden || @@ -148,6 +201,16 @@ return E; } + 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; + } + for (StringRef RPath : Config.RPathToAdd) { for (LoadCommand &LC : Obj.LoadCommands) { if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH && Index: llvm/tools/llvm-objcopy/MachO/Object.h =================================================================== --- llvm/tools/llvm-objcopy/MachO/Object.h +++ llvm/tools/llvm-objcopy/MachO/Object.h @@ -109,6 +109,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 @@ -305,6 +308,16 @@ void removeSections(function_ref ToRemove); void addLoadCommand(LoadCommand LC); + + /// Creates a new segment load command in the object and returns a reference + /// to the newly created load command. The caller should verify that SegName + /// is not too long (SegName.size() should be less than or equal to 16). + LoadCommand &addSegment(StringRef SegName); + + bool is64Bit() const { + return Header.Magic == MachO::MH_MAGIC_64 || + Header.Magic == MachO::MH_CIGAM_64; + } }; } // end namespace macho Index: llvm/tools/llvm-objcopy/MachO/Object.cpp =================================================================== --- llvm/tools/llvm-objcopy/MachO/Object.cpp +++ llvm/tools/llvm-objcopy/MachO/Object.cpp @@ -33,6 +33,40 @@ LoadCommands.push_back(std::move(LC)); } +LoadCommand &Object::addSegment(StringRef SegName) { + assert(is64Bit() && "adding segment_command is not yet supported"); + + LoadCommand LC; + MachO::segment_command_64 &Seg = LC.MachOLoadCommand.segment_command_64_data; + + assert(SegName.size() <= sizeof(Seg.segname) && "too long segment name"); + + memset(&Seg, 0, sizeof(MachO::segment_command_64)); + Seg.cmd = MachO::LC_SEGMENT_64; + strncpy(Seg.segname, SegName.data(), SegName.size()); + LoadCommands.push_back(LC); + + return LoadCommands.back(); +} + +/// Extracts a segment name from a string which is possibly non-null-terminated. +StringRef extractSegmentName(const char *SegName) { + return StringRef(SegName, + strnlen(SegName, sizeof(MachO::segment_command::segname))); +} + +Optional LoadCommand::getSegmentName() const { + const MachO::macho_load_command &MLC = MachOLoadCommand; + switch (MLC.load_command_data.cmd) { + case MachO::LC_SEGMENT: + return extractSegmentName(MLC.segment_command_data.segname); + case MachO::LC_SEGMENT_64: + return extractSegmentName(MLC.segment_command_64_data.segname); + default: + return None; + } +} + } // end namespace macho } // end namespace objcopy } // end namespace llvm