brintos

brintos / llvm-project-archived public Read only

0
0
Text · 20.3 KiB · fed67d6 Raw
547 lines · cpp
1//===- COFFWriter.cpp -----------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "COFFWriter.h"10#include "COFFObject.h"11#include "llvm/ADT/ArrayRef.h"12#include "llvm/ADT/StringRef.h"13#include "llvm/BinaryFormat/COFF.h"14#include "llvm/Object/COFF.h"15#include "llvm/Support/CRC.h"16#include "llvm/Support/Endian.h"17#include "llvm/Support/Errc.h"18#include "llvm/Support/ErrorHandling.h"19#include <cstddef>20#include <cstdint>21 22namespace llvm {23namespace objcopy {24namespace coff {25 26using namespace object;27using namespace COFF;28 29Error COFFWriter::finalizeRelocTargets() {30  for (Section &Sec : Obj.getMutableSections()) {31    for (Relocation &R : Sec.Relocs) {32      const Symbol *Sym = Obj.findSymbol(R.Target);33      if (Sym == nullptr)34        return createStringError(object_error::invalid_symbol_index,35                                 "relocation target '%s' (%zu) not found",36                                 R.TargetName.str().c_str(), R.Target);37      R.Reloc.SymbolTableIndex = Sym->RawIndex;38    }39  }40  return Error::success();41}42 43Error COFFWriter::finalizeSymbolContents() {44  for (Symbol &Sym : Obj.getMutableSymbols()) {45    if (Sym.TargetSectionId <= 0) {46      // Undefined, or a special kind of symbol. These negative values47      // are stored in the SectionNumber field which is unsigned.48      Sym.Sym.SectionNumber = static_cast<uint32_t>(Sym.TargetSectionId);49    } else {50      const Section *Sec = Obj.findSection(Sym.TargetSectionId);51      if (Sec == nullptr)52        return createStringError(object_error::invalid_symbol_index,53                                 "symbol '%s' points to a removed section",54                                 Sym.Name.str().c_str());55      Sym.Sym.SectionNumber = Sec->Index;56 57      if (Sym.Sym.NumberOfAuxSymbols == 1 &&58          Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC) {59        coff_aux_section_definition *SD =60            reinterpret_cast<coff_aux_section_definition *>(61                Sym.AuxData[0].Opaque);62        uint32_t SDSectionNumber;63        if (Sym.AssociativeComdatTargetSectionId == 0) {64          // Not a comdat associative section; just set the Number field to65          // the number of the section itself.66          SDSectionNumber = Sec->Index;67        } else {68          Sec = Obj.findSection(Sym.AssociativeComdatTargetSectionId);69          if (Sec == nullptr)70            return createStringError(71                object_error::invalid_symbol_index,72                "symbol '%s' is associative to a removed section",73                Sym.Name.str().c_str());74          SDSectionNumber = Sec->Index;75        }76        // Update the section definition with the new section number.77        SD->NumberLowPart = static_cast<uint16_t>(SDSectionNumber);78        SD->NumberHighPart = static_cast<uint16_t>(SDSectionNumber >> 16);79      }80    }81    // Check that we actually have got AuxData to match the weak symbol target82    // we want to set. Only >= 1 would be required, but only == 1 makes sense.83    if (Sym.WeakTargetSymbolId && Sym.Sym.NumberOfAuxSymbols == 1) {84      coff_aux_weak_external *WE =85          reinterpret_cast<coff_aux_weak_external *>(Sym.AuxData[0].Opaque);86      const Symbol *Target = Obj.findSymbol(*Sym.WeakTargetSymbolId);87      if (Target == nullptr)88        return createStringError(object_error::invalid_symbol_index,89                                 "symbol '%s' is missing its weak target",90                                 Sym.Name.str().c_str());91      WE->TagIndex = Target->RawIndex;92    }93  }94  return Error::success();95}96 97Error COFFWriter::finalizeSymIdxContents() {98  // CFGuards shouldn't be present in PE.99  if (Obj.IsPE)100    return Error::success();101 102  // Currently handle only sections consisting only of .symidx.103  // TODO: other sections such as .impcall and .hybmp$x require more complex104  // handling as they have more complex layout.105  auto IsSymIdxSection = [](StringRef Name) {106    return Name == ".gljmp$y" || Name == ".giats$y" || Name == ".gfids$y" ||107           Name == ".gehcont$y";108  };109 110  DenseMap<size_t, size_t> SymIdMap;111  SmallDenseMap<ssize_t, coff_aux_section_definition *, 4> SecIdMap;112  for (Symbol &Sym : Obj.getMutableSymbols()) {113    SymIdMap[Sym.OriginalRawIndex] = Sym.RawIndex;114 115    // We collect only definition symbols of the sections to update the116    // checksums.117    if (Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC &&118        Sym.Sym.NumberOfAuxSymbols == 1 && Sym.Sym.Value == 0 &&119        IsSymIdxSection(Sym.Name))120      SecIdMap[Sym.TargetSectionId] =121          reinterpret_cast<coff_aux_section_definition *>(122              Sym.AuxData[0].Opaque);123  }124 125  for (Section &Sec : Obj.getMutableSections()) {126    if (!IsSymIdxSection(Sec.Name))127      continue;128 129    ArrayRef<uint8_t> RawIds = Sec.getContents();130    // Nothing to do and also the checksum will be -1 instead of 0 if we131    // recalculate it on empty input.132    if (RawIds.size() == 0)133      continue;134 135    auto SecDefIt = SecIdMap.find(Sec.UniqueId);136    if (SecDefIt == SecIdMap.end())137      return createStringError(object_error::invalid_symbol_index,138                               "section '%s' does not have the corresponding "139                               "symbol or the symbol has unexpected format",140                               Sec.Name.str().c_str());141 142    // Create updated content.143    ArrayRef<support::ulittle32_t> Ids(144        reinterpret_cast<const support::ulittle32_t *>(RawIds.data()),145        RawIds.size() / 4);146    std::vector<support::ulittle32_t> NewIds;147    for (support::ulittle32_t Id : Ids) {148      auto SymIdIt = SymIdMap.find(Id);149      if (SymIdIt == SymIdMap.end())150        return createStringError(object_error::invalid_symbol_index,151                                 "section '%s' contains a .symidx (%d) that is "152                                 "incorrect or was stripped",153                                 Sec.Name.str().c_str(), Id.value());154      NewIds.push_back(support::ulittle32_t(SymIdIt->getSecond()));155    }156    ArrayRef<uint8_t> NewRawIds(reinterpret_cast<uint8_t *>(NewIds.data()),157                                RawIds.size());158    // Update the checksum.159    JamCRC JC(/*Init=*/0);160    JC.update(NewRawIds);161    SecDefIt->getSecond()->CheckSum = JC.getCRC();162    // Set new content.163    Sec.setOwnedContents(NewRawIds.vec());164  }165  return Error::success();166}167 168void COFFWriter::layoutSections() {169  for (auto &S : Obj.getMutableSections()) {170    if (S.Header.SizeOfRawData > 0)171      S.Header.PointerToRawData = FileSize;172    else173      S.Header.PointerToRawData = 0;174    FileSize += S.Header.SizeOfRawData; // For executables, this is already175                                        // aligned to FileAlignment.176    if (S.Relocs.size() >= 0xffff) {177      S.Header.Characteristics |= COFF::IMAGE_SCN_LNK_NRELOC_OVFL;178      S.Header.NumberOfRelocations = 0xffff;179      S.Header.PointerToRelocations = FileSize;180      FileSize += sizeof(coff_relocation);181    } else {182      S.Header.NumberOfRelocations = S.Relocs.size();183      S.Header.PointerToRelocations = S.Relocs.size() ? FileSize : 0;184    }185 186    FileSize += S.Relocs.size() * sizeof(coff_relocation);187    FileSize = alignTo(FileSize, FileAlignment);188 189    if (S.Header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)190      SizeOfInitializedData += S.Header.SizeOfRawData;191  }192}193 194Expected<size_t> COFFWriter::finalizeStringTable() {195  for (const auto &S : Obj.getSections())196    if (S.Name.size() > COFF::NameSize) {197      // Put the section name at the start of strtab to ensure its offset is198      // less than Max7DecimalOffset. Otherwise, lldb/gdb will not read it.199      StrTabBuilder.add(S.Name, /*Priority=*/UINT8_MAX);200    }201 202  for (const auto &S : Obj.getSymbols())203    if (S.Name.size() > COFF::NameSize)204      StrTabBuilder.add(S.Name);205 206  StrTabBuilder.finalize();207 208  for (auto &S : Obj.getMutableSections()) {209    memset(S.Header.Name, 0, sizeof(S.Header.Name));210    if (S.Name.size() <= COFF::NameSize) {211      // Short names can go in the field directly.212      memcpy(S.Header.Name, S.Name.data(), S.Name.size());213    } else {214      // Offset of the section name in the string table.215      size_t Offset = StrTabBuilder.getOffset(S.Name);216      if (!COFF::encodeSectionName(S.Header.Name, Offset))217        return createStringError(object_error::invalid_section_index,218                                 "COFF string table is greater than 64GB, "219                                 "unable to encode section name offset");220    }221  }222  for (auto &S : Obj.getMutableSymbols()) {223    if (S.Name.size() > COFF::NameSize) {224      S.Sym.Name.Offset.Zeroes = 0;225      S.Sym.Name.Offset.Offset = StrTabBuilder.getOffset(S.Name);226    } else {227      strncpy(S.Sym.Name.ShortName, S.Name.data(), COFF::NameSize);228    }229  }230  return StrTabBuilder.getSize();231}232 233template <class SymbolTy>234std::pair<size_t, size_t> COFFWriter::finalizeSymbolTable() {235  size_t RawSymIndex = 0;236  for (auto &S : Obj.getMutableSymbols()) {237    // Symbols normally have NumberOfAuxSymbols set correctly all the time.238    // For file symbols, we need to know the output file's symbol size to be239    // able to calculate the number of slots it occupies.240    if (!S.AuxFile.empty())241      S.Sym.NumberOfAuxSymbols =242          alignTo(S.AuxFile.size(), sizeof(SymbolTy)) / sizeof(SymbolTy);243    S.RawIndex = RawSymIndex;244    RawSymIndex += 1 + S.Sym.NumberOfAuxSymbols;245  }246  return std::make_pair(RawSymIndex * sizeof(SymbolTy), sizeof(SymbolTy));247}248 249Error COFFWriter::finalize(bool IsBigObj) {250  size_t SymTabSize, SymbolSize;251  std::tie(SymTabSize, SymbolSize) = IsBigObj252                                         ? finalizeSymbolTable<coff_symbol32>()253                                         : finalizeSymbolTable<coff_symbol16>();254 255  if (Error E = finalizeRelocTargets())256    return E;257  if (Error E = finalizeSymbolContents())258    return E;259  if (Error E = finalizeSymIdxContents())260    return E;261 262  size_t SizeOfHeaders = 0;263  FileAlignment = 1;264  size_t PeHeaderSize = 0;265  if (Obj.IsPE) {266    Obj.DosHeader.AddressOfNewExeHeader =267        sizeof(Obj.DosHeader) + Obj.DosStub.size();268    SizeOfHeaders += Obj.DosHeader.AddressOfNewExeHeader + sizeof(PEMagic);269 270    FileAlignment = Obj.PeHeader.FileAlignment;271    Obj.PeHeader.NumberOfRvaAndSize = Obj.DataDirectories.size();272 273    PeHeaderSize = Obj.Is64 ? sizeof(pe32plus_header) : sizeof(pe32_header);274    SizeOfHeaders +=275        PeHeaderSize + sizeof(data_directory) * Obj.DataDirectories.size();276  }277  Obj.CoffFileHeader.NumberOfSections = Obj.getSections().size();278  SizeOfHeaders +=279      IsBigObj ? sizeof(coff_bigobj_file_header) : sizeof(coff_file_header);280  SizeOfHeaders += sizeof(coff_section) * Obj.getSections().size();281  SizeOfHeaders = alignTo(SizeOfHeaders, FileAlignment);282 283  Obj.CoffFileHeader.SizeOfOptionalHeader =284      PeHeaderSize + sizeof(data_directory) * Obj.DataDirectories.size();285 286  FileSize = SizeOfHeaders;287  SizeOfInitializedData = 0;288 289  layoutSections();290 291  if (Obj.IsPE) {292    Obj.PeHeader.SizeOfHeaders = SizeOfHeaders;293    Obj.PeHeader.SizeOfInitializedData = SizeOfInitializedData;294 295    if (!Obj.getSections().empty()) {296      const Section &S = Obj.getSections().back();297      Obj.PeHeader.SizeOfImage =298          alignTo(S.Header.VirtualAddress + S.Header.VirtualSize,299                  Obj.PeHeader.SectionAlignment);300    }301 302    // If the PE header had a checksum, clear it, since it isn't valid303    // any longer. (We don't calculate a new one.)304    Obj.PeHeader.CheckSum = 0;305  }306 307  Expected<size_t> StrTabSizeOrErr = finalizeStringTable();308  if (!StrTabSizeOrErr)309    return StrTabSizeOrErr.takeError();310 311  size_t StrTabSize = *StrTabSizeOrErr;312 313  size_t PointerToSymbolTable = FileSize;314  // StrTabSize <= 4 is the size of an empty string table, only consisting315  // of the length field.316  if (SymTabSize == 0 && StrTabSize <= 4 && Obj.IsPE) {317    // For executables, don't point to the symbol table and skip writing318    // the length field, if both the symbol and string tables are empty.319    PointerToSymbolTable = 0;320    StrTabSize = 0;321  }322 323  size_t NumRawSymbols = SymTabSize / SymbolSize;324  Obj.CoffFileHeader.PointerToSymbolTable = PointerToSymbolTable;325  Obj.CoffFileHeader.NumberOfSymbols = NumRawSymbols;326  FileSize += SymTabSize + StrTabSize;327  FileSize = alignTo(FileSize, FileAlignment);328 329  return Error::success();330}331 332void COFFWriter::writeHeaders(bool IsBigObj) {333  uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart());334  if (Obj.IsPE) {335    memcpy(Ptr, &Obj.DosHeader, sizeof(Obj.DosHeader));336    Ptr += sizeof(Obj.DosHeader);337    memcpy(Ptr, Obj.DosStub.data(), Obj.DosStub.size());338    Ptr += Obj.DosStub.size();339    memcpy(Ptr, PEMagic, sizeof(PEMagic));340    Ptr += sizeof(PEMagic);341  }342  if (!IsBigObj) {343    memcpy(Ptr, &Obj.CoffFileHeader, sizeof(Obj.CoffFileHeader));344    Ptr += sizeof(Obj.CoffFileHeader);345  } else {346    // Generate a coff_bigobj_file_header, filling it in with the values347    // from Obj.CoffFileHeader. All extra fields that don't exist in348    // coff_file_header can be set to hardcoded values.349    coff_bigobj_file_header BigObjHeader;350    BigObjHeader.Sig1 = IMAGE_FILE_MACHINE_UNKNOWN;351    BigObjHeader.Sig2 = 0xffff;352    BigObjHeader.Version = BigObjHeader::MinBigObjectVersion;353    BigObjHeader.Machine = Obj.CoffFileHeader.Machine;354    BigObjHeader.TimeDateStamp = Obj.CoffFileHeader.TimeDateStamp;355    memcpy(BigObjHeader.UUID, BigObjMagic, sizeof(BigObjMagic));356    BigObjHeader.unused1 = 0;357    BigObjHeader.unused2 = 0;358    BigObjHeader.unused3 = 0;359    BigObjHeader.unused4 = 0;360    // The value in Obj.CoffFileHeader.NumberOfSections is truncated, thus361    // get the original one instead.362    BigObjHeader.NumberOfSections = Obj.getSections().size();363    BigObjHeader.PointerToSymbolTable = Obj.CoffFileHeader.PointerToSymbolTable;364    BigObjHeader.NumberOfSymbols = Obj.CoffFileHeader.NumberOfSymbols;365 366    memcpy(Ptr, &BigObjHeader, sizeof(BigObjHeader));367    Ptr += sizeof(BigObjHeader);368  }369  if (Obj.IsPE) {370    if (Obj.Is64) {371      memcpy(Ptr, &Obj.PeHeader, sizeof(Obj.PeHeader));372      Ptr += sizeof(Obj.PeHeader);373    } else {374      pe32_header PeHeader;375      copyPeHeader(PeHeader, Obj.PeHeader);376      // The pe32plus_header (stored in Object) lacks the BaseOfData field.377      PeHeader.BaseOfData = Obj.BaseOfData;378 379      memcpy(Ptr, &PeHeader, sizeof(PeHeader));380      Ptr += sizeof(PeHeader);381    }382    for (const auto &DD : Obj.DataDirectories) {383      memcpy(Ptr, &DD, sizeof(DD));384      Ptr += sizeof(DD);385    }386  }387  for (const auto &S : Obj.getSections()) {388    memcpy(Ptr, &S.Header, sizeof(S.Header));389    Ptr += sizeof(S.Header);390  }391}392 393void COFFWriter::writeSections() {394  for (const auto &S : Obj.getSections()) {395    uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) +396                   S.Header.PointerToRawData;397    ArrayRef<uint8_t> Contents = S.getContents();398    llvm::copy(Contents, Ptr);399 400    // For executable sections, pad the remainder of the raw data size with401    // 0xcc, which is int3 on x86.402    if ((S.Header.Characteristics & IMAGE_SCN_CNT_CODE) &&403        S.Header.SizeOfRawData > Contents.size())404      memset(Ptr + Contents.size(), 0xcc,405             S.Header.SizeOfRawData - Contents.size());406 407    Ptr += S.Header.SizeOfRawData;408 409    if (S.Relocs.size() >= 0xffff) {410      object::coff_relocation R;411      R.VirtualAddress = S.Relocs.size() + 1;412      R.SymbolTableIndex = 0;413      R.Type = 0;414      memcpy(Ptr, &R, sizeof(R));415      Ptr += sizeof(R);416    }417    for (const auto &R : S.Relocs) {418      memcpy(Ptr, &R.Reloc, sizeof(R.Reloc));419      Ptr += sizeof(R.Reloc);420    }421  }422}423 424template <class SymbolTy> void COFFWriter::writeSymbolStringTables() {425  uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) +426                 Obj.CoffFileHeader.PointerToSymbolTable;427  for (const auto &S : Obj.getSymbols()) {428    // Convert symbols back to the right size, from coff_symbol32.429    copySymbol<SymbolTy, coff_symbol32>(*reinterpret_cast<SymbolTy *>(Ptr),430                                        S.Sym);431    Ptr += sizeof(SymbolTy);432    if (!S.AuxFile.empty()) {433      // For file symbols, just write the string into the aux symbol slots,434      // assuming that the unwritten parts are initialized to zero in the memory435      // mapped file.436      llvm::copy(S.AuxFile, Ptr);437      Ptr += S.Sym.NumberOfAuxSymbols * sizeof(SymbolTy);438    } else {439      // For other auxillary symbols, write their opaque payload into one symbol440      // table slot each. For big object files, the symbols are larger than the441      // opaque auxillary symbol struct and we leave padding at the end of each442      // entry.443      for (const AuxSymbol &AuxSym : S.AuxData) {444        ArrayRef<uint8_t> Ref = AuxSym.getRef();445        llvm::copy(Ref, Ptr);446        Ptr += sizeof(SymbolTy);447      }448    }449  }450  if (StrTabBuilder.getSize() > 4 || !Obj.IsPE) {451    // Always write a string table in object files, even an empty one.452    StrTabBuilder.write(Ptr);453    Ptr += StrTabBuilder.getSize();454  }455}456 457Error COFFWriter::write(bool IsBigObj) {458  if (Error E = finalize(IsBigObj))459    return E;460 461  Buf = WritableMemoryBuffer::getNewMemBuffer(FileSize);462  if (!Buf)463    return createStringError(llvm::errc::not_enough_memory,464                             "failed to allocate memory buffer of " +465                                 Twine::utohexstr(FileSize) + " bytes.");466 467  writeHeaders(IsBigObj);468  writeSections();469  if (IsBigObj)470    writeSymbolStringTables<coff_symbol32>();471  else472    writeSymbolStringTables<coff_symbol16>();473 474  if (Obj.IsPE)475    if (Error E = patchDebugDirectory())476      return E;477 478  // TODO: Implement direct writing to the output stream (without intermediate479  // memory buffer Buf).480  Out.write(Buf->getBufferStart(), Buf->getBufferSize());481  return Error::success();482}483 484Expected<uint32_t> COFFWriter::virtualAddressToFileAddress(uint32_t RVA) {485  for (const auto &S : Obj.getSections()) {486    if (RVA >= S.Header.VirtualAddress &&487        RVA < S.Header.VirtualAddress + S.Header.SizeOfRawData)488      return S.Header.PointerToRawData + RVA - S.Header.VirtualAddress;489  }490  return createStringError(object_error::parse_failed,491                           "debug directory payload not found");492}493 494// Locate which sections contain the debug directories, iterate over all495// the debug_directory structs in there, and set the PointerToRawData field496// in all of them, according to their new physical location in the file.497Error COFFWriter::patchDebugDirectory() {498  if (Obj.DataDirectories.size() <= DEBUG_DIRECTORY)499    return Error::success();500  const data_directory *Dir = &Obj.DataDirectories[DEBUG_DIRECTORY];501  if (Dir->Size <= 0)502    return Error::success();503  for (const auto &S : Obj.getSections()) {504    if (Dir->RelativeVirtualAddress >= S.Header.VirtualAddress &&505        Dir->RelativeVirtualAddress <506            S.Header.VirtualAddress + S.Header.SizeOfRawData) {507      if (Dir->RelativeVirtualAddress + Dir->Size >508          S.Header.VirtualAddress + S.Header.SizeOfRawData)509        return createStringError(object_error::parse_failed,510                                 "debug directory extends past end of section");511 512      size_t Offset = Dir->RelativeVirtualAddress - S.Header.VirtualAddress;513      uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) +514                     S.Header.PointerToRawData + Offset;515      uint8_t *End = Ptr + Dir->Size;516      while (Ptr < End) {517        debug_directory *Debug = reinterpret_cast<debug_directory *>(Ptr);518        if (Debug->PointerToRawData) {519          if (Expected<uint32_t> FilePosOrErr =520                  virtualAddressToFileAddress(Debug->AddressOfRawData))521            Debug->PointerToRawData = *FilePosOrErr;522          else523            return FilePosOrErr.takeError();524        }525        Ptr += sizeof(debug_directory);526        Offset += sizeof(debug_directory);527      }528      // Debug directory found and patched, all done.529      return Error::success();530    }531  }532  return createStringError(object_error::parse_failed,533                           "debug directory not found");534}535 536Error COFFWriter::write() {537  bool IsBigObj = Obj.getSections().size() > MaxNumberOfSections16;538  if (IsBigObj && Obj.IsPE)539    return createStringError(object_error::parse_failed,540                             "too many sections for executable");541  return write(IsBigObj);542}543 544} // end namespace coff545} // end namespace objcopy546} // end namespace llvm547