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, + llvm::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 @@ -366,41 +366,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,34 +21,110 @@ using namespace objcopy; using namespace yaml; -void copySimpleInMemoryFileImpl( - const char *YamlCreationString, +// 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, 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)); + 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, + std::function 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 &Sect : File.sections()) { + Expected CurSectNameOrErr = Sect.getName(); + if (!CurSectNameOrErr) + continue; + + if (*CurSectNameOrErr == SectionName) + return true; + } + + return false; +} + +// Check that specified \p File has a section \p SectionName and its data +// match with \p SectionData. +void checkSectionData(ObjectFile &File, StringRef SectionName, + StringRef SectionData) { + for (const object::SectionRef &Sect : File.sections()) { + Expected CurSectNameOrErr = Sect.getName(); + ASSERT_THAT_EXPECTED(CurSectNameOrErr, Succeeded()); + + if (*CurSectNameOrErr == SectionName) { + Expected CurSectionData = Sect.getContents(); + ASSERT_THAT_EXPECTED(CurSectionData, Succeeded()); + EXPECT_TRUE(Sect.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, + std::function 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) { @@ -123,19 +199,19 @@ const char *YamlCreationString, std::function 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,37 +225,13 @@ // 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) { @@ -365,3 +417,141 @@ [](const Binary &File) { return File.isMachO(); }, "__foo", "1234", UpdateSection); } + +void removeSectionByPatternImpl( + const char *YamlCreationString, + std::function 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) -> Error { 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( + 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*", ".foo"); +} + +TEST(RemoveSectionByPattern, ELF) { + SCOPED_TRACE("removeSectionByPatternELF"); + + removeSectionByPatternImpl( + 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*", ".foo"); +} + +TEST(RemoveSectionByPattern, MachO) { + SCOPED_TRACE("removeSectionByPatternMachO"); + + removeSectionByPatternImpl( + 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(); }, "__TEXT,__foo*", + "__foo"); +} + +TEST(RemoveSectionByPattern, Wasm) { + SCOPED_TRACE("removeSectionByPatternWasm"); + + removeSectionByPatternImpl( + R"( +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: CUSTOM + Name: foo + Payload: ABC123 +... +)", + [](const Binary &File) { return File.isWasm(); }, "foo*", "foo"); +}