aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Transforms/Utils/LoopVersioning.cpp
blob: 548b0f3c55f0437d14a4b77cd2faf6e5fe254c38 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
//===- LoopVersioning.cpp - Utility to version a loop ---------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines a utility class to perform loop versioning.  The versioned
// loop speculates that otherwise may-aliasing memory accesses don't overlap and
// emits checks to prove this.
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Utils/LoopVersioning.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/InstSimplifyFolder.h"
#include "llvm/Analysis/LoopAccessAnalysis.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h"

using namespace llvm;

#define DEBUG_TYPE "loop-versioning"

static cl::opt<bool>
    AnnotateNoAlias("loop-version-annotate-no-alias", cl::init(true),
                    cl::Hidden,
                    cl::desc("Add no-alias annotation for instructions that "
                             "are disambiguated by memchecks"));

LoopVersioning::LoopVersioning(const LoopAccessInfo &LAI,
                               ArrayRef<RuntimePointerCheck> Checks, Loop *L,
                               LoopInfo *LI, DominatorTree *DT,
                               ScalarEvolution *SE)
    : VersionedLoop(L), AliasChecks(Checks.begin(), Checks.end()),
      Preds(LAI.getPSE().getPredicate()), LAI(LAI), LI(LI), DT(DT),
      SE(SE) {
}

void LoopVersioning::versionLoop(
    const SmallVectorImpl<Instruction *> &DefsUsedOutside) {
  assert(VersionedLoop->getUniqueExitBlock() && "No single exit block");
  assert(VersionedLoop->isLoopSimplifyForm() &&
         "Loop is not in loop-simplify form");

  Value *MemRuntimeCheck;
  Value *SCEVRuntimeCheck;
  Value *RuntimeCheck = nullptr;

  // Add the memcheck in the original preheader (this is empty initially).
  BasicBlock *RuntimeCheckBB = VersionedLoop->getLoopPreheader();
  const auto &RtPtrChecking = *LAI.getRuntimePointerChecking();

  SCEVExpander Exp2(*RtPtrChecking.getSE(),
                    VersionedLoop->getHeader()->getModule()->getDataLayout(),
                    "induction");
  MemRuntimeCheck = addRuntimeChecks(RuntimeCheckBB->getTerminator(),
                                     VersionedLoop, AliasChecks, Exp2);

  SCEVExpander Exp(*SE, RuntimeCheckBB->getModule()->getDataLayout(),
                   "scev.check");
  SCEVRuntimeCheck =
      Exp.expandCodeForPredicate(&Preds, RuntimeCheckBB->getTerminator());

  IRBuilder<InstSimplifyFolder> Builder(
      RuntimeCheckBB->getContext(),
      InstSimplifyFolder(RuntimeCheckBB->getModule()->getDataLayout()));
  if (MemRuntimeCheck && SCEVRuntimeCheck) {
    Builder.SetInsertPoint(RuntimeCheckBB->getTerminator());
    RuntimeCheck =
        Builder.CreateOr(MemRuntimeCheck, SCEVRuntimeCheck, "lver.safe");
  } else
    RuntimeCheck = MemRuntimeCheck ? MemRuntimeCheck : SCEVRuntimeCheck;

  assert(RuntimeCheck && "called even though we don't need "
                         "any runtime checks");

  // Rename the block to make the IR more readable.
  RuntimeCheckBB->setName(VersionedLoop->getHeader()->getName() +
                          ".lver.check");

  // Create empty preheader for the loop (and after cloning for the
  // non-versioned loop).
  BasicBlock *PH =
      SplitBlock(RuntimeCheckBB, RuntimeCheckBB->getTerminator(), DT, LI,
                 nullptr, VersionedLoop->getHeader()->getName() + ".ph");

  // Clone the loop including the preheader.
  //
  // FIXME: This does not currently preserve SimplifyLoop because the exit
  // block is a join between the two loops.
  SmallVector<BasicBlock *, 8> NonVersionedLoopBlocks;
  NonVersionedLoop =
      cloneLoopWithPreheader(PH, RuntimeCheckBB, VersionedLoop, VMap,
                             ".lver.orig", LI, DT, NonVersionedLoopBlocks);
  remapInstructionsInBlocks(NonVersionedLoopBlocks, VMap);

  // Insert the conditional branch based on the result of the memchecks.
  Instruction *OrigTerm = RuntimeCheckBB->getTerminator();
  Builder.SetInsertPoint(OrigTerm);
  Builder.CreateCondBr(RuntimeCheck, NonVersionedLoop->getLoopPreheader(),
                       VersionedLoop->getLoopPreheader());
  OrigTerm->eraseFromParent();

  // The loops merge in the original exit block.  This is now dominated by the
  // memchecking block.
  DT->changeImmediateDominator(VersionedLoop->getExitBlock(), RuntimeCheckBB);

  // Adds the necessary PHI nodes for the versioned loops based on the
  // loop-defined values used outside of the loop.
  addPHINodes(DefsUsedOutside);
  formDedicatedExitBlocks(NonVersionedLoop, DT, LI, nullptr, true);
  formDedicatedExitBlocks(VersionedLoop, DT, LI, nullptr, true);
  assert(NonVersionedLoop->isLoopSimplifyForm() &&
         VersionedLoop->isLoopSimplifyForm() &&
         "The versioned loops should be in simplify form.");
}

void LoopVersioning::addPHINodes(
    const SmallVectorImpl<Instruction *> &DefsUsedOutside) {
  BasicBlock *PHIBlock = VersionedLoop->getExitBlock();
  assert(PHIBlock && "No single successor to loop exit block");
  PHINode *PN;

  // First add a single-operand PHI for each DefsUsedOutside if one does not
  // exists yet.
  for (auto *Inst : DefsUsedOutside) {
    // See if we have a single-operand PHI with the value defined by the
    // original loop.
    for (auto I = PHIBlock->begin(); (PN = dyn_cast<PHINode>(I)); ++I) {
      if (PN->getIncomingValue(0) == Inst) {
        SE->forgetValue(PN);
        break;
      }
    }
    // If not create it.
    if (!PN) {
      PN = PHINode::Create(Inst->getType(), 2, Inst->getName() + ".lver");
      PN->insertBefore(PHIBlock->begin());
      SmallVector<User*, 8> UsersToUpdate;
      for (User *U : Inst->users())
        if (!VersionedLoop->contains(cast<Instruction>(U)->getParent()))
          UsersToUpdate.push_back(U);
      for (User *U : UsersToUpdate)
        U->replaceUsesOfWith(Inst, PN);
      PN->addIncoming(Inst, VersionedLoop->getExitingBlock());
    }
  }

  // Then for each PHI add the operand for the edge from the cloned loop.
  for (auto I = PHIBlock->begin(); (PN = dyn_cast<PHINode>(I)); ++I) {
    assert(PN->getNumOperands() == 1 &&
           "Exit block should only have on predecessor");

    // If the definition was cloned used that otherwise use the same value.
    Value *ClonedValue = PN->getIncomingValue(0);
    auto Mapped = VMap.find(ClonedValue);
    if (Mapped != VMap.end())
      ClonedValue = Mapped->second;

    PN->addIncoming(ClonedValue, NonVersionedLoop->getExitingBlock());
  }
}

void LoopVersioning::prepareNoAliasMetadata() {
  // We need to turn the no-alias relation between pointer checking groups into
  // no-aliasing annotations between instructions.
  //
  // We accomplish this by mapping each pointer checking group (a set of
  // pointers memchecked together) to an alias scope and then also mapping each
  // group to the list of scopes it can't alias.

  const RuntimePointerChecking *RtPtrChecking = LAI.getRuntimePointerChecking();
  LLVMContext &Context = VersionedLoop->getHeader()->getContext();

  // First allocate an aliasing scope for each pointer checking group.
  //
  // While traversing through the checking groups in the loop, also create a
  // reverse map from pointers to the pointer checking group they were assigned
  // to.
  MDBuilder MDB(Context);
  MDNode *Domain = MDB.createAnonymousAliasScopeDomain("LVerDomain");

  for (const auto &Group : RtPtrChecking->CheckingGroups) {
    GroupToScope[&Group] = MDB.createAnonymousAliasScope(Domain);

    for (unsigned PtrIdx : Group.Members)
      PtrToGroup[RtPtrChecking->getPointerInfo(PtrIdx).PointerValue] = &Group;
  }

  // Go through the checks and for each pointer group, collect the scopes for
  // each non-aliasing pointer group.
  DenseMap<const RuntimeCheckingPtrGroup *, SmallVector<Metadata *, 4>>
      GroupToNonAliasingScopes;

  for (const auto &Check : AliasChecks)
    GroupToNonAliasingScopes[Check.first].push_back(GroupToScope[Check.second]);

  // Finally, transform the above to actually map to scope list which is what
  // the metadata uses.

  for (const auto &Pair : GroupToNonAliasingScopes)
    GroupToNonAliasingScopeList[Pair.first] = MDNode::get(Context, Pair.second);
}

void LoopVersioning::annotateLoopWithNoAlias() {
  if (!AnnotateNoAlias)
    return;

  // First prepare the maps.
  prepareNoAliasMetadata();

  // Add the scope and no-alias metadata to the instructions.
  for (Instruction *I : LAI.getDepChecker().getMemoryInstructions()) {
    annotateInstWithNoAlias(I);
  }
}

void LoopVersioning::annotateInstWithNoAlias(Instruction *VersionedInst,
                                             const Instruction *OrigInst) {
  if (!AnnotateNoAlias)
    return;

  LLVMContext &Context = VersionedLoop->getHeader()->getContext();
  const Value *Ptr = isa<LoadInst>(OrigInst)
                         ? cast<LoadInst>(OrigInst)->getPointerOperand()
                         : cast<StoreInst>(OrigInst)->getPointerOperand();

  // Find the group for the pointer and then add the scope metadata.
  auto Group = PtrToGroup.find(Ptr);
  if (Group != PtrToGroup.end()) {
    VersionedInst->setMetadata(
        LLVMContext::MD_alias_scope,
        MDNode::concatenate(
            VersionedInst->getMetadata(LLVMContext::MD_alias_scope),
            MDNode::get(Context, GroupToScope[Group->second])));

    // Add the no-alias metadata.
    auto NonAliasingScopeList = GroupToNonAliasingScopeList.find(Group->second);
    if (NonAliasingScopeList != GroupToNonAliasingScopeList.end())
      VersionedInst->setMetadata(
          LLVMContext::MD_noalias,
          MDNode::concatenate(
              VersionedInst->getMetadata(LLVMContext::MD_noalias),
              NonAliasingScopeList->second));
  }
}

namespace {
bool runImpl(LoopInfo *LI, LoopAccessInfoManager &LAIs, DominatorTree *DT,
             ScalarEvolution *SE) {
  // Build up a worklist of inner-loops to version. This is necessary as the
  // act of versioning a loop creates new loops and can invalidate iterators
  // across the loops.
  SmallVector<Loop *, 8> Worklist;

  for (Loop *TopLevelLoop : *LI)
    for (Loop *L : depth_first(TopLevelLoop))
      // We only handle inner-most loops.
      if (L->isInnermost())
        Worklist.push_back(L);

  // Now walk the identified inner loops.
  bool Changed = false;
  for (Loop *L : Worklist) {
    if (!L->isLoopSimplifyForm() || !L->isRotatedForm() ||
        !L->getExitingBlock())
      continue;
    const LoopAccessInfo &LAI = LAIs.getInfo(*L);
    if (!LAI.hasConvergentOp() &&
        (LAI.getNumRuntimePointerChecks() ||
         !LAI.getPSE().getPredicate().isAlwaysTrue())) {
      LoopVersioning LVer(LAI, LAI.getRuntimePointerChecking()->getChecks(), L,
                          LI, DT, SE);
      LVer.versionLoop();
      LVer.annotateLoopWithNoAlias();
      Changed = true;
      LAIs.clear();
    }
  }

  return Changed;
}
}

PreservedAnalyses LoopVersioningPass::run(Function &F,
                                          FunctionAnalysisManager &AM) {
  auto &SE = AM.getResult<ScalarEvolutionAnalysis>(F);
  auto &LI = AM.getResult<LoopAnalysis>(F);
  LoopAccessInfoManager &LAIs = AM.getResult<LoopAccessAnalysis>(F);
  auto &DT = AM.getResult<DominatorTreeAnalysis>(F);

  if (runImpl(&LI, LAIs, &DT, &SE))
    return PreservedAnalyses::none();
  return PreservedAnalyses::all();
}
an> return Addr; } const coff_section *COFFObjectFile::toSec(DataRefImpl Ref) const { const coff_section *Addr = reinterpret_cast<const coff_section*>(Ref.p); # ifndef NDEBUG // Verify that the section points to a valid entry in the section table. if (Addr < SectionTable || Addr >= (SectionTable + COFFHeader->NumberOfSections)) report_fatal_error("Section was outside of section table."); uintptr_t Offset = uintptr_t(Addr) - uintptr_t(SectionTable); assert(Offset % sizeof(coff_section) == 0 && "Section did not point to the beginning of a section"); # endif return Addr; } void COFFObjectFile::moveSymbolNext(DataRefImpl &Ref) const { const coff_symbol *Symb = toSymb(Ref); Symb += 1 + Symb->NumberOfAuxSymbols; Ref.p = reinterpret_cast<uintptr_t>(Symb); } std::error_code COFFObjectFile::getSymbolName(DataRefImpl Ref, StringRef &Result) const { const coff_symbol *Symb = toSymb(Ref); return getSymbolName(Symb, Result); } std::error_code COFFObjectFile::getSymbolAddress(DataRefImpl Ref, uint64_t &Result) const { const coff_symbol *Symb = toSymb(Ref); const coff_section *Section = nullptr; if (std::error_code EC = getSection(Symb->SectionNumber, Section)) return EC; if (Symb->SectionNumber == COFF::IMAGE_SYM_UNDEFINED) Result = UnknownAddressOrSize; else if (Section) Result = Section->VirtualAddress + Symb->Value; else Result = Symb->Value; return object_error::success; } std::error_code COFFObjectFile::getSymbolType(DataRefImpl Ref, SymbolRef::Type &Result) const { const coff_symbol *Symb = toSymb(Ref); Result = SymbolRef::ST_Other; if (Symb->StorageClass == COFF::IMAGE_SYM_CLASS_EXTERNAL && Symb->SectionNumber == COFF::IMAGE_SYM_UNDEFINED) { Result = SymbolRef::ST_Unknown; } else if (Symb->isFunctionDefinition()) { Result = SymbolRef::ST_Function; } else { uint32_t Characteristics = 0; if (!COFF::isReservedSectionNumber(Symb->SectionNumber)) { const coff_section *Section = nullptr; if (std::error_code EC = getSection(Symb->SectionNumber, Section)) return EC; Characteristics = Section->Characteristics; } if (Characteristics & COFF::IMAGE_SCN_MEM_READ && ~Characteristics & COFF::IMAGE_SCN_MEM_WRITE) // Read only. Result = SymbolRef::ST_Data; } return object_error::success; } uint32_t COFFObjectFile::getSymbolFlags(DataRefImpl Ref) const { const coff_symbol *Symb = toSymb(Ref); uint32_t Result = SymbolRef::SF_None; // TODO: Correctly set SF_FormatSpecific, SF_Common if (Symb->SectionNumber == COFF::IMAGE_SYM_UNDEFINED) { if (Symb->Value == 0) Result |= SymbolRef::SF_Undefined; else Result |= SymbolRef::SF_Common; } // TODO: This are certainly too restrictive. if (Symb->StorageClass == COFF::IMAGE_SYM_CLASS_EXTERNAL) Result |= SymbolRef::SF_Global; if (Symb->StorageClass == COFF::IMAGE_SYM_CLASS_WEAK_EXTERNAL) Result |= SymbolRef::SF_Weak; if (Symb->SectionNumber == COFF::IMAGE_SYM_ABSOLUTE) Result |= SymbolRef::SF_Absolute; return Result; } std::error_code COFFObjectFile::getSymbolSize(DataRefImpl Ref, uint64_t &Result) const { // FIXME: Return the correct size. This requires looking at all the symbols // in the same section as this symbol, and looking for either the next // symbol, or the end of the section. const coff_symbol *Symb = toSymb(Ref); const coff_section *Section = nullptr; if (std::error_code EC = getSection(Symb->SectionNumber, Section)) return EC; if (Symb->SectionNumber == COFF::IMAGE_SYM_UNDEFINED) Result = UnknownAddressOrSize; else if (Section) Result = Section->SizeOfRawData - Symb->Value; else Result = 0; return object_error::success; } std::error_code COFFObjectFile::getSymbolSection(DataRefImpl Ref, section_iterator &Result) const { const coff_symbol *Symb = toSymb(Ref); if (COFF::isReservedSectionNumber(Symb->SectionNumber)) { Result = section_end(); } else { const coff_section *Sec = nullptr; if (std::error_code EC = getSection(Symb->SectionNumber, Sec)) return EC; DataRefImpl Ref; Ref.p = reinterpret_cast<uintptr_t>(Sec); Result = section_iterator(SectionRef(Ref, this)); } return object_error::success; } void COFFObjectFile::moveSectionNext(DataRefImpl &Ref) const { const coff_section *Sec = toSec(Ref); Sec += 1; Ref.p = reinterpret_cast<uintptr_t>(Sec); } std::error_code COFFObjectFile::getSectionName(DataRefImpl Ref, StringRef &Result) const { const coff_section *Sec = toSec(Ref); return getSectionName(Sec, Result); } std::error_code COFFObjectFile::getSectionAddress(DataRefImpl Ref, uint64_t &Result) const { const coff_section *Sec = toSec(Ref); Result = Sec->VirtualAddress; return object_error::success; } std::error_code COFFObjectFile::getSectionSize(DataRefImpl Ref, uint64_t &Result) const { const coff_section *Sec = toSec(Ref); Result = Sec->SizeOfRawData; return object_error::success; } std::error_code COFFObjectFile::getSectionContents(DataRefImpl Ref, StringRef &Result) const { const coff_section *Sec = toSec(Ref); ArrayRef<uint8_t> Res; std::error_code EC = getSectionContents(Sec, Res); Result = StringRef(reinterpret_cast<const char*>(Res.data()), Res.size()); return EC; } std::error_code COFFObjectFile::getSectionAlignment(DataRefImpl Ref, uint64_t &Res) const { const coff_section *Sec = toSec(Ref); if (!Sec) return object_error::parse_failed; Res = uint64_t(1) << (((Sec->Characteristics & 0x00F00000) >> 20) - 1); return object_error::success; } std::error_code COFFObjectFile::isSectionText(DataRefImpl Ref, bool &Result) const { const coff_section *Sec = toSec(Ref); Result = Sec->Characteristics & COFF::IMAGE_SCN_CNT_CODE; return object_error::success; } std::error_code COFFObjectFile::isSectionData(DataRefImpl Ref, bool &Result) const { const coff_section *Sec = toSec(Ref); Result = Sec->Characteristics & COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; return object_error::success; } std::error_code COFFObjectFile::isSectionBSS(DataRefImpl Ref, bool &Result) const { const coff_section *Sec = toSec(Ref); Result = Sec->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA; return object_error::success; } std::error_code COFFObjectFile::isSectionRequiredForExecution(DataRefImpl Ref, bool &Result) const { // FIXME: Unimplemented Result = true; return object_error::success; } std::error_code COFFObjectFile::isSectionVirtual(DataRefImpl Ref, bool &Result) const { const coff_section *Sec = toSec(Ref); Result = Sec->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA; return object_error::success; } std::error_code COFFObjectFile::isSectionZeroInit(DataRefImpl Ref, bool &Result) const { // FIXME: Unimplemented. Result = false; return object_error::success; } std::error_code COFFObjectFile::isSectionReadOnlyData(DataRefImpl Ref, bool &Result) const { // FIXME: Unimplemented. Result = false; return object_error::success; } std::error_code COFFObjectFile::sectionContainsSymbol(DataRefImpl SecRef, DataRefImpl SymbRef, bool &Result) const { const coff_section *Sec = toSec(SecRef); const coff_symbol *Symb = toSymb(SymbRef); const coff_section *SymbSec = nullptr; if (std::error_code EC = getSection(Symb->SectionNumber, SymbSec)) return EC; if (SymbSec == Sec) Result = true; else Result = false; return object_error::success; } relocation_iterator COFFObjectFile::section_rel_begin(DataRefImpl Ref) const { const coff_section *Sec = toSec(Ref); DataRefImpl Ret; if (Sec->NumberOfRelocations == 0) { Ret.p = 0; } else { auto begin = reinterpret_cast<const coff_relocation*>( base() + Sec->PointerToRelocations); if (Sec->hasExtendedRelocations()) { // Skip the first relocation entry repurposed to store the number of // relocations. begin++; } Ret.p = reinterpret_cast<uintptr_t>(begin); } return relocation_iterator(RelocationRef(Ret, this)); } static uint32_t getNumberOfRelocations(const coff_section *Sec, const uint8_t *base) { // The field for the number of relocations in COFF section table is only // 16-bit wide. If a section has more than 65535 relocations, 0xFFFF is set to // NumberOfRelocations field, and the actual relocation count is stored in the // VirtualAddress field in the first relocation entry. if (Sec->hasExtendedRelocations()) { auto *FirstReloc = reinterpret_cast<const coff_relocation*>( base + Sec->PointerToRelocations); return FirstReloc->VirtualAddress; } return Sec->NumberOfRelocations; } relocation_iterator COFFObjectFile::section_rel_end(DataRefImpl Ref) const { const coff_section *Sec = toSec(Ref); DataRefImpl Ret; if (Sec->NumberOfRelocations == 0) { Ret.p = 0; } else { auto begin = reinterpret_cast<const coff_relocation*>( base() + Sec->PointerToRelocations); uint32_t NumReloc = getNumberOfRelocations(Sec, base()); Ret.p = reinterpret_cast<uintptr_t>(begin + NumReloc); } return relocation_iterator(RelocationRef(Ret, this)); } // Initialize the pointer to the symbol table. std::error_code COFFObjectFile::initSymbolTablePtr() { if (std::error_code EC = getObject( SymbolTable, *Data, base() + COFFHeader->PointerToSymbolTable, COFFHeader->NumberOfSymbols * sizeof(coff_symbol))) return EC; // Find string table. The first four byte of the string table contains the // total size of the string table, including the size field itself. If the // string table is empty, the value of the first four byte would be 4. const uint8_t *StringTableAddr = base() + COFFHeader->PointerToSymbolTable + COFFHeader->NumberOfSymbols * sizeof(coff_symbol); const ulittle32_t *StringTableSizePtr; if (std::error_code EC = getObject(StringTableSizePtr, *Data, StringTableAddr)) return EC; StringTableSize = *StringTableSizePtr; if (std::error_code EC = getObject(StringTable, *Data, StringTableAddr, StringTableSize)) return EC; // Treat table sizes < 4 as empty because contrary to the PECOFF spec, some // tools like cvtres write a size of 0 for an empty table instead of 4. if (StringTableSize < 4) StringTableSize = 4; // Check that the string table is null terminated if has any in it. if (StringTableSize > 4 && StringTable[StringTableSize - 1] != 0) return object_error::parse_failed; return object_error::success; } // Returns the file offset for the given VA. std::error_code COFFObjectFile::getVaPtr(uint64_t Addr, uintptr_t &Res) const { uint64_t ImageBase = PE32Header ? (uint64_t)PE32Header->ImageBase : (uint64_t)PE32PlusHeader->ImageBase; uint64_t Rva = Addr - ImageBase; assert(Rva <= UINT32_MAX); return getRvaPtr((uint32_t)Rva, Res); } // Returns the file offset for the given RVA. std::error_code COFFObjectFile::getRvaPtr(uint32_t Addr, uintptr_t &Res) const { for (const SectionRef &S : sections()) { const coff_section *Section = getCOFFSection(S); uint32_t SectionStart = Section->VirtualAddress; uint32_t SectionEnd = Section->VirtualAddress + Section->VirtualSize; if (SectionStart <= Addr && Addr < SectionEnd) { uint32_t Offset = Addr - SectionStart; Res = uintptr_t(base()) + Section->PointerToRawData + Offset; return object_error::success; } } return object_error::parse_failed; } // Returns hint and name fields, assuming \p Rva is pointing to a Hint/Name // table entry. std::error_code COFFObjectFile::getHintName(uint32_t Rva, uint16_t &Hint, StringRef &Name) const { uintptr_t IntPtr = 0; if (std::error_code EC = getRvaPtr(Rva, IntPtr)) return EC; const uint8_t *Ptr = reinterpret_cast<const uint8_t *>(IntPtr); Hint = *reinterpret_cast<const ulittle16_t *>(Ptr); Name = StringRef(reinterpret_cast<const char *>(Ptr + 2)); return object_error::success; } // Find the import table. std::error_code COFFObjectFile::initImportTablePtr() { // First, we get the RVA of the import table. If the file lacks a pointer to // the import table, do nothing. const data_directory *DataEntry; if (getDataDirectory(COFF::IMPORT_TABLE, DataEntry)) return object_error::success; // Do nothing if the pointer to import table is NULL. if (DataEntry->RelativeVirtualAddress == 0) return object_error::success; uint32_t ImportTableRva = DataEntry->RelativeVirtualAddress; NumberOfImportDirectory = DataEntry->Size / sizeof(import_directory_table_entry); // Find the section that contains the RVA. This is needed because the RVA is // the import table's memory address which is different from its file offset. uintptr_t IntPtr = 0; if (std::error_code EC = getRvaPtr(ImportTableRva, IntPtr)) return EC; ImportDirectory = reinterpret_cast< const import_directory_table_entry *>(IntPtr); return object_error::success; } // Find the export table. std::error_code COFFObjectFile::initExportTablePtr() { // First, we get the RVA of the export table. If the file lacks a pointer to // the export table, do nothing. const data_directory *DataEntry; if (getDataDirectory(COFF::EXPORT_TABLE, DataEntry)) return object_error::success; // Do nothing if the pointer to export table is NULL. if (DataEntry->RelativeVirtualAddress == 0) return object_error::success; uint32_t ExportTableRva = DataEntry->RelativeVirtualAddress; uintptr_t IntPtr = 0; if (std::error_code EC = getRvaPtr(ExportTableRva, IntPtr)) return EC; ExportDirectory = reinterpret_cast<const export_directory_table_entry *>(IntPtr); return object_error::success; } COFFObjectFile::COFFObjectFile(std::unique_ptr<MemoryBuffer> Object, std::error_code &EC) : ObjectFile(Binary::ID_COFF, std::move(Object)), COFFHeader(nullptr), PE32Header(nullptr), PE32PlusHeader(nullptr), DataDirectory(nullptr), SectionTable(nullptr), SymbolTable(nullptr), StringTable(nullptr), StringTableSize(0), ImportDirectory(nullptr), NumberOfImportDirectory(0), ExportDirectory(nullptr) { // Check that we at least have enough room for a header. if (!checkSize(*Data, EC, sizeof(coff_file_header))) return; // The current location in the file where we are looking at. uint64_t CurPtr = 0; // PE header is optional and is present only in executables. If it exists, // it is placed right after COFF header. bool HasPEHeader = false; // Check if this is a PE/COFF file. if (base()[0] == 0x4d && base()[1] == 0x5a) { // PE/COFF, seek through MS-DOS compatibility stub and 4-byte // PE signature to find 'normal' COFF header. if (!checkSize(*Data, EC, 0x3c + 8)) return; CurPtr = *reinterpret_cast<const ulittle16_t *>(base() + 0x3c); // Check the PE magic bytes. ("PE\0\0") if (std::memcmp(base() + CurPtr, "PE\0\0", 4) != 0) { EC = object_error::parse_failed; return; } CurPtr += 4; // Skip the PE magic bytes. HasPEHeader = true; } if ((EC = getObject(COFFHeader, *Data, base() + CurPtr))) return; CurPtr += sizeof(coff_file_header); if (HasPEHeader) { const pe32_header *Header; if ((EC = getObject(Header, *Data, base() + CurPtr))) return; const uint8_t *DataDirAddr; uint64_t DataDirSize; if (Header->Magic == 0x10b) { PE32Header = Header; DataDirAddr = base() + CurPtr + sizeof(pe32_header); DataDirSize = sizeof(data_directory) * PE32Header->NumberOfRvaAndSize; } else if (Header->Magic == 0x20b) { PE32PlusHeader = reinterpret_cast<const pe32plus_header *>(Header); DataDirAddr = base() + CurPtr + sizeof(pe32plus_header); DataDirSize = sizeof(data_directory) * PE32PlusHeader->NumberOfRvaAndSize; } else { // It's neither PE32 nor PE32+. EC = object_error::parse_failed; return; } if ((EC = getObject(DataDirectory, *Data, DataDirAddr, DataDirSize))) return; CurPtr += COFFHeader->SizeOfOptionalHeader; } if (COFFHeader->isImportLibrary()) return; if ((EC = getObject(SectionTable, *Data, base() + CurPtr, COFFHeader->NumberOfSections * sizeof(coff_section)))) return; // Initialize the pointer to the symbol table. if (COFFHeader->PointerToSymbolTable != 0) if ((EC = initSymbolTablePtr())) return; // Initialize the pointer to the beginning of the import table. if ((EC = initImportTablePtr())) return; // Initialize the pointer to the export table. if ((EC = initExportTablePtr())) return; EC = object_error::success; } basic_symbol_iterator COFFObjectFile::symbol_begin_impl() const { DataRefImpl Ret; Ret.p = reinterpret_cast<uintptr_t>(SymbolTable); return basic_symbol_iterator(SymbolRef(Ret, this)); } basic_symbol_iterator COFFObjectFile::symbol_end_impl() const { // The symbol table ends where the string table begins. DataRefImpl Ret; Ret.p = reinterpret_cast<uintptr_t>(StringTable); return basic_symbol_iterator(SymbolRef(Ret, this)); } import_directory_iterator COFFObjectFile::import_directory_begin() const { return import_directory_iterator( ImportDirectoryEntryRef(ImportDirectory, 0, this)); } import_directory_iterator COFFObjectFile::import_directory_end() const { return import_directory_iterator( ImportDirectoryEntryRef(ImportDirectory, NumberOfImportDirectory, this)); } export_directory_iterator COFFObjectFile::export_directory_begin() const { return export_directory_iterator( ExportDirectoryEntryRef(ExportDirectory, 0, this)); } export_directory_iterator COFFObjectFile::export_directory_end() const { if (!ExportDirectory) return export_directory_iterator(ExportDirectoryEntryRef(nullptr, 0, this)); ExportDirectoryEntryRef Ref(ExportDirectory, ExportDirectory->AddressTableEntries, this); return export_directory_iterator(Ref); } section_iterator COFFObjectFile::section_begin() const { DataRefImpl Ret; Ret.p = reinterpret_cast<uintptr_t>(SectionTable); return section_iterator(SectionRef(Ret, this)); } section_iterator COFFObjectFile::section_end() const { DataRefImpl Ret; int NumSections = COFFHeader->isImportLibrary() ? 0 : COFFHeader->NumberOfSections; Ret.p = reinterpret_cast<uintptr_t>(SectionTable + NumSections); return section_iterator(SectionRef(Ret, this)); } uint8_t COFFObjectFile::getBytesInAddress() const { return getArch() == Triple::x86_64 ? 8 : 4; } StringRef COFFObjectFile::getFileFormatName() const { switch(COFFHeader->Machine) { case COFF::IMAGE_FILE_MACHINE_I386: return "COFF-i386"; case COFF::IMAGE_FILE_MACHINE_AMD64: return "COFF-x86-64"; case COFF::IMAGE_FILE_MACHINE_ARMNT: return "COFF-ARM"; default: return "COFF-<unknown arch>"; } } unsigned COFFObjectFile::getArch() const { switch(COFFHeader->Machine) { case COFF::IMAGE_FILE_MACHINE_I386: return Triple::x86; case COFF::IMAGE_FILE_MACHINE_AMD64: return Triple::x86_64; case COFF::IMAGE_FILE_MACHINE_ARMNT: return Triple::thumb; default: return Triple::UnknownArch; } } // This method is kept here because lld uses this. As soon as we make // lld to use getCOFFHeader, this method will be removed. std::error_code COFFObjectFile::getHeader(const coff_file_header *&Res) const { return getCOFFHeader(Res); } std::error_code COFFObjectFile::getCOFFHeader(const coff_file_header *&Res) const { Res = COFFHeader; return object_error::success; } std::error_code COFFObjectFile::getPE32Header(const pe32_header *&Res) const { Res = PE32Header; return object_error::success; } std::error_code COFFObjectFile::getPE32PlusHeader(const pe32plus_header *&Res) const { Res = PE32PlusHeader; return object_error::success; } std::error_code COFFObjectFile::getDataDirectory(uint32_t Index, const data_directory *&Res) const { // Error if if there's no data directory or the index is out of range. if (!DataDirectory) return object_error::parse_failed; assert(PE32Header || PE32PlusHeader); uint32_t NumEnt = PE32Header ? PE32Header->NumberOfRvaAndSize : PE32PlusHeader->NumberOfRvaAndSize; if (Index > NumEnt) return object_error::parse_failed; Res = &DataDirectory[Index]; return object_error::success; } std::error_code COFFObjectFile::getSection(int32_t Index, const coff_section *&Result) const { // Check for special index values. if (COFF::isReservedSectionNumber(Index)) Result = nullptr; else if (Index > 0 && Index <= COFFHeader->NumberOfSections) // We already verified the section table data, so no need to check again. Result = SectionTable + (Index - 1); else return object_error::parse_failed; return object_error::success; } std::error_code COFFObjectFile::getString(uint32_t Offset, StringRef &Result) const { if (StringTableSize <= 4) // Tried to get a string from an empty string table. return object_error::parse_failed; if (Offset >= StringTableSize) return object_error::unexpected_eof; Result = StringRef(StringTable + Offset); return object_error::success; } std::error_code COFFObjectFile::getSymbol(uint32_t Index, const coff_symbol *&Result) const { if (Index < COFFHeader->NumberOfSymbols) Result = SymbolTable + Index; else return object_error::parse_failed; return object_error::success; } std::error_code COFFObjectFile::getSymbolName(const coff_symbol *Symbol, StringRef &Res) const { // Check for string table entry. First 4 bytes are 0. if (Symbol->Name.Offset.Zeroes == 0) { uint32_t Offset = Symbol->Name.Offset.Offset; if (std::error_code EC = getString(Offset, Res)) return EC; return object_error::success; } if (Symbol->Name.ShortName[7] == 0) // Null terminated, let ::strlen figure out the length. Res = StringRef(Symbol->Name.ShortName); else // Not null terminated, use all 8 bytes. Res = StringRef(Symbol->Name.ShortName, 8); return object_error::success; } ArrayRef<uint8_t> COFFObjectFile::getSymbolAuxData( const coff_symbol *Symbol) const { const uint8_t *Aux = nullptr; if (Symbol->NumberOfAuxSymbols > 0) { // AUX data comes immediately after the symbol in COFF Aux = reinterpret_cast<const uint8_t *>(Symbol + 1); # ifndef NDEBUG // Verify that the Aux symbol points to a valid entry in the symbol table. uintptr_t Offset = uintptr_t(Aux) - uintptr_t(base()); if (Offset < COFFHeader->PointerToSymbolTable || Offset >= COFFHeader->PointerToSymbolTable + (COFFHeader->NumberOfSymbols * sizeof(coff_symbol))) report_fatal_error("Aux Symbol data was outside of symbol table."); assert((Offset - COFFHeader->PointerToSymbolTable) % sizeof(coff_symbol) == 0 && "Aux Symbol data did not point to the beginning of a symbol"); # endif } return ArrayRef<uint8_t>(Aux, Symbol->NumberOfAuxSymbols * sizeof(coff_symbol)); } std::error_code COFFObjectFile::getSectionName(const coff_section *Sec, StringRef &Res) const { StringRef Name; if (Sec->Name[7] == 0) // Null terminated, let ::strlen figure out the length. Name = Sec->Name; else // Not null terminated, use all 8 bytes. Name = StringRef(Sec->Name, 8); // Check for string table entry. First byte is '/'. if (Name[0] == '/') { uint32_t Offset; if (Name[1] == '/') { if (decodeBase64StringEntry(Name.substr(2), Offset)) return object_error::parse_failed; } else { if (Name.substr(1).getAsInteger(10, Offset)) return object_error::parse_failed; } if (std::error_code EC = getString(Offset, Name)) return EC; } Res = Name; return object_error::success; } std::error_code COFFObjectFile::getSectionContents(const coff_section *Sec, ArrayRef<uint8_t> &Res) const { // The only thing that we need to verify is that the contents is contained // within the file bounds. We don't need to make sure it doesn't cover other // data, as there's nothing that says that is not allowed. uintptr_t ConStart = uintptr_t(base()) + Sec->PointerToRawData; uintptr_t ConEnd = ConStart + Sec->SizeOfRawData; if (ConEnd > uintptr_t(Data->getBufferEnd())) return object_error::parse_failed; Res = ArrayRef<uint8_t>(reinterpret_cast<const unsigned char*>(ConStart), Sec->SizeOfRawData); return object_error::success; } const coff_relocation *COFFObjectFile::toRel(DataRefImpl Rel) const { return reinterpret_cast<const coff_relocation*>(Rel.p); } void COFFObjectFile::moveRelocationNext(DataRefImpl &Rel) const { Rel.p = reinterpret_cast<uintptr_t>( reinterpret_cast<const coff_relocation*>(Rel.p) + 1); } std::error_code COFFObjectFile::getRelocationAddress(DataRefImpl Rel, uint64_t &Res) const { report_fatal_error("getRelocationAddress not implemented in COFFObjectFile"); } std::error_code COFFObjectFile::getRelocationOffset(DataRefImpl Rel, uint64_t &Res) const { Res = toRel(Rel)->VirtualAddress; return object_error::success; } symbol_iterator COFFObjectFile::getRelocationSymbol(DataRefImpl Rel) const { const coff_relocation* R = toRel(Rel); DataRefImpl Ref; Ref.p = reinterpret_cast<uintptr_t>(SymbolTable + R->SymbolTableIndex); return symbol_iterator(SymbolRef(Ref, this)); } std::error_code COFFObjectFile::getRelocationType(DataRefImpl Rel, uint64_t &Res) const { const coff_relocation* R = toRel(Rel); Res = R->Type; return object_error::success; } const coff_section * COFFObjectFile::getCOFFSection(const SectionRef &Section) const { return toSec(Section.getRawDataRefImpl()); } const coff_symbol * COFFObjectFile::getCOFFSymbol(const SymbolRef &Symbol) const { return toSymb(Symbol.getRawDataRefImpl()); } const coff_relocation * COFFObjectFile::getCOFFRelocation(const RelocationRef &Reloc) const { return toRel(Reloc.getRawDataRefImpl()); } #define LLVM_COFF_SWITCH_RELOC_TYPE_NAME(reloc_type) \ case COFF::reloc_type: \ Res = #reloc_type; \ break; std::error_code COFFObjectFile::getRelocationTypeName(DataRefImpl Rel, SmallVectorImpl<char> &Result) const { const coff_relocation *Reloc = toRel(Rel); StringRef Res; switch (COFFHeader->Machine) { case COFF::IMAGE_FILE_MACHINE_AMD64: switch (Reloc->Type) { LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_AMD64_ABSOLUTE); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_AMD64_ADDR64); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_AMD64_ADDR32); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_AMD64_ADDR32NB); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_AMD64_REL32); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_AMD64_REL32_1); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_AMD64_REL32_2); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_AMD64_REL32_3); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_AMD64_REL32_4); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_AMD64_REL32_5); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_AMD64_SECTION); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_AMD64_SECREL); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_AMD64_SECREL7); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_AMD64_TOKEN); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_AMD64_SREL32); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_AMD64_PAIR); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_AMD64_SSPAN32); default: Res = "Unknown"; } break; case COFF::IMAGE_FILE_MACHINE_ARMNT: switch (Reloc->Type) { LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_ARM_ABSOLUTE); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_ARM_ADDR32); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_ARM_ADDR32NB); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_ARM_BRANCH24); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_ARM_BRANCH11); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_ARM_TOKEN); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_ARM_BLX24); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_ARM_BLX11); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_ARM_SECTION); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_ARM_SECREL); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_ARM_MOV32A); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_ARM_MOV32T); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_ARM_BRANCH20T); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_ARM_BRANCH24T); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_ARM_BLX23T); default: Res = "Unknown"; } break; case COFF::IMAGE_FILE_MACHINE_I386: switch (Reloc->Type) { LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_I386_ABSOLUTE); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_I386_DIR16); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_I386_REL16); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_I386_DIR32); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_I386_DIR32NB); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_I386_SEG12); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_I386_SECTION); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_I386_SECREL); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_I386_TOKEN); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_I386_SECREL7); LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_I386_REL32); default: Res = "Unknown"; } break; default: Res = "Unknown"; } Result.append(Res.begin(), Res.end()); return object_error::success; } #undef LLVM_COFF_SWITCH_RELOC_TYPE_NAME std::error_code COFFObjectFile::getRelocationValueString(DataRefImpl Rel, SmallVectorImpl<char> &Result) const { const coff_relocation *Reloc = toRel(Rel); const coff_symbol *Symb = nullptr; if (std::error_code EC = getSymbol(Reloc->SymbolTableIndex, Symb)) return EC; DataRefImpl Sym; Sym.p = reinterpret_cast<uintptr_t>(Symb); StringRef SymName; if (std::error_code EC = getSymbolName(Sym, SymName)) return EC; Result.append(SymName.begin(), SymName.end()); return object_error::success; } bool COFFObjectFile::isRelocatableObject() const { return !DataDirectory; } bool ImportDirectoryEntryRef:: operator==(const ImportDirectoryEntryRef &Other) const { return ImportTable == Other.ImportTable && Index == Other.Index; } void ImportDirectoryEntryRef::moveNext() { ++Index; } std::error_code ImportDirectoryEntryRef::getImportTableEntry( const import_directory_table_entry *&Result) const { Result = ImportTable; return object_error::success; } std::error_code ImportDirectoryEntryRef::getName(StringRef &Result) const { uintptr_t IntPtr = 0; if (std::error_code EC = OwningObject->getRvaPtr(ImportTable->NameRVA, IntPtr)) return EC; Result = StringRef(reinterpret_cast<const char *>(IntPtr)); return object_error::success; } std::error_code ImportDirectoryEntryRef::getImportLookupEntry( const import_lookup_table_entry32 *&Result) const { uintptr_t IntPtr = 0; if (std::error_code EC = OwningObject->getRvaPtr(ImportTable->ImportLookupTableRVA, IntPtr)) return EC; Result = reinterpret_cast<const import_lookup_table_entry32 *>(IntPtr); return object_error::success; } bool ExportDirectoryEntryRef:: operator==(const ExportDirectoryEntryRef &Other) const { return ExportTable == Other.ExportTable && Index == Other.Index; } void ExportDirectoryEntryRef::moveNext() { ++Index; } // Returns the name of the current export symbol. If the symbol is exported only // by ordinal, the empty string is set as a result. std::error_code ExportDirectoryEntryRef::getDllName(StringRef &Result) const { uintptr_t IntPtr = 0; if (std::error_code EC = OwningObject->getRvaPtr(ExportTable->NameRVA, IntPtr)) return EC; Result = StringRef(reinterpret_cast<const char *>(IntPtr)); return object_error::success; } // Returns the starting ordinal number. std::error_code ExportDirectoryEntryRef::getOrdinalBase(uint32_t &Result) const { Result = ExportTable->OrdinalBase; return object_error::success; } // Returns the export ordinal of the current export symbol. std::error_code ExportDirectoryEntryRef::getOrdinal(uint32_t &Result) const { Result = ExportTable->OrdinalBase + Index; return object_error::success; } // Returns the address of the current export symbol. std::error_code ExportDirectoryEntryRef::getExportRVA(uint32_t &Result) const { uintptr_t IntPtr = 0; if (std::error_code EC = OwningObject->getRvaPtr(ExportTable->ExportAddressTableRVA, IntPtr)) return EC; const export_address_table_entry *entry = reinterpret_cast<const export_address_table_entry *>(IntPtr); Result = entry[Index].ExportRVA; return object_error::success; } // Returns the name of the current export symbol. If the symbol is exported only // by ordinal, the empty string is set as a result. std::error_code ExportDirectoryEntryRef::getSymbolName(StringRef &Result) const { uintptr_t IntPtr = 0; if (std::error_code EC = OwningObject->getRvaPtr(ExportTable->OrdinalTableRVA, IntPtr)) return EC; const ulittle16_t *Start = reinterpret_cast<const ulittle16_t *>(IntPtr); uint32_t NumEntries = ExportTable->NumberOfNamePointers; int Offset = 0; for (const ulittle16_t *I = Start, *E = Start + NumEntries; I < E; ++I, ++Offset) { if (*I != Index) continue; if (std::error_code EC = OwningObject->getRvaPtr(ExportTable->NamePointerRVA, IntPtr)) return EC; const ulittle32_t *NamePtr = reinterpret_cast<const ulittle32_t *>(IntPtr); if (std::error_code EC = OwningObject->getRvaPtr(NamePtr[Offset], IntPtr)) return EC; Result = StringRef(reinterpret_cast<const char *>(IntPtr)); return object_error::success; } Result = ""; return object_error::success; } ErrorOr<std::unique_ptr<COFFObjectFile>> ObjectFile::createCOFFObjectFile(std::unique_ptr<MemoryBuffer> Object) { std::error_code EC; std::unique_ptr<COFFObjectFile> Ret( new COFFObjectFile(std::move(Object), EC)); if (EC) return EC; return std::move(Ret); }