diff --git a/llvm/docs/CommandGuide/llvm-objcopy.rst b/llvm/docs/CommandGuide/llvm-objcopy.rst --- a/llvm/docs/CommandGuide/llvm-objcopy.rst +++ b/llvm/docs/CommandGuide/llvm-objcopy.rst @@ -72,6 +72,9 @@ Remove all sections from the output, except for sections named ``
``. Can be specified multiple times to keep multiple sections. + For MachO objects, ``
`` must be formatted as + ``,
``. + .. option:: --regex If specified, symbol and section names specified by other switches are treated diff --git a/llvm/test/tools/llvm-objcopy/MachO/only-section.test b/llvm/test/tools/llvm-objcopy/MachO/only-section.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/MachO/only-section.test @@ -0,0 +1,140 @@ +## Show that if --only-section is given, llvm-objcopy removes all sections +## except ones specified in the option. + +# RUN: yaml2obj %s > %t + +## Error case: Invalid section name. +# RUN: not llvm-objcopy --only-section __text %t %t2 2>&1 \ +# RUN: | FileCheck %s --check-prefix=BAD-SECTION-NAME +# BAD-SECTION-NAME: error: invalid section name '__text' (should be formatted as ',
') + +## Specify all sections. The output file should be the same as the input. +# RUN: llvm-objcopy -j __TEXT,__text -j __DATA,__data -j __TEXT,__const %t %t3 +# RUN: cmp %t %t3 + +## Specify one section. The output file should contain only that section. +# RUN: llvm-objcopy --only-section __TEXT,__text %t %t4 +# RUN: llvm-readobj --sections --section-data --macho-segment %t4 \ +# RUN: | FileCheck %s --check-prefix=ONLY-TEXT-SECTION + +# ONLY-TEXT-SECTION: Sections [ +# ONLY-TEXT-SECTION-NEXT: Section { +# ONLY-TEXT-SECTION-NEXT: Index: 0 +# ONLY-TEXT-SECTION-NEXT: Name: __text (5F 5F 74 65 78 74 00 00 00 00 00 00 00 00 00 00) +# ONLY-TEXT-SECTION-NEXT: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00) +# ONLY-TEXT-SECTION-NEXT: Address: 0x0 +# ONLY-TEXT-SECTION-NEXT: Size: 0x4 +# ONLY-TEXT-SECTION-NEXT: Offset: 184 +# ONLY-TEXT-SECTION-NEXT: Alignment: 0 +# ONLY-TEXT-SECTION-NEXT: RelocationOffset: 0x0 +# ONLY-TEXT-SECTION-NEXT: RelocationCount: 0 +# ONLY-TEXT-SECTION-NEXT: Type: Regular (0x0) +# ONLY-TEXT-SECTION-NEXT: Attributes [ (0x800004) +# ONLY-TEXT-SECTION-NEXT: PureInstructions (0x800000) +# ONLY-TEXT-SECTION-NEXT: SomeInstructions (0x4) +# ONLY-TEXT-SECTION-NEXT: ] +# ONLY-TEXT-SECTION-NEXT: Reserved1: 0x0 +# ONLY-TEXT-SECTION-NEXT: Reserved2: 0x0 +# ONLY-TEXT-SECTION-NEXT: Reserved3: 0x0 +# ONLY-TEXT-SECTION-NEXT: SectionData ( +# ONLY-TEXT-SECTION-NEXT: 0000: AABBCCDD |....| +# ONLY-TEXT-SECTION-NEXT: ) +# ONLY-TEXT-SECTION-NEXT: } +# ONLY-TEXT-SECTION-NEXT: ] +# ONLY-TEXT-SECTION-NEXT: Segment { +# ONLY-TEXT-SECTION-NEXT: Cmd: LC_SEGMENT_64 +# ONLY-TEXT-SECTION-NEXT: Name: +# ONLY-TEXT-SECTION-NEXT: Size: 152 +# ONLY-TEXT-SECTION-NEXT: vmaddr: 0x0 +# ONLY-TEXT-SECTION-NEXT: vmsize: 0x4 +# ONLY-TEXT-SECTION-NEXT: fileoff: 184 +# ONLY-TEXT-SECTION-NEXT: filesize: 4 +# ONLY-TEXT-SECTION-NEXT: maxprot: rwx +# ONLY-TEXT-SECTION-NEXT: initprot: rwx +# ONLY-TEXT-SECTION-NEXT: nsects: 1 +# ONLY-TEXT-SECTION-NEXT: flags: 0x0 +# ONLY-TEXT-SECTION-NEXT: } + +## Remove all sections if the specified section name is not present in the input. +# RUN: llvm-objcopy --only-section __TEXT,__nonexistent %t %t5 2>&1 +# RUN: llvm-readobj --sections --section-data --macho-segment %t5 \ +# RUN: | FileCheck %s --check-prefix=NONEXISTENT-SECTION + +# NONEXISTENT-SECTION: Sections [ +# NONEXISTENT-SECTION-NEXT: ] +# NONEXISTENT-SECTION-NEXT: Segment { +# NONEXISTENT-SECTION-NEXT: Cmd: LC_SEGMENT_64 +# NONEXISTENT-SECTION-NEXT: Name: +# NONEXISTENT-SECTION-NEXT: Size: 72 +# NONEXISTENT-SECTION-NEXT: vmaddr: 0x0 +# NONEXISTENT-SECTION-NEXT: vmsize: 0x0 +# NONEXISTENT-SECTION-NEXT: fileoff: 104 +# NONEXISTENT-SECTION-NEXT: filesize: 0 +# NONEXISTENT-SECTION-NEXT: maxprot: rwx +# NONEXISTENT-SECTION-NEXT: initprot: rwx +# NONEXISTENT-SECTION-NEXT: nsects: 0 +# NONEXISTENT-SECTION-NEXT: flags: 0x0 +# NONEXISTENT-SECTION-NEXT: } + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x00000003 + filetype: 0x00000001 + ncmds: 1 + sizeofcmds: 312 + flags: 0x00002000 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 312 + segname: '' + vmaddr: 0 + vmsize: 12 + fileoff: 344 + filesize: 12 + maxprot: 7 + initprot: 7 + nsects: 3 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000000000000 + content: 'AABBCCDD' + size: 4 + offset: 344 + align: 0 + reloff: 0x00000000 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + - sectname: __data + segname: __DATA + addr: 0x0000000000000004 + content: 'DDAADDAA' + size: 4 + offset: 348 + align: 0 + reloff: 0x00000000 + nreloc: 0 + flags: 0x00000000 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + - sectname: __const + segname: __TEXT + addr: 0x0000000000000008 + content: 'EEFFEEFF' + size: 4 + offset: 352 + align: 0 + reloff: 0x00000000 + nreloc: 0 + flags: 0x00000000 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 diff --git a/llvm/tools/llvm-objcopy/CMakeLists.txt b/llvm/tools/llvm-objcopy/CMakeLists.txt --- a/llvm/tools/llvm-objcopy/CMakeLists.txt +++ b/llvm/tools/llvm-objcopy/CMakeLists.txt @@ -24,6 +24,7 @@ ELF/ELFConfig.cpp ELF/ELFObjcopy.cpp ELF/Object.cpp + MachO/MachOConfig.cpp MachO/MachOObjcopy.cpp MachO/MachOReader.cpp MachO/MachOWriter.cpp diff --git a/llvm/tools/llvm-objcopy/CopyConfig.h b/llvm/tools/llvm-objcopy/CopyConfig.h --- a/llvm/tools/llvm-objcopy/CopyConfig.h +++ b/llvm/tools/llvm-objcopy/CopyConfig.h @@ -10,6 +10,7 @@ #define LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H #include "ELF/ELFConfig.h" +#include "MachO/MachOConfig.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/BitmaskEnum.h" #include "llvm/ADT/Optional.h" @@ -97,25 +98,27 @@ NameOrRegex(StringRef Pattern, bool IsRegex); bool operator==(StringRef S) const { return R ? R->match(S) : Name == S; } bool operator!=(StringRef S) const { return !operator==(S); } + Error isMachOCannonicalName() const; }; // Matcher that checks symbol or section names against the command line flags // provided for that option. class NameMatcher { std::vector Matchers; - public: void addMatcher(NameOrRegex Matcher) { Matchers.push_back(std::move(Matcher)); } bool matches(StringRef S) const { return is_contained(Matchers, S); } bool empty() const { return Matchers.empty(); } + const std::vector &matchers() const { return Matchers; } }; // Configuration for copying/stripping a single file. struct CopyConfig { // Format-specific options to be initialized lazily when needed. Optional ELF; + Optional MachO; // Main input/output options StringRef InputFilename; @@ -201,6 +204,18 @@ } return Error::success(); } + + // parseMachOConfig performs Mach-O-specific command-line parsing. Fills + // `MachO` on success or returns an Error otherwise. + Error parseMachOConfig() { + if (!MachO) { + Expected MachOConfig = macho::parseConfig(*this); + if (!MachOConfig) + return MachOConfig.takeError(); + MachO = *MachOConfig; + } + return Error::success(); + } }; // Configuration for the overall invocation of this tool. When invoked as @@ -211,6 +226,11 @@ BumpPtrAllocator Alloc; }; +// isValidMachOCannonicalName returns success if Name is a MachO cannonical name +// (",
") and lengths of both segment and section names are +// valid. +Error isValidMachOCannonicalName(StringRef Name); + // ParseObjcopyOptions returns the config and sets the input arguments. If a // help flag is set then ParseObjcopyOptions will print the help messege and // exit. diff --git a/llvm/tools/llvm-objcopy/CopyConfig.cpp b/llvm/tools/llvm-objcopy/CopyConfig.cpp --- a/llvm/tools/llvm-objcopy/CopyConfig.cpp +++ b/llvm/tools/llvm-objcopy/CopyConfig.cpp @@ -272,6 +272,10 @@ ("^" + Pattern.ltrim('^').rtrim('$') + "$").toStringRef(Data)); } +Error NameOrRegex::isMachOCannonicalName() const { + return R ? Error::success() : isValidMachOCannonicalName(Name); +} + static Error addSymbolsToRenameFromFile(StringMap &SymbolsToRename, BumpPtrAllocator &Alloc, StringRef Filename) { @@ -316,6 +320,28 @@ OS << "\nPass @FILE as argument to read options from FILE.\n"; } +// 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(); +} + // ParseObjcopyOptions returns the config and sets the input arguments. If a // help flag is set then ParseObjcopyOptions will print the help messege and // exit. diff --git a/llvm/tools/llvm-objcopy/MachO/MachOConfig.h b/llvm/tools/llvm-objcopy/MachO/MachOConfig.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-objcopy/MachO/MachOConfig.h @@ -0,0 +1,31 @@ +//===- MachOConfig.h --------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OBJCOPY_MACHOCONFIG_H +#define LLVM_TOOLS_OBJCOPY_MACHOCONFIG_H + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace objcopy { +struct CopyConfig; + +namespace macho { + +struct MachOCopyConfig { +}; + +Expected parseConfig(const CopyConfig &Config); + +} // namespace macho +} // namespace objcopy +} // namespace llvm + +#endif diff --git a/llvm/tools/llvm-objcopy/MachO/MachOConfig.cpp b/llvm/tools/llvm-objcopy/MachO/MachOConfig.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-objcopy/MachO/MachOConfig.cpp @@ -0,0 +1,35 @@ +//===- MachOConfig.cpp ----------------------------------------------------===// +// +// 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 "CopyConfig.h" +#include "MachOConfig.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace objcopy { +namespace macho { + +Expected parseConfig(const CopyConfig &Config) { + MachOCopyConfig MachOConfig; + + // Validate section names. + // + // TODO: Support section renaming in GNU objcopy for compatibility (see + // http://lists.llvm.org/pipermail/llvm-dev/2019-May/132570.html). + if (!Config.OnlySection.empty()) { + for (const NameOrRegex &NR : Config.OnlySection.matchers()) + if (Error E = NR.isMachOCannonicalName()) + return std::move(E); + } + + return MachOConfig; +} + +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm 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 @@ -18,6 +18,19 @@ namespace macho { using namespace object; +using SectionPred = std::function; + +static void removeSections(const CopyConfig &Config, Object &Obj) { + SectionPred RemovePred = [](const Section &) { return false; }; + + if (!Config.OnlySection.empty()) { + RemovePred = [&Config, RemovePred](const Section &Sec) { + return !Config.OnlySection.matches(Sec.CannonicalName); + }; + } + + return Obj.removeSections(RemovePred); +} static Error handleArgs(const CopyConfig &Config, Object &Obj) { if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() || @@ -25,7 +38,7 @@ !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() || !Config.AllocSectionsPrefix.empty() || !Config.AddSection.empty() || !Config.DumpSection.empty() || !Config.KeepSection.empty() || - Config.NewSymbolVisibility || !Config.OnlySection.empty() || + Config.NewSymbolVisibility || !Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() || @@ -42,6 +55,7 @@ "option not supported by llvm-objcopy for MachO"); } + removeSections(Config, Obj); return Error::success(); } 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 @@ -35,6 +35,7 @@ .str(); S.Segname = StringRef(Sec.segname, strnlen(Sec.segname, sizeof(Sec.sectname))).str(); + S.CannonicalName = (Twine(S.Segname) + "," + S.Sectname).str(); S.Addr = Sec.addr; S.Size = Sec.size; S.Offset = Sec.offset; 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 @@ -38,6 +38,8 @@ struct Section { std::string Sectname; std::string Segname; + // CannonicalName is a string formatted as “,". + std::string CannonicalName; uint64_t Addr; uint64_t Size; uint32_t Offset; @@ -250,6 +252,8 @@ Optional DataInCodeCommandIndex; /// The index LC_FUNCTION_STARTS load comamnd if present. Optional FunctionStartsCommandIndex; + + void removeSections(function_ref ToRemove); }; } // 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 @@ -10,6 +10,13 @@ return Symbols[Index].get(); } +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), + std::end(LC.Sections)); +} + } // end namespace macho } // end namespace objcopy } // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp --- a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp +++ b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp @@ -170,9 +170,11 @@ return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out); } else if (auto *COFFBinary = dyn_cast(&In)) return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out); - else if (auto *MachOBinary = dyn_cast(&In)) + else if (auto *MachOBinary = dyn_cast(&In)) { + if (Error E = Config.parseMachOConfig()) + return E; return macho::executeObjcopyOnBinary(Config, *MachOBinary, Out); - else + } else return createStringError(object_error::invalid_file_type, "unsupported object file format"); }