[PECOFF] Improve /merge option handling.

/MERGE option is a bit complicated for many reasons. Firstly, it takes both
positive and negative arguments. That means we have to have one of three
distinctive values (set, clear or unchange) for each permission bit. In this
patch we represent the three values using two bitmasks.

Secondly, the permissions specified by the parameter is bitwise or-ed with the
default permissions of a section. There is an exception for that rule; if one
of READ, WRITE or EXECUTE bit is specified, unspecified bits need to be
cleared. (So if you specify only WRITE for example, the resulting section will
not have WRITE nor EXECUTE bits.)

Lastly, multiple /merge options are allowed.

llvm-svn: 195882
This commit is contained in:
Rui Ueyama 2013-11-27 21:34:16 +00:00
parent 175e7a8c97
commit 615b200cc2
4 changed files with 64 additions and 60 deletions

View File

@ -19,7 +19,6 @@
#include "lld/ReaderWriter/Writer.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/COFF.h"
#include "llvm/Support/ErrorHandling.h"
@ -209,25 +208,9 @@ public:
virtual ErrorOr<Reference::Kind> relocKindFromString(StringRef str) const;
virtual ErrorOr<std::string> stringFromRelocKind(Reference::Kind kind) const;
void setSectionAttributes(StringRef sectionName, uint32_t flags) {
_sectionAttributes[sectionName] = flags;
}
llvm::Optional<uint32_t> getSectionAttributes(StringRef sectionName) const {
auto it = _sectionAttributes.find(sectionName);
if (it == _sectionAttributes.end())
return llvm::None;
return it->second;
}
void setSectionAttributeMask(StringRef sectionName, uint32_t flags) {
_sectionAttributeMask[sectionName] = flags;
}
uint32_t getSectionAttributeMask(StringRef sectionName) const {
auto it = _sectionAttributeMask.find(sectionName);
return it == _sectionAttributeMask.end() ? 0 : it->second;
}
void setSectionSetMask(StringRef sectionName, uint32_t flags);
void setSectionClearMask(StringRef sectionName, uint32_t flags);
uint32_t getSectionAttributes(StringRef sectionName, uint32_t flags) const;
void setDosStub(ArrayRef<uint8_t> data) { _dosStub = data; }
ArrayRef<uint8_t> getDosStub() const { return _dosStub; }
@ -304,14 +287,9 @@ private:
// merged to .text in the resulting executable.
std::map<std::string, std::string> _renamedSections;
// Section attributes specified by /section option. The uint32_t value will be
// copied to the Characteristics field of the section header.
std::map<std::string, uint32_t> _sectionAttributes;
// Section attributes specified by /section option in conjunction with the
// negative flag "!". The uint32_t value is a mask of section attributes that
// should be disabled.
std::map<std::string, uint32_t> _sectionAttributeMask;
// Section attributes specified by /section option.
std::map<std::string, uint32_t> _sectionSetMask;
std::map<std::string, uint32_t> _sectionClearMask;
// List of files that will be removed on destruction.
std::vector<std::unique_ptr<llvm::FileRemover> > _tempFiles;

View File

@ -723,9 +723,9 @@ WinLinkDriver::parse(int argc, const char *argv[], PECOFFLinkingContext &ctx,
return false;
}
if (flags.hasValue())
ctx.setSectionAttributes(section, *flags);
ctx.setSectionSetMask(section, *flags);
if (mask.hasValue())
ctx.setSectionAttributeMask(section, *mask);
ctx.setSectionClearMask(section, *mask);
break;
}

View File

@ -204,6 +204,34 @@ PECOFFLinkingContext::stringFromRelocKind(Reference::Kind kind) const {
return make_error_code(YamlReaderError::illegal_value);
}
void PECOFFLinkingContext::setSectionSetMask(StringRef sectionName,
uint32_t newFlags) {
_sectionSetMask[sectionName] |= newFlags;
_sectionClearMask[sectionName] &= ~newFlags;
const uint32_t rwx = (llvm::COFF::IMAGE_SCN_MEM_READ |
llvm::COFF::IMAGE_SCN_MEM_WRITE |
llvm::COFF::IMAGE_SCN_MEM_EXECUTE);
if (newFlags & rwx)
_sectionClearMask[sectionName] |= ~_sectionSetMask[sectionName] & rwx;
assert((_sectionSetMask[sectionName] & _sectionClearMask[sectionName]) == 0);
}
void PECOFFLinkingContext::setSectionClearMask(StringRef sectionName,
uint32_t newFlags) {
_sectionClearMask[sectionName] |= newFlags;
_sectionSetMask[sectionName] &= ~newFlags;
assert((_sectionSetMask[sectionName] & _sectionClearMask[sectionName]) == 0);
}
uint32_t PECOFFLinkingContext::getSectionAttributes(StringRef sectionName,
uint32_t flags) const {
auto si = _sectionSetMask.find(sectionName);
uint32_t setMask = (si == _sectionSetMask.end()) ? 0 : si->second;
auto ci = _sectionClearMask.find(sectionName);
uint32_t clearMask = (ci == _sectionClearMask.end()) ? 0 : ci->second;
return (flags | setMask) & ~clearMask;
}
void PECOFFLinkingContext::addPasses(PassManager &pm) {
pm.add(std::unique_ptr<Pass>(new pecoff::SetSubsystemPass(*this)));
pm.add(std::unique_ptr<Pass>(new pecoff::GroupedSectionsPass()));

View File

@ -275,43 +275,41 @@ const uint32_t execute = llvm::COFF::IMAGE_SCN_MEM_EXECUTE;
const uint32_t read = llvm::COFF::IMAGE_SCN_MEM_READ;
const uint32_t write = llvm::COFF::IMAGE_SCN_MEM_WRITE;
TEST_F(WinLinkParserTest, Section) {
EXPECT_TRUE(parse("link.exe", "/section:.teXT,dekpRSW", "a.obj", nullptr));
uint32_t expect =
discardable | not_cached | not_paged | shared | execute | read | write;
llvm::Optional<uint32_t> val = _context.getSectionAttributes(".teXT");
EXPECT_TRUE(val.hasValue());
EXPECT_EQ(expect, *val);
EXPECT_EQ(0U, _context.getSectionAttributeMask(".teXT"));
}
TEST_F(WinLinkParserTest, SectionNegative) {
EXPECT_TRUE(parse("link.exe", "/section:.teXT,!dekpRSW", "a.obj", nullptr));
llvm::Optional<uint32_t> val = _context.getSectionAttributes(".teXT");
EXPECT_FALSE(val.hasValue());
uint32_t expect =
discardable | not_cached | not_paged | shared | execute | read | write;
EXPECT_EQ(expect, _context.getSectionAttributeMask(".teXT"));
}
#define TEST_SECTION(testname, arg, expect) \
TEST_F(WinLinkParserTest, testname) { \
EXPECT_TRUE(parse("link.exe", "/section:.text," arg, "a.obj", nullptr)); \
llvm::Optional<uint32_t> val = _context.getSectionAttributes(".text"); \
EXPECT_TRUE(val.hasValue()); \
EXPECT_EQ(expect, *val); \
#define TEST_SECTION(testname, arg, expect) \
TEST_F(WinLinkParserTest, testname) { \
EXPECT_TRUE(parse("link.exe", "/section:.text," arg, "a.obj", nullptr)); \
EXPECT_EQ(expect, _context.getSectionAttributes(".text", execute | read)); \
}
TEST_SECTION(SectionD, "d", discardable);
TEST_SECTION(SectionD, "d", execute | read | discardable);
TEST_SECTION(SectionE, "e", execute);
TEST_SECTION(SectionK, "k", not_cached);
TEST_SECTION(SectionP, "p", not_paged);
TEST_SECTION(SectionK, "k", execute | read | not_cached);
TEST_SECTION(SectionP, "p", execute | read | not_paged);
TEST_SECTION(SectionR, "r", read);
TEST_SECTION(SectionS, "s", shared);
TEST_SECTION(SectionS, "s", execute | read | shared);
TEST_SECTION(SectionW, "w", write);
#undef TEST_SECTION
TEST_F(WinLinkParserTest, Section) {
EXPECT_TRUE(parse("link.exe", "/section:.text,dekprsw",
"/section:.text,!dekprsw", "a.obj", nullptr));
EXPECT_EQ(0U, _context.getSectionAttributes(".text", execute | read));
}
TEST_F(WinLinkParserTest, SectionNegate) {
EXPECT_TRUE(parse("link.exe", "/section:.text,!e", "a.obj", nullptr));
EXPECT_EQ(read, _context.getSectionAttributes(".text", execute | read));
}
TEST_F(WinLinkParserTest, SectionMultiple) {
EXPECT_TRUE(parse("link.exe", "/section:.foo,e", "/section:.foo,rw",
"/section:.foo,!d", "a.obj", nullptr));
uint32_t flags = execute | read | not_paged | discardable;
uint32_t expected = execute | read | write | not_paged;
EXPECT_EQ(expected, _context.getSectionAttributes(".foo", flags));
}
} // end anonymous namespace
//