398 lines · cpp
1//===- ObjCopyTest.cpp ----------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/ObjCopy/ObjCopy.h"10#include "llvm/ObjCopy/ConfigManager.h"11#include "llvm/Object/ObjectFile.h"12#include "llvm/ObjectYAML/yaml2obj.h"13#include "llvm/Support/Error.h"14#include "llvm/Support/FileUtilities.h"15#include "llvm/Testing/Support/Error.h"16#include "gtest/gtest.h"17 18using namespace llvm;19using namespace object;20using namespace objcopy;21using namespace yaml;22 23const char *SimpleFileCOFFYAML = R"(24--- !COFF25header:26 Machine: IMAGE_FILE_MACHINE_AMD6427 Characteristics: [ ]28sections:29 - Name: .text30 Characteristics: [ ]31 Alignment: 432 SectionData: E800000000C3C3C333symbols:34...35)";36 37const char *SimpleFileELFYAML = R"(38--- !ELF39FileHeader:40 Class: ELFCLASS6441 Data: ELFDATA2LSB42 Type: ET_REL43Sections:44 - Name: .text45 Type: SHT_PROGBITS46 Flags: [ SHF_ALLOC ]47 Content: "12345678"48)";49 50const char *SimpleFileMachOYAML = R"(51--- !mach-o52FileHeader:53 magic: 0xFEEDFACF54 cputype: 0x0100000755 cpusubtype: 0x8000000356 filetype: 0x0000000157 ncmds: 158 sizeofcmds: 15259 flags: 0x0000200060 reserved: 0x0000000061LoadCommands:62 - cmd: LC_SEGMENT_6463 cmdsize: 15264 segname: __TEXT65 vmaddr: 066 vmsize: 467 fileoff: 18468 filesize: 469 maxprot: 770 initprot: 771 nsects: 172 flags: 073 Sections:74 - sectname: __text75 segname: __TEXT76 addr: 0x000000000000000077 content: 'AABBCCDD'78 size: 479 offset: 18480 align: 081 reloff: 0x0000000082 nreloc: 083 flags: 0x8000040084 reserved1: 0x0000000085 reserved2: 0x0000000086 reserved3: 0x0000000087...88)";89 90const char *SimpleFileWasmYAML = R"(91--- !WASM92FileHeader:93 Version: 0x0000000194Sections:95 - Type: CUSTOM96 Name: text97 Payload: ABC12398...99)";100 101// Create ObjectFile from \p YamlCreationString and do validation using \p102// IsValidFormat checker. \p Storage is a storage for data. \returns created103// ObjectFile.104Expected<std::unique_ptr<ObjectFile>> createObjectFileFromYamlDescription(105 const char *YamlCreationString, SmallVector<char> &Storage,106 function_ref<bool(const Binary &File)> IsValidFormat) {107 auto ErrHandler = [&](const Twine &Msg) { FAIL() << "Error: " << Msg; };108 109 std::unique_ptr<ObjectFile> Obj =110 yaml2ObjectFile(Storage, YamlCreationString, ErrHandler);111 if (!Obj)112 return createError("could not create ObjectFile from yaml description");113 114 if (!IsValidFormat(*Obj))115 return createError("wrong file format");116 117 return std::move(Obj);118}119 120// Call objcopy::executeObjcopyOnBinary for \p Config and \p In. \p DataVector121// is a holder for data. \returns Binary for copied data.122Expected<std::unique_ptr<Binary>>123callObjCopy(ConfigManager &Config, object::Binary &In,124 SmallVector<char> &DataVector,125 function_ref<bool(const Binary &File)> IsValidFormat) {126 raw_svector_ostream OutStream(DataVector);127 128 if (Error Err = objcopy::executeObjcopyOnBinary(Config, In, OutStream))129 return std::move(Err);130 131 MemoryBufferRef Buffer(StringRef(DataVector.data(), DataVector.size()),132 Config.Common.OutputFilename);133 134 Expected<std::unique_ptr<Binary>> Result = createBinary(Buffer);135 136 // Check copied file.137 if (!Result)138 return Result;139 140 if (!IsValidFormat(**Result))141 return createError("wrong file format");142 143 if (!(*Result)->isObject())144 return createError("binary is not object file");145 146 return Result;147}148 149// \returns true if specified \p File has a section named \p SectionName.150bool hasSection(ObjectFile &File, StringRef SectionName) {151 for (const object::SectionRef &Sec : File.sections()) {152 Expected<StringRef> CurSecNameOrErr = Sec.getName();153 if (!CurSecNameOrErr)154 continue;155 156 if (*CurSecNameOrErr == SectionName)157 return true;158 }159 160 return false;161}162 163// Check that specified \p File has a section \p SectionName and its data164// matches \p SectionData.165void checkSectionData(ObjectFile &File, StringRef SectionName,166 StringRef SectionData) {167 for (const object::SectionRef &Sec : File.sections()) {168 Expected<StringRef> CurSecNameOrErr = Sec.getName();169 ASSERT_THAT_EXPECTED(CurSecNameOrErr, Succeeded());170 171 if (*CurSecNameOrErr == SectionName) {172 Expected<StringRef> CurSectionData = Sec.getContents();173 ASSERT_THAT_EXPECTED(CurSectionData, Succeeded());174 EXPECT_TRUE(Sec.getSize() == SectionData.size());175 EXPECT_TRUE(memcmp(CurSectionData->data(), SectionData.data(),176 SectionData.size()) == 0);177 return;178 }179 }180 181 // Section SectionName must be presented.182 EXPECT_TRUE(false);183}184 185void copySimpleInMemoryFileImpl(186 const char *YamlCreationString,187 function_ref<bool(const Binary &File)> IsValidFormat) {188 SCOPED_TRACE("copySimpleInMemoryFileImpl");189 190 // Create Object file from YAML description.191 SmallVector<char> Storage;192 Expected<std::unique_ptr<ObjectFile>> Obj =193 createObjectFileFromYamlDescription(YamlCreationString, Storage,194 IsValidFormat);195 ASSERT_THAT_EXPECTED(Obj, Succeeded());196 197 ConfigManager Config;198 Config.Common.OutputFilename = "a.out";199 200 // Call executeObjcopyOnBinary()201 SmallVector<char> DataVector;202 Expected<std::unique_ptr<Binary>> Result =203 callObjCopy(Config, *Obj.get(), DataVector, IsValidFormat);204 ASSERT_THAT_EXPECTED(Result, Succeeded());205}206 207TEST(CopySimpleInMemoryFile, COFF) {208 SCOPED_TRACE("CopySimpleInMemoryFileCOFF");209 210 copySimpleInMemoryFileImpl(SimpleFileCOFFYAML,211 [](const Binary &File) { return File.isCOFF(); });212}213 214TEST(CopySimpleInMemoryFile, ELF) {215 SCOPED_TRACE("CopySimpleInMemoryFileELF");216 217 copySimpleInMemoryFileImpl(SimpleFileELFYAML,218 [](const Binary &File) { return File.isELF(); });219}220 221TEST(CopySimpleInMemoryFile, MachO) {222 SCOPED_TRACE("CopySimpleInMemoryFileMachO");223 224 copySimpleInMemoryFileImpl(SimpleFileMachOYAML,225 [](const Binary &File) { return File.isMachO(); });226}227 228TEST(CopySimpleInMemoryFile, Wasm) {229 SCOPED_TRACE("CopySimpleInMemoryFileWasm");230 231 copySimpleInMemoryFileImpl(SimpleFileWasmYAML,232 [](const Binary &File) { return File.isWasm(); });233}234 235enum Action : uint8_t { AddSection, UpdateSection };236 237void addOrUpdateSectionToFileImpl(238 const char *YamlCreationString,239 function_ref<bool(const Binary &File)> IsValidFormat,240 StringRef NewSectionName, StringRef NewSectionData, Action SectionAction) {241 SCOPED_TRACE("addOrUpdateSectionToFileImpl");242 243 // Create Object file from YAML description.244 SmallVector<char> Storage;245 Expected<std::unique_ptr<ObjectFile>> Obj =246 createObjectFileFromYamlDescription(YamlCreationString, Storage,247 IsValidFormat);248 ASSERT_THAT_EXPECTED(Obj, Succeeded());249 250 std::unique_ptr<MemoryBuffer> NewSectionBuffer =251 MemoryBuffer::getMemBuffer(NewSectionData, NewSectionName, false);252 std::string Name;253 if ((*Obj)->isMachO())254 Name = "__TEXT," + NewSectionName.str();255 else256 Name = NewSectionName.str();257 258 ConfigManager Config;259 Config.Common.OutputFilename = "a.out";260 if (SectionAction == AddSection)261 Config.Common.AddSection.push_back({Name, std::move(NewSectionBuffer)});262 else263 Config.Common.UpdateSection.push_back({Name, std::move(NewSectionBuffer)});264 265 // Call executeObjcopyOnBinary()266 SmallVector<char> DataVector;267 Expected<std::unique_ptr<Binary>> Result =268 callObjCopy(Config, *Obj.get(), DataVector, IsValidFormat);269 ASSERT_THAT_EXPECTED(Result, Succeeded());270 271 // Check that copied file has the new section.272 checkSectionData(*static_cast<ObjectFile *>((*Result).get()), NewSectionName,273 NewSectionData);274}275 276TEST(AddSection, COFF) {277 SCOPED_TRACE("addSectionToFileCOFF");278 279 addOrUpdateSectionToFileImpl(280 SimpleFileCOFFYAML, [](const Binary &File) { return File.isCOFF(); },281 ".foo", "1234", AddSection);282}283 284TEST(AddSection, ELF) {285 SCOPED_TRACE("addSectionToFileELF");286 287 addOrUpdateSectionToFileImpl(288 SimpleFileELFYAML, [](const Binary &File) { return File.isELF(); },289 ".foo", "1234", AddSection);290}291 292TEST(AddSection, MachO) {293 SCOPED_TRACE("addSectionToFileMachO");294 295 addOrUpdateSectionToFileImpl(296 SimpleFileMachOYAML, [](const Binary &File) { return File.isMachO(); },297 "__foo", "1234", AddSection);298}299 300TEST(AddSection, Wasm) {301 SCOPED_TRACE("addSectionToFileWasm");302 303 addOrUpdateSectionToFileImpl(304 SimpleFileWasmYAML, [](const Binary &File) { return File.isWasm(); },305 ".foo", "1234", AddSection);306}307 308TEST(UpdateSection, COFF) {309 SCOPED_TRACE("updateSectionToFileCOFF");310 311 addOrUpdateSectionToFileImpl(312 SimpleFileCOFFYAML, [](const Binary &File) { return File.isCOFF(); },313 ".text", "1234", UpdateSection);314}315 316TEST(UpdateSection, ELF) {317 SCOPED_TRACE("updateSectionToFileELF");318 319 addOrUpdateSectionToFileImpl(320 SimpleFileELFYAML, [](const Binary &File) { return File.isELF(); },321 ".text", "1234", UpdateSection);322}323 324TEST(UpdateSection, MachO) {325 SCOPED_TRACE("updateSectionToFileMachO");326 327 addOrUpdateSectionToFileImpl(328 SimpleFileMachOYAML, [](const Binary &File) { return File.isMachO(); },329 "__text", "1234", UpdateSection);330}331 332void removeSectionByPatternImpl(333 const char *YamlCreationString,334 function_ref<bool(const Binary &File)> IsValidFormat,335 StringRef SectionWildcard, StringRef SectionName) {336 SCOPED_TRACE("removeSectionByPatternImpl");337 338 // Create Object file from YAML description.339 SmallVector<char> Storage;340 Expected<std::unique_ptr<ObjectFile>> Obj =341 createObjectFileFromYamlDescription(YamlCreationString, Storage,342 IsValidFormat);343 ASSERT_THAT_EXPECTED(Obj, Succeeded());344 345 // Check that section is present.346 EXPECT_TRUE(hasSection(**Obj, SectionName));347 348 Expected<NameOrPattern> Pattern = objcopy::NameOrPattern::create(349 SectionWildcard, objcopy::MatchStyle::Wildcard,350 [](Error Err) { return Err; });351 352 ConfigManager Config;353 Config.Common.OutputFilename = "a.out";354 EXPECT_THAT_ERROR(Config.Common.ToRemove.addMatcher(std::move(Pattern)),355 Succeeded());356 357 SmallVector<char> DataVector;358 Expected<std::unique_ptr<Binary>> Result =359 callObjCopy(Config, *Obj.get(), DataVector, IsValidFormat);360 ASSERT_THAT_EXPECTED(Result, Succeeded());361 362 // Check that section was removed.363 EXPECT_FALSE(364 hasSection(*static_cast<ObjectFile *>((*Result).get()), SectionName));365}366 367TEST(RemoveSectionByPattern, COFF) {368 SCOPED_TRACE("removeSectionByPatternCOFF");369 370 removeSectionByPatternImpl(371 SimpleFileCOFFYAML, [](const Binary &File) { return File.isCOFF(); },372 "\\.text*", ".text");373}374 375TEST(RemoveSectionByPattern, ELF) {376 SCOPED_TRACE("removeSectionByPatternELF");377 378 removeSectionByPatternImpl(379 SimpleFileELFYAML, [](const Binary &File) { return File.isELF(); },380 "\\.text*", ".text");381}382 383TEST(RemoveSectionByPattern, MachO) {384 SCOPED_TRACE("removeSectionByPatternMachO");385 386 removeSectionByPatternImpl(387 SimpleFileMachOYAML, [](const Binary &File) { return File.isMachO(); },388 "__TEXT,__text*", "__text");389}390 391TEST(RemoveSectionByPattern, Wasm) {392 SCOPED_TRACE("removeSectionByPatternWasm");393 394 removeSectionByPatternImpl(395 SimpleFileWasmYAML, [](const Binary &File) { return File.isWasm(); },396 "text*", "text");397}398