diff options
| author | Martin Storsjo <martin@martin.st> | 2018-12-19 07:24:38 +0000 |
|---|---|---|
| committer | Martin Storsjo <martin@martin.st> | 2018-12-19 07:24:38 +0000 |
| commit | e84a0b5a9e2c8003dc857f2760134e36df1e9987 (patch) | |
| tree | aecebf768c7a3199e49d042918208e424327118a /llvm/tools/llvm-objcopy/COFF | |
| parent | 642e140bebe0825168078f15b992e604b6af4e81 (diff) | |
| download | bcm5719-llvm-e84a0b5a9e2c8003dc857f2760134e36df1e9987.tar.gz bcm5719-llvm-e84a0b5a9e2c8003dc857f2760134e36df1e9987.zip | |
[llvm-objcopy] Initial COFF support
This is an initial implementation of no-op passthrough copying of COFF
with objcopy.
Differential Revision: https://reviews.llvm.org/D54939
llvm-svn: 349605
Diffstat (limited to 'llvm/tools/llvm-objcopy/COFF')
| -rw-r--r-- | llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp | 40 | ||||
| -rw-r--r-- | llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h | 31 | ||||
| -rw-r--r-- | llvm/tools/llvm-objcopy/COFF/Object.h | 110 | ||||
| -rw-r--r-- | llvm/tools/llvm-objcopy/COFF/Reader.cpp | 141 | ||||
| -rw-r--r-- | llvm/tools/llvm-objcopy/COFF/Reader.h | 47 | ||||
| -rw-r--r-- | llvm/tools/llvm-objcopy/COFF/Writer.cpp | 318 | ||||
| -rw-r--r-- | llvm/tools/llvm-objcopy/COFF/Writer.h | 68 |
7 files changed, 755 insertions, 0 deletions
diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp new file mode 100644 index 00000000000..89e926af567 --- /dev/null +++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp @@ -0,0 +1,40 @@ +//===- COFFObjcopy.cpp ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "COFFObjcopy.h" +#include "Buffer.h" +#include "CopyConfig.h" +#include "Object.h" +#include "Reader.h" +#include "Writer.h" +#include "llvm-objcopy.h" + +#include "llvm/Object/Binary.h" +#include "llvm/Object/COFF.h" +#include <cassert> + +namespace llvm { +namespace objcopy { +namespace coff { + +using namespace object; +using namespace COFF; + +void executeObjcopyOnBinary(const CopyConfig &Config, + object::COFFObjectFile &In, Buffer &Out) { + COFFReader Reader(In); + std::unique_ptr<Object> Obj = Reader.create(); + assert(Obj && "Unable to deserialize COFF object"); + COFFWriter Writer(*Obj, Out); + Writer.write(); +} + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h new file mode 100644 index 00000000000..bf70bd9b4d8 --- /dev/null +++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h @@ -0,0 +1,31 @@ +//===- COFFObjcopy.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H +#define LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H + +namespace llvm { + +namespace object { +class COFFObjectFile; +} // end namespace object + +namespace objcopy { +struct CopyConfig; +class Buffer; + +namespace coff { +void executeObjcopyOnBinary(const CopyConfig &Config, + object::COFFObjectFile &In, Buffer &Out); + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H diff --git a/llvm/tools/llvm-objcopy/COFF/Object.h b/llvm/tools/llvm-objcopy/COFF/Object.h new file mode 100644 index 00000000000..89f9903c0fd --- /dev/null +++ b/llvm/tools/llvm-objcopy/COFF/Object.h @@ -0,0 +1,110 @@ +//===- Object.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OBJCOPY_COFF_OBJECT_H +#define LLVM_TOOLS_OBJCOPY_COFF_OBJECT_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/Object/COFF.h" +#include <cstddef> +#include <cstdint> +#include <vector> + +namespace llvm { +namespace objcopy { +namespace coff { + +struct Section { + object::coff_section Header; + ArrayRef<uint8_t> Contents; + std::vector<object::coff_relocation> Relocs; + StringRef Name; +}; + +struct Symbol { + object::coff_symbol32 Sym; + StringRef Name; + ArrayRef<uint8_t> AuxData; +}; + +struct Object { + bool IsPE = false; + + object::dos_header DosHeader; + ArrayRef<uint8_t> DosStub; + + object::coff_file_header CoffFileHeader; + + bool Is64 = false; + object::pe32plus_header PeHeader; + uint32_t BaseOfData = 0; // pe32plus_header lacks this field. + + std::vector<object::data_directory> DataDirectories; + std::vector<Section> Sections; + std::vector<Symbol> Symbols; +}; + +// Copy between coff_symbol16 and coff_symbol32. +// The source and destination files can use either coff_symbol16 or +// coff_symbol32, while we always store them as coff_symbol32 in the +// intermediate data structure. +template <class Symbol1Ty, class Symbol2Ty> +void copySymbol(Symbol1Ty &Dest, const Symbol2Ty &Src) { + static_assert(sizeof(Dest.Name.ShortName) == sizeof(Src.Name.ShortName), + "Mismatched name sizes"); + memcpy(Dest.Name.ShortName, Src.Name.ShortName, sizeof(Dest.Name.ShortName)); + Dest.Value = Src.Value; + Dest.SectionNumber = Src.SectionNumber; + Dest.Type = Src.Type; + Dest.StorageClass = Src.StorageClass; + Dest.NumberOfAuxSymbols = Src.NumberOfAuxSymbols; +} + +// Copy between pe32_header and pe32plus_header. +// We store the intermediate state in a pe32plus_header. +template <class PeHeader1Ty, class PeHeader2Ty> +void copyPeHeader(PeHeader1Ty &Dest, const PeHeader2Ty &Src) { + Dest.Magic = Src.Magic; + Dest.MajorLinkerVersion = Src.MajorLinkerVersion; + Dest.MinorLinkerVersion = Src.MinorLinkerVersion; + Dest.SizeOfCode = Src.SizeOfCode; + Dest.SizeOfInitializedData = Src.SizeOfInitializedData; + Dest.SizeOfUninitializedData = Src.SizeOfUninitializedData; + Dest.AddressOfEntryPoint = Src.AddressOfEntryPoint; + Dest.BaseOfCode = Src.BaseOfCode; + Dest.ImageBase = Src.ImageBase; + Dest.SectionAlignment = Src.SectionAlignment; + Dest.FileAlignment = Src.FileAlignment; + Dest.MajorOperatingSystemVersion = Src.MajorOperatingSystemVersion; + Dest.MinorOperatingSystemVersion = Src.MinorOperatingSystemVersion; + Dest.MajorImageVersion = Src.MajorImageVersion; + Dest.MinorImageVersion = Src.MinorImageVersion; + Dest.MajorSubsystemVersion = Src.MajorSubsystemVersion; + Dest.MinorSubsystemVersion = Src.MinorSubsystemVersion; + Dest.Win32VersionValue = Src.Win32VersionValue; + Dest.SizeOfImage = Src.SizeOfImage; + Dest.SizeOfHeaders = Src.SizeOfHeaders; + Dest.CheckSum = Src.CheckSum; + Dest.Subsystem = Src.Subsystem; + Dest.DLLCharacteristics = Src.DLLCharacteristics; + Dest.SizeOfStackReserve = Src.SizeOfStackReserve; + Dest.SizeOfStackCommit = Src.SizeOfStackCommit; + Dest.SizeOfHeapReserve = Src.SizeOfHeapReserve; + Dest.SizeOfHeapCommit = Src.SizeOfHeapCommit; + Dest.LoaderFlags = Src.LoaderFlags; + Dest.NumberOfRvaAndSize = Src.NumberOfRvaAndSize; +} + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_COFF_OBJECT_H diff --git a/llvm/tools/llvm-objcopy/COFF/Reader.cpp b/llvm/tools/llvm-objcopy/COFF/Reader.cpp new file mode 100644 index 00000000000..a8a3576084c --- /dev/null +++ b/llvm/tools/llvm-objcopy/COFF/Reader.cpp @@ -0,0 +1,141 @@ +//===- Reader.cpp ---------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Reader.h" +#include "Object.h" +#include "llvm-objcopy.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/ErrorHandling.h" +#include <cstddef> +#include <cstdint> + +namespace llvm { +namespace objcopy { +namespace coff { + +using namespace object; + +Reader::~Reader() {} + +void COFFReader::readExecutableHeaders(Object &Obj) const { + const dos_header *DH = COFFObj.getDOSHeader(); + Obj.Is64 = COFFObj.is64(); + if (!DH) + return; + + Obj.IsPE = true; + Obj.DosHeader = *DH; + if (DH->AddressOfNewExeHeader > sizeof(*DH)) + Obj.DosStub = ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&DH[1]), + DH->AddressOfNewExeHeader - sizeof(*DH)); + + if (COFFObj.is64()) { + const pe32plus_header *PE32Plus = nullptr; + if (auto EC = COFFObj.getPE32PlusHeader(PE32Plus)) + reportError(COFFObj.getFileName(), std::move(EC)); + Obj.PeHeader = *PE32Plus; + } else { + const pe32_header *PE32 = nullptr; + if (auto EC = COFFObj.getPE32Header(PE32)) + reportError(COFFObj.getFileName(), std::move(EC)); + copyPeHeader(Obj.PeHeader, *PE32); + // The pe32plus_header (stored in Object) lacks the BaseOfData field. + Obj.BaseOfData = PE32->BaseOfData; + } + + for (size_t I = 0; I < Obj.PeHeader.NumberOfRvaAndSize; I++) { + const data_directory *Dir; + if (auto EC = COFFObj.getDataDirectory(I, Dir)) + reportError(COFFObj.getFileName(), std::move(EC)); + Obj.DataDirectories.emplace_back(*Dir); + } +} + +void COFFReader::readSections(Object &Obj) const { + // Section indexing starts from 1. + for (size_t I = 1, E = COFFObj.getNumberOfSections(); I <= E; I++) { + const coff_section *Sec; + if (auto EC = COFFObj.getSection(I, Sec)) + reportError(COFFObj.getFileName(), std::move(EC)); + Obj.Sections.push_back(Section()); + Section &S = Obj.Sections.back(); + S.Header = *Sec; + if (auto EC = COFFObj.getSectionContents(Sec, S.Contents)) + reportError(COFFObj.getFileName(), std::move(EC)); + ArrayRef<coff_relocation> Relocs = COFFObj.getRelocations(Sec); + S.Relocs.insert(S.Relocs.end(), Relocs.begin(), Relocs.end()); + if (auto EC = COFFObj.getSectionName(Sec, S.Name)) + reportError(COFFObj.getFileName(), std::move(EC)); + if (Sec->hasExtendedRelocations()) + reportError( + COFFObj.getFileName(), + make_error<StringError>("Extended relocations not supported yet", + object_error::parse_failed)); + } +} + +void COFFReader::readSymbols(Object &Obj, bool IsBigObj) const { + for (uint32_t I = 0, E = COFFObj.getRawNumberOfSymbols(); I < E;) { + Expected<COFFSymbolRef> SymOrErr = COFFObj.getSymbol(I); + if (!SymOrErr) + reportError(COFFObj.getFileName(), SymOrErr.takeError()); + COFFSymbolRef SymRef = *SymOrErr; + + Obj.Symbols.push_back(Symbol()); + Symbol &Sym = Obj.Symbols.back(); + // Copy symbols from the original form into an intermediate coff_symbol32. + if (IsBigObj) + copySymbol(Sym.Sym, + *reinterpret_cast<const coff_symbol32 *>(SymRef.getRawPtr())); + else + copySymbol(Sym.Sym, + *reinterpret_cast<const coff_symbol16 *>(SymRef.getRawPtr())); + if (auto EC = COFFObj.getSymbolName(SymRef, Sym.Name)) + reportError(COFFObj.getFileName(), std::move(EC)); + Sym.AuxData = COFFObj.getSymbolAuxData(SymRef); + assert((Sym.AuxData.size() % + (IsBigObj ? sizeof(coff_symbol32) : sizeof(coff_symbol16))) == 0); + I += 1 + SymRef.getNumberOfAuxSymbols(); + } +} + +std::unique_ptr<Object> COFFReader::create() const { + auto Obj = llvm::make_unique<Object>(); + + const coff_file_header *CFH = nullptr; + const coff_bigobj_file_header *CBFH = nullptr; + COFFObj.getCOFFHeader(CFH); + COFFObj.getCOFFBigObjHeader(CBFH); + bool IsBigObj = false; + if (CFH) { + Obj->CoffFileHeader = *CFH; + } else { + if (!CBFH) + reportError(COFFObj.getFileName(), + make_error<StringError>("No COFF file header returned", + object_error::parse_failed)); + // Only copying the few fields from the bigobj header that we need + // and won't recreate in the end. + Obj->CoffFileHeader.Machine = CBFH->Machine; + Obj->CoffFileHeader.TimeDateStamp = CBFH->TimeDateStamp; + IsBigObj = true; + } + + readExecutableHeaders(*Obj); + readSections(*Obj); + readSymbols(*Obj, IsBigObj); + + return Obj; +} + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/COFF/Reader.h b/llvm/tools/llvm-objcopy/COFF/Reader.h new file mode 100644 index 00000000000..0a6b17adeac --- /dev/null +++ b/llvm/tools/llvm-objcopy/COFF/Reader.h @@ -0,0 +1,47 @@ +//===- Reader.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OBJCOPY_COFF_READER_H +#define LLVM_TOOLS_OBJCOPY_COFF_READER_H + +#include "Buffer.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/Object/COFF.h" + +namespace llvm { +namespace objcopy { +namespace coff { + +class Object; + +using object::COFFObjectFile; + +class Reader { +public: + virtual ~Reader(); + virtual std::unique_ptr<Object> create() const = 0; +}; + +class COFFReader : public Reader { + const COFFObjectFile &COFFObj; + + void readExecutableHeaders(Object &Obj) const; + void readSections(Object &Obj) const; + void readSymbols(Object &Obj, bool IsBigObj) const; + +public: + explicit COFFReader(const COFFObjectFile &O) : COFFObj(O) {} + std::unique_ptr<Object> create() const override; +}; + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_COFF_READER_H diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.cpp b/llvm/tools/llvm-objcopy/COFF/Writer.cpp new file mode 100644 index 00000000000..48afe7889e8 --- /dev/null +++ b/llvm/tools/llvm-objcopy/COFF/Writer.cpp @@ -0,0 +1,318 @@ +//===- Writer.cpp ---------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Writer.h" +#include "Object.h" +#include "llvm-objcopy.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/ErrorHandling.h" +#include <cstddef> +#include <cstdint> + +namespace llvm { +namespace objcopy { +namespace coff { + +using namespace object; +using namespace COFF; + +Writer::~Writer() {} + +void COFFWriter::layoutSections() { + for (auto &S : Obj.Sections) { + if (S.Header.SizeOfRawData > 0) + S.Header.PointerToRawData = FileSize; + FileSize += S.Header.SizeOfRawData; // For executables, this is already + // aligned to FileAlignment. + if (S.Header.NumberOfRelocations > 0) + S.Header.PointerToRelocations = FileSize; + FileSize += S.Relocs.size() * sizeof(coff_relocation); + FileSize = alignTo(FileSize, FileAlignment); + + if (S.Header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) + SizeOfInitializedData += S.Header.SizeOfRawData; + } +} + +size_t COFFWriter::finalizeStringTable() { + for (auto &S : Obj.Sections) + if (S.Name.size() > COFF::NameSize) + StrTabBuilder.add(S.Name); + + for (const auto &S : Obj.Symbols) + if (S.Name.size() > COFF::NameSize) + StrTabBuilder.add(S.Name); + + StrTabBuilder.finalize(); + + for (auto &S : Obj.Sections) { + if (S.Name.size() > COFF::NameSize) { + snprintf(S.Header.Name, sizeof(S.Header.Name), "/%d", + (int)StrTabBuilder.getOffset(S.Name)); + } else { + strncpy(S.Header.Name, S.Name.data(), COFF::NameSize); + } + } + for (auto &S : Obj.Symbols) { + if (S.Name.size() > COFF::NameSize) { + S.Sym.Name.Offset.Zeroes = 0; + S.Sym.Name.Offset.Offset = StrTabBuilder.getOffset(S.Name); + } else { + strncpy(S.Sym.Name.ShortName, S.Name.data(), COFF::NameSize); + } + } + return StrTabBuilder.getSize(); +} + +template <class SymbolTy> +std::pair<size_t, size_t> COFFWriter::finalizeSymbolTable() { + size_t SymTabSize = Obj.Symbols.size() * sizeof(SymbolTy); + for (const auto &S : Obj.Symbols) + SymTabSize += S.AuxData.size(); + return std::make_pair(SymTabSize, sizeof(SymbolTy)); +} + +void COFFWriter::finalize(bool IsBigObj) { + size_t SizeOfHeaders = 0; + FileAlignment = 1; + size_t PeHeaderSize = 0; + if (Obj.IsPE) { + Obj.DosHeader.AddressOfNewExeHeader = + sizeof(Obj.DosHeader) + Obj.DosStub.size(); + SizeOfHeaders += Obj.DosHeader.AddressOfNewExeHeader + sizeof(PEMagic); + + FileAlignment = Obj.PeHeader.FileAlignment; + Obj.PeHeader.NumberOfRvaAndSize = Obj.DataDirectories.size(); + + PeHeaderSize = Obj.Is64 ? sizeof(pe32plus_header) : sizeof(pe32_header); + SizeOfHeaders += + PeHeaderSize + sizeof(data_directory) * Obj.DataDirectories.size(); + } + Obj.CoffFileHeader.NumberOfSections = Obj.Sections.size(); + SizeOfHeaders += + IsBigObj ? sizeof(coff_bigobj_file_header) : sizeof(coff_file_header); + SizeOfHeaders += sizeof(coff_section) * Obj.Sections.size(); + SizeOfHeaders = alignTo(SizeOfHeaders, FileAlignment); + + Obj.CoffFileHeader.SizeOfOptionalHeader = + PeHeaderSize + sizeof(data_directory) * Obj.DataDirectories.size(); + + FileSize = SizeOfHeaders; + SizeOfInitializedData = 0; + + layoutSections(); + + if (Obj.IsPE) { + Obj.PeHeader.SizeOfHeaders = SizeOfHeaders; + Obj.PeHeader.SizeOfInitializedData = SizeOfInitializedData; + + if (!Obj.Sections.empty()) { + const Section &S = Obj.Sections.back(); + Obj.PeHeader.SizeOfImage = + alignTo(S.Header.VirtualAddress + S.Header.VirtualSize, + Obj.PeHeader.SectionAlignment); + } + + // If the PE header had a checksum, clear it, since it isn't valid + // any longer. (We don't calculate a new one.) + Obj.PeHeader.CheckSum = 0; + } + + size_t StrTabSize = finalizeStringTable(); + size_t SymTabSize, SymbolSize; + std::tie(SymTabSize, SymbolSize) = IsBigObj + ? finalizeSymbolTable<coff_symbol32>() + : finalizeSymbolTable<coff_symbol16>(); + + size_t PointerToSymbolTable = FileSize; + // StrTabSize <= 4 is the size of an empty string table, only consisting + // of the length field. + if (SymTabSize == 0 && StrTabSize <= 4) { + // Don't point to the symbol table if empty. + PointerToSymbolTable = 0; + // For executables, skip the length field of an empty string table. + if (Obj.IsPE) + StrTabSize = 0; + } + + size_t NumRawSymbols = SymTabSize / SymbolSize; + Obj.CoffFileHeader.PointerToSymbolTable = PointerToSymbolTable; + Obj.CoffFileHeader.NumberOfSymbols = NumRawSymbols; + FileSize += SymTabSize + StrTabSize; + FileSize = alignTo(FileSize, FileAlignment); +} + +void COFFWriter::writeHeaders(bool IsBigObj) { + uint8_t *Ptr = Buf.getBufferStart(); + if (Obj.IsPE) { + memcpy(Ptr, &Obj.DosHeader, sizeof(Obj.DosHeader)); + Ptr += sizeof(Obj.DosHeader); + memcpy(Ptr, Obj.DosStub.data(), Obj.DosStub.size()); + Ptr += Obj.DosStub.size(); + memcpy(Ptr, PEMagic, sizeof(PEMagic)); + Ptr += sizeof(PEMagic); + } + if (!IsBigObj) { + memcpy(Ptr, &Obj.CoffFileHeader, sizeof(Obj.CoffFileHeader)); + Ptr += sizeof(Obj.CoffFileHeader); + } else { + // Generate a coff_bigobj_file_header, filling it in with the values + // from Obj.CoffFileHeader. All extra fields that don't exist in + // coff_file_header can be set to hardcoded values. + coff_bigobj_file_header BigObjHeader; + BigObjHeader.Sig1 = IMAGE_FILE_MACHINE_UNKNOWN; + BigObjHeader.Sig2 = 0xffff; + BigObjHeader.Version = BigObjHeader::MinBigObjectVersion; + BigObjHeader.Machine = Obj.CoffFileHeader.Machine; + BigObjHeader.TimeDateStamp = Obj.CoffFileHeader.TimeDateStamp; + memcpy(BigObjHeader.UUID, BigObjMagic, sizeof(BigObjMagic)); + BigObjHeader.unused1 = 0; + BigObjHeader.unused2 = 0; + BigObjHeader.unused3 = 0; + BigObjHeader.unused4 = 0; + // The value in Obj.CoffFileHeader.NumberOfSections is truncated, thus + // get the original one instead. + BigObjHeader.NumberOfSections = Obj.Sections.size(); + BigObjHeader.PointerToSymbolTable = Obj.CoffFileHeader.PointerToSymbolTable; + BigObjHeader.NumberOfSymbols = Obj.CoffFileHeader.NumberOfSymbols; + + memcpy(Ptr, &BigObjHeader, sizeof(BigObjHeader)); + Ptr += sizeof(BigObjHeader); + } + if (Obj.IsPE) { + if (Obj.Is64) { + memcpy(Ptr, &Obj.PeHeader, sizeof(Obj.PeHeader)); + Ptr += sizeof(Obj.PeHeader); + } else { + pe32_header PeHeader; + copyPeHeader(PeHeader, Obj.PeHeader); + // The pe32plus_header (stored in Object) lacks the BaseOfData field. + PeHeader.BaseOfData = Obj.BaseOfData; + + memcpy(Ptr, &PeHeader, sizeof(PeHeader)); + Ptr += sizeof(PeHeader); + } + for (const auto &DD : Obj.DataDirectories) { + memcpy(Ptr, &DD, sizeof(DD)); + Ptr += sizeof(DD); + } + } + for (const auto &S : Obj.Sections) { + memcpy(Ptr, &S.Header, sizeof(S.Header)); + Ptr += sizeof(S.Header); + } +} + +void COFFWriter::writeSections() { + for (const auto &S : Obj.Sections) { + uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData; + memcpy(Ptr, S.Contents.data(), S.Contents.size()); + + // For executable sections, pad the remainder of the raw data size with + // 0xcc, which is int3 on x86. + if ((S.Header.Characteristics & IMAGE_SCN_CNT_CODE) && + S.Header.SizeOfRawData > S.Contents.size()) + memset(Ptr + S.Contents.size(), 0xcc, + S.Header.SizeOfRawData - S.Contents.size()); + + Ptr += S.Header.SizeOfRawData; + memcpy(Ptr, S.Relocs.data(), S.Relocs.size() * sizeof(coff_relocation)); + } +} + +template <class SymbolTy> void COFFWriter::writeSymbolStringTables() { + uint8_t *Ptr = Buf.getBufferStart() + Obj.CoffFileHeader.PointerToSymbolTable; + for (const auto &S : Obj.Symbols) { + // Convert symbols back to the right size, from coff_symbol32. + copySymbol<SymbolTy, coff_symbol32>(*reinterpret_cast<SymbolTy *>(Ptr), + S.Sym); + Ptr += sizeof(SymbolTy); + memcpy(Ptr, S.AuxData.data(), S.AuxData.size()); + Ptr += S.AuxData.size(); + } + if (StrTabBuilder.getSize() > 4 || !Obj.IsPE) { + // Always write a string table in object files, even an empty one. + StrTabBuilder.write(Ptr); + Ptr += StrTabBuilder.getSize(); + } +} + +void COFFWriter::write(bool IsBigObj) { + finalize(IsBigObj); + + Buf.allocate(FileSize); + + writeHeaders(IsBigObj); + writeSections(); + if (IsBigObj) + writeSymbolStringTables<coff_symbol32>(); + else + writeSymbolStringTables<coff_symbol16>(); + + if (Obj.IsPE) + patchDebugDirectory(); + + if (auto E = Buf.commit()) + reportError(Buf.getName(), errorToErrorCode(std::move(E))); +} + +// Locate which sections contain the debug directories, iterate over all +// the debug_directory structs in there, and set the PointerToRawData field +// in all of them, according to their new physical location in the file. +void COFFWriter::patchDebugDirectory() { + if (Obj.DataDirectories.size() < DEBUG_DIRECTORY) + return; + const data_directory *Dir = &Obj.DataDirectories[DEBUG_DIRECTORY]; + if (Dir->Size <= 0) + return; + for (const auto &S : Obj.Sections) { + if (Dir->RelativeVirtualAddress >= S.Header.VirtualAddress && + Dir->RelativeVirtualAddress < + S.Header.VirtualAddress + S.Header.SizeOfRawData) { + if (Dir->RelativeVirtualAddress + Dir->Size > + S.Header.VirtualAddress + S.Header.SizeOfRawData) + reportError(Buf.getName(), + make_error<StringError>( + "Debug directory extends past end of section", + object_error::parse_failed)); + + size_t Offset = Dir->RelativeVirtualAddress - S.Header.VirtualAddress; + uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData + Offset; + uint8_t *End = Ptr + Dir->Size; + while (Ptr < End) { + debug_directory *Debug = reinterpret_cast<debug_directory *>(Ptr); + Debug->PointerToRawData = + S.Header.PointerToRawData + Offset + sizeof(debug_directory); + Ptr += sizeof(debug_directory) + Debug->SizeOfData; + Offset += sizeof(debug_directory) + Debug->SizeOfData; + } + // Debug directory found and patched, all done. + return; + } + } + reportError(Buf.getName(), + make_error<StringError>("Debug directory not found", + object_error::parse_failed)); +} + +void COFFWriter::write() { + bool IsBigObj = Obj.Sections.size() > MaxNumberOfSections16; + if (IsBigObj && Obj.IsPE) + reportError(Buf.getName(), + make_error<StringError>("Too many sections for executable", + object_error::parse_failed)); + write(IsBigObj); +} + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.h b/llvm/tools/llvm-objcopy/COFF/Writer.h new file mode 100644 index 00000000000..e23276664ee --- /dev/null +++ b/llvm/tools/llvm-objcopy/COFF/Writer.h @@ -0,0 +1,68 @@ +//===- Writer.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OBJCOPY_COFF_WRITER_H +#define LLVM_TOOLS_OBJCOPY_COFF_WRITER_H + +#include "Buffer.h" +#include "llvm/MC/StringTableBuilder.h" +#include <cstddef> +#include <utility> + +namespace llvm { +namespace objcopy { +namespace coff { + +class Object; + +class Writer { +protected: + Object &Obj; + Buffer &Buf; + +public: + virtual ~Writer(); + virtual void write() = 0; + + Writer(Object &O, Buffer &B) : Obj(O), Buf(B) {} +}; + +class COFFWriter : public Writer { + size_t FileSize; + size_t FileAlignment; + size_t SizeOfInitializedData; + StringTableBuilder StrTabBuilder; + + void layoutSections(); + size_t finalizeStringTable(); + template <class SymbolTy> std::pair<size_t, size_t> finalizeSymbolTable(); + + void finalize(bool IsBigObj); + + void writeHeaders(bool IsBigObj); + void writeSections(); + template <class SymbolTy> void writeSymbolStringTables(); + + void write(bool IsBigObj); + + void patchDebugDirectory(); + +public: + virtual ~COFFWriter() {} + void write() override; + + COFFWriter(Object &Obj, Buffer &Buf) + : Writer(Obj, Buf), StrTabBuilder(StringTableBuilder::WinCOFF) {} +}; + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_COFF_WRITER_H |

