summaryrefslogtreecommitdiffstats
path: root/lld/wasm/InputChunks.cpp
blob: ef3edb8b9cafbcbb70061cf6a81cebde21f916d9 (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
//===- InputChunks.cpp ----------------------------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "InputChunks.h"
#include "Config.h"
#include "OutputSegment.h"
#include "WriterUtils.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/LLVM.h"
#include "llvm/Support/LEB128.h"

#define DEBUG_TYPE "lld"

using namespace llvm;
using namespace llvm::wasm;
using namespace llvm::support::endian;
using namespace lld;
using namespace lld::wasm;

StringRef ReloctTypeToString(uint8_t RelocType) {
  switch (RelocType) {
#define WASM_RELOC(NAME, REL) case REL: return #NAME;
#include "llvm/BinaryFormat/WasmRelocs.def"
#undef WASM_RELOC
  }
  llvm_unreachable("unknown reloc type");
}

std::string lld::toString(const InputChunk *C) {
  return (toString(C->File) + ":(" + C->getName() + ")").str();
}

void InputChunk::copyRelocations(const WasmSection &Section) {
  if (Section.Relocations.empty())
    return;
  size_t Start = getInputSectionOffset();
  size_t Size = getSize();
  for (const WasmRelocation &R : Section.Relocations)
    if (R.Offset >= Start && R.Offset < Start + Size)
      Relocations.push_back(R);
}

// Copy this input chunk to an mmap'ed output file and apply relocations.
void InputChunk::writeTo(uint8_t *Buf) const {
  // Copy contents
  memcpy(Buf + OutputOffset, data().data(), data().size());

  // Apply relocations
  if (Relocations.empty())
    return;

  DEBUG(dbgs() << "applying relocations: " << getName()
               << " count=" << Relocations.size() << "\n");
  int32_t Off = OutputOffset - getInputSectionOffset();

  for (const WasmRelocation &Rel : Relocations) {
    uint8_t *Loc = Buf + Rel.Offset + Off;
    uint32_t Value = File->calcNewValue(Rel);
    uint32_t ExistingValue;
    DEBUG(dbgs() << "apply reloc: type=" << ReloctTypeToString(Rel.Type)
                 << " addend=" << Rel.Addend << " index=" << Rel.Index
                 << " value=" << Value << " offset=" << Rel.Offset << "\n");

    switch (Rel.Type) {
    case R_WEBASSEMBLY_TYPE_INDEX_LEB:
    case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
    case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
    case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
      ExistingValue = decodeULEB128(Loc);
      encodeULEB128(Value, Loc, 5);
      break;
    case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
    case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
      ExistingValue = static_cast<uint32_t>(decodeSLEB128(Loc));
      encodeSLEB128(static_cast<int32_t>(Value), Loc, 5);
      break;
    case R_WEBASSEMBLY_TABLE_INDEX_I32:
    case R_WEBASSEMBLY_MEMORY_ADDR_I32:
      ExistingValue = static_cast<uint32_t>(read32le(Loc));
      write32le(Loc, Value);
      break;
    default:
      llvm_unreachable("unknown relocation type");
    }

    uint32_t ExpectedValue = File->calcExpectedValue(Rel);
    if (ExpectedValue != ExistingValue)
      error("unexpected existing value for " + ReloctTypeToString(Rel.Type) +
            ": existing=" + Twine(ExistingValue) +
            " expected=" + Twine(ExpectedValue));
  }
}

// Copy relocation entries to a given output stream.
// This function is used only when a user passes "-r". For a regular link,
// we consume relocations instead of copying them to an output file.
void InputChunk::writeRelocations(raw_ostream &OS) const {
  if (Relocations.empty())
    return;

  int32_t Off = OutputOffset - getInputSectionOffset();
  DEBUG(dbgs() << "writeRelocations: " << File->getName()
               << " offset=" << Twine(Off) << "\n");

  for (const WasmRelocation &Rel : Relocations) {
    writeUleb128(OS, Rel.Type, "reloc type");
    writeUleb128(OS, Rel.Offset + Off, "reloc offset");
    writeUleb128(OS, File->calcNewIndex(Rel), "reloc index");

    switch (Rel.Type) {
    case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
    case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
    case R_WEBASSEMBLY_MEMORY_ADDR_I32:
      writeUleb128(OS, Rel.Addend, "reloc addend");
      break;
    }
  }
}

void InputFunction::setFunctionIndex(uint32_t Index) {
  DEBUG(dbgs() << "InputFunction::setFunctionIndex: " << getName() << " -> "
               << Index << "\n");
  assert(!hasFunctionIndex());
  FunctionIndex = Index;
}

void InputFunction::setTableIndex(uint32_t Index) {
  DEBUG(dbgs() << "InputFunction::setTableIndex: " << getName() << " -> "
               << Index << "\n");
  assert(!hasTableIndex());
  TableIndex = Index;
}
OpenPOWER on IntegriCloud