diff --git a/llvm/lib/ObjCopy/CMakeLists.txt b/llvm/lib/ObjCopy/CMakeLists.txt --- a/llvm/lib/ObjCopy/CMakeLists.txt +++ b/llvm/lib/ObjCopy/CMakeLists.txt @@ -37,6 +37,7 @@ add_llvm_component_library(LLVMObjCopy Archive.cpp + CommonConfig.cpp ObjCopy.cpp ConfigManager.cpp COFF/COFFObjcopy.cpp diff --git a/llvm/lib/ObjCopy/CommonConfig.cpp b/llvm/lib/ObjCopy/CommonConfig.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/ObjCopy/CommonConfig.cpp @@ -0,0 +1,50 @@ +//===- CommonConfig.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 "llvm/ObjCopy/CommonConfig.h" + +namespace llvm { +namespace objcopy { + +Expected +NameOrPattern::create(StringRef Pattern, MatchStyle MS, + function_ref ErrorCallback) { + switch (MS) { + case MatchStyle::Literal: + return NameOrPattern(Pattern); + case MatchStyle::Wildcard: { + SmallVector Data; + bool IsPositiveMatch = true; + if (Pattern[0] == '!') { + IsPositiveMatch = false; + Pattern = Pattern.drop_front(); + } + Expected GlobOrErr = GlobPattern::create(Pattern); + + // If we couldn't create it as a glob, report the error, but try again + // with a literal if the error reporting is non-fatal. + if (!GlobOrErr) { + if (Error E = ErrorCallback(GlobOrErr.takeError())) + return std::move(E); + return create(Pattern, MatchStyle::Literal, ErrorCallback); + } + + return NameOrPattern(std::make_shared(*GlobOrErr), + IsPositiveMatch); + } + case MatchStyle::Regex: { + SmallVector Data; + return NameOrPattern(std::make_shared( + ("^" + Pattern.ltrim('^').rtrim('$') + "$").toStringRef(Data))); + } + } + llvm_unreachable("Unhandled llvm.objcopy.MatchStyle enum"); +} + +} // end namespace objcopy +} // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp --- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp +++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp @@ -367,41 +367,6 @@ return Error::success(); } -Expected -NameOrPattern::create(StringRef Pattern, MatchStyle MS, - function_ref ErrorCallback) { - switch (MS) { - case MatchStyle::Literal: - return NameOrPattern(Pattern); - case MatchStyle::Wildcard: { - SmallVector Data; - bool IsPositiveMatch = true; - if (Pattern[0] == '!') { - IsPositiveMatch = false; - Pattern = Pattern.drop_front(); - } - Expected GlobOrErr = GlobPattern::create(Pattern); - - // If we couldn't create it as a glob, report the error, but try again with - // a literal if the error reporting is non-fatal. - if (!GlobOrErr) { - if (Error E = ErrorCallback(GlobOrErr.takeError())) - return std::move(E); - return create(Pattern, MatchStyle::Literal, ErrorCallback); - } - - return NameOrPattern(std::make_shared(*GlobOrErr), - IsPositiveMatch); - } - case MatchStyle::Regex: { - SmallVector Data; - return NameOrPattern(std::make_shared( - ("^" + Pattern.ltrim('^').rtrim('$') + "$").toStringRef(Data))); - } - } - llvm_unreachable("Unhandled llvm.objcopy.MatchStyle enum"); -} - static Error addSymbolsToRenameFromFile(StringMap &SymbolsToRename, BumpPtrAllocator &Alloc, StringRef Filename) { diff --git a/llvm/unittests/ObjCopy/ObjCopyTest.cpp b/llvm/unittests/ObjCopy/ObjCopyTest.cpp --- a/llvm/unittests/ObjCopy/ObjCopyTest.cpp +++ b/llvm/unittests/ObjCopy/ObjCopyTest.cpp @@ -21,121 +21,237 @@ using namespace objcopy; using namespace yaml; -void copySimpleInMemoryFileImpl( - const char *YamlCreationString, - std::function IsValidFormat) { +const char *SimpleFileCOFFYAML = R"( +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ ] + Alignment: 4 + SectionData: E800000000C3C3C3 +symbols: +... +)"; + +const char *SimpleFileELFYAML = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Content: "12345678" +)"; + +const char *SimpleFileMachOYAML = R"( +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x80000003 + filetype: 0x00000001 + ncmds: 1 + sizeofcmds: 152 + flags: 0x00002000 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 152 + segname: __TEXT + 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 +... +)"; + +const char *SimpleFileWasmYAML = R"( +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: CUSTOM + Name: text + Payload: ABC123 +... +)"; + +// Create ObjectFile from \p YamlCreationString and do validation using \p +// IsValidFormat checker. \p Storage is a storage for data. \returns created +// ObjectFile. +Expected> createObjectFileFromYamlDescription( + const char *YamlCreationString, SmallVector &Storage, + function_ref IsValidFormat) { auto ErrHandler = [&](const Twine &Msg) { FAIL() << "Error: " << Msg; }; - // Create Object file from YAML description. - SmallVector Storage; std::unique_ptr Obj = yaml2ObjectFile(Storage, YamlCreationString, ErrHandler); - ASSERT_TRUE(Obj); - ASSERT_TRUE(IsValidFormat(*Obj)); + if (!Obj) + return createError("could not create ObjectFile from yaml description"); - ConfigManager Config; - Config.Common.OutputFilename = "a.out"; + if (!IsValidFormat(*Obj)) + return createError("wrong file format"); - // Call executeObjcopyOnBinary() - SmallVector DataVector; + return Obj; +} + +// Call objcopy::executeObjcopyOnBinary for \p Config and \p In. \p DataVector +// is a holder for data. \returns Binary for copied data. +Expected> +callObjCopy(ConfigManager &Config, object::Binary &In, + SmallVector &DataVector, + function_ref IsValidFormat) { raw_svector_ostream OutStream(DataVector); - Error Err = objcopy::executeObjcopyOnBinary(Config, *Obj.get(), OutStream); - ASSERT_FALSE(std::move(Err)); + + if (Error Err = objcopy::executeObjcopyOnBinary(Config, In, OutStream)) + return std::move(Err); MemoryBufferRef Buffer(StringRef(DataVector.data(), DataVector.size()), Config.Common.OutputFilename); - // Check copied file. Expected> Result = createBinary(Buffer); + + // Check copied file. + if (!Result) + return Result; + + if (!IsValidFormat(**Result)) + return createError("wrong file format"); + + if (!(*Result)->isObject()) + return createError("binary is not object file"); + + return Result; +} + +// \returns true if specified \p File has a section named \p SectionName. +bool hasSection(ObjectFile &File, StringRef SectionName) { + for (const object::SectionRef &Sec : File.sections()) { + Expected CurSecNameOrErr = Sec.getName(); + if (!CurSecNameOrErr) + continue; + + if (*CurSecNameOrErr == SectionName) + return true; + } + + return false; +} + +// Check that specified \p File has a section \p SectionName and its data +// matches \p SectionData. +void checkSectionData(ObjectFile &File, StringRef SectionName, + StringRef SectionData) { + for (const object::SectionRef &Sec : File.sections()) { + Expected CurSecNameOrErr = Sec.getName(); + ASSERT_THAT_EXPECTED(CurSecNameOrErr, Succeeded()); + + if (*CurSecNameOrErr == SectionName) { + Expected CurSectionData = Sec.getContents(); + ASSERT_THAT_EXPECTED(CurSectionData, Succeeded()); + EXPECT_TRUE(Sec.getSize() == SectionData.size()); + EXPECT_TRUE(memcmp(CurSectionData->data(), SectionData.data(), + SectionData.size()) == 0); + return; + } + } + + // Section SectionName must be presented. + EXPECT_TRUE(false); +} + +void copySimpleInMemoryFileImpl( + const char *YamlCreationString, + function_ref IsValidFormat) { + SCOPED_TRACE("copySimpleInMemoryFileImpl"); + + // Create Object file from YAML description. + SmallVector Storage; + Expected> Obj = + createObjectFileFromYamlDescription(YamlCreationString, Storage, + IsValidFormat); + ASSERT_THAT_EXPECTED(Obj, Succeeded()); + + ConfigManager Config; + Config.Common.OutputFilename = "a.out"; + + // Call executeObjcopyOnBinary() + SmallVector DataVector; + Expected> Result = + callObjCopy(Config, *Obj.get(), DataVector, IsValidFormat); ASSERT_THAT_EXPECTED(Result, Succeeded()); - ASSERT_TRUE(IsValidFormat(**Result)); } TEST(CopySimpleInMemoryFile, COFF) { SCOPED_TRACE("CopySimpleInMemoryFileCOFF"); - copySimpleInMemoryFileImpl( - R"( ---- !COFF -header: - Machine: IMAGE_FILE_MACHINE_AMD64 - Characteristics: [ ] -sections: - - Name: .text - Characteristics: [ ] - Alignment: 4 - SectionData: E800000000C3C3C3 -symbols: -... -)", - [](const Binary &File) { return File.isCOFF(); }); + copySimpleInMemoryFileImpl(SimpleFileCOFFYAML, + [](const Binary &File) { return File.isCOFF(); }); } TEST(CopySimpleInMemoryFile, ELF) { SCOPED_TRACE("CopySimpleInMemoryFileELF"); - copySimpleInMemoryFileImpl( - R"( ---- !ELF -FileHeader: - Class: ELFCLASS64 - Data: ELFDATA2LSB - Type: ET_REL)", - [](const Binary &File) { return File.isELF(); }); + copySimpleInMemoryFileImpl(SimpleFileELFYAML, + [](const Binary &File) { return File.isELF(); }); } TEST(CopySimpleInMemoryFile, MachO) { SCOPED_TRACE("CopySimpleInMemoryFileMachO"); - copySimpleInMemoryFileImpl( - R"( ---- !mach-o -FileHeader: - magic: 0xFEEDFACF - cputype: 0x01000007 - cpusubtype: 0x80000003 - filetype: 0x00000002 - ncmds: 0 - sizeofcmds: 0 - flags: 0x00218085 - reserved: 0x00000000 -... -)", - [](const Binary &File) { return File.isMachO(); }); + copySimpleInMemoryFileImpl(SimpleFileMachOYAML, + [](const Binary &File) { return File.isMachO(); }); } TEST(CopySimpleInMemoryFile, Wasm) { SCOPED_TRACE("CopySimpleInMemoryFileWasm"); - copySimpleInMemoryFileImpl( - R"( ---- !WASM -FileHeader: - Version: 0x00000001 -... -)", - [](const Binary &File) { return File.isWasm(); }); + copySimpleInMemoryFileImpl(SimpleFileWasmYAML, + [](const Binary &File) { return File.isWasm(); }); } enum Action : uint8_t { AddSection, UpdateSection }; void addOrUpdateSectionToFileImpl( const char *YamlCreationString, - std::function IsValidFormat, + function_ref IsValidFormat, StringRef NewSectionName, StringRef NewSectionData, Action SectionAction) { - auto ErrHandler = [&](const Twine &Msg) { FAIL() << "Error: " << Msg; }; + SCOPED_TRACE("addOrUpdateSectionToFileImpl"); // Create Object file from YAML description. SmallVector Storage; - std::unique_ptr Obj = - yaml2ObjectFile(Storage, YamlCreationString, ErrHandler); - ASSERT_TRUE(Obj); - ASSERT_TRUE(IsValidFormat(*Obj)); + Expected> Obj = + createObjectFileFromYamlDescription(YamlCreationString, Storage, + IsValidFormat); + ASSERT_THAT_EXPECTED(Obj, Succeeded()); std::unique_ptr NewSectionBuffer = MemoryBuffer::getMemBuffer(NewSectionData, NewSectionName, false); std::string Name; - if (Obj->isMachO()) + if ((*Obj)->isMachO()) Name = "__TEXT," + NewSectionName.str(); else Name = NewSectionName.str(); @@ -149,219 +265,134 @@ // Call executeObjcopyOnBinary() SmallVector DataVector; - raw_svector_ostream OutStream(DataVector); - Error Err = objcopy::executeObjcopyOnBinary(Config, *Obj.get(), OutStream); - ASSERT_FALSE(std::move(Err)); - - MemoryBufferRef Buffer(StringRef(DataVector.data(), DataVector.size()), - Config.Common.OutputFilename); - - // Check copied file. - Expected> Result = createBinary(Buffer); + Expected> Result = + callObjCopy(Config, *Obj.get(), DataVector, IsValidFormat); ASSERT_THAT_EXPECTED(Result, Succeeded()); - ASSERT_TRUE(IsValidFormat(**Result)); - ASSERT_TRUE((*Result)->isObject()); // Check that copied file has the new section. - bool HasNewSection = false; - for (const object::SectionRef &Sect : - static_cast((*Result).get())->sections()) { - Expected SectNameOrErr = Sect.getName(); - ASSERT_THAT_EXPECTED(SectNameOrErr, Succeeded()); - - if (*SectNameOrErr == NewSectionName) { - HasNewSection = true; - Expected SectionData = Sect.getContents(); - ASSERT_THAT_EXPECTED(SectionData, Succeeded()); - EXPECT_TRUE(Sect.getSize() == NewSectionData.size()); - EXPECT_TRUE(memcmp(SectionData->data(), NewSectionData.data(), - NewSectionData.size()) == 0); - break; - } - } - EXPECT_TRUE(HasNewSection); + checkSectionData(*static_cast((*Result).get()), NewSectionName, + NewSectionData); } TEST(AddSection, COFF) { SCOPED_TRACE("addSectionToFileCOFF"); addOrUpdateSectionToFileImpl( - R"( ---- !COFF -header: - Machine: IMAGE_FILE_MACHINE_AMD64 - Characteristics: [ ] -sections: - - Name: .text - Characteristics: [ ] - Alignment: 4 - SectionData: E800000000C3C3C3 -symbols: -... -)", - [](const Binary &File) { return File.isCOFF(); }, ".foo", "1234", - AddSection); + SimpleFileCOFFYAML, [](const Binary &File) { return File.isCOFF(); }, + ".foo", "1234", AddSection); } TEST(AddSection, ELF) { SCOPED_TRACE("addSectionToFileELF"); addOrUpdateSectionToFileImpl( - R"( ---- !ELF -FileHeader: - Class: ELFCLASS64 - Data: ELFDATA2LSB - Type: ET_REL)", - [](const Binary &File) { return File.isELF(); }, ".foo", "1234", - AddSection); + SimpleFileELFYAML, [](const Binary &File) { return File.isELF(); }, + ".foo", "1234", AddSection); } TEST(AddSection, MachO) { SCOPED_TRACE("addSectionToFileMachO"); addOrUpdateSectionToFileImpl( - R"( ---- !mach-o -FileHeader: - magic: 0xFEEDFACF - cputype: 0x01000007 - cpusubtype: 0x80000003 - filetype: 0x00000001 - ncmds: 1 - sizeofcmds: 152 - flags: 0x00002000 - reserved: 0x00000000 -LoadCommands: - - cmd: LC_SEGMENT_64 - cmdsize: 152 - segname: __TEXT - 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 -... -)", - [](const Binary &File) { return File.isMachO(); }, "__foo", "1234", - AddSection); + SimpleFileMachOYAML, [](const Binary &File) { return File.isMachO(); }, + "__foo", "1234", AddSection); } TEST(AddSection, Wasm) { SCOPED_TRACE("addSectionToFileWasm"); addOrUpdateSectionToFileImpl( - R"( ---- !WASM -FileHeader: - Version: 0x00000001 -... -)", - [](const Binary &File) { return File.isWasm(); }, ".foo", "1234", - AddSection); + SimpleFileWasmYAML, [](const Binary &File) { return File.isWasm(); }, + ".foo", "1234", AddSection); } TEST(UpdateSection, COFF) { SCOPED_TRACE("updateSectionToFileCOFF"); addOrUpdateSectionToFileImpl( - R"( ---- !COFF -header: - Machine: IMAGE_FILE_MACHINE_AMD64 - Characteristics: [ ] -sections: - - Name: .foo - Characteristics: [ ] - Alignment: 4 - SectionData: E800000000C3C3C3 -symbols: -... -)", - [](const Binary &File) { return File.isCOFF(); }, ".foo", "1234", - UpdateSection); + SimpleFileCOFFYAML, [](const Binary &File) { return File.isCOFF(); }, + ".text", "1234", UpdateSection); } TEST(UpdateSection, ELF) { SCOPED_TRACE("updateSectionToFileELF"); addOrUpdateSectionToFileImpl( - R"( ---- !ELF -FileHeader: - Class: ELFCLASS64 - Data: ELFDATA2LSB - Type: ET_REL -Sections: - - Name: .foo - Type: SHT_PROGBITS - Flags: [ SHF_ALLOC ] - Content: "12345678" -)", - [](const Binary &File) { return File.isELF(); }, ".foo", "1234", - UpdateSection); + SimpleFileELFYAML, [](const Binary &File) { return File.isELF(); }, + ".text", "1234", UpdateSection); } TEST(UpdateSection, MachO) { SCOPED_TRACE("updateSectionToFileMachO"); addOrUpdateSectionToFileImpl( - R"( ---- !mach-o -FileHeader: - magic: 0xFEEDFACF - cputype: 0x01000007 - cpusubtype: 0x80000003 - filetype: 0x00000001 - ncmds: 1 - sizeofcmds: 152 - flags: 0x00002000 - reserved: 0x00000000 -LoadCommands: - - cmd: LC_SEGMENT_64 - cmdsize: 152 - segname: __TEXT - vmaddr: 0 - vmsize: 4 - fileoff: 184 - filesize: 4 - maxprot: 7 - initprot: 7 - nsects: 1 - flags: 0 - Sections: - - sectname: __foo - segname: __TEXT - addr: 0x0000000000000000 - content: 'AABBCCDD' - size: 4 - offset: 184 - align: 0 - reloff: 0x00000000 - nreloc: 0 - flags: 0x80000400 - reserved1: 0x00000000 - reserved2: 0x00000000 - reserved3: 0x00000000 -... -)", - [](const Binary &File) { return File.isMachO(); }, "__foo", "1234", - UpdateSection); + SimpleFileMachOYAML, [](const Binary &File) { return File.isMachO(); }, + "__text", "1234", UpdateSection); +} + +void removeSectionByPatternImpl( + const char *YamlCreationString, + function_ref IsValidFormat, + StringRef SectionWildcard, StringRef SectionName) { + SCOPED_TRACE("removeSectionByPatternImpl"); + + // Create Object file from YAML description. + SmallVector Storage; + Expected> Obj = + createObjectFileFromYamlDescription(YamlCreationString, Storage, + IsValidFormat); + ASSERT_THAT_EXPECTED(Obj, Succeeded()); + + // Check that section is present. + EXPECT_TRUE(hasSection(**Obj, SectionName)); + + Expected Pattern = objcopy::NameOrPattern::create( + SectionWildcard, objcopy::MatchStyle::Wildcard, + [](Error Err) { return Err; }); + + ConfigManager Config; + Config.Common.OutputFilename = "a.out"; + EXPECT_THAT_ERROR(Config.Common.ToRemove.addMatcher(std::move(Pattern)), + Succeeded()); + + SmallVector DataVector; + Expected> Result = + callObjCopy(Config, *Obj.get(), DataVector, IsValidFormat); + ASSERT_THAT_EXPECTED(Result, Succeeded()); + + // Check that section was removed. + EXPECT_FALSE( + hasSection(*static_cast((*Result).get()), SectionName)); +} + +TEST(RemoveSectionByPattern, COFF) { + SCOPED_TRACE("removeSectionByPatternCOFF"); + + removeSectionByPatternImpl( + SimpleFileCOFFYAML, [](const Binary &File) { return File.isCOFF(); }, + "\\.text*", ".text"); +} + +TEST(RemoveSectionByPattern, ELF) { + SCOPED_TRACE("removeSectionByPatternELF"); + + removeSectionByPatternImpl( + SimpleFileELFYAML, [](const Binary &File) { return File.isELF(); }, + "\\.text*", ".text"); +} + +TEST(RemoveSectionByPattern, MachO) { + SCOPED_TRACE("removeSectionByPatternMachO"); + + removeSectionByPatternImpl( + SimpleFileMachOYAML, [](const Binary &File) { return File.isMachO(); }, + "__TEXT,__text*", "__text"); +} + +TEST(RemoveSectionByPattern, Wasm) { + SCOPED_TRACE("removeSectionByPatternWasm"); + + removeSectionByPatternImpl( + SimpleFileWasmYAML, [](const Binary &File) { return File.isWasm(); }, + "text*", "text"); }