diff --git a/llvm/lib/ObjCopy/CMakeLists.txt b/llvm/lib/ObjCopy/CMakeLists.txt index 54557f260f8d..2d6bee94875f 100644 --- a/llvm/lib/ObjCopy/CMakeLists.txt +++ b/llvm/lib/ObjCopy/CMakeLists.txt @@ -37,6 +37,7 @@ source_group("Source Files\\XCOFF" REGULAR_EXPRESSION 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 index 000000000000..e85715d0c44c --- /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 index 1281af3b6237..29ed65be23a0 100644 --- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp +++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp @@ -367,41 +367,6 @@ static Error addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc, 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 index bc16e954ca56..867742aa9799 100644 --- a/llvm/unittests/ObjCopy/ObjCopyTest.cpp +++ b/llvm/unittests/ObjCopy/ObjCopyTest.cpp @@ -21,41 +21,7 @@ using namespace object; using namespace objcopy; using namespace yaml; -void copySimpleInMemoryFileImpl( - const char *YamlCreationString, - std::function 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)); - - ConfigManager Config; - Config.Common.OutputFilename = "a.out"; - - // 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); - ASSERT_THAT_EXPECTED(Result, Succeeded()); - ASSERT_TRUE(IsValidFormat(**Result)); -} - -TEST(CopySimpleInMemoryFile, COFF) { - SCOPED_TRACE("CopySimpleInMemoryFileCOFF"); - - copySimpleInMemoryFileImpl( - R"( +const char *SimpleFileCOFFYAML = R"( --- !COFF header: Machine: IMAGE_FILE_MACHINE_AMD64 @@ -67,161 +33,22 @@ sections: SectionData: E800000000C3C3C3 symbols: ... -)", - [](const Binary &File) { return File.isCOFF(); }); -} +)"; -TEST(CopySimpleInMemoryFile, ELF) { - SCOPED_TRACE("CopySimpleInMemoryFileELF"); - - copySimpleInMemoryFileImpl( - R"( +const char *SimpleFileELFYAML = R"( --- !ELF FileHeader: - Class: ELFCLASS64 - Data: ELFDATA2LSB - Type: ET_REL)", - [](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(); }); -} - -TEST(CopySimpleInMemoryFile, Wasm) { - SCOPED_TRACE("CopySimpleInMemoryFileWasm"); - - copySimpleInMemoryFileImpl( - R"( ---- !WASM -FileHeader: - Version: 0x00000001 -... -)", - [](const Binary &File) { return File.isWasm(); }); -} - -enum Action : uint8_t { AddSection, UpdateSection }; - -void addOrUpdateSectionToFileImpl( - const char *YamlCreationString, - std::function IsValidFormat, - StringRef NewSectionName, StringRef NewSectionData, Action SectionAction) { - 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)); - - std::unique_ptr NewSectionBuffer = - MemoryBuffer::getMemBuffer(NewSectionData, NewSectionName, false); - std::string Name; - if (Obj->isMachO()) - Name = "__TEXT," + NewSectionName.str(); - else - Name = NewSectionName.str(); - - ConfigManager Config; - Config.Common.OutputFilename = "a.out"; - if (SectionAction == AddSection) - Config.Common.AddSection.push_back({Name, std::move(NewSectionBuffer)}); - else - Config.Common.UpdateSection.push_back({Name, std::move(NewSectionBuffer)}); - - // 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); - 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); -} - -TEST(AddSection, COFF) { - SCOPED_TRACE("addSectionToFileCOFF"); - - addOrUpdateSectionToFileImpl( - R"( ---- !COFF -header: - Machine: IMAGE_FILE_MACHINE_AMD64 - Characteristics: [ ] -sections: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL +Sections: - Name: .text - Characteristics: [ ] - Alignment: 4 - SectionData: E800000000C3C3C3 -symbols: -... -)", - [](const Binary &File) { return File.isCOFF(); }, ".foo", "1234", - AddSection); -} + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Content: "12345678" +)"; -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); -} - -TEST(AddSection, MachO) { - SCOPED_TRACE("addSectionToFileMachO"); - - addOrUpdateSectionToFileImpl( - R"( +const char *SimpleFileMachOYAML = R"( --- !mach-o FileHeader: magic: 0xFEEDFACF @@ -259,109 +86,313 @@ LoadCommands: reserved2: 0x00000000 reserved3: 0x00000000 ... -)", - [](const Binary &File) { return File.isMachO(); }, "__foo", "1234", - AddSection); +)"; + +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; }; + + std::unique_ptr Obj = + yaml2ObjectFile(Storage, YamlCreationString, ErrHandler); + if (!Obj) + return createError("could not create ObjectFile from yaml description"); + + if (!IsValidFormat(*Obj)) + return createError("wrong file format"); + + 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); + + if (Error Err = objcopy::executeObjcopyOnBinary(Config, In, OutStream)) + return std::move(Err); + + MemoryBufferRef Buffer(StringRef(DataVector.data(), DataVector.size()), + Config.Common.OutputFilename); + + 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()); +} + +TEST(CopySimpleInMemoryFile, COFF) { + SCOPED_TRACE("CopySimpleInMemoryFileCOFF"); + + copySimpleInMemoryFileImpl(SimpleFileCOFFYAML, + [](const Binary &File) { return File.isCOFF(); }); +} + +TEST(CopySimpleInMemoryFile, ELF) { + SCOPED_TRACE("CopySimpleInMemoryFileELF"); + + copySimpleInMemoryFileImpl(SimpleFileELFYAML, + [](const Binary &File) { return File.isELF(); }); +} + +TEST(CopySimpleInMemoryFile, MachO) { + SCOPED_TRACE("CopySimpleInMemoryFileMachO"); + + copySimpleInMemoryFileImpl(SimpleFileMachOYAML, + [](const Binary &File) { return File.isMachO(); }); +} + +TEST(CopySimpleInMemoryFile, Wasm) { + SCOPED_TRACE("CopySimpleInMemoryFileWasm"); + + copySimpleInMemoryFileImpl(SimpleFileWasmYAML, + [](const Binary &File) { return File.isWasm(); }); +} + +enum Action : uint8_t { AddSection, UpdateSection }; + +void addOrUpdateSectionToFileImpl( + const char *YamlCreationString, + function_ref IsValidFormat, + StringRef NewSectionName, StringRef NewSectionData, Action SectionAction) { + SCOPED_TRACE("addOrUpdateSectionToFileImpl"); + + // Create Object file from YAML description. + SmallVector Storage; + 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()) + Name = "__TEXT," + NewSectionName.str(); + else + Name = NewSectionName.str(); + + ConfigManager Config; + Config.Common.OutputFilename = "a.out"; + if (SectionAction == AddSection) + Config.Common.AddSection.push_back({Name, std::move(NewSectionBuffer)}); + else + Config.Common.UpdateSection.push_back({Name, std::move(NewSectionBuffer)}); + + // Call executeObjcopyOnBinary() + SmallVector DataVector; + Expected> Result = + callObjCopy(Config, *Obj.get(), DataVector, IsValidFormat); + ASSERT_THAT_EXPECTED(Result, Succeeded()); + + // Check that copied file has the new section. + checkSectionData(*static_cast((*Result).get()), NewSectionName, + NewSectionData); +} + +TEST(AddSection, COFF) { + SCOPED_TRACE("addSectionToFileCOFF"); + + addOrUpdateSectionToFileImpl( + SimpleFileCOFFYAML, [](const Binary &File) { return File.isCOFF(); }, + ".foo", "1234", AddSection); +} + +TEST(AddSection, ELF) { + SCOPED_TRACE("addSectionToFileELF"); + + addOrUpdateSectionToFileImpl( + SimpleFileELFYAML, [](const Binary &File) { return File.isELF(); }, + ".foo", "1234", AddSection); +} + +TEST(AddSection, MachO) { + SCOPED_TRACE("addSectionToFileMachO"); + + addOrUpdateSectionToFileImpl( + 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"); }