[lld] r195159 - Do not inline large member functions.
Rui Ueyama
ruiu at google.com
Tue Nov 19 14:12:27 PST 2013
Author: ruiu
Date: Tue Nov 19 16:12:26 2013
New Revision: 195159
URL: http://llvm.org/viewvc/llvm-project?rev=195159&view=rev
Log:
Do not inline large member functions.
This should improve code readability as the class definitions are now
more readable than before.
Modified:
lld/trunk/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp
Modified: lld/trunk/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp?rev=195159&r1=195158&r2=195159&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp (original)
+++ lld/trunk/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp Tue Nov 19 16:12:26 2013
@@ -129,105 +129,9 @@ private:
/// A PEHeaderChunk represents PE header including COFF header.
class PEHeaderChunk : public HeaderChunk {
public:
- explicit PEHeaderChunk(const PECOFFLinkingContext &context) : HeaderChunk() {
- // Set the size of the chunk and initialize the header with null bytes.
- _size = sizeof(llvm::COFF::PEMagic) + sizeof(_coffHeader)
- + sizeof(_peHeader);
- std::memset(&_coffHeader, 0, sizeof(_coffHeader));
- std::memset(&_peHeader, 0, sizeof(_peHeader));
-
- _coffHeader.Machine = context.getMachineType();
- _coffHeader.TimeDateStamp = time(NULL);
-
- // The size of PE header including optional data directory is always 224.
- _coffHeader.SizeOfOptionalHeader = 224;
-
- // Attributes of the executable.
- uint16_t characteristics = llvm::COFF::IMAGE_FILE_32BIT_MACHINE |
- llvm::COFF::IMAGE_FILE_EXECUTABLE_IMAGE;
- if (context.getLargeAddressAware())
- characteristics |= llvm::COFF::IMAGE_FILE_LARGE_ADDRESS_AWARE;
- if (context.getSwapRunFromCD())
- characteristics |= llvm::COFF::IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP;
- if (context.getSwapRunFromNet())
- characteristics |= llvm::COFF::IMAGE_FILE_NET_RUN_FROM_SWAP;
- if (!context.getBaseRelocationEnabled())
- characteristics |= llvm::COFF::IMAGE_FILE_RELOCS_STRIPPED;
-
- _coffHeader.Characteristics = characteristics;
-
- // 0x10b indicates a normal PE32 executable. For PE32+ it should be 0x20b.
- _peHeader.Magic = 0x10b;
-
- // The address of entry point relative to ImageBase. Windows executable
- // usually starts at address 0x401000.
- _peHeader.AddressOfEntryPoint = 0x1000;
-
- // The address of the executable when loaded into memory. The default for
- // DLLs is 0x10000000. The default for executables is 0x400000.
- _peHeader.ImageBase = context.getBaseAddress();
-
- // Sections should be page-aligned when loaded into memory, which is 4KB on
- // x86.
- _peHeader.SectionAlignment = context.getSectionDefaultAlignment();
-
- // Sections in an executable file on disk should be sector-aligned (512 byte).
- _peHeader.FileAlignment = SECTOR_SIZE;
-
- // The version number of the resultant executable/DLL. The number is purely
- // informative, and neither the linker nor the loader won't use it. User can
- // set the value using /version command line option. Default is 0.0.
- PECOFFLinkingContext::Version imageVersion = context.getImageVersion();
- _peHeader.MajorImageVersion = imageVersion.majorVersion;
- _peHeader.MinorImageVersion = imageVersion.minorVersion;
-
- // The required Windows version number. This is the internal version and
- // shouldn't be confused with product name. Windows 7 is version 6.1 and
- // Windows 8 is 6.2, for example.
- PECOFFLinkingContext::Version minOSVersion = context.getMinOSVersion();
- _peHeader.MajorOperatingSystemVersion = minOSVersion.majorVersion;
- _peHeader.MinorOperatingSystemVersion = minOSVersion.minorVersion;
- _peHeader.MajorSubsystemVersion = minOSVersion.majorVersion;
- _peHeader.MinorSubsystemVersion = minOSVersion.minorVersion;
-
- _peHeader.Subsystem = context.getSubsystem();
-
- // Despite its name, DLL characteristics field has meaning both for
- // executables and DLLs. We are not very sure if the following bits must
- // be set, but regular binaries seem to have these bits, so we follow
- // them.
- uint16_t dllCharacteristics =
- llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_SEH;
- if (context.isTerminalServerAware())
- dllCharacteristics |=
- llvm::COFF::IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE;
- if (context.isNxCompat())
- dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NX_COMPAT;
- if (context.getDynamicBaseEnabled())
- dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE;
- if (!context.getAllowBind())
- dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_BIND;
- if (!context.getAllowIsolation())
- dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION;
- _peHeader.DLLCharacteristics = dllCharacteristics;
-
- _peHeader.SizeOfStackReserve = context.getStackReserve();
- _peHeader.SizeOfStackCommit = context.getStackCommit();
- _peHeader.SizeOfHeapReserve = context.getHeapReserve();
- _peHeader.SizeOfHeapCommit = context.getHeapCommit();
-
- // The number of data directory entries. We always have 16 entries.
- _peHeader.NumberOfRvaAndSize = 16;
- }
-
- virtual void write(uint8_t *fileBuffer) {
- fileBuffer += fileOffset();
- std::memcpy(fileBuffer, llvm::COFF::PEMagic, sizeof(llvm::COFF::PEMagic));
- fileBuffer += sizeof(llvm::COFF::PEMagic);
- std::memcpy(fileBuffer, &_coffHeader, sizeof(_coffHeader));
- fileBuffer += sizeof(_coffHeader);
- std::memcpy(fileBuffer, &_peHeader, sizeof(_peHeader));
- }
+ explicit PEHeaderChunk(const PECOFFLinkingContext &context);
+
+ virtual void write(uint8_t *fileBuffer);
virtual void setSizeOfHeaders(uint64_t size) {
// Must be multiple of FileAlignment.
@@ -281,14 +185,7 @@ private:
/// An AtomChunk represents a section containing atoms.
class AtomChunk : public Chunk {
public:
- virtual void write(uint8_t *fileBuffer) {
- for (const auto *layout : _atomLayouts) {
- const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
- ArrayRef<uint8_t> rawContent = atom->rawContent();
- std::memcpy(fileBuffer + layout->_fileOffset, rawContent.data(),
- rawContent.size());
- }
- }
+ virtual void write(uint8_t *fileBuffer);
/// Add all atoms to the given map. This data will be used to do relocation.
void buildAtomToVirtualAddr(std::map<const Atom *, uint64_t> &atomRva) {
@@ -298,74 +195,9 @@ public:
void applyRelocations(uint8_t *fileBuffer,
std::map<const Atom *, uint64_t> &atomRva,
- uint64_t imageBaseAddress) {
- for (const auto *layout : _atomLayouts) {
- const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
- for (const Reference *ref : *atom) {
- auto relocSite = reinterpret_cast<ulittle32_t *>(
- fileBuffer + layout->_fileOffset + ref->offsetInAtom());
- uint64_t targetAddr = atomRva[ref->target()];
- // Also account for whatever offset is already stored at the relocation site.
- targetAddr += *relocSite;
-
- // Skip if this reference is not for relocation.
- if (ref->kind() < lld::Reference::kindTargetLow)
- continue;
-
- switch (ref->kind()) {
- case llvm::COFF::IMAGE_REL_I386_ABSOLUTE:
- // This relocation is no-op.
- break;
- case llvm::COFF::IMAGE_REL_I386_DIR32:
- // Set target's 32-bit VA.
- *relocSite = targetAddr + imageBaseAddress;
- break;
- case llvm::COFF::IMAGE_REL_I386_DIR32NB:
- // Set target's 32-bit RVA.
- *relocSite = targetAddr;
- break;
- case llvm::COFF::IMAGE_REL_I386_REL32: {
- // Set 32-bit relative address of the target. This relocation is
- // usually used for relative branch or call instruction.
- uint32_t disp = atomRva[atom] + ref->offsetInAtom() + 4;
- *relocSite = targetAddr - disp;
- break;
- }
- default:
- llvm_unreachable("Unsupported relocation kind");
- }
- }
- }
- }
-
- /// Print atom VAs. Used only for debugging.
- void printAtomAddresses(uint64_t baseAddr) {
- for (const auto *layout : _atomLayouts) {
- const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
- uint64_t addr = layout->_virtualAddr;
- llvm::dbgs() << llvm::format("0x%08llx: ", addr + baseAddr)
- << (atom->name().empty() ? "(anonymous)" : atom->name())
- << "\n";
- }
- }
-
- /// List all virtual addresses (and not relative virtual addresses) that need
- /// to be fixed up if image base is relocated. The only relocation type that
- /// needs to be fixed is DIR32 on i386. REL32 is not (and should not be)
- /// fixed up because it's PC-relative.
- void addBaseRelocations(std::vector<uint64_t> &relocSites) {
- // TODO: llvm-objdump doesn't support parsing the base relocation table, so
- // we can't really test this at the moment. As a temporary solution, we
- // should output debug messages with atom names and addresses so that we
- // can inspect relocations, and fix the tests (base-reloc.test, maybe
- // others) to use those messages.
- for (const auto *layout : _atomLayouts) {
- const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
- for (const Reference *ref : *atom)
- if (ref->kind() == llvm::COFF::IMAGE_REL_I386_DIR32)
- relocSites.push_back(layout->_virtualAddr + ref->offsetInAtom());
- }
- }
+ uint64_t imageBaseAddress);
+ void printAtomAddresses(uint64_t baseAddr);
+ void addBaseRelocations(std::vector<uint64_t> &relocSites);
// Set the file offset of the beginning of this section.
virtual void setFileOffset(uint64_t fileOffset) {
@@ -426,22 +258,7 @@ public:
_baseRelocSize = size;
}
- virtual void write(uint8_t *fileBuffer) {
- if (!_atomLayouts.empty()) {
- assert(_atomLayouts.size() == 1);
- const AtomLayout *layout = _atomLayouts[0];
- ArrayRef<uint8_t> content = static_cast<const DefinedAtom *>(layout->_atom)->rawContent();
- std::memcpy(fileBuffer + _fileOffset, content.data(), content.size());
- }
-
- // Write base relocation table entry.
- int baseRelocOffset = llvm::COFF::DataDirectoryIndex::BASE_RELOCATION_TABLE
- * sizeof(llvm::object::data_directory);
- auto *baseReloc = reinterpret_cast<llvm::object::data_directory *>(
- fileBuffer + _fileOffset + baseRelocOffset);
- baseReloc->RelativeVirtualAddress = _baseRelocAddr;
- baseReloc->Size = _baseRelocSize;
- }
+ virtual void write(uint8_t *fileBuffer);
private:
uint32_t _baseRelocAddr;
@@ -478,123 +295,32 @@ public:
virtual uint32_t getVirtualAddress() { return _sectionHeader.VirtualAddress; }
- virtual llvm::object::coff_section &getSectionHeader() {
- // Fix up section size before returning it. VirtualSize should be the size
- // of the actual content, and SizeOfRawData should be aligned to the section
- // alignment.
- _sectionHeader.VirtualSize = _size;
- _sectionHeader.SizeOfRawData = size();
- return _sectionHeader;
- }
-
- ulittle32_t getSectionCharacteristics() {
- return _sectionHeader.Characteristics;
- }
-
- void appendAtom(const DefinedAtom *atom) {
- // Atom may have to be at a proper alignment boundary. If so, move the
- // pointer to make a room after the last atom before adding new one.
- _size = llvm::RoundUpToAlignment(_size, 1 << atom->alignment().powerOf2);
-
- // Create an AtomLayout and move the current pointer.
- auto *layout = new (_alloc) AtomLayout(atom, _size, _size);
- _atomLayouts.push_back(layout);
- _size += atom->size();
- }
+ virtual llvm::object::coff_section &getSectionHeader();
+
+ ulittle32_t getSectionCharacteristics();
+
+ void appendAtom(const DefinedAtom *atom);
static bool classof(const Chunk *c) { return c->getKind() == kindSection; }
protected:
- SectionChunk(StringRef sectionName, uint32_t characteristics)
- : AtomChunk(kindSection),
- _sectionHeader(createSectionHeader(sectionName, characteristics)) {
- // The section should be aligned to disk sector.
- _align = SECTOR_SIZE;
- }
+ SectionChunk(StringRef sectionName, uint32_t characteristics);
void buildContents(const File &linkedFile,
- bool (*isEligible)(const DefinedAtom *)) {
- // Extract atoms from the linked file and append them to this section.
- for (const DefinedAtom *atom : linkedFile.defined()) {
- assert(atom->sectionChoice() == DefinedAtom::sectionBasedOnContent);
- if (isEligible(atom))
- appendAtom(atom);
- }
-
- // Now that we have a list of atoms that to be written in this section,
- // and we know the size of the section. Let's write them to the section
- // header. VirtualSize should be the size of the actual content, and
- // SizeOfRawData should be aligned to the section alignment.
- _sectionHeader.VirtualSize = _size;
- _sectionHeader.SizeOfRawData = size();
- }
+ bool (*isEligible)(const DefinedAtom *));
private:
llvm::object::coff_section
- createSectionHeader(StringRef sectionName, uint32_t characteristics) const {
- llvm::object::coff_section header;
-
- // Section name equal to or shorter than 8 byte fits in the section
- // header. Longer names should be stored to string table, which is not
- // implemented yet.
- if (sizeof(header.Name) < sectionName.size())
- llvm_unreachable("Cannot handle section name longer than 8 byte");
-
- // 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(), sizeof(header.Name));
-
- header.VirtualSize = 0;
- header.VirtualAddress = 0;
- header.SizeOfRawData = 0;
- header.PointerToRawData = 0;
- header.PointerToRelocations = 0;
- header.PointerToLinenumbers = 0;
- header.NumberOfRelocations = 0;
- header.NumberOfLinenumbers = 0;
- header.Characteristics = characteristics;
- return header;
- }
+ createSectionHeader(StringRef sectionName, uint32_t characteristics) const;
llvm::object::coff_section _sectionHeader;
mutable llvm::BumpPtrAllocator _alloc;
};
-void SectionHeaderTableChunk::addSection(SectionChunk *chunk) {
- _sections.push_back(chunk);
-}
-
-uint64_t SectionHeaderTableChunk::size() const {
- return _sections.size() * sizeof(llvm::object::coff_section);
-}
-
-void SectionHeaderTableChunk::write(uint8_t *fileBuffer) {
- uint64_t offset = 0;
- fileBuffer += fileOffset();
- for (const auto &chunk : _sections) {
- const llvm::object::coff_section &header = chunk->getSectionHeader();
- std::memcpy(fileBuffer + offset, &header, sizeof(header));
- offset += sizeof(header);
- }
-}
-
// \brief A TextSectionChunk represents a .text section.
class TextSectionChunk : public SectionChunk {
public:
- virtual void write(uint8_t *fileBuffer) {
- if (_atomLayouts.empty())
- return;
- // Fill the section with INT 3 (0xCC) rather than NUL, so that the
- // disassembler will not interpret a garbage between atoms as the beginning
- // of multi-byte machine code. This does not change the behavior of
- // resulting binary but help debugging.
- uint8_t *start = fileBuffer + _atomLayouts.front()->_fileOffset;
- uint8_t *end = fileBuffer + _atomLayouts.back()->_fileOffset;
- memset(start, 0xCC, end - start);
-
- SectionChunk::write(fileBuffer);
- }
+ virtual void write(uint8_t *fileBuffer);
TextSectionChunk(const File &linkedFile)
: SectionChunk(".text", characteristics) {
@@ -716,24 +442,7 @@ public:
BaseRelocChunk(const File &linkedFile)
: SectionChunk(".reloc", characteristics), _file(linkedFile) {}
- /// Creates .reloc section content from the other sections. The content of
- /// .reloc is basically a list of relocation sites. The relocation sites are
- /// divided into blocks. Each block represents the base relocation for a 4K
- /// page.
- ///
- /// By dividing 32 bit RVAs into blocks, COFF saves disk and memory space for
- /// the base relocation. A block consists of a 32 bit page RVA and 16 bit
- /// relocation entries which represent offsets in the page. That is a more
- /// compact representation than a simple vector of 32 bit RVAs.
- void setContents(ChunkVectorT &chunks) {
- std::vector<uint64_t> relocSites = listRelocSites(chunks);
- PageOffsetT blocks = groupByPage(relocSites);
- for (auto &i : blocks) {
- uint64_t pageAddr = i.first;
- const std::vector<uint16_t> &offsetsInPage = i.second;
- appendAtom(createBaseRelocBlock(_file, pageAddr, offsetsInPage));
- }
- }
+ void setContents(ChunkVectorT &chunks);
private:
// When loaded into memory, reloc section should be readable and writable.
@@ -744,222 +453,413 @@ private:
// Returns a list of RVAs that needs to be relocated if the binary is loaded
// at an address different from its preferred one.
- std::vector<uint64_t> listRelocSites(ChunkVectorT &chunks) {
- std::vector<uint64_t> ret;
- for (auto &cp : chunks)
- if (SectionChunk *chunk = dyn_cast<SectionChunk>(&*cp))
- chunk->addBaseRelocations(ret);
- return ret;
- }
+ std::vector<uint64_t> listRelocSites(ChunkVectorT &chunks);
// Divide the given RVAs into blocks.
- PageOffsetT groupByPage(std::vector<uint64_t> relocSites) {
- PageOffsetT blocks;
- uint64_t mask = static_cast<uint64_t>(PAGE_SIZE) - 1;
- for (uint64_t addr : relocSites)
- blocks[addr & ~mask].push_back(addr & mask);
- return blocks;
- }
+ PageOffsetT groupByPage(std::vector<uint64_t> relocSites);
// Create the content of a relocation block.
DefinedAtom *createBaseRelocBlock(const File &file, uint64_t pageAddr,
- const std::vector<uint16_t> &offsets) {
- // Relocation blocks should be padded with IMAGE_REL_I386_ABSOLUTE to be
- // aligned to a DWORD size boundary.
- uint32_t size = llvm::RoundUpToAlignment(sizeof(ulittle32_t) * 2
- + sizeof(ulittle16_t) * offsets.size(), sizeof(ulittle32_t));
- std::vector<uint8_t> contents(size);
- uint8_t *ptr = &contents[0];
-
- // The first four bytes is the page RVA.
- *reinterpret_cast<ulittle32_t *>(ptr) = pageAddr;
- ptr += sizeof(ulittle32_t);
-
- // The second four bytes is the size of the block, including the the page
- // RVA and this size field.
- *reinterpret_cast<ulittle32_t *>(ptr) = size;
- ptr += sizeof(ulittle32_t);
-
- // The rest of the block consists of offsets in the page.
- for (uint16_t offset : offsets) {
- assert(offset < PAGE_SIZE);
- uint16_t val = (llvm::COFF::IMAGE_REL_BASED_HIGHLOW << 12) | offset;
- *reinterpret_cast<ulittle16_t *>(ptr) = val;
- ptr += sizeof(ulittle16_t);
- }
- return new (_alloc) BaseRelocAtom(file, std::move(contents));
- }
+ const std::vector<uint16_t> &offsets);
mutable llvm::BumpPtrAllocator _alloc;
const File &_file;
};
-} // end anonymous namespace
+PEHeaderChunk::PEHeaderChunk(const PECOFFLinkingContext &context)
+ : HeaderChunk() {
+ // Set the size of the chunk and initialize the header with null bytes.
+ _size = sizeof(llvm::COFF::PEMagic) + sizeof(_coffHeader) + sizeof(_peHeader);
+ std::memset(&_coffHeader, 0, sizeof(_coffHeader));
+ std::memset(&_peHeader, 0, sizeof(_peHeader));
+
+ _coffHeader.Machine = context.getMachineType();
+ _coffHeader.TimeDateStamp = time(NULL);
+
+ // The size of PE header including optional data directory is always 224.
+ _coffHeader.SizeOfOptionalHeader = 224;
+
+ // Attributes of the executable.
+ uint16_t characteristics = llvm::COFF::IMAGE_FILE_32BIT_MACHINE |
+ llvm::COFF::IMAGE_FILE_EXECUTABLE_IMAGE;
+ if (context.getLargeAddressAware())
+ characteristics |= llvm::COFF::IMAGE_FILE_LARGE_ADDRESS_AWARE;
+ if (context.getSwapRunFromCD())
+ characteristics |= llvm::COFF::IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP;
+ if (context.getSwapRunFromNet())
+ characteristics |= llvm::COFF::IMAGE_FILE_NET_RUN_FROM_SWAP;
+ if (!context.getBaseRelocationEnabled())
+ characteristics |= llvm::COFF::IMAGE_FILE_RELOCS_STRIPPED;
+
+ _coffHeader.Characteristics = characteristics;
+
+ // 0x10b indicates a normal PE32 executable. For PE32+ it should be 0x20b.
+ _peHeader.Magic = 0x10b;
+
+ // The address of entry point relative to ImageBase. Windows executable
+ // usually starts at address 0x401000.
+ _peHeader.AddressOfEntryPoint = 0x1000;
+
+ // The address of the executable when loaded into memory. The default for
+ // DLLs is 0x10000000. The default for executables is 0x400000.
+ _peHeader.ImageBase = context.getBaseAddress();
+
+ // Sections should be page-aligned when loaded into memory, which is 4KB on
+ // x86.
+ _peHeader.SectionAlignment = context.getSectionDefaultAlignment();
+
+ // Sections in an executable file on disk should be sector-aligned (512 byte).
+ _peHeader.FileAlignment = SECTOR_SIZE;
+
+ // The version number of the resultant executable/DLL. The number is purely
+ // informative, and neither the linker nor the loader won't use it. User can
+ // set the value using /version command line option. Default is 0.0.
+ PECOFFLinkingContext::Version imageVersion = context.getImageVersion();
+ _peHeader.MajorImageVersion = imageVersion.majorVersion;
+ _peHeader.MinorImageVersion = imageVersion.minorVersion;
+
+ // The required Windows version number. This is the internal version and
+ // shouldn't be confused with product name. Windows 7 is version 6.1 and
+ // Windows 8 is 6.2, for example.
+ PECOFFLinkingContext::Version minOSVersion = context.getMinOSVersion();
+ _peHeader.MajorOperatingSystemVersion = minOSVersion.majorVersion;
+ _peHeader.MinorOperatingSystemVersion = minOSVersion.minorVersion;
+ _peHeader.MajorSubsystemVersion = minOSVersion.majorVersion;
+ _peHeader.MinorSubsystemVersion = minOSVersion.minorVersion;
+
+ _peHeader.Subsystem = context.getSubsystem();
+
+ // Despite its name, DLL characteristics field has meaning both for
+ // executables and DLLs. We are not very sure if the following bits must
+ // be set, but regular binaries seem to have these bits, so we follow
+ // them.
+ uint16_t dllCharacteristics = llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_SEH;
+ if (context.isTerminalServerAware())
+ dllCharacteristics |=
+ llvm::COFF::IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE;
+ if (context.isNxCompat())
+ dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NX_COMPAT;
+ if (context.getDynamicBaseEnabled())
+ dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE;
+ if (!context.getAllowBind())
+ dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_BIND;
+ if (!context.getAllowIsolation())
+ dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION;
+ _peHeader.DLLCharacteristics = dllCharacteristics;
+
+ _peHeader.SizeOfStackReserve = context.getStackReserve();
+ _peHeader.SizeOfStackCommit = context.getStackCommit();
+ _peHeader.SizeOfHeapReserve = context.getHeapReserve();
+ _peHeader.SizeOfHeapCommit = context.getHeapCommit();
-class ExecutableWriter : public Writer {
-public:
- explicit ExecutableWriter(const PECOFFLinkingContext &context)
- : _PECOFFLinkingContext(context), _numSections(0),
- _imageSizeInMemory(PAGE_SIZE), _imageSizeOnDisk(0) {}
+ // The number of data directory entries. We always have 16 entries.
+ _peHeader.NumberOfRvaAndSize = 16;
+}
- // Create all chunks that consist of the output file.
- void build(const File &linkedFile) {
- // Create file chunks and add them to the list.
- auto *dosStub = new DOSStubChunk(_PECOFFLinkingContext);
- auto *peHeader = new PEHeaderChunk(_PECOFFLinkingContext);
- auto *dataDirectory = new DataDirectoryChunk(linkedFile);
- auto *sectionTable = new SectionHeaderTableChunk();
- auto *text = new TextSectionChunk(linkedFile);
- auto *rdata = new RDataSectionChunk(linkedFile);
- auto *data = new DataSectionChunk(linkedFile);
- auto *bss = new BssSectionChunk(linkedFile);
- BaseRelocChunk *baseReloc = nullptr;
- if (_PECOFFLinkingContext.getBaseRelocationEnabled())
- baseReloc = new BaseRelocChunk(linkedFile);
-
- addChunk(dosStub);
- addChunk(peHeader);
- addChunk(dataDirectory);
- addChunk(sectionTable);
-
- // Do not add the empty section. Windows loader does not like a section of
- // size zero and rejects such executable.
- if (text->size())
- addSectionChunk(text, sectionTable);
- if (rdata->size())
- addSectionChunk(rdata, sectionTable);
- if (data->size())
- addSectionChunk(data, sectionTable);
- if (bss->size())
- addSectionChunk(bss, sectionTable);
-
- // Now that we know the addresses of all defined atoms that needs to be
- // relocated. So we can create the ".reloc" section which contains all the
- // relocation sites.
- if (baseReloc) {
- baseReloc->setContents(_chunks);
- if (baseReloc->size()) {
- addSectionChunk(baseReloc, sectionTable);
- dataDirectory->setBaseRelocField(baseReloc->getSectionRva(),
- baseReloc->rawSize());
+void PEHeaderChunk::write(uint8_t *fileBuffer) {
+ fileBuffer += fileOffset();
+ std::memcpy(fileBuffer, llvm::COFF::PEMagic, sizeof(llvm::COFF::PEMagic));
+ fileBuffer += sizeof(llvm::COFF::PEMagic);
+ std::memcpy(fileBuffer, &_coffHeader, sizeof(_coffHeader));
+ fileBuffer += sizeof(_coffHeader);
+ std::memcpy(fileBuffer, &_peHeader, sizeof(_peHeader));
+}
+
+void AtomChunk::write(uint8_t *fileBuffer) {
+ for (const auto *layout : _atomLayouts) {
+ const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
+ ArrayRef<uint8_t> rawContent = atom->rawContent();
+ std::memcpy(fileBuffer + layout->_fileOffset, rawContent.data(),
+ rawContent.size());
+ }
+}
+
+void AtomChunk::applyRelocations(uint8_t *fileBuffer,
+ std::map<const Atom *, uint64_t> &atomRva,
+ uint64_t imageBaseAddress) {
+ for (const auto *layout : _atomLayouts) {
+ const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
+ for (const Reference *ref : *atom) {
+ auto relocSite = reinterpret_cast<ulittle32_t *>(
+ fileBuffer + layout->_fileOffset + ref->offsetInAtom());
+ uint64_t targetAddr = atomRva[ref->target()];
+ // Also account for whatever offset is already stored at the relocation
+ // site.
+ targetAddr += *relocSite;
+
+ // Skip if this reference is not for relocation.
+ if (ref->kind() < lld::Reference::kindTargetLow)
+ continue;
+
+ switch (ref->kind()) {
+ case llvm::COFF::IMAGE_REL_I386_ABSOLUTE:
+ // This relocation is no-op.
+ break;
+ case llvm::COFF::IMAGE_REL_I386_DIR32:
+ // Set target's 32-bit VA.
+ *relocSite = targetAddr + imageBaseAddress;
+ break;
+ case llvm::COFF::IMAGE_REL_I386_DIR32NB:
+ // Set target's 32-bit RVA.
+ *relocSite = targetAddr;
+ break;
+ case llvm::COFF::IMAGE_REL_I386_REL32: {
+ // Set 32-bit relative address of the target. This relocation is
+ // usually used for relative branch or call instruction.
+ uint32_t disp = atomRva[atom] + ref->offsetInAtom() + 4;
+ *relocSite = targetAddr - disp;
+ break;
+ }
+ default:
+ llvm_unreachable("Unsupported relocation kind");
}
}
+ }
+}
- setImageSizeOnDisk();
+/// Print atom VAs. Used only for debugging.
+void AtomChunk::printAtomAddresses(uint64_t baseAddr) {
+ for (const auto *layout : _atomLayouts) {
+ const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
+ uint64_t addr = layout->_virtualAddr;
+ llvm::dbgs() << llvm::format("0x%08llx: ", addr + baseAddr)
+ << (atom->name().empty() ? "(anonymous)" : atom->name())
+ << "\n";
+ }
+}
- // Now that we know the size and file offset of sections. Set the file
- // header accordingly.
- peHeader->setSizeOfCode(calcSizeOfCode());
- if (text->size()) {
- peHeader->setBaseOfCode(text->getVirtualAddress());
- }
- if (rdata->size()) {
- peHeader->setBaseOfData(rdata->getVirtualAddress());
- } else if (data->size()) {
- peHeader->setBaseOfData(data->getVirtualAddress());
- }
- peHeader->setSizeOfInitializedData(calcSizeOfInitializedData());
- peHeader->setSizeOfUninitializedData(calcSizeOfUninitializedData());
- peHeader->setNumberOfSections(_numSections);
- peHeader->setSizeOfImage(_imageSizeInMemory);
-
- // The combined size of the DOS, PE and section headers including garbage
- // between the end of the header and the beginning of the first section.
- peHeader->setSizeOfHeaders(dosStub->size() + peHeader->size() +
- sectionTable->size() + dataDirectory->size());
-
- setAddressOfEntryPoint(text, peHeader);
- }
-
- virtual error_code writeFile(const File &linkedFile, StringRef path) {
- this->build(linkedFile);
-
- uint64_t totalSize = _chunks.back()->fileOffset() + _chunks.back()->size();
- OwningPtr<llvm::FileOutputBuffer> buffer;
- error_code ec = llvm::FileOutputBuffer::create(
- path, totalSize, buffer, llvm::FileOutputBuffer::F_executable);
- if (ec)
- return ec;
-
- for (const auto &chunk : _chunks)
- chunk->write(buffer->getBufferStart());
- applyAllRelocations(buffer->getBufferStart());
- DEBUG(printAllAtomAddresses());
- return buffer->commit();
+/// List all virtual addresses (and not relative virtual addresses) that need
+/// to be fixed up if image base is relocated. The only relocation type that
+/// needs to be fixed is DIR32 on i386. REL32 is not (and should not be)
+/// fixed up because it's PC-relative.
+void AtomChunk::addBaseRelocations(std::vector<uint64_t> &relocSites) {
+ // TODO: llvm-objdump doesn't support parsing the base relocation table, so
+ // we can't really test this at the moment. As a temporary solution, we
+ // should output debug messages with atom names and addresses so that we
+ // can inspect relocations, and fix the tests (base-reloc.test, maybe
+ // others) to use those messages.
+ for (const auto *layout : _atomLayouts) {
+ const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
+ for (const Reference *ref : *atom)
+ if (ref->kind() == llvm::COFF::IMAGE_REL_I386_DIR32)
+ relocSites.push_back(layout->_virtualAddr + ref->offsetInAtom());
}
+}
-private:
- /// Apply relocations to the output file buffer. This two pass. In the first
- /// pass, we visit all atoms to create a map from atom to its virtual
- /// address. In the second pass, we visit all relocation references to fix
- /// up addresses in the buffer.
- void applyAllRelocations(uint8_t *bufferStart) {
- for (auto &cp : _chunks)
- if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp))
- chunk->applyRelocations(bufferStart, atomRva,
- _PECOFFLinkingContext.getBaseAddress());
- }
-
- /// Print atom VAs. Used only for debugging.
- void printAllAtomAddresses() {
- for (auto &cp : _chunks)
- if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp))
- chunk->printAtomAddresses(_PECOFFLinkingContext.getBaseAddress());
- }
-
- void addChunk(Chunk *chunk) {
- _chunks.push_back(std::unique_ptr<Chunk>(chunk));
- }
-
- void addSectionChunk(SectionChunk *chunk,
- SectionHeaderTableChunk *table) {
- _chunks.push_back(std::unique_ptr<Chunk>(chunk));
- table->addSection(chunk);
- _numSections++;
-
- // 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.
- chunk->setVirtualAddress(_imageSizeInMemory);
- chunk->buildAtomToVirtualAddr(atomRva);
- _imageSizeInMemory = llvm::RoundUpToAlignment(
- _imageSizeInMemory + chunk->size(), PAGE_SIZE);
- }
-
- void setImageSizeOnDisk() {
- for (auto &chunk : _chunks) {
- // Compute and set the offset of the chunk in the output file.
- _imageSizeOnDisk = llvm::RoundUpToAlignment(_imageSizeOnDisk,
- chunk->align());
- chunk->setFileOffset(_imageSizeOnDisk);
- _imageSizeOnDisk += chunk->size();
- }
+void DataDirectoryChunk::write(uint8_t *fileBuffer) {
+ if (!_atomLayouts.empty()) {
+ assert(_atomLayouts.size() == 1);
+ const AtomLayout *layout = _atomLayouts[0];
+ ArrayRef<uint8_t> content =
+ static_cast<const DefinedAtom *>(layout->_atom)->rawContent();
+ std::memcpy(fileBuffer + _fileOffset, content.data(), content.size());
+ }
+
+ // Write base relocation table entry.
+ int baseRelocOffset = llvm::COFF::DataDirectoryIndex::BASE_RELOCATION_TABLE *
+ sizeof(llvm::object::data_directory);
+ auto *baseReloc = reinterpret_cast<llvm::object::data_directory *>(
+ fileBuffer + _fileOffset + baseRelocOffset);
+ baseReloc->RelativeVirtualAddress = _baseRelocAddr;
+ baseReloc->Size = _baseRelocSize;
+}
+
+llvm::object::coff_section &SectionChunk::getSectionHeader() {
+ // Fix up section size before returning it. VirtualSize should be the size
+ // of the actual content, and SizeOfRawData should be aligned to the section
+ // alignment.
+ _sectionHeader.VirtualSize = _size;
+ _sectionHeader.SizeOfRawData = size();
+ return _sectionHeader;
+}
+
+ulittle32_t SectionChunk::getSectionCharacteristics() {
+ return _sectionHeader.Characteristics;
+}
+
+void SectionChunk::appendAtom(const DefinedAtom *atom) {
+ // Atom may have to be at a proper alignment boundary. If so, move the
+ // pointer to make a room after the last atom before adding new one.
+ _size = llvm::RoundUpToAlignment(_size, 1 << atom->alignment().powerOf2);
+
+ // Create an AtomLayout and move the current pointer.
+ auto *layout = new (_alloc) AtomLayout(atom, _size, _size);
+ _atomLayouts.push_back(layout);
+ _size += atom->size();
+}
+
+SectionChunk::SectionChunk(StringRef sectionName, uint32_t characteristics)
+ : AtomChunk(kindSection),
+ _sectionHeader(createSectionHeader(sectionName, characteristics)) {
+ // The section should be aligned to disk sector.
+ _align = SECTOR_SIZE;
+}
+
+void SectionChunk::buildContents(const File &linkedFile,
+ bool (*isEligible)(const DefinedAtom *)) {
+ // Extract atoms from the linked file and append them to this section.
+ for (const DefinedAtom *atom : linkedFile.defined()) {
+ assert(atom->sectionChoice() == DefinedAtom::sectionBasedOnContent);
+ if (isEligible(atom))
+ appendAtom(atom);
}
- void setAddressOfEntryPoint(TextSectionChunk *text, PEHeaderChunk *peHeader) {
- // Find the virtual address of the entry point symbol if any.
- // PECOFF spec says that entry point for dll images is optional, in which
- // case it must be set to 0.
- if (_PECOFFLinkingContext.entrySymbolName().empty() &&
- _PECOFFLinkingContext.getImageType()
- == PECOFFLinkingContext::IMAGE_DLL) {
- peHeader->setAddressOfEntryPoint(0);
- } else {
- uint64_t entryPointAddress = text->getAtomVirtualAddress(
- _PECOFFLinkingContext.entrySymbolName());
- if (entryPointAddress != 0)
- peHeader->setAddressOfEntryPoint(entryPointAddress);
- }
+ // Now that we have a list of atoms that to be written in this section,
+ // and we know the size of the section. Let's write them to the section
+ // header. VirtualSize should be the size of the actual content, and
+ // SizeOfRawData should be aligned to the section alignment.
+ _sectionHeader.VirtualSize = _size;
+ _sectionHeader.SizeOfRawData = size();
+}
+
+llvm::object::coff_section
+SectionChunk::createSectionHeader(StringRef sectionName,
+ uint32_t characteristics) const {
+ llvm::object::coff_section header;
+
+ // Section name equal to or shorter than 8 byte fits in the section
+ // header. Longer names should be stored to string table, which is not
+ // implemented yet.
+ if (sizeof(header.Name) < sectionName.size())
+ llvm_unreachable("Cannot handle section name longer than 8 byte");
+
+ // 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(), sizeof(header.Name));
+
+ header.VirtualSize = 0;
+ header.VirtualAddress = 0;
+ header.SizeOfRawData = 0;
+ header.PointerToRawData = 0;
+ header.PointerToRelocations = 0;
+ header.PointerToLinenumbers = 0;
+ header.NumberOfRelocations = 0;
+ header.NumberOfLinenumbers = 0;
+ header.Characteristics = characteristics;
+ return header;
+}
+
+void TextSectionChunk::write(uint8_t *fileBuffer) {
+ if (_atomLayouts.empty())
+ return;
+ // Fill the section with INT 3 (0xCC) rather than NUL, so that the
+ // disassembler will not interpret a garbage between atoms as the beginning
+ // of multi-byte machine code. This does not change the behavior of
+ // resulting binary but help debugging.
+ uint8_t *start = fileBuffer + _atomLayouts.front()->_fileOffset;
+ uint8_t *end = fileBuffer + _atomLayouts.back()->_fileOffset;
+ memset(start, 0xCC, end - start);
+ SectionChunk::write(fileBuffer);
+}
+
+void SectionHeaderTableChunk::addSection(SectionChunk *chunk) {
+ _sections.push_back(chunk);
+}
+
+uint64_t SectionHeaderTableChunk::size() const {
+ return _sections.size() * sizeof(llvm::object::coff_section);
+}
+
+void SectionHeaderTableChunk::write(uint8_t *fileBuffer) {
+ uint64_t offset = 0;
+ fileBuffer += fileOffset();
+ for (const auto &chunk : _sections) {
+ const llvm::object::coff_section &header = chunk->getSectionHeader();
+ std::memcpy(fileBuffer + offset, &header, sizeof(header));
+ offset += sizeof(header);
+ }
+}
+
+/// Creates .reloc section content from the other sections. The content of
+/// .reloc is basically a list of relocation sites. The relocation sites are
+/// divided into blocks. Each block represents the base relocation for a 4K
+/// page.
+///
+/// By dividing 32 bit RVAs into blocks, COFF saves disk and memory space for
+/// the base relocation. A block consists of a 32 bit page RVA and 16 bit
+/// relocation entries which represent offsets in the page. That is a more
+/// compact representation than a simple vector of 32 bit RVAs.
+void BaseRelocChunk::setContents(ChunkVectorT &chunks) {
+ std::vector<uint64_t> relocSites = listRelocSites(chunks);
+ PageOffsetT blocks = groupByPage(relocSites);
+ for (auto &i : blocks) {
+ uint64_t pageAddr = i.first;
+ const std::vector<uint16_t> &offsetsInPage = i.second;
+ appendAtom(createBaseRelocBlock(_file, pageAddr, offsetsInPage));
}
+}
+
+// Returns a list of RVAs that needs to be relocated if the binary is loaded
+// at an address different from its preferred one.
+std::vector<uint64_t> BaseRelocChunk::listRelocSites(ChunkVectorT &chunks) {
+ std::vector<uint64_t> ret;
+ for (auto &cp : chunks)
+ if (SectionChunk *chunk = dyn_cast<SectionChunk>(&*cp))
+ chunk->addBaseRelocations(ret);
+ return ret;
+}
+
+// Divide the given RVAs into blocks.
+BaseRelocChunk::PageOffsetT
+BaseRelocChunk::groupByPage(std::vector<uint64_t> relocSites) {
+ PageOffsetT blocks;
+ uint64_t mask = static_cast<uint64_t>(PAGE_SIZE) - 1;
+ for (uint64_t addr : relocSites)
+ blocks[addr & ~mask].push_back(addr & mask);
+ return blocks;
+}
- uint64_t calcSectionSize(llvm::COFF::SectionCharacteristics sectionType) {
- uint64_t ret = 0;
- for (auto &cp : _chunks)
- if (SectionChunk *chunk = dyn_cast<SectionChunk>(&*cp))
- if (chunk->getSectionCharacteristics() & sectionType)
- ret += chunk->size();
- return ret;
+// Create the content of a relocation block.
+DefinedAtom *
+BaseRelocChunk::createBaseRelocBlock(const File &file, uint64_t pageAddr,
+ const std::vector<uint16_t> &offsets) {
+ // Relocation blocks should be padded with IMAGE_REL_I386_ABSOLUTE to be
+ // aligned to a DWORD size boundary.
+ uint32_t size = llvm::RoundUpToAlignment(
+ sizeof(ulittle32_t) * 2 + sizeof(ulittle16_t) * offsets.size(),
+ sizeof(ulittle32_t));
+ std::vector<uint8_t> contents(size);
+ uint8_t *ptr = &contents[0];
+
+ // The first four bytes is the page RVA.
+ *reinterpret_cast<ulittle32_t *>(ptr) = pageAddr;
+ ptr += sizeof(ulittle32_t);
+
+ // The second four bytes is the size of the block, including the the page
+ // RVA and this size field.
+ *reinterpret_cast<ulittle32_t *>(ptr) = size;
+ ptr += sizeof(ulittle32_t);
+
+ // The rest of the block consists of offsets in the page.
+ for (uint16_t offset : offsets) {
+ assert(offset < PAGE_SIZE);
+ uint16_t val = (llvm::COFF::IMAGE_REL_BASED_HIGHLOW << 12) | offset;
+ *reinterpret_cast<ulittle16_t *>(ptr) = val;
+ ptr += sizeof(ulittle16_t);
}
+ return new (_alloc) BaseRelocAtom(file, std::move(contents));
+}
+
+} // end anonymous namespace
+
+class ExecutableWriter : public Writer {
+public:
+ explicit ExecutableWriter(const PECOFFLinkingContext &context)
+ : _PECOFFLinkingContext(context), _numSections(0),
+ _imageSizeInMemory(PAGE_SIZE), _imageSizeOnDisk(0) {}
+
+ void build(const File &linkedFile);
+ virtual error_code writeFile(const File &linkedFile, StringRef path);
+
+private:
+ void applyAllRelocations(uint8_t *bufferStart);
+ void printAllAtomAddresses();
+ void addChunk(Chunk *chunk);
+ void addSectionChunk(SectionChunk *chunk, SectionHeaderTableChunk *table);
+ void setImageSizeOnDisk();
+ void setAddressOfEntryPoint(TextSectionChunk *text, PEHeaderChunk *peHeader);
+ uint64_t calcSectionSize(llvm::COFF::SectionCharacteristics sectionType);
uint64_t calcSizeOfInitializedData() {
return calcSectionSize(llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA);
@@ -991,6 +891,165 @@ private:
std::map<const Atom *, uint64_t> atomRva;
};
+// Create all chunks that consist of the output file.
+void ExecutableWriter::build(const File &linkedFile) {
+ // Create file chunks and add them to the list.
+ auto *dosStub = new DOSStubChunk(_PECOFFLinkingContext);
+ auto *peHeader = new PEHeaderChunk(_PECOFFLinkingContext);
+ auto *dataDirectory = new DataDirectoryChunk(linkedFile);
+ auto *sectionTable = new SectionHeaderTableChunk();
+ auto *text = new TextSectionChunk(linkedFile);
+ auto *rdata = new RDataSectionChunk(linkedFile);
+ auto *data = new DataSectionChunk(linkedFile);
+ auto *bss = new BssSectionChunk(linkedFile);
+ BaseRelocChunk *baseReloc = nullptr;
+ if (_PECOFFLinkingContext.getBaseRelocationEnabled())
+ baseReloc = new BaseRelocChunk(linkedFile);
+
+ addChunk(dosStub);
+ addChunk(peHeader);
+ addChunk(dataDirectory);
+ addChunk(sectionTable);
+
+ // Do not add the empty section. Windows loader does not like a section of
+ // size zero and rejects such executable.
+ if (text->size())
+ addSectionChunk(text, sectionTable);
+ if (rdata->size())
+ addSectionChunk(rdata, sectionTable);
+ if (data->size())
+ addSectionChunk(data, sectionTable);
+ if (bss->size())
+ addSectionChunk(bss, sectionTable);
+
+ // Now that we know the addresses of all defined atoms that needs to be
+ // relocated. So we can create the ".reloc" section which contains all the
+ // relocation sites.
+ if (baseReloc) {
+ baseReloc->setContents(_chunks);
+ if (baseReloc->size()) {
+ addSectionChunk(baseReloc, sectionTable);
+ dataDirectory->setBaseRelocField(baseReloc->getSectionRva(),
+ baseReloc->rawSize());
+ }
+ }
+
+ setImageSizeOnDisk();
+
+ // Now that we know the size and file offset of sections. Set the file
+ // header accordingly.
+ peHeader->setSizeOfCode(calcSizeOfCode());
+ if (text->size()) {
+ peHeader->setBaseOfCode(text->getVirtualAddress());
+ }
+ if (rdata->size()) {
+ peHeader->setBaseOfData(rdata->getVirtualAddress());
+ } else if (data->size()) {
+ peHeader->setBaseOfData(data->getVirtualAddress());
+ }
+ peHeader->setSizeOfInitializedData(calcSizeOfInitializedData());
+ peHeader->setSizeOfUninitializedData(calcSizeOfUninitializedData());
+ peHeader->setNumberOfSections(_numSections);
+ peHeader->setSizeOfImage(_imageSizeInMemory);
+
+ // The combined size of the DOS, PE and section headers including garbage
+ // between the end of the header and the beginning of the first section.
+ peHeader->setSizeOfHeaders(dosStub->size() + peHeader->size() +
+ sectionTable->size() + dataDirectory->size());
+
+ setAddressOfEntryPoint(text, peHeader);
+}
+
+error_code ExecutableWriter::writeFile(const File &linkedFile, StringRef path) {
+ this->build(linkedFile);
+
+ uint64_t totalSize = _chunks.back()->fileOffset() + _chunks.back()->size();
+ OwningPtr<llvm::FileOutputBuffer> buffer;
+ error_code ec = llvm::FileOutputBuffer::create(
+ path, totalSize, buffer, llvm::FileOutputBuffer::F_executable);
+ if (ec)
+ return ec;
+
+ for (const auto &chunk : _chunks)
+ chunk->write(buffer->getBufferStart());
+ applyAllRelocations(buffer->getBufferStart());
+ DEBUG(printAllAtomAddresses());
+ return buffer->commit();
+}
+
+/// Apply relocations to the output file buffer. This two pass. In the first
+/// pass, we visit all atoms to create a map from atom to its virtual
+/// address. In the second pass, we visit all relocation references to fix
+/// up addresses in the buffer.
+void ExecutableWriter::applyAllRelocations(uint8_t *bufferStart) {
+ for (auto &cp : _chunks)
+ if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp))
+ chunk->applyRelocations(bufferStart, atomRva,
+ _PECOFFLinkingContext.getBaseAddress());
+}
+
+/// Print atom VAs. Used only for debugging.
+void ExecutableWriter::printAllAtomAddresses() {
+ for (auto &cp : _chunks)
+ if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp))
+ chunk->printAtomAddresses(_PECOFFLinkingContext.getBaseAddress());
+}
+
+void ExecutableWriter::addChunk(Chunk *chunk) {
+ _chunks.push_back(std::unique_ptr<Chunk>(chunk));
+}
+
+void ExecutableWriter::addSectionChunk(SectionChunk *chunk,
+ SectionHeaderTableChunk *table) {
+ _chunks.push_back(std::unique_ptr<Chunk>(chunk));
+ table->addSection(chunk);
+ _numSections++;
+
+ // 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.
+ chunk->setVirtualAddress(_imageSizeInMemory);
+ chunk->buildAtomToVirtualAddr(atomRva);
+ _imageSizeInMemory =
+ llvm::RoundUpToAlignment(_imageSizeInMemory + chunk->size(), PAGE_SIZE);
+}
+
+void ExecutableWriter::setImageSizeOnDisk() {
+ for (auto &chunk : _chunks) {
+ // Compute and set the offset of the chunk in the output file.
+ _imageSizeOnDisk =
+ llvm::RoundUpToAlignment(_imageSizeOnDisk, chunk->align());
+ chunk->setFileOffset(_imageSizeOnDisk);
+ _imageSizeOnDisk += chunk->size();
+ }
+}
+
+void ExecutableWriter::setAddressOfEntryPoint(TextSectionChunk *text,
+ PEHeaderChunk *peHeader) {
+ // Find the virtual address of the entry point symbol if any.
+ // PECOFF spec says that entry point for dll images is optional, in which
+ // case it must be set to 0.
+ if (_PECOFFLinkingContext.entrySymbolName().empty() &&
+ _PECOFFLinkingContext.getImageType() == PECOFFLinkingContext::IMAGE_DLL) {
+ peHeader->setAddressOfEntryPoint(0);
+ } else {
+ uint64_t entryPointAddress =
+ text->getAtomVirtualAddress(_PECOFFLinkingContext.entrySymbolName());
+ if (entryPointAddress != 0)
+ peHeader->setAddressOfEntryPoint(entryPointAddress);
+ }
+}
+
+uint64_t ExecutableWriter::calcSectionSize(
+ llvm::COFF::SectionCharacteristics sectionType) {
+ uint64_t ret = 0;
+ for (auto &cp : _chunks)
+ if (SectionChunk *chunk = dyn_cast<SectionChunk>(&*cp))
+ if (chunk->getSectionCharacteristics() & sectionType)
+ ret += chunk->size();
+ return ret;
+}
+
} // end namespace pecoff
std::unique_ptr<Writer> createWriterPECOFF(const PECOFFLinkingContext &info) {
More information about the llvm-commits
mailing list