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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "llvm/ObjCopy/ObjCopy.h" 10 #include "llvm/ADT/SmallString.h" 11 #include "llvm/ObjCopy/ConfigManager.h" 12 #include "llvm/Object/ObjectFile.h" 13 #include "llvm/ObjectYAML/yaml2obj.h" 14 #include "llvm/Support/Error.h" 15 #include "llvm/Support/FileUtilities.h" 16 #include "llvm/Testing/Support/Error.h" 17 #include "gtest/gtest.h" 18 19 using namespace llvm; 20 using namespace object; 21 using namespace objcopy; 22 using namespace yaml; 23 24 void copySimpleInMemoryFileImpl( 25 const char *YamlCreationString, 26 std::function<bool(const Binary &File)> IsValidFormat) { 27 auto ErrHandler = [&](const Twine &Msg) { FAIL() << "Error: " << Msg; }; 28 29 // Create Object file from YAML description. 30 SmallVector<char> Storage; 31 std::unique_ptr<ObjectFile> Obj = 32 yaml2ObjectFile(Storage, YamlCreationString, ErrHandler); 33 ASSERT_TRUE(Obj); 34 ASSERT_TRUE(IsValidFormat(*Obj)); 35 36 ConfigManager Config; 37 Config.Common.OutputFilename = "a.out"; 38 39 // Call executeObjcopyOnBinary() 40 SmallVector<char> DataVector; 41 raw_svector_ostream OutStream(DataVector); 42 Error Err = objcopy::executeObjcopyOnBinary(Config, *Obj.get(), OutStream); 43 ASSERT_FALSE(std::move(Err)); 44 45 MemoryBufferRef Buffer(StringRef(DataVector.data(), DataVector.size()), 46 Config.Common.OutputFilename); 47 48 // Check copied file. 49 Expected<std::unique_ptr<Binary>> Result = createBinary(Buffer); 50 ASSERT_THAT_EXPECTED(Result, Succeeded()); 51 ASSERT_TRUE(IsValidFormat(**Result)); 52 } 53 54 TEST(CopySimpleInMemoryFile, COFF) { 55 SCOPED_TRACE("CopySimpleInMemoryFileCOFF"); 56 57 copySimpleInMemoryFileImpl( 58 R"( 59 --- !COFF 60 header: 61 Machine: IMAGE_FILE_MACHINE_AMD64 62 Characteristics: [ ] 63 sections: 64 - Name: .text 65 Characteristics: [ ] 66 Alignment: 4 67 SectionData: E800000000C3C3C3 68 symbols: 69 ... 70 )", 71 [](const Binary &File) { return File.isCOFF(); }); 72 } 73 74 TEST(CopySimpleInMemoryFile, ELF) { 75 SCOPED_TRACE("CopySimpleInMemoryFileELF"); 76 77 copySimpleInMemoryFileImpl( 78 R"( 79 --- !ELF 80 FileHeader: 81 Class: ELFCLASS64 82 Data: ELFDATA2LSB 83 Type: ET_REL)", 84 [](const Binary &File) { return File.isELF(); }); 85 } 86 87 TEST(CopySimpleInMemoryFile, MachO) { 88 SCOPED_TRACE("CopySimpleInMemoryFileMachO"); 89 90 copySimpleInMemoryFileImpl( 91 R"( 92 --- !mach-o 93 FileHeader: 94 magic: 0xFEEDFACF 95 cputype: 0x01000007 96 cpusubtype: 0x80000003 97 filetype: 0x00000002 98 ncmds: 0 99 sizeofcmds: 0 100 flags: 0x00218085 101 reserved: 0x00000000 102 ... 103 )", 104 [](const Binary &File) { return File.isMachO(); }); 105 } 106 107 TEST(CopySimpleInMemoryFile, Wasm) { 108 SCOPED_TRACE("CopySimpleInMemoryFileWasm"); 109 110 copySimpleInMemoryFileImpl( 111 R"( 112 --- !WASM 113 FileHeader: 114 Version: 0x00000001 115 ... 116 )", 117 [](const Binary &File) { return File.isWasm(); }); 118 } 119 120 enum Action : uint8_t { AddSection, UpdateSection }; 121 122 void addOrUpdateSectionToFileImpl( 123 const char *YamlCreationString, 124 std::function<bool(const Binary &File)> IsValidFormat, 125 StringRef NewSectionName, StringRef NewSectionData, Action SectionAction) { 126 auto ErrHandler = [&](const Twine &Msg) { FAIL() << "Error: " << Msg; }; 127 128 // Create Object file from YAML description. 129 SmallVector<char> Storage; 130 std::unique_ptr<ObjectFile> Obj = 131 yaml2ObjectFile(Storage, YamlCreationString, ErrHandler); 132 ASSERT_TRUE(Obj); 133 ASSERT_TRUE(IsValidFormat(*Obj)); 134 135 std::unique_ptr<MemoryBuffer> NewSectionBuffer = 136 MemoryBuffer::getMemBuffer(NewSectionData, NewSectionName, false); 137 std::string Name; 138 if (Obj->isMachO()) 139 Name = "__TEXT," + NewSectionName.str(); 140 else 141 Name = NewSectionName.str(); 142 143 ConfigManager Config; 144 Config.Common.OutputFilename = "a.out"; 145 if (SectionAction == AddSection) 146 Config.Common.AddSection.push_back({Name, std::move(NewSectionBuffer)}); 147 else 148 Config.Common.UpdateSection.push_back({Name, std::move(NewSectionBuffer)}); 149 150 // Call executeObjcopyOnBinary() 151 SmallVector<char> DataVector; 152 raw_svector_ostream OutStream(DataVector); 153 Error Err = objcopy::executeObjcopyOnBinary(Config, *Obj.get(), OutStream); 154 ASSERT_FALSE(std::move(Err)); 155 156 MemoryBufferRef Buffer(StringRef(DataVector.data(), DataVector.size()), 157 Config.Common.OutputFilename); 158 159 // Check copied file. 160 Expected<std::unique_ptr<Binary>> Result = createBinary(Buffer); 161 ASSERT_THAT_EXPECTED(Result, Succeeded()); 162 ASSERT_TRUE(IsValidFormat(**Result)); 163 ASSERT_TRUE((*Result)->isObject()); 164 165 // Check that copied file has the new section. 166 bool HasNewSection = false; 167 for (const object::SectionRef &Sect : 168 static_cast<ObjectFile *>((*Result).get())->sections()) { 169 Expected<StringRef> SectNameOrErr = Sect.getName(); 170 ASSERT_THAT_EXPECTED(SectNameOrErr, Succeeded()); 171 172 if (*SectNameOrErr == NewSectionName) { 173 HasNewSection = true; 174 Expected<StringRef> SectionData = Sect.getContents(); 175 ASSERT_THAT_EXPECTED(SectionData, Succeeded()); 176 EXPECT_TRUE(Sect.getSize() == NewSectionData.size()); 177 EXPECT_TRUE(memcmp(SectionData->data(), NewSectionData.data(), 178 NewSectionData.size()) == 0); 179 break; 180 } 181 } 182 EXPECT_TRUE(HasNewSection); 183 } 184 185 TEST(AddSection, COFF) { 186 SCOPED_TRACE("addSectionToFileCOFF"); 187 188 addOrUpdateSectionToFileImpl( 189 R"( 190 --- !COFF 191 header: 192 Machine: IMAGE_FILE_MACHINE_AMD64 193 Characteristics: [ ] 194 sections: 195 - Name: .text 196 Characteristics: [ ] 197 Alignment: 4 198 SectionData: E800000000C3C3C3 199 symbols: 200 ... 201 )", 202 [](const Binary &File) { return File.isCOFF(); }, ".foo", "1234", 203 AddSection); 204 } 205 206 TEST(AddSection, ELF) { 207 SCOPED_TRACE("addSectionToFileELF"); 208 209 addOrUpdateSectionToFileImpl( 210 R"( 211 --- !ELF 212 FileHeader: 213 Class: ELFCLASS64 214 Data: ELFDATA2LSB 215 Type: ET_REL)", 216 [](const Binary &File) { return File.isELF(); }, ".foo", "1234", 217 AddSection); 218 } 219 220 TEST(AddSection, MachO) { 221 SCOPED_TRACE("addSectionToFileMachO"); 222 223 addOrUpdateSectionToFileImpl( 224 R"( 225 --- !mach-o 226 FileHeader: 227 magic: 0xFEEDFACF 228 cputype: 0x01000007 229 cpusubtype: 0x80000003 230 filetype: 0x00000001 231 ncmds: 1 232 sizeofcmds: 152 233 flags: 0x00002000 234 reserved: 0x00000000 235 LoadCommands: 236 - cmd: LC_SEGMENT_64 237 cmdsize: 152 238 segname: __TEXT 239 vmaddr: 0 240 vmsize: 4 241 fileoff: 184 242 filesize: 4 243 maxprot: 7 244 initprot: 7 245 nsects: 1 246 flags: 0 247 Sections: 248 - sectname: __text 249 segname: __TEXT 250 addr: 0x0000000000000000 251 content: 'AABBCCDD' 252 size: 4 253 offset: 184 254 align: 0 255 reloff: 0x00000000 256 nreloc: 0 257 flags: 0x80000400 258 reserved1: 0x00000000 259 reserved2: 0x00000000 260 reserved3: 0x00000000 261 ... 262 )", 263 [](const Binary &File) { return File.isMachO(); }, "__foo", "1234", 264 AddSection); 265 } 266 267 TEST(AddSection, Wasm) { 268 SCOPED_TRACE("addSectionToFileWasm"); 269 270 addOrUpdateSectionToFileImpl( 271 R"( 272 --- !WASM 273 FileHeader: 274 Version: 0x00000001 275 ... 276 )", 277 [](const Binary &File) { return File.isWasm(); }, ".foo", "1234", 278 AddSection); 279 } 280 281 TEST(UpdateSection, COFF) { 282 SCOPED_TRACE("updateSectionToFileCOFF"); 283 284 addOrUpdateSectionToFileImpl( 285 R"( 286 --- !COFF 287 header: 288 Machine: IMAGE_FILE_MACHINE_AMD64 289 Characteristics: [ ] 290 sections: 291 - Name: .foo 292 Characteristics: [ ] 293 Alignment: 4 294 SectionData: E800000000C3C3C3 295 symbols: 296 ... 297 )", 298 [](const Binary &File) { return File.isCOFF(); }, ".foo", "1234", 299 UpdateSection); 300 } 301 302 TEST(UpdateSection, ELF) { 303 SCOPED_TRACE("updateSectionToFileELF"); 304 305 addOrUpdateSectionToFileImpl( 306 R"( 307 --- !ELF 308 FileHeader: 309 Class: ELFCLASS64 310 Data: ELFDATA2LSB 311 Type: ET_REL 312 Sections: 313 - Name: .foo 314 Type: SHT_PROGBITS 315 Flags: [ SHF_ALLOC ] 316 Content: "12345678" 317 )", 318 [](const Binary &File) { return File.isELF(); }, ".foo", "1234", 319 UpdateSection); 320 } 321 322 TEST(UpdateSection, MachO) { 323 SCOPED_TRACE("updateSectionToFileMachO"); 324 325 addOrUpdateSectionToFileImpl( 326 R"( 327 --- !mach-o 328 FileHeader: 329 magic: 0xFEEDFACF 330 cputype: 0x01000007 331 cpusubtype: 0x80000003 332 filetype: 0x00000001 333 ncmds: 1 334 sizeofcmds: 152 335 flags: 0x00002000 336 reserved: 0x00000000 337 LoadCommands: 338 - cmd: LC_SEGMENT_64 339 cmdsize: 152 340 segname: __TEXT 341 vmaddr: 0 342 vmsize: 4 343 fileoff: 184 344 filesize: 4 345 maxprot: 7 346 initprot: 7 347 nsects: 1 348 flags: 0 349 Sections: 350 - sectname: __foo 351 segname: __TEXT 352 addr: 0x0000000000000000 353 content: 'AABBCCDD' 354 size: 4 355 offset: 184 356 align: 0 357 reloff: 0x00000000 358 nreloc: 0 359 flags: 0x80000400 360 reserved1: 0x00000000 361 reserved2: 0x00000000 362 reserved3: 0x00000000 363 ... 364 )", 365 [](const Binary &File) { return File.isMachO(); }, "__foo", "1234", 366 UpdateSection); 367 } 368