[objcopy][NFC] Move NameOrPattern::create() into CommonConfig.h

While moving objcopy into separate library(D88827), NameOrPattern::create()
was mistakenly placed into ObjcopyOptions.cpp. This patch moves
the NameOrPattern::create() into CommonConfig.h. Additionally it adds
test for using NameOrPattern.

Differential Revision: https://reviews.llvm.org/D121005
This commit is contained in:
Alexey Lapshin 2022-03-03 16:23:42 +03:00
parent e0cc28dfdc
commit 2dc4a80ec0
4 changed files with 348 additions and 301 deletions

View File

@ -37,6 +37,7 @@ source_group("Source Files\\XCOFF" REGULAR_EXPRESSION
add_llvm_component_library(LLVMObjCopy
Archive.cpp
CommonConfig.cpp
ObjCopy.cpp
ConfigManager.cpp
COFF/COFFObjcopy.cpp

View File

@ -0,0 +1,50 @@
//===- CommonConfig.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/CommonConfig.h"
namespace llvm {
namespace objcopy {
Expected<NameOrPattern>
NameOrPattern::create(StringRef Pattern, MatchStyle MS,
function_ref<Error(Error)> ErrorCallback) {
switch (MS) {
case MatchStyle::Literal:
return NameOrPattern(Pattern);
case MatchStyle::Wildcard: {
SmallVector<char, 32> Data;
bool IsPositiveMatch = true;
if (Pattern[0] == '!') {
IsPositiveMatch = false;
Pattern = Pattern.drop_front();
}
Expected<GlobPattern> GlobOrErr = GlobPattern::create(Pattern);
// If we couldn't create it as a glob, report the error, but try again
// with a literal if the error reporting is non-fatal.
if (!GlobOrErr) {
if (Error E = ErrorCallback(GlobOrErr.takeError()))
return std::move(E);
return create(Pattern, MatchStyle::Literal, ErrorCallback);
}
return NameOrPattern(std::make_shared<GlobPattern>(*GlobOrErr),
IsPositiveMatch);
}
case MatchStyle::Regex: {
SmallVector<char, 32> Data;
return NameOrPattern(std::make_shared<Regex>(
("^" + Pattern.ltrim('^').rtrim('$') + "$").toStringRef(Data)));
}
}
llvm_unreachable("Unhandled llvm.objcopy.MatchStyle enum");
}
} // end namespace objcopy
} // end namespace llvm

View File

@ -367,41 +367,6 @@ static Error addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc,
return Error::success();
}
Expected<NameOrPattern>
NameOrPattern::create(StringRef Pattern, MatchStyle MS,
function_ref<Error(Error)> ErrorCallback) {
switch (MS) {
case MatchStyle::Literal:
return NameOrPattern(Pattern);
case MatchStyle::Wildcard: {
SmallVector<char, 32> Data;
bool IsPositiveMatch = true;
if (Pattern[0] == '!') {
IsPositiveMatch = false;
Pattern = Pattern.drop_front();
}
Expected<GlobPattern> GlobOrErr = GlobPattern::create(Pattern);
// If we couldn't create it as a glob, report the error, but try again with
// a literal if the error reporting is non-fatal.
if (!GlobOrErr) {
if (Error E = ErrorCallback(GlobOrErr.takeError()))
return std::move(E);
return create(Pattern, MatchStyle::Literal, ErrorCallback);
}
return NameOrPattern(std::make_shared<GlobPattern>(*GlobOrErr),
IsPositiveMatch);
}
case MatchStyle::Regex: {
SmallVector<char, 32> Data;
return NameOrPattern(std::make_shared<Regex>(
("^" + Pattern.ltrim('^').rtrim('$') + "$").toStringRef(Data)));
}
}
llvm_unreachable("Unhandled llvm.objcopy.MatchStyle enum");
}
static Error addSymbolsToRenameFromFile(StringMap<StringRef> &SymbolsToRename,
BumpPtrAllocator &Alloc,
StringRef Filename) {

View File

@ -21,41 +21,7 @@ using namespace object;
using namespace objcopy;
using namespace yaml;
void copySimpleInMemoryFileImpl(
const char *YamlCreationString,
std::function<bool(const Binary &File)> IsValidFormat) {
auto ErrHandler = [&](const Twine &Msg) { FAIL() << "Error: " << Msg; };
// Create Object file from YAML description.
SmallVector<char> Storage;
std::unique_ptr<ObjectFile> Obj =
yaml2ObjectFile(Storage, YamlCreationString, ErrHandler);
ASSERT_TRUE(Obj);
ASSERT_TRUE(IsValidFormat(*Obj));
ConfigManager Config;
Config.Common.OutputFilename = "a.out";
// Call executeObjcopyOnBinary()
SmallVector<char> DataVector;
raw_svector_ostream OutStream(DataVector);
Error Err = objcopy::executeObjcopyOnBinary(Config, *Obj.get(), OutStream);
ASSERT_FALSE(std::move(Err));
MemoryBufferRef Buffer(StringRef(DataVector.data(), DataVector.size()),
Config.Common.OutputFilename);
// Check copied file.
Expected<std::unique_ptr<Binary>> Result = createBinary(Buffer);
ASSERT_THAT_EXPECTED(Result, Succeeded());
ASSERT_TRUE(IsValidFormat(**Result));
}
TEST(CopySimpleInMemoryFile, COFF) {
SCOPED_TRACE("CopySimpleInMemoryFileCOFF");
copySimpleInMemoryFileImpl(
R"(
const char *SimpleFileCOFFYAML = R"(
--- !COFF
header:
Machine: IMAGE_FILE_MACHINE_AMD64
@ -67,161 +33,22 @@ sections:
SectionData: E800000000C3C3C3
symbols:
...
)",
[](const Binary &File) { return File.isCOFF(); });
}
)";
TEST(CopySimpleInMemoryFile, ELF) {
SCOPED_TRACE("CopySimpleInMemoryFileELF");
copySimpleInMemoryFileImpl(
R"(
const char *SimpleFileELFYAML = R"(
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_REL)",
[](const Binary &File) { return File.isELF(); });
}
TEST(CopySimpleInMemoryFile, MachO) {
SCOPED_TRACE("CopySimpleInMemoryFileMachO");
copySimpleInMemoryFileImpl(
R"(
--- !mach-o
FileHeader:
magic: 0xFEEDFACF
cputype: 0x01000007
cpusubtype: 0x80000003
filetype: 0x00000002
ncmds: 0
sizeofcmds: 0
flags: 0x00218085
reserved: 0x00000000
...
)",
[](const Binary &File) { return File.isMachO(); });
}
TEST(CopySimpleInMemoryFile, Wasm) {
SCOPED_TRACE("CopySimpleInMemoryFileWasm");
copySimpleInMemoryFileImpl(
R"(
--- !WASM
FileHeader:
Version: 0x00000001
...
)",
[](const Binary &File) { return File.isWasm(); });
}
enum Action : uint8_t { AddSection, UpdateSection };
void addOrUpdateSectionToFileImpl(
const char *YamlCreationString,
std::function<bool(const Binary &File)> IsValidFormat,
StringRef NewSectionName, StringRef NewSectionData, Action SectionAction) {
auto ErrHandler = [&](const Twine &Msg) { FAIL() << "Error: " << Msg; };
// Create Object file from YAML description.
SmallVector<char> Storage;
std::unique_ptr<ObjectFile> Obj =
yaml2ObjectFile(Storage, YamlCreationString, ErrHandler);
ASSERT_TRUE(Obj);
ASSERT_TRUE(IsValidFormat(*Obj));
std::unique_ptr<MemoryBuffer> 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<char> DataVector;
raw_svector_ostream OutStream(DataVector);
Error Err = objcopy::executeObjcopyOnBinary(Config, *Obj.get(), OutStream);
ASSERT_FALSE(std::move(Err));
MemoryBufferRef Buffer(StringRef(DataVector.data(), DataVector.size()),
Config.Common.OutputFilename);
// Check copied file.
Expected<std::unique_ptr<Binary>> Result = createBinary(Buffer);
ASSERT_THAT_EXPECTED(Result, Succeeded());
ASSERT_TRUE(IsValidFormat(**Result));
ASSERT_TRUE((*Result)->isObject());
// Check that copied file has the new section.
bool HasNewSection = false;
for (const object::SectionRef &Sect :
static_cast<ObjectFile *>((*Result).get())->sections()) {
Expected<StringRef> SectNameOrErr = Sect.getName();
ASSERT_THAT_EXPECTED(SectNameOrErr, Succeeded());
if (*SectNameOrErr == NewSectionName) {
HasNewSection = true;
Expected<StringRef> SectionData = Sect.getContents();
ASSERT_THAT_EXPECTED(SectionData, Succeeded());
EXPECT_TRUE(Sect.getSize() == NewSectionData.size());
EXPECT_TRUE(memcmp(SectionData->data(), NewSectionData.data(),
NewSectionData.size()) == 0);
break;
}
}
EXPECT_TRUE(HasNewSection);
}
TEST(AddSection, COFF) {
SCOPED_TRACE("addSectionToFileCOFF");
addOrUpdateSectionToFileImpl(
R"(
--- !COFF
header:
Machine: IMAGE_FILE_MACHINE_AMD64
Characteristics: [ ]
sections:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_REL
Sections:
- Name: .text
Characteristics: [ ]
Alignment: 4
SectionData: E800000000C3C3C3
symbols:
...
)",
[](const Binary &File) { return File.isCOFF(); }, ".foo", "1234",
AddSection);
}
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Content: "12345678"
)";
TEST(AddSection, ELF) {
SCOPED_TRACE("addSectionToFileELF");
addOrUpdateSectionToFileImpl(
R"(
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_REL)",
[](const Binary &File) { return File.isELF(); }, ".foo", "1234",
AddSection);
}
TEST(AddSection, MachO) {
SCOPED_TRACE("addSectionToFileMachO");
addOrUpdateSectionToFileImpl(
R"(
const char *SimpleFileMachOYAML = R"(
--- !mach-o
FileHeader:
magic: 0xFEEDFACF
@ -259,109 +86,313 @@ LoadCommands:
reserved2: 0x00000000
reserved3: 0x00000000
...
)",
[](const Binary &File) { return File.isMachO(); }, "__foo", "1234",
AddSection);
)";
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<std::unique_ptr<ObjectFile>> createObjectFileFromYamlDescription(
const char *YamlCreationString, SmallVector<char> &Storage,
function_ref<bool(const Binary &File)> IsValidFormat) {
auto ErrHandler = [&](const Twine &Msg) { FAIL() << "Error: " << Msg; };
std::unique_ptr<ObjectFile> 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 Obj;
}
// Call objcopy::executeObjcopyOnBinary for \p Config and \p In. \p DataVector
// is a holder for data. \returns Binary for copied data.
Expected<std::unique_ptr<Binary>>
callObjCopy(ConfigManager &Config, object::Binary &In,
SmallVector<char> &DataVector,
function_ref<bool(const Binary &File)> 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<std::unique_ptr<Binary>> 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<StringRef> 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<StringRef> CurSecNameOrErr = Sec.getName();
ASSERT_THAT_EXPECTED(CurSecNameOrErr, Succeeded());
if (*CurSecNameOrErr == SectionName) {
Expected<StringRef> 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<bool(const Binary &File)> IsValidFormat) {
SCOPED_TRACE("copySimpleInMemoryFileImpl");
// Create Object file from YAML description.
SmallVector<char> Storage;
Expected<std::unique_ptr<ObjectFile>> Obj =
createObjectFileFromYamlDescription(YamlCreationString, Storage,
IsValidFormat);
ASSERT_THAT_EXPECTED(Obj, Succeeded());
ConfigManager Config;
Config.Common.OutputFilename = "a.out";
// Call executeObjcopyOnBinary()
SmallVector<char> DataVector;
Expected<std::unique_ptr<Binary>> 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<bool(const Binary &File)> IsValidFormat,
StringRef NewSectionName, StringRef NewSectionData, Action SectionAction) {
SCOPED_TRACE("addOrUpdateSectionToFileImpl");
// Create Object file from YAML description.
SmallVector<char> Storage;
Expected<std::unique_ptr<ObjectFile>> Obj =
createObjectFileFromYamlDescription(YamlCreationString, Storage,
IsValidFormat);
ASSERT_THAT_EXPECTED(Obj, Succeeded());
std::unique_ptr<MemoryBuffer> 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<char> DataVector;
Expected<std::unique_ptr<Binary>> Result =
callObjCopy(Config, *Obj.get(), DataVector, IsValidFormat);
ASSERT_THAT_EXPECTED(Result, Succeeded());
// Check that copied file has the new section.
checkSectionData(*static_cast<ObjectFile *>((*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(
R"(
--- !WASM
FileHeader:
Version: 0x00000001
...
)",
[](const Binary &File) { return File.isWasm(); }, ".foo", "1234",
AddSection);
SimpleFileWasmYAML, [](const Binary &File) { return File.isWasm(); },
".foo", "1234", AddSection);
}
TEST(UpdateSection, COFF) {
SCOPED_TRACE("updateSectionToFileCOFF");
addOrUpdateSectionToFileImpl(
R"(
--- !COFF
header:
Machine: IMAGE_FILE_MACHINE_AMD64
Characteristics: [ ]
sections:
- Name: .foo
Characteristics: [ ]
Alignment: 4
SectionData: E800000000C3C3C3
symbols:
...
)",
[](const Binary &File) { return File.isCOFF(); }, ".foo", "1234",
UpdateSection);
SimpleFileCOFFYAML, [](const Binary &File) { return File.isCOFF(); },
".text", "1234", UpdateSection);
}
TEST(UpdateSection, ELF) {
SCOPED_TRACE("updateSectionToFileELF");
addOrUpdateSectionToFileImpl(
R"(
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_REL
Sections:
- Name: .foo
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Content: "12345678"
)",
[](const Binary &File) { return File.isELF(); }, ".foo", "1234",
UpdateSection);
SimpleFileELFYAML, [](const Binary &File) { return File.isELF(); },
".text", "1234", UpdateSection);
}
TEST(UpdateSection, MachO) {
SCOPED_TRACE("updateSectionToFileMachO");
addOrUpdateSectionToFileImpl(
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: __foo
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 Binary &File) { return File.isMachO(); }, "__foo", "1234",
UpdateSection);
SimpleFileMachOYAML, [](const Binary &File) { return File.isMachO(); },
"__text", "1234", UpdateSection);
}
void removeSectionByPatternImpl(
const char *YamlCreationString,
function_ref<bool(const Binary &File)> IsValidFormat,
StringRef SectionWildcard, StringRef SectionName) {
SCOPED_TRACE("removeSectionByPatternImpl");
// Create Object file from YAML description.
SmallVector<char> Storage;
Expected<std::unique_ptr<ObjectFile>> Obj =
createObjectFileFromYamlDescription(YamlCreationString, Storage,
IsValidFormat);
ASSERT_THAT_EXPECTED(Obj, Succeeded());
// Check that section is present.
EXPECT_TRUE(hasSection(**Obj, SectionName));
Expected<NameOrPattern> 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<char> DataVector;
Expected<std::unique_ptr<Binary>> Result =
callObjCopy(Config, *Obj.get(), DataVector, IsValidFormat);
ASSERT_THAT_EXPECTED(Result, Succeeded());
// Check that section was removed.
EXPECT_FALSE(
hasSection(*static_cast<ObjectFile *>((*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");
}