Index: lld/trunk/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp =================================================================== --- lld/trunk/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp +++ lld/trunk/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp @@ -65,6 +65,7 @@ enum Kind { kindHeader, kindSection, + kindStringTable, kindAtomChunk }; @@ -151,6 +152,10 @@ _peHeader.AddressOfEntryPoint = address; } + void setPointerToSymbolTable(uint32_t rva) { + _coffHeader.PointerToSymbolTable = rva; + } + private: llvm::object::coff_file_header _coffHeader; PEHeader _peHeader; @@ -171,6 +176,37 @@ std::vector _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(_stringTable.data()) = _stringTable.size(); + std::memcpy(buffer, _stringTable.data(), _stringTable.size()); + } + +private: + std::vector _stringTable; +}; + class SectionChunk : public Chunk { public: uint64_t onDiskSize() const override { @@ -191,15 +227,20 @@ uint64_t getVirtualAddress() { return _virtualAddress; } virtual void setVirtualAddress(uint32_t rva) { _virtualAddress = rva; } + uint32_t getStringTableOffset() const { return _stringTableOffset; } + void setStringTableOffset(uint32_t offset) { _stringTableOffset = offset; } + protected: SectionChunk(Kind kind, StringRef sectionName, uint32_t characteristics) : Chunk(kind), _sectionName(sectionName), - _characteristics(characteristics), _virtualAddress(0) {} + _characteristics(characteristics), _virtualAddress(0), + _stringTableOffset(0) {} private: StringRef _sectionName; const uint32_t _characteristics; uint64_t _virtualAddress; + uint32_t _stringTableOffset; }; /// An AtomChunk represents a section containing atoms. @@ -750,15 +791,15 @@ SectionHeaderTableChunk::createSectionHeader(SectionChunk *chunk) { llvm::object::coff_section header; - // Section name must be equal to or less than 8 characters in the - // executable. Longer names will be truncated. + // We have extended the COFF specification by allowing section names to be + // 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(); - - // Name field must be NUL-padded. If the name is exactly 8 byte long, - // there's no terminating NUL. - std::memset(header.Name, 0, sizeof(header.Name)); - std::strncpy(header.Name, sectionName.data(), - std::min(sizeof(header.Name), sectionName.size())); + std::memset(header.Name, 0, llvm::COFF::NameSize); + if (uint32_t stringTableOffset = chunk->getStringTableOffset()) + sprintf(header.Name, "/%u", stringTableOffset); + else + std::strncpy(header.Name, sectionName.data(), sectionName.size()); uint32_t characteristics = chunk->getCharacteristics(); header.VirtualSize = chunk->size(); @@ -873,7 +914,8 @@ void addChunk(Chunk *chunk); void addSectionChunk(std::unique_ptr chunk, - SectionHeaderTableChunk *table); + SectionHeaderTableChunk *table, + StringTableChunk *stringTable); void setImageSizeOnDisk(); uint64_t calcSectionSize(llvm::COFF::SectionCharacteristics sectionType) const; @@ -974,10 +1016,12 @@ auto *peHeader = new PEHeaderChunk(_ctx); auto *dataDirectory = new DataDirectoryChunk(); auto *sectionTable = new SectionHeaderTableChunk(); + auto *stringTable = new StringTableChunk(); addChunk(dosStub); addChunk(peHeader); addChunk(dataDirectory); addChunk(sectionTable); + addChunk(stringTable); // Create sections and add the atoms to them. for (auto i : atoms) { @@ -986,7 +1030,7 @@ std::unique_ptr section( new AtomChunk(_ctx, sectionName, contents)); if (section->size() > 0) - addSectionChunk(std::move(section), sectionTable); + addSectionChunk(std::move(section), sectionTable, stringTable); } // Build atom to its RVA map. @@ -1001,13 +1045,18 @@ std::unique_ptr baseReloc(new BaseRelocChunk(_chunks, _ctx)); if (baseReloc->size()) { SectionChunk &ref = *baseReloc; - addSectionChunk(std::move(baseReloc), sectionTable); + addSectionChunk(std::move(baseReloc), sectionTable, stringTable); dataDirectory->setField(DataDirectoryIndex::BASE_RELOCATION_TABLE, ref.getVirtualAddress(), ref.size()); } } 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 : _chunks) { SectionChunk *section = dyn_cast(chunk.get()); @@ -1180,12 +1229,19 @@ } void PECOFFWriter::addSectionChunk(std::unique_ptr chunk, - SectionHeaderTableChunk *table) { + SectionHeaderTableChunk *table, + StringTableChunk *stringTable) { SectionChunk &ref = *chunk; _chunks.push_back(std::move(chunk)); table->addSection(&ref); _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 // memory. They are different from positions on disk because sections need // to be sector-aligned on disk but page-aligned in memory. Index: lld/trunk/test/pecoff/long-section-name.test =================================================================== --- lld/trunk/test/pecoff/long-section-name.test +++ lld/trunk/test/pecoff/long-section-name.test @@ -4,4 +4,4 @@ # RUN: /merge:.text=.longsectionname -- %t.obj # 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)