xref: /llvm-project/llvm/unittests/ObjCopy/ObjCopyTest.cpp (revision a6f3fedc3f12a444924b4d30a4dcad1b6eaad965)
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