blob: 1b4ef8a8228ef659e8e4ef527c170dbdcd411e75 [file] [log] [blame]
Sam Cleggc94d3932017-11-17 18:14:09 +00001//===- OutputSections.cpp -------------------------------------------------===//
2//
3// The LLVM Linker
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "OutputSections.h"
11
12#include "Config.h"
13#include "InputFiles.h"
Sam Cleggc94d3932017-11-17 18:14:09 +000014#include "OutputSegment.h"
15#include "SymbolTable.h"
16#include "lld/Common/ErrorHandler.h"
Rui Ueyama2017d522017-11-28 20:39:17 +000017#include "lld/Common/Memory.h"
Sam Cleggc94d3932017-11-17 18:14:09 +000018#include "lld/Common/Threads.h"
19#include "llvm/ADT/Twine.h"
20#include "llvm/Support/LEB128.h"
21
22#define DEBUG_TYPE "lld"
23
24using namespace llvm;
25using namespace llvm::wasm;
26using namespace lld;
27using namespace lld::wasm;
28
29enum class RelocEncoding {
30 Uleb128,
31 Sleb128,
32 I32,
33};
34
35static StringRef sectionTypeToString(uint32_t SectionType) {
36 switch (SectionType) {
37 case WASM_SEC_CUSTOM:
38 return "CUSTOM";
39 case WASM_SEC_TYPE:
40 return "TYPE";
41 case WASM_SEC_IMPORT:
42 return "IMPORT";
43 case WASM_SEC_FUNCTION:
44 return "FUNCTION";
45 case WASM_SEC_TABLE:
46 return "TABLE";
47 case WASM_SEC_MEMORY:
48 return "MEMORY";
49 case WASM_SEC_GLOBAL:
50 return "GLOBAL";
51 case WASM_SEC_EXPORT:
52 return "EXPORT";
53 case WASM_SEC_START:
54 return "START";
55 case WASM_SEC_ELEM:
56 return "ELEM";
57 case WASM_SEC_CODE:
58 return "CODE";
59 case WASM_SEC_DATA:
60 return "DATA";
61 default:
62 fatal("invalid section type");
63 }
64}
65
66std::string lld::toString(OutputSection *Section) {
67 std::string rtn = sectionTypeToString(Section->Type);
68 if (!Section->Name.empty())
69 rtn += "(" + Section->Name + ")";
70 return rtn;
71}
72
73static void applyRelocation(uint8_t *Buf, const OutputRelocation &Reloc) {
74 DEBUG(dbgs() << "write reloc: type=" << Reloc.Reloc.Type
Sam Clegg74fe0ba2017-12-07 01:51:24 +000075 << " index=" << Reloc.Reloc.Index << " value=" << Reloc.Value
76 << " offset=" << Reloc.Reloc.Offset << "\n");
Sam Cleggc94d3932017-11-17 18:14:09 +000077 Buf += Reloc.Reloc.Offset;
78 int64_t ExistingValue;
79 switch (Reloc.Reloc.Type) {
80 case R_WEBASSEMBLY_TYPE_INDEX_LEB:
81 case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
82 ExistingValue = decodeULEB128(Buf);
83 if (ExistingValue != Reloc.Reloc.Index) {
84 DEBUG(dbgs() << "existing value: " << decodeULEB128(Buf) << "\n");
85 assert(decodeULEB128(Buf) == Reloc.Reloc.Index);
86 }
87 LLVM_FALLTHROUGH;
88 case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
89 case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
90 encodeULEB128(Reloc.Value, Buf, 5);
91 break;
92 case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
93 ExistingValue = decodeSLEB128(Buf);
94 if (ExistingValue != Reloc.Reloc.Index) {
95 DEBUG(dbgs() << "existing value: " << decodeSLEB128(Buf) << "\n");
96 assert(decodeSLEB128(Buf) == Reloc.Reloc.Index);
97 }
98 LLVM_FALLTHROUGH;
99 case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
100 encodeSLEB128(static_cast<int32_t>(Reloc.Value), Buf, 5);
101 break;
102 case R_WEBASSEMBLY_TABLE_INDEX_I32:
103 case R_WEBASSEMBLY_MEMORY_ADDR_I32:
104 support::endian::write32<support::little>(Buf, Reloc.Value);
105 break;
106 default:
107 llvm_unreachable("unknown relocation type");
108 }
109}
110
Sam Cleggca16b732017-12-18 20:20:24 +0000111static void applyRelocations(uint8_t *Buf, ArrayRef<OutputRelocation> Relocs) {
Sam Cleggc94d3932017-11-17 18:14:09 +0000112 log("applyRelocations: count=" + Twine(Relocs.size()));
113 for (const OutputRelocation &Reloc : Relocs) {
114 applyRelocation(Buf, Reloc);
115 }
116}
117
118// Relocations contain an index into the function, global or table index
119// space of the input file. This function takes a relocation and returns the
120// relocated index (i.e. translates from the input index space to the output
121// index space).
122static uint32_t calcNewIndex(const ObjFile &File, const WasmRelocation &Reloc) {
123 switch (Reloc.Type) {
124 case R_WEBASSEMBLY_TYPE_INDEX_LEB:
125 return File.relocateTypeIndex(Reloc.Index);
126 case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
127 return File.relocateFunctionIndex(Reloc.Index);
128 case R_WEBASSEMBLY_TABLE_INDEX_I32:
129 case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
130 return File.relocateTableIndex(Reloc.Index);
131 case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
132 case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
133 case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
134 case R_WEBASSEMBLY_MEMORY_ADDR_I32:
135 return File.relocateGlobalIndex(Reloc.Index);
136 default:
137 llvm_unreachable("unknown relocation type");
138 }
139}
140
141// Take a vector of relocations from an input file and create output
142// relocations based on them. Calculates the updated index and offset for
143// each relocation as well as the value to write out in the final binary.
144static void calcRelocations(const ObjFile &File,
145 ArrayRef<WasmRelocation> Relocs,
146 std::vector<OutputRelocation> &OutputRelocs,
147 int32_t OutputOffset) {
148 log("calcRelocations: " + File.getName() + " offset=" + Twine(OutputOffset));
149 for (const WasmRelocation &Reloc : Relocs) {
Sam Cleggc94d3932017-11-17 18:14:09 +0000150 OutputRelocation NewReloc;
151 NewReloc.Reloc = Reloc;
152 NewReloc.Reloc.Offset += OutputOffset;
Sam Cleggc94d3932017-11-17 18:14:09 +0000153 DEBUG(dbgs() << "reloc: type=" << Reloc.Type << " index=" << Reloc.Index
Sam Clegg74fe0ba2017-12-07 01:51:24 +0000154 << " offset=" << Reloc.Offset
Sam Cleggc94d3932017-11-17 18:14:09 +0000155 << " newOffset=" << NewReloc.Reloc.Offset << "\n");
156
Sam Clegg74fe0ba2017-12-07 01:51:24 +0000157 if (Config->EmitRelocs)
158 NewReloc.NewIndex = calcNewIndex(File, Reloc);
159 else
160 NewReloc.NewIndex = UINT32_MAX;
161
Sam Cleggc94d3932017-11-17 18:14:09 +0000162 switch (Reloc.Type) {
163 case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
164 case R_WEBASSEMBLY_MEMORY_ADDR_I32:
165 case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
166 NewReloc.Value = File.getRelocatedAddress(Reloc.Index);
167 if (NewReloc.Value != UINT32_MAX)
168 NewReloc.Value += Reloc.Addend;
169 break;
170 default:
Sam Clegg74fe0ba2017-12-07 01:51:24 +0000171 NewReloc.Value = calcNewIndex(File, Reloc);
172 break;
Sam Cleggc94d3932017-11-17 18:14:09 +0000173 }
174
175 OutputRelocs.emplace_back(NewReloc);
176 }
177}
178
179void OutputSection::createHeader(size_t BodySize) {
180 raw_string_ostream OS(Header);
181 debugWrite(OS.tell(),
182 "section type [" + Twine(sectionTypeToString(Type)) + "]");
183 writeUleb128(OS, Type, nullptr);
184 writeUleb128(OS, BodySize, "section size");
185 OS.flush();
186 log("createHeader: " + toString(this) + " body=" + Twine(BodySize) +
187 " total=" + Twine(getSize()));
188}
189
Sam Clegg0fb6faa2017-12-08 01:09:21 +0000190CodeSection::CodeSection(uint32_t NumFunctions, ArrayRef<ObjFile *> Objs)
Sam Cleggc94d3932017-11-17 18:14:09 +0000191 : OutputSection(WASM_SEC_CODE), InputObjects(Objs) {
192 raw_string_ostream OS(CodeSectionHeader);
193 writeUleb128(OS, NumFunctions, "function count");
194 OS.flush();
195 BodySize = CodeSectionHeader.size();
196
197 for (ObjFile *File : InputObjects) {
198 if (!File->CodeSection)
199 continue;
200
201 File->CodeOffset = BodySize;
202 ArrayRef<uint8_t> Content = File->CodeSection->Content;
203 unsigned HeaderSize = 0;
204 decodeULEB128(Content.data(), &HeaderSize);
205
206 calcRelocations(*File, File->CodeSection->Relocations,
207 File->CodeRelocations, BodySize - HeaderSize);
208
209 size_t PayloadSize = Content.size() - HeaderSize;
210 BodySize += PayloadSize;
211 }
212
213 createHeader(BodySize);
214}
215
216void CodeSection::writeTo(uint8_t *Buf) {
217 log("writing " + toString(this));
218 log(" size=" + Twine(getSize()));
219 Buf += Offset;
220
221 // Write section header
222 memcpy(Buf, Header.data(), Header.size());
223 Buf += Header.size();
224
225 uint8_t *ContentsStart = Buf;
226
227 // Write code section headers
228 memcpy(Buf, CodeSectionHeader.data(), CodeSectionHeader.size());
229 Buf += CodeSectionHeader.size();
230
231 // Write code section bodies
232 parallelForEach(InputObjects, [ContentsStart](ObjFile *File) {
233 if (!File->CodeSection)
234 return;
235
236 ArrayRef<uint8_t> Content(File->CodeSection->Content);
237
238 // Payload doesn't include the initial header (function count)
239 unsigned HeaderSize = 0;
240 decodeULEB128(Content.data(), &HeaderSize);
241
242 size_t PayloadSize = Content.size() - HeaderSize;
243 memcpy(ContentsStart + File->CodeOffset, Content.data() + HeaderSize,
244 PayloadSize);
245
246 log("applying relocations for: " + File->getName());
247 if (File->CodeRelocations.size())
248 applyRelocations(ContentsStart, File->CodeRelocations);
249 });
250}
251
252uint32_t CodeSection::numRelocations() const {
253 uint32_t Count = 0;
254 for (ObjFile *File : InputObjects)
255 Count += File->CodeRelocations.size();
256 return Count;
257}
258
259void CodeSection::writeRelocations(raw_ostream &OS) const {
260 for (ObjFile *File : InputObjects)
261 for (const OutputRelocation &Reloc : File->CodeRelocations)
262 writeReloc(OS, Reloc);
263}
264
Sam Clegg0fb6faa2017-12-08 01:09:21 +0000265DataSection::DataSection(ArrayRef<OutputSegment *> Segments)
Sam Cleggc94d3932017-11-17 18:14:09 +0000266 : OutputSection(WASM_SEC_DATA), Segments(Segments) {
267 raw_string_ostream OS(DataSectionHeader);
268
269 writeUleb128(OS, Segments.size(), "data segment count");
270 OS.flush();
271 BodySize = DataSectionHeader.size();
272
273 for (OutputSegment *Segment : Segments) {
274 raw_string_ostream OS(Segment->Header);
275 writeUleb128(OS, 0, "memory index");
276 writeUleb128(OS, WASM_OPCODE_I32_CONST, "opcode:i32const");
277 writeSleb128(OS, Segment->StartVA, "memory offset");
278 writeUleb128(OS, WASM_OPCODE_END, "opcode:end");
279 writeUleb128(OS, Segment->Size, "segment size");
280 OS.flush();
281 Segment->setSectionOffset(BodySize);
282 BodySize += Segment->Header.size();
283 log("Data segment: size=" + Twine(Segment->Size));
284 for (const InputSegment *InputSeg : Segment->InputSegments) {
285 uint32_t InputOffset = InputSeg->getInputSectionOffset();
286 uint32_t OutputOffset = Segment->getSectionOffset() +
287 Segment->Header.size() +
288 InputSeg->getOutputSegmentOffset();
289 calcRelocations(*InputSeg->File, InputSeg->Relocations, Relocations,
290 OutputOffset - InputOffset);
291 }
292 BodySize += Segment->Size;
293 }
294
295 createHeader(BodySize);
296}
297
298void DataSection::writeTo(uint8_t *Buf) {
299 log("writing " + toString(this) + " size=" + Twine(getSize()) +
300 " body=" + Twine(BodySize));
301 Buf += Offset;
302
303 // Write section header
304 memcpy(Buf, Header.data(), Header.size());
305 Buf += Header.size();
306
307 uint8_t *ContentsStart = Buf;
308
309 // Write data section headers
310 memcpy(Buf, DataSectionHeader.data(), DataSectionHeader.size());
311
312 parallelForEach(Segments, [ContentsStart](const OutputSegment *Segment) {
313 // Write data segment header
314 uint8_t *SegStart = ContentsStart + Segment->getSectionOffset();
315 memcpy(SegStart, Segment->Header.data(), Segment->Header.size());
316
317 // Write segment data payload
318 for (const InputSegment *Input : Segment->InputSegments) {
319 ArrayRef<uint8_t> Content(Input->Segment->Data.Content);
320 memcpy(SegStart + Segment->Header.size() +
321 Input->getOutputSegmentOffset(),
322 Content.data(), Content.size());
323 }
324 });
325
326 applyRelocations(ContentsStart, Relocations);
327}
328
329void DataSection::writeRelocations(raw_ostream &OS) const {
330 for (const OutputRelocation &Reloc : Relocations)
331 writeReloc(OS, Reloc);
332}