PECOFF: Use the string table for long section names in EXEs/DLLs

Normally, PE files have section names of eight characters or less.
However, this is problematic for DWARF because DWARF section names are
things like .debug_aranges.

Instead of truncating the section name, redirect the section name into
the string table.

Differential Revision: http://reviews.llvm.org/D6104

llvm-svn: 221212
This commit is contained in:
David Majnemer 2014-11-04 00:53:57 +00:00
parent 9907d0a3c2
commit 4eb0a3fd25
2 changed files with 70 additions and 14 deletions

View File

@ -65,6 +65,7 @@ public:
enum Kind { enum Kind {
kindHeader, kindHeader,
kindSection, kindSection,
kindStringTable,
kindAtomChunk kindAtomChunk
}; };
@ -151,6 +152,10 @@ public:
_peHeader.AddressOfEntryPoint = address; _peHeader.AddressOfEntryPoint = address;
} }
void setPointerToSymbolTable(uint32_t rva) {
_coffHeader.PointerToSymbolTable = rva;
}
private: private:
llvm::object::coff_file_header _coffHeader; llvm::object::coff_file_header _coffHeader;
PEHeader _peHeader; PEHeader _peHeader;
@ -171,6 +176,37 @@ private:
std::vector<SectionChunk *> _sections; std::vector<SectionChunk *> _sections;
}; };
class StringTableChunk : public Chunk {
public:
StringTableChunk() : Chunk(kindStringTable) {}
static bool classof(const Chunk *c) {
return c->getKind() == kindStringTable;
}
uint32_t addSectionName(StringRef sectionName) {
if (_stringTable.empty())
_stringTable.insert(_stringTable.begin(), 4, 0);
uint32_t offset = _stringTable.size();
_stringTable.insert(_stringTable.end(), sectionName.begin(),
sectionName.end());
_stringTable.push_back('\0');
return offset;
}
uint64_t size() const override { return _stringTable.size(); }
void write(uint8_t *buffer) override {
if (_stringTable.empty())
return;
*reinterpret_cast<ulittle32_t *>(_stringTable.data()) = _stringTable.size();
std::memcpy(buffer, _stringTable.data(), _stringTable.size());
}
private:
std::vector<char> _stringTable;
};
class SectionChunk : public Chunk { class SectionChunk : public Chunk {
public: public:
uint64_t onDiskSize() const override { uint64_t onDiskSize() const override {
@ -191,15 +227,20 @@ public:
uint64_t getVirtualAddress() { return _virtualAddress; } uint64_t getVirtualAddress() { return _virtualAddress; }
virtual void setVirtualAddress(uint32_t rva) { _virtualAddress = rva; } virtual void setVirtualAddress(uint32_t rva) { _virtualAddress = rva; }
uint32_t getStringTableOffset() const { return _stringTableOffset; }
void setStringTableOffset(uint32_t offset) { _stringTableOffset = offset; }
protected: protected:
SectionChunk(Kind kind, StringRef sectionName, uint32_t characteristics) SectionChunk(Kind kind, StringRef sectionName, uint32_t characteristics)
: Chunk(kind), _sectionName(sectionName), : Chunk(kind), _sectionName(sectionName),
_characteristics(characteristics), _virtualAddress(0) {} _characteristics(characteristics), _virtualAddress(0),
_stringTableOffset(0) {}
private: private:
StringRef _sectionName; StringRef _sectionName;
const uint32_t _characteristics; const uint32_t _characteristics;
uint64_t _virtualAddress; uint64_t _virtualAddress;
uint32_t _stringTableOffset;
}; };
/// An AtomChunk represents a section containing atoms. /// An AtomChunk represents a section containing atoms.
@ -750,15 +791,15 @@ llvm::object::coff_section
SectionHeaderTableChunk::createSectionHeader(SectionChunk *chunk) { SectionHeaderTableChunk::createSectionHeader(SectionChunk *chunk) {
llvm::object::coff_section header; llvm::object::coff_section header;
// Section name must be equal to or less than 8 characters in the // We have extended the COFF specification by allowing section names to be
// executable. Longer names will be truncated. // greater than eight characters. We achieve this by adding the section names
// to the string table. Binutils' linker, ld, performs the same trick.
StringRef sectionName = chunk->getSectionName(); StringRef sectionName = chunk->getSectionName();
std::memset(header.Name, 0, llvm::COFF::NameSize);
// Name field must be NUL-padded. If the name is exactly 8 byte long, if (uint32_t stringTableOffset = chunk->getStringTableOffset())
// there's no terminating NUL. sprintf(header.Name, "/%u", stringTableOffset);
std::memset(header.Name, 0, sizeof(header.Name)); else
std::strncpy(header.Name, sectionName.data(), std::strncpy(header.Name, sectionName.data(), sectionName.size());
std::min(sizeof(header.Name), sectionName.size()));
uint32_t characteristics = chunk->getCharacteristics(); uint32_t characteristics = chunk->getCharacteristics();
header.VirtualSize = chunk->size(); header.VirtualSize = chunk->size();
@ -873,7 +914,8 @@ private:
void addChunk(Chunk *chunk); void addChunk(Chunk *chunk);
void addSectionChunk(std::unique_ptr<SectionChunk> chunk, void addSectionChunk(std::unique_ptr<SectionChunk> chunk,
SectionHeaderTableChunk *table); SectionHeaderTableChunk *table,
StringTableChunk *stringTable);
void setImageSizeOnDisk(); void setImageSizeOnDisk();
uint64_t uint64_t
calcSectionSize(llvm::COFF::SectionCharacteristics sectionType) const; calcSectionSize(llvm::COFF::SectionCharacteristics sectionType) const;
@ -974,10 +1016,12 @@ void PECOFFWriter::build(const File &linkedFile) {
auto *peHeader = new PEHeaderChunk<PEHeader>(_ctx); auto *peHeader = new PEHeaderChunk<PEHeader>(_ctx);
auto *dataDirectory = new DataDirectoryChunk(); auto *dataDirectory = new DataDirectoryChunk();
auto *sectionTable = new SectionHeaderTableChunk(); auto *sectionTable = new SectionHeaderTableChunk();
auto *stringTable = new StringTableChunk();
addChunk(dosStub); addChunk(dosStub);
addChunk(peHeader); addChunk(peHeader);
addChunk(dataDirectory); addChunk(dataDirectory);
addChunk(sectionTable); addChunk(sectionTable);
addChunk(stringTable);
// Create sections and add the atoms to them. // Create sections and add the atoms to them.
for (auto i : atoms) { for (auto i : atoms) {
@ -986,7 +1030,7 @@ void PECOFFWriter::build(const File &linkedFile) {
std::unique_ptr<SectionChunk> section( std::unique_ptr<SectionChunk> section(
new AtomChunk(_ctx, sectionName, contents)); new AtomChunk(_ctx, sectionName, contents));
if (section->size() > 0) if (section->size() > 0)
addSectionChunk(std::move(section), sectionTable); addSectionChunk(std::move(section), sectionTable, stringTable);
} }
// Build atom to its RVA map. // Build atom to its RVA map.
@ -1001,13 +1045,18 @@ void PECOFFWriter::build(const File &linkedFile) {
std::unique_ptr<SectionChunk> baseReloc(new BaseRelocChunk(_chunks, _ctx)); std::unique_ptr<SectionChunk> baseReloc(new BaseRelocChunk(_chunks, _ctx));
if (baseReloc->size()) { if (baseReloc->size()) {
SectionChunk &ref = *baseReloc; SectionChunk &ref = *baseReloc;
addSectionChunk(std::move(baseReloc), sectionTable); addSectionChunk(std::move(baseReloc), sectionTable, stringTable);
dataDirectory->setField(DataDirectoryIndex::BASE_RELOCATION_TABLE, dataDirectory->setField(DataDirectoryIndex::BASE_RELOCATION_TABLE,
ref.getVirtualAddress(), ref.size()); ref.getVirtualAddress(), ref.size());
} }
} }
setImageSizeOnDisk(); setImageSizeOnDisk();
// N.B. Currently released versions of dumpbin do not appropriately handle
// symbol tables which NumberOfSymbols set to zero but a non-zero
// PointerToSymbolTable.
if (stringTable->size())
peHeader->setPointerToSymbolTable(stringTable->fileOffset());
for (std::unique_ptr<Chunk> &chunk : _chunks) { for (std::unique_ptr<Chunk> &chunk : _chunks) {
SectionChunk *section = dyn_cast<SectionChunk>(chunk.get()); SectionChunk *section = dyn_cast<SectionChunk>(chunk.get());
@ -1180,12 +1229,19 @@ void PECOFFWriter::addChunk(Chunk *chunk) {
} }
void PECOFFWriter::addSectionChunk(std::unique_ptr<SectionChunk> chunk, void PECOFFWriter::addSectionChunk(std::unique_ptr<SectionChunk> chunk,
SectionHeaderTableChunk *table) { SectionHeaderTableChunk *table,
StringTableChunk *stringTable) {
SectionChunk &ref = *chunk; SectionChunk &ref = *chunk;
_chunks.push_back(std::move(chunk)); _chunks.push_back(std::move(chunk));
table->addSection(&ref); table->addSection(&ref);
_numSections++; _numSections++;
StringRef sectionName = ref.getSectionName();
if (sectionName.size() > llvm::COFF::NameSize) {
uint32_t stringTableOffset = stringTable->addSectionName(sectionName);
ref.setStringTableOffset(stringTableOffset);
}
// Compute and set the starting address of sections when loaded in // Compute and set the starting address of sections when loaded in
// memory. They are different from positions on disk because sections need // memory. They are different from positions on disk because sections need
// to be sector-aligned on disk but page-aligned in memory. // to be sector-aligned on disk but page-aligned in memory.

View File

@ -4,4 +4,4 @@
# RUN: /merge:.text=.longsectionname -- %t.obj # RUN: /merge:.text=.longsectionname -- %t.obj
# RUN: llvm-readobj -sections %t.exe | FileCheck %s # RUN: llvm-readobj -sections %t.exe | FileCheck %s
CHECK: Name: .longsec (2E 6C 6F 6E 67 73 65 63) CHECK: Name: .longsectionname (2F 34 00 00 00 00 00 00)