1 //===- ObjCopyTest.cpp ----------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
20 using namespace object
;
21 using namespace objcopy
;
24 const char *SimpleFileCOFFYAML
= R
"(
27 Machine: IMAGE_FILE_MACHINE_AMD64
33 SectionData: E800000000C3C3C3
38 const char *SimpleFileELFYAML
= R
"(
51 const char *SimpleFileMachOYAML
= R
"(
56 cpusubtype: 0x80000003
77 addr: 0x0000000000000000
91 const char *SimpleFileWasmYAML
= R
"(
102 // Create ObjectFile from \p YamlCreationString and do validation using \p
103 // IsValidFormat checker. \p Storage is a storage for data. \returns created
105 Expected
<std::unique_ptr
<ObjectFile
>> createObjectFileFromYamlDescription(
106 const char *YamlCreationString
, SmallVector
<char> &Storage
,
107 function_ref
<bool(const Binary
&File
)> IsValidFormat
) {
108 auto ErrHandler
= [&](const Twine
&Msg
) { FAIL() << "Error: " << Msg
; };
110 std::unique_ptr
<ObjectFile
> Obj
=
111 yaml2ObjectFile(Storage
, YamlCreationString
, ErrHandler
);
113 return createError("could not create ObjectFile from yaml description");
115 if (!IsValidFormat(*Obj
))
116 return createError("wrong file format");
118 return std::move(Obj
);
121 // Call objcopy::executeObjcopyOnBinary for \p Config and \p In. \p DataVector
122 // is a holder for data. \returns Binary for copied data.
123 Expected
<std::unique_ptr
<Binary
>>
124 callObjCopy(ConfigManager
&Config
, object::Binary
&In
,
125 SmallVector
<char> &DataVector
,
126 function_ref
<bool(const Binary
&File
)> IsValidFormat
) {
127 raw_svector_ostream
OutStream(DataVector
);
129 if (Error Err
= objcopy::executeObjcopyOnBinary(Config
, In
, OutStream
))
130 return std::move(Err
);
132 MemoryBufferRef
Buffer(StringRef(DataVector
.data(), DataVector
.size()),
133 Config
.Common
.OutputFilename
);
135 Expected
<std::unique_ptr
<Binary
>> Result
= createBinary(Buffer
);
137 // Check copied file.
141 if (!IsValidFormat(**Result
))
142 return createError("wrong file format");
144 if (!(*Result
)->isObject())
145 return createError("binary is not object file");
150 // \returns true if specified \p File has a section named \p SectionName.
151 bool hasSection(ObjectFile
&File
, StringRef SectionName
) {
152 for (const object::SectionRef
&Sec
: File
.sections()) {
153 Expected
<StringRef
> CurSecNameOrErr
= Sec
.getName();
154 if (!CurSecNameOrErr
)
157 if (*CurSecNameOrErr
== SectionName
)
164 // Check that specified \p File has a section \p SectionName and its data
165 // matches \p SectionData.
166 void checkSectionData(ObjectFile
&File
, StringRef SectionName
,
167 StringRef SectionData
) {
168 for (const object::SectionRef
&Sec
: File
.sections()) {
169 Expected
<StringRef
> CurSecNameOrErr
= Sec
.getName();
170 ASSERT_THAT_EXPECTED(CurSecNameOrErr
, Succeeded());
172 if (*CurSecNameOrErr
== SectionName
) {
173 Expected
<StringRef
> CurSectionData
= Sec
.getContents();
174 ASSERT_THAT_EXPECTED(CurSectionData
, Succeeded());
175 EXPECT_TRUE(Sec
.getSize() == SectionData
.size());
176 EXPECT_TRUE(memcmp(CurSectionData
->data(), SectionData
.data(),
177 SectionData
.size()) == 0);
182 // Section SectionName must be presented.
186 void copySimpleInMemoryFileImpl(
187 const char *YamlCreationString
,
188 function_ref
<bool(const Binary
&File
)> IsValidFormat
) {
189 SCOPED_TRACE("copySimpleInMemoryFileImpl");
191 // Create Object file from YAML description.
192 SmallVector
<char> Storage
;
193 Expected
<std::unique_ptr
<ObjectFile
>> Obj
=
194 createObjectFileFromYamlDescription(YamlCreationString
, Storage
,
196 ASSERT_THAT_EXPECTED(Obj
, Succeeded());
198 ConfigManager Config
;
199 Config
.Common
.OutputFilename
= "a.out";
201 // Call executeObjcopyOnBinary()
202 SmallVector
<char> DataVector
;
203 Expected
<std::unique_ptr
<Binary
>> Result
=
204 callObjCopy(Config
, *Obj
.get(), DataVector
, IsValidFormat
);
205 ASSERT_THAT_EXPECTED(Result
, Succeeded());
208 TEST(CopySimpleInMemoryFile
, COFF
) {
209 SCOPED_TRACE("CopySimpleInMemoryFileCOFF");
211 copySimpleInMemoryFileImpl(SimpleFileCOFFYAML
,
212 [](const Binary
&File
) { return File
.isCOFF(); });
215 TEST(CopySimpleInMemoryFile
, ELF
) {
216 SCOPED_TRACE("CopySimpleInMemoryFileELF");
218 copySimpleInMemoryFileImpl(SimpleFileELFYAML
,
219 [](const Binary
&File
) { return File
.isELF(); });
222 TEST(CopySimpleInMemoryFile
, MachO
) {
223 SCOPED_TRACE("CopySimpleInMemoryFileMachO");
225 copySimpleInMemoryFileImpl(SimpleFileMachOYAML
,
226 [](const Binary
&File
) { return File
.isMachO(); });
229 TEST(CopySimpleInMemoryFile
, Wasm
) {
230 SCOPED_TRACE("CopySimpleInMemoryFileWasm");
232 copySimpleInMemoryFileImpl(SimpleFileWasmYAML
,
233 [](const Binary
&File
) { return File
.isWasm(); });
236 enum Action
: uint8_t { AddSection
, UpdateSection
};
238 void addOrUpdateSectionToFileImpl(
239 const char *YamlCreationString
,
240 function_ref
<bool(const Binary
&File
)> IsValidFormat
,
241 StringRef NewSectionName
, StringRef NewSectionData
, Action SectionAction
) {
242 SCOPED_TRACE("addOrUpdateSectionToFileImpl");
244 // Create Object file from YAML description.
245 SmallVector
<char> Storage
;
246 Expected
<std::unique_ptr
<ObjectFile
>> Obj
=
247 createObjectFileFromYamlDescription(YamlCreationString
, Storage
,
249 ASSERT_THAT_EXPECTED(Obj
, Succeeded());
251 std::unique_ptr
<MemoryBuffer
> NewSectionBuffer
=
252 MemoryBuffer::getMemBuffer(NewSectionData
, NewSectionName
, false);
254 if ((*Obj
)->isMachO())
255 Name
= "__TEXT," + NewSectionName
.str();
257 Name
= NewSectionName
.str();
259 ConfigManager Config
;
260 Config
.Common
.OutputFilename
= "a.out";
261 if (SectionAction
== AddSection
)
262 Config
.Common
.AddSection
.push_back({Name
, std::move(NewSectionBuffer
)});
264 Config
.Common
.UpdateSection
.push_back({Name
, std::move(NewSectionBuffer
)});
266 // Call executeObjcopyOnBinary()
267 SmallVector
<char> DataVector
;
268 Expected
<std::unique_ptr
<Binary
>> Result
=
269 callObjCopy(Config
, *Obj
.get(), DataVector
, IsValidFormat
);
270 ASSERT_THAT_EXPECTED(Result
, Succeeded());
272 // Check that copied file has the new section.
273 checkSectionData(*static_cast<ObjectFile
*>((*Result
).get()), NewSectionName
,
277 TEST(AddSection
, COFF
) {
278 SCOPED_TRACE("addSectionToFileCOFF");
280 addOrUpdateSectionToFileImpl(
281 SimpleFileCOFFYAML
, [](const Binary
&File
) { return File
.isCOFF(); },
282 ".foo", "1234", AddSection
);
285 TEST(AddSection
, ELF
) {
286 SCOPED_TRACE("addSectionToFileELF");
288 addOrUpdateSectionToFileImpl(
289 SimpleFileELFYAML
, [](const Binary
&File
) { return File
.isELF(); },
290 ".foo", "1234", AddSection
);
293 TEST(AddSection
, MachO
) {
294 SCOPED_TRACE("addSectionToFileMachO");
296 addOrUpdateSectionToFileImpl(
297 SimpleFileMachOYAML
, [](const Binary
&File
) { return File
.isMachO(); },
298 "__foo", "1234", AddSection
);
301 TEST(AddSection
, Wasm
) {
302 SCOPED_TRACE("addSectionToFileWasm");
304 addOrUpdateSectionToFileImpl(
305 SimpleFileWasmYAML
, [](const Binary
&File
) { return File
.isWasm(); },
306 ".foo", "1234", AddSection
);
309 TEST(UpdateSection
, COFF
) {
310 SCOPED_TRACE("updateSectionToFileCOFF");
312 addOrUpdateSectionToFileImpl(
313 SimpleFileCOFFYAML
, [](const Binary
&File
) { return File
.isCOFF(); },
314 ".text", "1234", UpdateSection
);
317 TEST(UpdateSection
, ELF
) {
318 SCOPED_TRACE("updateSectionToFileELF");
320 addOrUpdateSectionToFileImpl(
321 SimpleFileELFYAML
, [](const Binary
&File
) { return File
.isELF(); },
322 ".text", "1234", UpdateSection
);
325 TEST(UpdateSection
, MachO
) {
326 SCOPED_TRACE("updateSectionToFileMachO");
328 addOrUpdateSectionToFileImpl(
329 SimpleFileMachOYAML
, [](const Binary
&File
) { return File
.isMachO(); },
330 "__text", "1234", UpdateSection
);
333 void removeSectionByPatternImpl(
334 const char *YamlCreationString
,
335 function_ref
<bool(const Binary
&File
)> IsValidFormat
,
336 StringRef SectionWildcard
, StringRef SectionName
) {
337 SCOPED_TRACE("removeSectionByPatternImpl");
339 // Create Object file from YAML description.
340 SmallVector
<char> Storage
;
341 Expected
<std::unique_ptr
<ObjectFile
>> Obj
=
342 createObjectFileFromYamlDescription(YamlCreationString
, Storage
,
344 ASSERT_THAT_EXPECTED(Obj
, Succeeded());
346 // Check that section is present.
347 EXPECT_TRUE(hasSection(**Obj
, SectionName
));
349 Expected
<NameOrPattern
> Pattern
= objcopy::NameOrPattern::create(
350 SectionWildcard
, objcopy::MatchStyle::Wildcard
,
351 [](Error Err
) { return Err
; });
353 ConfigManager Config
;
354 Config
.Common
.OutputFilename
= "a.out";
355 EXPECT_THAT_ERROR(Config
.Common
.ToRemove
.addMatcher(std::move(Pattern
)),
358 SmallVector
<char> DataVector
;
359 Expected
<std::unique_ptr
<Binary
>> Result
=
360 callObjCopy(Config
, *Obj
.get(), DataVector
, IsValidFormat
);
361 ASSERT_THAT_EXPECTED(Result
, Succeeded());
363 // Check that section was removed.
365 hasSection(*static_cast<ObjectFile
*>((*Result
).get()), SectionName
));
368 TEST(RemoveSectionByPattern
, COFF
) {
369 SCOPED_TRACE("removeSectionByPatternCOFF");
371 removeSectionByPatternImpl(
372 SimpleFileCOFFYAML
, [](const Binary
&File
) { return File
.isCOFF(); },
373 "\\.text*", ".text");
376 TEST(RemoveSectionByPattern
, ELF
) {
377 SCOPED_TRACE("removeSectionByPatternELF");
379 removeSectionByPatternImpl(
380 SimpleFileELFYAML
, [](const Binary
&File
) { return File
.isELF(); },
381 "\\.text*", ".text");
384 TEST(RemoveSectionByPattern
, MachO
) {
385 SCOPED_TRACE("removeSectionByPatternMachO");
387 removeSectionByPatternImpl(
388 SimpleFileMachOYAML
, [](const Binary
&File
) { return File
.isMachO(); },
389 "__TEXT,__text*", "__text");
392 TEST(RemoveSectionByPattern
, Wasm
) {
393 SCOPED_TRACE("removeSectionByPatternWasm");
395 removeSectionByPatternImpl(
396 SimpleFileWasmYAML
, [](const Binary
&File
) { return File
.isWasm(); },