brintos

brintos / llvm-project-archived public Read only

0
0
Text · 33.2 KiB · cf8857d Raw
1014 lines · c
1//===- Chunks.h -------------------------------------------------*- C++ -*-===//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#ifndef LLD_COFF_CHUNKS_H10#define LLD_COFF_CHUNKS_H11 12#include "Config.h"13#include "InputFiles.h"14#include "lld/Common/LLVM.h"15#include "llvm/ADT/ArrayRef.h"16#include "llvm/ADT/PointerIntPair.h"17#include "llvm/ADT/iterator.h"18#include "llvm/ADT/iterator_range.h"19#include "llvm/MC/StringTableBuilder.h"20#include "llvm/Object/COFF.h"21#include "llvm/Object/WindowsMachineFlag.h"22#include <utility>23#include <vector>24 25namespace lld::coff {26 27using llvm::COFF::ImportDirectoryTableEntry;28using llvm::object::chpe_range_type;29using llvm::object::coff_relocation;30using llvm::object::coff_section;31using llvm::object::COFFSymbolRef;32using llvm::object::SectionRef;33 34class Baserel;35class Defined;36class DefinedImportData;37class DefinedRegular;38class ObjFile;39class OutputSection;40class RuntimePseudoReloc;41class Symbol;42 43// Mask for permissions (discardable, writable, readable, executable, etc).44const uint32_t permMask = 0xFE000000;45 46// Mask for section types (code, data, bss).47const uint32_t typeMask = 0x000000E0;48 49// The log base 2 of the largest section alignment, which is log2(8192), or 13.50enum : unsigned { Log2MaxSectionAlignment = 13 };51 52// A Chunk represents a chunk of data that will occupy space in the53// output (if the resolver chose that). It may or may not be backed by54// a section of an input file. It could be linker-created data, or55// doesn't even have actual data (if common or bss).56class Chunk {57public:58  enum Kind : uint8_t {59    SectionKind,60    SectionECKind,61    OtherKind,62    ImportThunkKind,63    ECExportThunkKind64  };65  Kind kind() const { return chunkKind; }66 67  // Returns the size of this chunk (even if this is a common or BSS.)68  size_t getSize() const;69 70  // Returns chunk alignment in power of two form. Value values are powers of71  // two from 1 to 8192.72  uint32_t getAlignment() const { return 1U << p2Align; }73 74  // Update the chunk section alignment measured in bytes. Internally alignment75  // is stored in log2.76  void setAlignment(uint32_t align) {77    // Treat zero byte alignment as 1 byte alignment.78    align = align ? align : 1;79    assert(llvm::isPowerOf2_32(align) && "alignment is not a power of 2");80    p2Align = llvm::Log2_32(align);81    assert(p2Align <= Log2MaxSectionAlignment &&82           "impossible requested alignment");83  }84 85  // Write this chunk to a mmap'ed file, assuming Buf is pointing to86  // beginning of the file. Because this function may use RVA values87  // of other chunks for relocations, you need to set them properly88  // before calling this function.89  void writeTo(uint8_t *buf) const;90 91  // The writer sets and uses the addresses. In practice, PE images cannot be92  // larger than 2GB. Chunks are always laid as part of the image, so Chunk RVAs93  // can be stored with 32 bits.94  uint32_t getRVA() const { return rva; }95  void setRVA(uint64_t v) {96    // This may truncate. The writer checks for overflow later.97    rva = (uint32_t)v;98  }99 100  // Returns readable/writable/executable bits.101  uint32_t getOutputCharacteristics() const;102 103  // Returns the section name if this is a section chunk.104  // It is illegal to call this function on non-section chunks.105  StringRef getSectionName() const;106 107  // An output section has pointers to chunks in the section, and each108  // chunk has a back pointer to an output section.109  void setOutputSectionIdx(uint16_t o) { osidx = o; }110  uint16_t getOutputSectionIdx() const { return osidx; }111 112  // Windows-specific.113  // Collect all locations that contain absolute addresses for base relocations.114  void getBaserels(std::vector<Baserel> *res);115 116  // Returns a human-readable name of this chunk. Chunks are unnamed chunks of117  // bytes, so this is used only for logging or debugging.118  StringRef getDebugName() const;119 120  // Return true if this file has the hotpatch flag set to true in the121  // S_COMPILE3 record in codeview debug info. Also returns true for some thunks122  // synthesized by the linker.123  bool isHotPatchable() const;124 125  MachineTypes getMachine() const;126  llvm::Triple::ArchType getArch() const;127  std::optional<chpe_range_type> getArm64ECRangeType() const;128 129  // ARM64EC entry thunk associated with the chunk.130  Defined *getEntryThunk() const;131  void setEntryThunk(Defined *entryThunk);132 133protected:134  Chunk(Kind k = OtherKind) : chunkKind(k), hasData(true), p2Align(0) {}135 136  const Kind chunkKind;137 138public:139  // Returns true if this has non-zero data. BSS chunks return140  // false. If false is returned, the space occupied by this chunk141  // will be filled with zeros. Corresponds to the142  // IMAGE_SCN_CNT_UNINITIALIZED_DATA section characteristic bit.143  uint8_t hasData : 1;144 145public:146  // The alignment of this chunk, stored in log2 form. The writer uses the147  // value.148  uint8_t p2Align : 7;149 150  // The output section index for this chunk. The first valid section number is151  // one.152  uint16_t osidx = 0;153 154  // The RVA of this chunk in the output. The writer sets a value.155  uint32_t rva = 0;156};157 158class NonSectionChunk : public Chunk {159public:160  virtual ~NonSectionChunk() = default;161 162  // Returns the size of this chunk (even if this is a common or BSS.)163  virtual size_t getSize() const = 0;164 165  virtual uint32_t getOutputCharacteristics() const { return 0; }166 167  // Write this chunk to a mmap'ed file, assuming Buf is pointing to168  // beginning of the file. Because this function may use RVA values169  // of other chunks for relocations, you need to set them properly170  // before calling this function.171  virtual void writeTo(uint8_t *buf) const {}172 173  // Returns the section name if this is a section chunk.174  // It is illegal to call this function on non-section chunks.175  virtual StringRef getSectionName() const {176    llvm_unreachable("unimplemented getSectionName");177  }178 179  // Windows-specific.180  // Collect all locations that contain absolute addresses for base relocations.181  virtual void getBaserels(std::vector<Baserel> *res) {}182 183  virtual MachineTypes getMachine() const { return IMAGE_FILE_MACHINE_UNKNOWN; }184 185  // Returns a human-readable name of this chunk. Chunks are unnamed chunks of186  // bytes, so this is used only for logging or debugging.187  virtual StringRef getDebugName() const { return ""; }188 189  // Verify that chunk relocations are within their ranges.190  virtual bool verifyRanges() { return true; };191 192  // If needed, extend the chunk to ensure all relocations are within the193  // allowed ranges. Return the additional space required for the extension.194  virtual uint32_t extendRanges() { return 0; };195 196  virtual Defined *getEntryThunk() const { return nullptr; };197 198  static bool classof(const Chunk *c) { return c->kind() >= OtherKind; }199 200protected:201  NonSectionChunk(Kind k = OtherKind) : Chunk(k) {}202};203 204class NonSectionCodeChunk : public NonSectionChunk {205public:206  virtual uint32_t getOutputCharacteristics() const override {207    return llvm::COFF::IMAGE_SCN_MEM_READ | llvm::COFF::IMAGE_SCN_MEM_EXECUTE;208  }209 210protected:211  NonSectionCodeChunk(Kind k = OtherKind) : NonSectionChunk(k) {}212};213 214// MinGW specific; information about one individual location in the image215// that needs to be fixed up at runtime after loading. This represents216// one individual element in the PseudoRelocTableChunk table.217class RuntimePseudoReloc {218public:219  RuntimePseudoReloc(Defined *sym, SectionChunk *target, uint32_t targetOffset,220                     int flags)221      : sym(sym), target(target), targetOffset(targetOffset), flags(flags) {}222 223  Defined *sym;224  SectionChunk *target;225  uint32_t targetOffset;226  // The Flags field contains the size of the relocation, in bits. No other227  // flags are currently defined.228  int flags;229};230 231// A chunk corresponding a section of an input file.232class SectionChunk : public Chunk {233  // Identical COMDAT Folding feature accesses section internal data.234  friend class ICF;235 236public:237  class symbol_iterator : public llvm::iterator_adaptor_base<238                              symbol_iterator, const coff_relocation *,239                              std::random_access_iterator_tag, Symbol *> {240    friend SectionChunk;241 242    ObjFile *file;243 244    symbol_iterator(ObjFile *file, const coff_relocation *i)245        : symbol_iterator::iterator_adaptor_base(i), file(file) {}246 247  public:248    symbol_iterator() = default;249 250    Symbol *operator*() const { return file->getSymbol(I->SymbolTableIndex); }251  };252 253  SectionChunk(ObjFile *file, const coff_section *header, Kind k = SectionKind);254  static bool classof(const Chunk *c) { return c->kind() <= SectionECKind; }255  size_t getSize() const { return header->SizeOfRawData; }256  ArrayRef<uint8_t> getContents() const;257  void writeTo(uint8_t *buf) const;258  MachineTypes getMachine() const;259 260  // Defend against unsorted relocations. This may be overly conservative.261  void sortRelocations();262 263  // Write and relocate a portion of the section. This is intended to be called264  // in a loop. Relocations must be sorted first.265  void writeAndRelocateSubsection(ArrayRef<uint8_t> sec,266                                  ArrayRef<uint8_t> subsec,267                                  uint32_t &nextRelocIndex, uint8_t *buf) const;268 269  uint32_t getOutputCharacteristics() const {270    return header->Characteristics & (permMask | typeMask);271  }272  StringRef getSectionName() const {273    return StringRef(sectionNameData, sectionNameSize);274  }275  void getBaserels(std::vector<Baserel> *res);276  bool isCOMDAT() const;277  void applyRelocation(uint8_t *off, const coff_relocation &rel) const;278  void applyRelX64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,279                   uint64_t p, uint64_t imageBase) const;280  void applyRelX86(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,281                   uint64_t p, uint64_t imageBase) const;282  void applyRelARM(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,283                   uint64_t p, uint64_t imageBase) const;284  void applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,285                     uint64_t p, uint64_t imageBase) const;286 287  void getRuntimePseudoRelocs(std::vector<RuntimePseudoReloc> &res);288 289  // Called if the garbage collector decides to not include this chunk290  // in a final output. It's supposed to print out a log message to stdout.291  void printDiscardedMessage() const;292 293  // Adds COMDAT associative sections to this COMDAT section. A chunk294  // and its children are treated as a group by the garbage collector.295  void addAssociative(SectionChunk *child);296 297  StringRef getDebugName() const;298 299  // True if this is a codeview debug info chunk. These will not be laid out in300  // the image. Instead they will end up in the PDB, if one is requested.301  bool isCodeView() const {302    return getSectionName() == ".debug" || getSectionName().starts_with(".debug$");303  }304 305  // True if this is a DWARF debug info or exception handling chunk.306  bool isDWARF() const {307    return getSectionName().starts_with(".debug_") || getSectionName() == ".eh_frame";308  }309 310  // Allow iteration over the bodies of this chunk's relocated symbols.311  llvm::iterator_range<symbol_iterator> symbols() const {312    return llvm::make_range(symbol_iterator(file, relocsData),313                            symbol_iterator(file, relocsData + relocsSize));314  }315 316  ArrayRef<coff_relocation> getRelocs() const {317    return llvm::ArrayRef(relocsData, relocsSize);318  }319 320  // Reloc setter used by ARM range extension thunk insertion.321  void setRelocs(ArrayRef<coff_relocation> newRelocs) {322    relocsData = newRelocs.data();323    relocsSize = newRelocs.size();324    assert(relocsSize == newRelocs.size() && "reloc size truncation");325  }326 327  // Single linked list iterator for associated comdat children.328  class AssociatedIterator329      : public llvm::iterator_facade_base<330            AssociatedIterator, std::forward_iterator_tag, SectionChunk> {331  public:332    AssociatedIterator() = default;333    AssociatedIterator(SectionChunk *head) : cur(head) {}334    bool operator==(const AssociatedIterator &r) const { return cur == r.cur; }335    // FIXME: Wrong const-ness, but it makes filter ranges work.336    SectionChunk &operator*() const { return *cur; }337    SectionChunk &operator*() { return *cur; }338    AssociatedIterator &operator++() {339      cur = cur->assocChildren;340      return *this;341    }342 343  private:344    SectionChunk *cur = nullptr;345  };346 347  // Allow iteration over the associated child chunks for this section.348  llvm::iterator_range<AssociatedIterator> children() const {349    // Associated sections do not have children. The assocChildren field is350    // part of the parent's list of children.351    bool isAssoc = selection == llvm::COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE;352    return llvm::make_range(353        AssociatedIterator(isAssoc ? nullptr : assocChildren),354        AssociatedIterator(nullptr));355  }356 357  // The section ID this chunk belongs to in its Obj.358  uint32_t getSectionNumber() const;359 360  ArrayRef<uint8_t> consumeDebugMagic();361 362  static ArrayRef<uint8_t> consumeDebugMagic(ArrayRef<uint8_t> data,363                                             StringRef sectionName);364 365  static SectionChunk *findByName(ArrayRef<SectionChunk *> sections,366                                  StringRef name);367 368  // The file that this chunk was created from.369  ObjFile *file;370 371  // Pointer to the COFF section header in the input file.372  const coff_section *header;373 374  // The COMDAT leader symbol if this is a COMDAT chunk.375  DefinedRegular *sym = nullptr;376 377  // The CRC of the contents as described in the COFF spec 4.5.5.378  // Auxiliary Format 5: Section Definitions. Used for ICF.379  uint32_t checksum = 0;380 381  // Used by the garbage collector.382  bool live;383 384  // Whether this section needs to be kept distinct from other sections during385  // ICF. This is set by the driver using address-significance tables.386  bool keepUnique = false;387 388  // The COMDAT selection if this is a COMDAT chunk.389  llvm::COFF::COMDATType selection = (llvm::COFF::COMDATType)0;390 391  // A pointer pointing to a replacement for this chunk.392  // Initially it points to "this" object. If this chunk is merged393  // with other chunk by ICF, it points to another chunk,394  // and this chunk is considered as dead.395  SectionChunk *repl;396 397private:398  SectionChunk *assocChildren = nullptr;399 400  // Used for ICF (Identical COMDAT Folding)401  void replace(SectionChunk *other);402  uint32_t eqClass[2] = {0, 0};403 404  // Relocations for this section. Size is stored below.405  const coff_relocation *relocsData;406 407  // Section name string. Size is stored below.408  const char *sectionNameData;409 410  uint32_t relocsSize = 0;411  uint32_t sectionNameSize = 0;412};413 414// A section chunk corresponding a section of an EC input file.415class SectionChunkEC final : public SectionChunk {416public:417  static bool classof(const Chunk *c) { return c->kind() == SectionECKind; }418 419  SectionChunkEC(ObjFile *file, const coff_section *header)420      : SectionChunk(file, header, SectionECKind) {}421  Defined *entryThunk = nullptr;422};423 424// Inline methods to implement faux-virtual dispatch for SectionChunk.425 426inline size_t Chunk::getSize() const {427  if (isa<SectionChunk>(this))428    return static_cast<const SectionChunk *>(this)->getSize();429  return static_cast<const NonSectionChunk *>(this)->getSize();430}431 432inline uint32_t Chunk::getOutputCharacteristics() const {433  if (isa<SectionChunk>(this))434    return static_cast<const SectionChunk *>(this)->getOutputCharacteristics();435  return static_cast<const NonSectionChunk *>(this)->getOutputCharacteristics();436}437 438inline void Chunk::writeTo(uint8_t *buf) const {439  if (isa<SectionChunk>(this))440    static_cast<const SectionChunk *>(this)->writeTo(buf);441  else442    static_cast<const NonSectionChunk *>(this)->writeTo(buf);443}444 445inline StringRef Chunk::getSectionName() const {446  if (isa<SectionChunk>(this))447    return static_cast<const SectionChunk *>(this)->getSectionName();448  return static_cast<const NonSectionChunk *>(this)->getSectionName();449}450 451inline void Chunk::getBaserels(std::vector<Baserel> *res) {452  if (isa<SectionChunk>(this))453    static_cast<SectionChunk *>(this)->getBaserels(res);454  else455    static_cast<NonSectionChunk *>(this)->getBaserels(res);456}457 458inline StringRef Chunk::getDebugName() const {459  if (isa<SectionChunk>(this))460    return static_cast<const SectionChunk *>(this)->getDebugName();461  return static_cast<const NonSectionChunk *>(this)->getDebugName();462}463 464inline MachineTypes Chunk::getMachine() const {465  if (isa<SectionChunk>(this))466    return static_cast<const SectionChunk *>(this)->getMachine();467  return static_cast<const NonSectionChunk *>(this)->getMachine();468}469 470inline llvm::Triple::ArchType Chunk::getArch() const {471  return llvm::getMachineArchType(getMachine());472}473 474inline std::optional<chpe_range_type> Chunk::getArm64ECRangeType() const {475  // Data sections don't need codemap entries.476  if (!(getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE))477    return std::nullopt;478 479  switch (getMachine()) {480  case AMD64:481    return chpe_range_type::Amd64;482  case ARM64EC:483    return chpe_range_type::Arm64EC;484  default:485    return chpe_range_type::Arm64;486  }487}488 489// This class is used to implement an lld-specific feature (not implemented in490// MSVC) that minimizes the output size by finding string literals sharing tail491// parts and merging them.492//493// If string tail merging is enabled and a section is identified as containing a494// string literal, it is added to a MergeChunk with an appropriate alignment.495// The MergeChunk then tail merges the strings using the StringTableBuilder496// class and assigns RVAs and section offsets to each of the member chunks based497// on the offsets assigned by the StringTableBuilder.498class MergeChunk : public NonSectionChunk {499public:500  MergeChunk(uint32_t alignment);501  static void addSection(COFFLinkerContext &ctx, SectionChunk *c);502  void finalizeContents();503  void assignSubsectionRVAs();504 505  uint32_t getOutputCharacteristics() const override;506  StringRef getSectionName() const override { return ".rdata"; }507  size_t getSize() const override;508  void writeTo(uint8_t *buf) const override;509 510  std::vector<SectionChunk *> sections;511 512private:513  llvm::StringTableBuilder builder;514  bool finalized = false;515};516 517// A chunk for common symbols. Common chunks don't have actual data.518class CommonChunk : public NonSectionChunk {519public:520  CommonChunk(const COFFSymbolRef sym);521  size_t getSize() const override { return sym.getValue(); }522  uint32_t getOutputCharacteristics() const override;523  StringRef getSectionName() const override { return ".bss"; }524 525  bool live;526 527private:528  const COFFSymbolRef sym;529};530 531// A chunk for linker-created strings.532class StringChunk : public NonSectionChunk {533public:534  explicit StringChunk(StringRef s) : str(s) {}535  size_t getSize() const override { return str.size() + 1; }536  void writeTo(uint8_t *buf) const override;537 538private:539  StringRef str;540};541 542static const uint8_t importThunkX86[] = {543    0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // JMP *0x0544};545 546static const uint8_t importThunkARM[] = {547    0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0548    0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0549    0xdc, 0xf8, 0x00, 0xf0, // ldr.w pc, [ip]550};551 552static const uint8_t importThunkARM64[] = {553    0x10, 0x00, 0x00, 0x90, // adrp x16, #0554    0x10, 0x02, 0x40, 0xf9, // ldr  x16, [x16]555    0x00, 0x02, 0x1f, 0xd6, // br   x16556};557 558static const uint8_t importThunkARM64EC[] = {559    0x0b, 0x00, 0x00, 0x90, // adrp x11, 0x0560    0x6b, 0x01, 0x40, 0xf9, // ldr  x11, [x11]561    0x0a, 0x00, 0x00, 0x90, // adrp x10, 0x0562    0x4a, 0x01, 0x00, 0x91, // add  x10, x10, #0x0563    0x00, 0x00, 0x00, 0x14  // b    0x0564};565 566// Windows-specific.567// A chunk for DLL import jump table entry. In a final output, its568// contents will be a JMP instruction to some __imp_ symbol.569class ImportThunkChunk : public NonSectionCodeChunk {570public:571  ImportThunkChunk(COFFLinkerContext &ctx, Defined *s);572  static bool classof(const Chunk *c) { return c->kind() == ImportThunkKind; }573 574  // We track the usage of the thunk symbol separately from the import file575  // to avoid generating unnecessary thunks.576  bool live;577 578protected:579  Defined *impSymbol;580  COFFLinkerContext &ctx;581};582 583class ImportThunkChunkX64 : public ImportThunkChunk {584public:585  explicit ImportThunkChunkX64(COFFLinkerContext &ctx, Defined *s);586  size_t getSize() const override { return sizeof(importThunkX86); }587  void writeTo(uint8_t *buf) const override;588  MachineTypes getMachine() const override { return AMD64; }589};590 591class ImportThunkChunkX86 : public ImportThunkChunk {592public:593  explicit ImportThunkChunkX86(COFFLinkerContext &ctx, Defined *s)594      : ImportThunkChunk(ctx, s) {}595  size_t getSize() const override { return sizeof(importThunkX86); }596  void getBaserels(std::vector<Baserel> *res) override;597  void writeTo(uint8_t *buf) const override;598  MachineTypes getMachine() const override { return I386; }599};600 601class ImportThunkChunkARM : public ImportThunkChunk {602public:603  explicit ImportThunkChunkARM(COFFLinkerContext &ctx, Defined *s)604      : ImportThunkChunk(ctx, s) {605    setAlignment(2);606  }607  size_t getSize() const override { return sizeof(importThunkARM); }608  void getBaserels(std::vector<Baserel> *res) override;609  void writeTo(uint8_t *buf) const override;610  MachineTypes getMachine() const override { return ARMNT; }611};612 613class ImportThunkChunkARM64 : public ImportThunkChunk {614public:615  explicit ImportThunkChunkARM64(COFFLinkerContext &ctx, Defined *s,616                                 MachineTypes machine)617      : ImportThunkChunk(ctx, s), machine(machine) {618    setAlignment(4);619  }620  size_t getSize() const override { return sizeof(importThunkARM64); }621  void writeTo(uint8_t *buf) const override;622  MachineTypes getMachine() const override { return machine; }623 624private:625  MachineTypes machine;626};627 628// ARM64EC __impchk_* thunk implementation.629// Performs an indirect call to an imported function pointer630// using the __icall_helper_arm64ec helper function.631class ImportThunkChunkARM64EC : public ImportThunkChunk {632public:633  explicit ImportThunkChunkARM64EC(ImportFile *file);634  size_t getSize() const override;635  MachineTypes getMachine() const override { return ARM64EC; }636  void writeTo(uint8_t *buf) const override;637  bool verifyRanges() override;638  uint32_t extendRanges() override;639 640  Defined *exitThunk = nullptr;641  Defined *sym = nullptr;642  bool extended = false;643 644private:645  ImportFile *file;646};647 648class RangeExtensionThunkARM : public NonSectionCodeChunk {649public:650  explicit RangeExtensionThunkARM(COFFLinkerContext &ctx, Defined *t)651      : target(t), ctx(ctx) {652    setAlignment(2);653  }654  size_t getSize() const override;655  void writeTo(uint8_t *buf) const override;656  MachineTypes getMachine() const override { return ARMNT; }657 658  Defined *target;659 660private:661  COFFLinkerContext &ctx;662};663 664// A ragnge extension thunk used for both ARM64EC and ARM64 machine types.665class RangeExtensionThunkARM64 : public NonSectionCodeChunk {666public:667  explicit RangeExtensionThunkARM64(MachineTypes machine, Defined *t)668      : target(t), machine(machine) {669    setAlignment(4);670    assert(llvm::COFF::isAnyArm64(machine));671  }672  size_t getSize() const override;673  void writeTo(uint8_t *buf) const override;674  MachineTypes getMachine() const override { return machine; }675 676  Defined *target;677 678private:679  MachineTypes machine;680};681 682// A chunk used to guarantee the same address for a function in both views of683// a hybrid image. Similar to RangeExtensionThunkARM64 chunks, it calls the684// target symbol using a BR instruction. It also contains an entry thunk for EC685// compatibility and additional ARM64X relocations that swap targets between686// views.687class SameAddressThunkARM64EC : public RangeExtensionThunkARM64 {688public:689  explicit SameAddressThunkARM64EC(Defined *t, Defined *hybridTarget,690                                   Defined *entryThunk)691      : RangeExtensionThunkARM64(ARM64EC, t), hybridTarget(hybridTarget),692        entryThunk(entryThunk) {}693 694  Defined *getEntryThunk() const override { return entryThunk; }695  void setDynamicRelocs(COFFLinkerContext &ctx) const;696 697private:698  Defined *hybridTarget;699  Defined *entryThunk;700};701 702// Windows-specific.703// See comments for DefinedLocalImport class.704class LocalImportChunk : public NonSectionChunk {705public:706  explicit LocalImportChunk(COFFLinkerContext &ctx, Defined *s);707  size_t getSize() const override;708  void getBaserels(std::vector<Baserel> *res) override;709  void writeTo(uint8_t *buf) const override;710 711private:712  Defined *sym;713  COFFLinkerContext &ctx;714};715 716// Duplicate RVAs are not allowed in RVA tables, so unique symbols by chunk and717// offset into the chunk. Order does not matter as the RVA table will be sorted718// later.719struct ChunkAndOffset {720  Chunk *inputChunk;721  uint32_t offset;722 723  struct DenseMapInfo {724    static ChunkAndOffset getEmptyKey() {725      return {llvm::DenseMapInfo<Chunk *>::getEmptyKey(), 0};726    }727    static ChunkAndOffset getTombstoneKey() {728      return {llvm::DenseMapInfo<Chunk *>::getTombstoneKey(), 0};729    }730    static unsigned getHashValue(const ChunkAndOffset &co) {731      return llvm::DenseMapInfo<std::pair<Chunk *, uint32_t>>::getHashValue(732          {co.inputChunk, co.offset});733    }734    static bool isEqual(const ChunkAndOffset &lhs, const ChunkAndOffset &rhs) {735      return lhs.inputChunk == rhs.inputChunk && lhs.offset == rhs.offset;736    }737  };738};739 740using SymbolRVASet = llvm::DenseSet<ChunkAndOffset>;741 742// Table which contains symbol RVAs. Used for /safeseh and /guard:cf.743class RVATableChunk : public NonSectionChunk {744public:745  explicit RVATableChunk(SymbolRVASet s) : syms(std::move(s)) {}746  size_t getSize() const override { return syms.size() * 4; }747  void writeTo(uint8_t *buf) const override;748 749private:750  SymbolRVASet syms;751};752 753// Table which contains symbol RVAs with flags. Used for /guard:ehcont.754class RVAFlagTableChunk : public NonSectionChunk {755public:756  explicit RVAFlagTableChunk(SymbolRVASet s) : syms(std::move(s)) {}757  size_t getSize() const override { return syms.size() * 5; }758  void writeTo(uint8_t *buf) const override;759 760private:761  SymbolRVASet syms;762};763 764// Windows-specific.765// This class represents a block in .reloc section.766// See the PE/COFF spec 5.6 for details.767class BaserelChunk : public NonSectionChunk {768public:769  BaserelChunk(uint32_t page, Baserel *begin, Baserel *end);770  size_t getSize() const override { return data.size(); }771  void writeTo(uint8_t *buf) const override;772 773private:774  std::vector<uint8_t> data;775};776 777class Baserel {778public:779  Baserel(uint32_t v, uint8_t ty) : rva(v), type(ty) {}780  explicit Baserel(uint32_t v, llvm::COFF::MachineTypes machine)781      : Baserel(v, getDefaultType(machine)) {}782  static uint8_t getDefaultType(llvm::COFF::MachineTypes machine);783 784  uint32_t rva;785  uint8_t type;786};787 788// This is a placeholder Chunk, to allow attaching a DefinedSynthetic to a789// specific place in a section, without any data. This is used for the MinGW790// specific symbol __RUNTIME_PSEUDO_RELOC_LIST_END__, even though the concept791// of an empty chunk isn't MinGW specific.792class EmptyChunk : public NonSectionChunk {793public:794  EmptyChunk() {}795  size_t getSize() const override { return 0; }796  void writeTo(uint8_t *buf) const override {}797};798 799class ECCodeMapEntry {800public:801  ECCodeMapEntry(Chunk *first, Chunk *last, chpe_range_type type)802      : first(first), last(last), type(type) {}803  Chunk *first;804  Chunk *last;805  chpe_range_type type;806};807 808// This is a chunk containing CHPE code map on EC targets. It's a table809// of address ranges and their types.810class ECCodeMapChunk : public NonSectionChunk {811public:812  ECCodeMapChunk(std::vector<ECCodeMapEntry> &map) : map(map) {}813  size_t getSize() const override;814  void writeTo(uint8_t *buf) const override;815 816private:817  std::vector<ECCodeMapEntry> &map;818};819 820class CHPECodeRangesChunk : public NonSectionChunk {821public:822  CHPECodeRangesChunk(std::vector<std::pair<Chunk *, Defined *>> &exportThunks)823      : exportThunks(exportThunks) {}824  size_t getSize() const override;825  void writeTo(uint8_t *buf) const override;826 827private:828  std::vector<std::pair<Chunk *, Defined *>> &exportThunks;829};830 831class CHPERedirectionChunk : public NonSectionChunk {832public:833  CHPERedirectionChunk(std::vector<std::pair<Chunk *, Defined *>> &exportThunks)834      : exportThunks(exportThunks) {}835  size_t getSize() const override;836  void writeTo(uint8_t *buf) const override;837 838private:839  std::vector<std::pair<Chunk *, Defined *>> &exportThunks;840};841 842static const uint8_t ECExportThunkCode[] = {843    0x48, 0x8b, 0xc4,          // movq    %rsp, %rax844    0x48, 0x89, 0x58, 0x20,    // movq    %rbx, 0x20(%rax)845    0x55,                      // pushq   %rbp846    0x5d,                      // popq    %rbp847    0xe9, 0,    0,    0,    0, // jmp *0x0848    0xcc,                      // int3849    0xcc                       // int3850};851 852class ECExportThunkChunk : public NonSectionCodeChunk {853public:854  explicit ECExportThunkChunk(Defined *targetSym)855      : NonSectionCodeChunk(ECExportThunkKind), target(targetSym) {}856  static bool classof(const Chunk *c) { return c->kind() == ECExportThunkKind; }857 858  size_t getSize() const override { return sizeof(ECExportThunkCode); };859  void writeTo(uint8_t *buf) const override;860  MachineTypes getMachine() const override { return AMD64; }861 862  Defined *target;863};864 865// ARM64X relocation value, potentially relative to a symbol.866class Arm64XRelocVal {867public:868  Arm64XRelocVal(uint64_t value = 0) : value(value) {}869  Arm64XRelocVal(Defined *sym, int32_t offset = 0) : sym(sym), value(offset) {}870  Arm64XRelocVal(const Chunk *chunk, int32_t offset = 0)871      : chunk(chunk), value(offset) {}872  uint64_t get() const;873 874private:875  Defined *sym = nullptr;876  const Chunk *chunk = nullptr;877  uint64_t value;878};879 880// ARM64X entry for dynamic relocations.881class Arm64XDynamicRelocEntry {882public:883  Arm64XDynamicRelocEntry(llvm::COFF::Arm64XFixupType type, uint8_t size,884                          Arm64XRelocVal offset, Arm64XRelocVal value)885      : offset(offset), value(value), type(type), size(size) {}886 887  size_t getSize() const;888  void writeTo(uint8_t *buf) const;889 890  Arm64XRelocVal offset;891  Arm64XRelocVal value;892 893private:894  llvm::COFF::Arm64XFixupType type;895  uint8_t size;896};897 898// Dynamic relocation chunk containing ARM64X relocations for the hybrid image.899class DynamicRelocsChunk : public NonSectionChunk {900public:901  DynamicRelocsChunk() {}902  size_t getSize() const override { return size; }903  void writeTo(uint8_t *buf) const override;904  void finalize();905 906  void add(llvm::COFF::Arm64XFixupType type, uint8_t size,907           Arm64XRelocVal offset, Arm64XRelocVal value = Arm64XRelocVal()) {908    arm64xRelocs.emplace_back(type, size, offset, value);909  }910 911  void set(Arm64XRelocVal offset, Arm64XRelocVal value);912 913private:914  std::vector<Arm64XDynamicRelocEntry> arm64xRelocs;915  size_t size;916};917 918// MinGW specific, for the "automatic import of variables from DLLs" feature.919// This provides the table of runtime pseudo relocations, for variable920// references that turned out to need to be imported from a DLL even though921// the reference didn't use the dllimport attribute. The MinGW runtime will922// process this table after loading, before handling control over to user923// code.924class PseudoRelocTableChunk : public NonSectionChunk {925public:926  PseudoRelocTableChunk(std::vector<RuntimePseudoReloc> &relocs)927      : relocs(std::move(relocs)) {928    setAlignment(4);929  }930  size_t getSize() const override;931  void writeTo(uint8_t *buf) const override;932 933private:934  std::vector<RuntimePseudoReloc> relocs;935};936 937// MinGW specific. A Chunk that contains one pointer-sized absolute value.938class AbsolutePointerChunk : public NonSectionChunk {939public:940  AbsolutePointerChunk(SymbolTable &symtab, uint64_t value)941      : value(value), symtab(symtab) {942    setAlignment(getSize());943  }944  size_t getSize() const override;945  void writeTo(uint8_t *buf) const override;946  MachineTypes getMachine() const override;947 948private:949  uint64_t value;950  SymbolTable &symtab;951};952 953// Return true if this file has the hotpatch flag set to true in the S_COMPILE3954// record in codeview debug info. Also returns true for some thunks synthesized955// by the linker.956inline bool Chunk::isHotPatchable() const {957  if (auto *sc = dyn_cast<SectionChunk>(this))958    return sc->file->hotPatchable;959  else if (isa<ImportThunkChunk>(this))960    return true;961  return false;962}963 964inline Defined *Chunk::getEntryThunk() const {965  if (auto *c = dyn_cast<const SectionChunkEC>(this))966    return c->entryThunk;967  if (auto *c = dyn_cast<const NonSectionChunk>(this))968    return c->getEntryThunk();969  return nullptr;970}971 972inline void Chunk::setEntryThunk(Defined *entryThunk) {973  if (auto c = dyn_cast<SectionChunkEC>(this))974    c->entryThunk = entryThunk;975}976 977void applyMOV32T(uint8_t *off, uint32_t v);978void applyBranch24T(uint8_t *off, int32_t v);979 980void applyArm64Addr(uint8_t *off, uint64_t s, uint64_t p, int shift);981void applyArm64Imm(uint8_t *off, uint64_t imm, uint32_t rangeLimit);982void applyArm64Branch26(uint8_t *off, int64_t v);983 984// Convenience class for initializing a coff_section with specific flags.985class FakeSection {986public:987  FakeSection(int c) { section.Characteristics = c; }988 989  coff_section section;990};991 992// Convenience class for initializing a SectionChunk with specific flags.993class FakeSectionChunk {994public:995  FakeSectionChunk(const coff_section *section) : chunk(nullptr, section) {996    // Comdats from LTO files can't be fully treated as regular comdats997    // at this point; we don't know what size or contents they are going to998    // have, so we can't do proper checking of such aspects of them.999    chunk.selection = llvm::COFF::IMAGE_COMDAT_SELECT_ANY;1000  }1001 1002  SectionChunk chunk;1003};1004 1005} // namespace lld::coff1006 1007namespace llvm {1008template <>1009struct DenseMapInfo<lld::coff::ChunkAndOffset>1010    : lld::coff::ChunkAndOffset::DenseMapInfo {};1011}1012 1013#endif1014