Coverage Report

Created: 2019-07-24 05:18

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/lld/wasm/InputChunks.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- InputChunks.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-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "InputChunks.h"
10
#include "Config.h"
11
#include "OutputSegment.h"
12
#include "WriterUtils.h"
13
#include "lld/Common/ErrorHandler.h"
14
#include "lld/Common/LLVM.h"
15
#include "llvm/Support/LEB128.h"
16
17
#define DEBUG_TYPE "lld"
18
19
using namespace llvm;
20
using namespace llvm::wasm;
21
using namespace llvm::support::endian;
22
using namespace lld;
23
using namespace lld::wasm;
24
25
1
StringRef lld::relocTypeToString(uint8_t relocType) {
26
1
  switch (relocType) {
27
1
#define WASM_RELOC(NAME, REL)                                                  \
28
1
  case REL:                                                                    \
29
1
    return #NAME;
30
1
#include 
"llvm/BinaryFormat/WasmRelocs.def"0
31
1
#undef WASM_RELOC
32
1
  }
33
1
  
llvm_unreachable0
("unknown reloc type");
34
1
}
35
36
5
std::string lld::toString(const InputChunk *c) {
37
5
  return (toString(c->file) + ":(" + c->getName() + ")").str();
38
5
}
39
40
228
StringRef InputChunk::getComdatName() const {
41
228
  uint32_t index = getComdat();
42
228
  if (index == UINT32_MAX)
43
228
    
return StringRef()226
;
44
2
  return file->getWasmObj()->linkingData().Comdats[index];
45
2
}
46
47
0
void InputChunk::verifyRelocTargets() const {
48
0
  for (const WasmRelocation &rel : relocations) {
49
0
    uint32_t existingValue;
50
0
    unsigned bytesRead = 0;
51
0
    uint32_t offset = rel.Offset - getInputSectionOffset();
52
0
    const uint8_t *loc = data().data() + offset;
53
0
    switch (rel.Type) {
54
0
    case R_WASM_TYPE_INDEX_LEB:
55
0
    case R_WASM_FUNCTION_INDEX_LEB:
56
0
    case R_WASM_GLOBAL_INDEX_LEB:
57
0
    case R_WASM_EVENT_INDEX_LEB:
58
0
    case R_WASM_MEMORY_ADDR_LEB:
59
0
      existingValue = decodeULEB128(loc, &bytesRead);
60
0
      break;
61
0
    case R_WASM_TABLE_INDEX_SLEB:
62
0
    case R_WASM_TABLE_INDEX_REL_SLEB:
63
0
    case R_WASM_MEMORY_ADDR_SLEB:
64
0
    case R_WASM_MEMORY_ADDR_REL_SLEB:
65
0
      existingValue = static_cast<uint32_t>(decodeSLEB128(loc, &bytesRead));
66
0
      break;
67
0
    case R_WASM_TABLE_INDEX_I32:
68
0
    case R_WASM_MEMORY_ADDR_I32:
69
0
    case R_WASM_FUNCTION_OFFSET_I32:
70
0
    case R_WASM_SECTION_OFFSET_I32:
71
0
      existingValue = static_cast<uint32_t>(read32le(loc));
72
0
      break;
73
0
    default:
74
0
      llvm_unreachable("unknown relocation type");
75
0
    }
76
0
77
0
    if (bytesRead && bytesRead != 5)
78
0
      warn("expected LEB at relocation site be 5-byte padded");
79
0
80
0
    if (rel.Type != R_WASM_GLOBAL_INDEX_LEB) {
81
0
      uint32_t expectedValue = file->calcExpectedValue(rel);
82
0
      if (expectedValue != existingValue)
83
0
        warn("unexpected existing value for " + relocTypeToString(rel.Type) +
84
0
             ": existing=" + Twine(existingValue) +
85
0
             " expected=" + Twine(expectedValue));
86
0
    }
87
0
  }
88
0
}
89
90
// Copy this input chunk to an mmap'ed output file and apply relocations.
91
723
void InputChunk::writeTo(uint8_t *buf) const {
92
723
  // Copy contents
93
723
  memcpy(buf + outputOffset, data().data(), data().size());
94
723
95
723
  // Apply relocations
96
723
  if (relocations.empty())
97
371
    return;
98
352
99
#ifndef NDEBUG
100
  verifyRelocTargets();
101
#endif
102
103
352
  LLVM_DEBUG(dbgs() << "applying relocations: " << getName()
104
352
                    << " count=" << relocations.size() << "\n");
105
352
  int32_t off = outputOffset - getInputSectionOffset();
106
352
107
516
  for (const WasmRelocation &rel : relocations) {
108
516
    uint8_t *loc = buf + rel.Offset + off;
109
516
    uint32_t value = file->calcNewValue(rel);
110
516
    LLVM_DEBUG(dbgs() << "apply reloc: type=" << relocTypeToString(rel.Type));
111
516
    if (rel.Type != R_WASM_TYPE_INDEX_LEB)
112
516
      LLVM_DEBUG(dbgs() << " sym=" << file->getSymbols()[rel.Index]->getName());
113
516
    LLVM_DEBUG(dbgs() << " addend=" << rel.Addend << " index=" << rel.Index
114
516
                      << " value=" << value << " offset=" << rel.Offset
115
516
                      << "\n");
116
516
117
516
    switch (rel.Type) {
118
516
    case R_WASM_TYPE_INDEX_LEB:
119
334
    case R_WASM_FUNCTION_INDEX_LEB:
120
334
    case R_WASM_GLOBAL_INDEX_LEB:
121
334
    case R_WASM_EVENT_INDEX_LEB:
122
334
    case R_WASM_MEMORY_ADDR_LEB:
123
334
      encodeULEB128(value, loc, 5);
124
334
      break;
125
334
    case R_WASM_TABLE_INDEX_SLEB:
126
84
    case R_WASM_TABLE_INDEX_REL_SLEB:
127
84
    case R_WASM_MEMORY_ADDR_SLEB:
128
84
    case R_WASM_MEMORY_ADDR_REL_SLEB:
129
84
      encodeSLEB128(static_cast<int32_t>(value), loc, 5);
130
84
      break;
131
99
    case R_WASM_TABLE_INDEX_I32:
132
99
    case R_WASM_MEMORY_ADDR_I32:
133
99
    case R_WASM_FUNCTION_OFFSET_I32:
134
99
    case R_WASM_SECTION_OFFSET_I32:
135
99
      write32le(loc, value);
136
99
      break;
137
99
    default:
138
0
      llvm_unreachable("unknown relocation type");
139
516
    }
140
516
  }
141
352
}
142
143
// Copy relocation entries to a given output stream.
144
// This function is used only when a user passes "-r". For a regular link,
145
// we consume relocations instead of copying them to an output file.
146
210
void InputChunk::writeRelocations(raw_ostream &os) const {
147
210
  if (relocations.empty())
148
27
    return;
149
183
150
183
  int32_t off = outputOffset - getInputSectionOffset();
151
183
  LLVM_DEBUG(dbgs() << "writeRelocations: " << file->getName()
152
183
                    << " offset=" << Twine(off) << "\n");
153
183
154
210
  for (const WasmRelocation &rel : relocations) {
155
210
    writeUleb128(os, rel.Type, "reloc type");
156
210
    writeUleb128(os, rel.Offset + off, "reloc offset");
157
210
    writeUleb128(os, file->calcNewIndex(rel), "reloc index");
158
210
159
210
    if (relocTypeHasAddend(rel.Type))
160
152
      writeSleb128(os, file->calcNewAddend(rel), "reloc addend");
161
210
  }
162
183
}
163
164
551
void InputFunction::setFunctionIndex(uint32_t index) {
165
551
  LLVM_DEBUG(dbgs() << "InputFunction::setFunctionIndex: " << getName()
166
551
                    << " -> " << index << "\n");
167
551
  assert(!hasFunctionIndex());
168
551
  functionIndex = index;
169
551
}
170
171
56
void InputFunction::setTableIndex(uint32_t index) {
172
56
  LLVM_DEBUG(dbgs() << "InputFunction::setTableIndex: " << getName() << " -> "
173
56
                    << index << "\n");
174
56
  assert(!hasTableIndex());
175
56
  tableIndex = index;
176
56
}
177
178
// Write a relocation value without padding and return the number of bytes
179
// witten.
180
static unsigned writeCompressedReloc(uint8_t *buf, const WasmRelocation &rel,
181
8
                                     uint32_t value) {
182
8
  switch (rel.Type) {
183
8
  case R_WASM_TYPE_INDEX_LEB:
184
8
  case R_WASM_FUNCTION_INDEX_LEB:
185
8
  case R_WASM_GLOBAL_INDEX_LEB:
186
8
  case R_WASM_EVENT_INDEX_LEB:
187
8
  case R_WASM_MEMORY_ADDR_LEB:
188
8
    return encodeULEB128(value, buf);
189
8
  case R_WASM_TABLE_INDEX_SLEB:
190
0
  case R_WASM_MEMORY_ADDR_SLEB:
191
0
    return encodeSLEB128(static_cast<int32_t>(value), buf);
192
0
  default:
193
0
    llvm_unreachable("unexpected relocation type");
194
8
  }
195
8
}
196
197
8
static unsigned getRelocWidthPadded(const WasmRelocation &rel) {
198
8
  switch (rel.Type) {
199
8
  case R_WASM_TYPE_INDEX_LEB:
200
8
  case R_WASM_FUNCTION_INDEX_LEB:
201
8
  case R_WASM_GLOBAL_INDEX_LEB:
202
8
  case R_WASM_EVENT_INDEX_LEB:
203
8
  case R_WASM_MEMORY_ADDR_LEB:
204
8
  case R_WASM_TABLE_INDEX_SLEB:
205
8
  case R_WASM_MEMORY_ADDR_SLEB:
206
8
    return 5;
207
8
  default:
208
0
    llvm_unreachable("unexpected relocation type");
209
8
  }
210
8
}
211
212
4
static unsigned getRelocWidth(const WasmRelocation &rel, uint32_t value) {
213
4
  uint8_t buf[5];
214
4
  return writeCompressedReloc(buf, rel, value);
215
4
}
216
217
// Relocations of type LEB and SLEB in the code section are padded to 5 bytes
218
// so that a fast linker can blindly overwrite them without needing to worry
219
// about the number of bytes needed to encode the values.
220
// However, for optimal output the code section can be compressed to remove
221
// the padding then outputting non-relocatable files.
222
// In this case we need to perform a size calculation based on the value at each
223
// relocation.  At best we end up saving 4 bytes for each relocation entry.
224
//
225
// This function only computes the final output size.  It must be called
226
// before getSize() is used to calculate of layout of the code section.
227
533
void InputFunction::calculateSize() {
228
533
  if (!file || 
!config->compressRelocations463
)
229
529
    return;
230
4
231
4
  LLVM_DEBUG(dbgs() << "calculateSize: " << getName() << "\n");
232
4
233
4
  const uint8_t *secStart = file->codeSection->Content.data();
234
4
  const uint8_t *funcStart = secStart + getInputSectionOffset();
235
4
  uint32_t functionSizeLength;
236
4
  decodeULEB128(funcStart, &functionSizeLength);
237
4
238
4
  uint32_t start = getInputSectionOffset();
239
4
  uint32_t end = start + function->Size;
240
4
241
4
  uint32_t lastRelocEnd = start + functionSizeLength;
242
4
  for (const WasmRelocation &rel : relocations) {
243
4
    LLVM_DEBUG(dbgs() << "  region: " << (rel.Offset - lastRelocEnd) << "\n");
244
4
    compressedFuncSize += rel.Offset - lastRelocEnd;
245
4
    compressedFuncSize += getRelocWidth(rel, file->calcNewValue(rel));
246
4
    lastRelocEnd = rel.Offset + getRelocWidthPadded(rel);
247
4
  }
248
4
  LLVM_DEBUG(dbgs() << "  final region: " << (end - lastRelocEnd) << "\n");
249
4
  compressedFuncSize += end - lastRelocEnd;
250
4
251
4
  // Now we know how long the resulting function is we can add the encoding
252
4
  // of its length
253
4
  uint8_t buf[5];
254
4
  compressedSize = compressedFuncSize + encodeULEB128(compressedFuncSize, buf);
255
4
256
4
  LLVM_DEBUG(dbgs() << "  calculateSize orig: " << function->Size << "\n");
257
4
  LLVM_DEBUG(dbgs() << "  calculateSize  new: " << compressedSize << "\n");
258
4
}
259
260
// Override the default writeTo method so that we can (optionally) write the
261
// compressed version of the function.
262
533
void InputFunction::writeTo(uint8_t *buf) const {
263
533
  if (!file || 
!config->compressRelocations463
)
264
529
    return InputChunk::writeTo(buf);
265
4
266
4
  buf += outputOffset;
267
4
  uint8_t *orig = buf;
268
4
  (void)orig;
269
4
270
4
  const uint8_t *secStart = file->codeSection->Content.data();
271
4
  const uint8_t *funcStart = secStart + getInputSectionOffset();
272
4
  const uint8_t *end = funcStart + function->Size;
273
4
  uint32_t count;
274
4
  decodeULEB128(funcStart, &count);
275
4
  funcStart += count;
276
4
277
4
  LLVM_DEBUG(dbgs() << "write func: " << getName() << "\n");
278
4
  buf += encodeULEB128(compressedFuncSize, buf);
279
4
  const uint8_t *lastRelocEnd = funcStart;
280
4
  for (const WasmRelocation &rel : relocations) {
281
4
    unsigned chunkSize = (secStart + rel.Offset) - lastRelocEnd;
282
4
    LLVM_DEBUG(dbgs() << "  write chunk: " << chunkSize << "\n");
283
4
    memcpy(buf, lastRelocEnd, chunkSize);
284
4
    buf += chunkSize;
285
4
    buf += writeCompressedReloc(buf, rel, file->calcNewValue(rel));
286
4
    lastRelocEnd = secStart + rel.Offset + getRelocWidthPadded(rel);
287
4
  }
288
4
289
4
  unsigned chunkSize = end - lastRelocEnd;
290
4
  LLVM_DEBUG(dbgs() << "  write final chunk: " << chunkSize << "\n");
291
4
  memcpy(buf, lastRelocEnd, chunkSize);
292
4
  LLVM_DEBUG(dbgs() << "  total: " << (buf + chunkSize - orig) << "\n");
293
4
}
294
295
// Generate code to apply relocations to the data section at runtime.
296
// This is only called when generating shared libaries (PIC) where address are
297
// not known at static link time.
298
12
void InputSegment::generateRelocationCode(raw_ostream &os) const {
299
12
  LLVM_DEBUG(dbgs() << "generating runtime relocations: " << getName()
300
12
                    << " count=" << relocations.size() << "\n");
301
12
302
12
  // TODO(sbc): Encode the relocations in the data section and write a loop
303
12
  // here to apply them.
304
12
  uint32_t segmentVA = outputSeg->startVA + outputSegmentOffset;
305
12
  for (const WasmRelocation &rel : relocations) {
306
8
    uint32_t offset = rel.Offset - getInputSectionOffset();
307
8
    uint32_t outputOffset = segmentVA + offset;
308
8
309
8
    LLVM_DEBUG(dbgs() << "gen reloc: type=" << relocTypeToString(rel.Type)
310
8
                      << " addend=" << rel.Addend << " index=" << rel.Index
311
8
                      << " output offset=" << outputOffset << "\n");
312
8
313
8
    // Get __memory_base
314
8
    writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
315
8
    writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base");
316
8
317
8
    // Add the offset of the relocation
318
8
    writeU8(os, WASM_OPCODE_I32_CONST, "I32_CONST");
319
8
    writeSleb128(os, outputOffset, "offset");
320
8
    writeU8(os, WASM_OPCODE_I32_ADD, "ADD");
321
8
322
8
    Symbol *sym = file->getSymbol(rel);
323
8
    // Now figure out what we want to store
324
8
    if (sym->hasGOTIndex()) {
325
5
      writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
326
5
      writeUleb128(os, sym->getGOTIndex(), "global index");
327
5
      if (rel.Addend) {
328
1
        writeU8(os, WASM_OPCODE_I32_CONST, "CONST");
329
1
        writeSleb128(os, rel.Addend, "addend");
330
1
        writeU8(os, WASM_OPCODE_I32_ADD, "ADD");
331
1
      }
332
5
    } else {
333
3
      const GlobalSymbol* baseSymbol = WasmSym::memoryBase;
334
3
      if (rel.Type == R_WASM_TABLE_INDEX_I32)
335
2
        baseSymbol = WasmSym::tableBase;
336
3
      writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
337
3
      writeUleb128(os, baseSymbol->getGlobalIndex(), "base");
338
3
      writeU8(os, WASM_OPCODE_I32_CONST, "CONST");
339
3
      writeSleb128(os, file->calcNewValue(rel), "offset");
340
3
      writeU8(os, WASM_OPCODE_I32_ADD, "ADD");
341
3
    }
342
8
343
8
    // Store that value at the virtual address
344
8
    writeU8(os, WASM_OPCODE_I32_STORE, "I32_STORE");
345
8
    writeUleb128(os, 2, "align");
346
8
    writeUleb128(os, 0, "offset");
347
8
  }
348
12
}