//===- ObjCopyTest.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/ObjCopy.h" #include "llvm/ObjCopy/ConfigManager.h" #include "llvm/Object/ObjectFile.h" #include "llvm/ObjectYAML/yaml2obj.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileUtilities.h" #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" using namespace llvm; using namespace object; using namespace objcopy; using namespace yaml; 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; }; 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 std::move(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( SimpleFileWasmYAML, [](const Binary &File) { return File.isWasm(); }, ".foo", "1234", AddSection); } TEST(UpdateSection, COFF) { SCOPED_TRACE("updateSectionToFileCOFF"); addOrUpdateSectionToFileImpl( SimpleFileCOFFYAML, [](const Binary &File) { return File.isCOFF(); }, ".text", "1234", UpdateSection); } TEST(UpdateSection, ELF) { SCOPED_TRACE("updateSectionToFileELF"); addOrUpdateSectionToFileImpl( SimpleFileELFYAML, [](const Binary &File) { return File.isELF(); }, ".text", "1234", UpdateSection); } TEST(UpdateSection, MachO) { SCOPED_TRACE("updateSectionToFileMachO"); addOrUpdateSectionToFileImpl( 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"); }