blob: 444099ca8327e6e658ad3e0a386b9f21b3d39312 [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"
14#include "Memory.h"
15#include "OutputSegment.h"
16#include "SymbolTable.h"
17#include "lld/Common/ErrorHandler.h"
18#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
75 << " index=" << Reloc.Reloc.Index << " new=" << Reloc.NewIndex
76 << " value=" << Reloc.Value << " offset=" << Reloc.Reloc.Offset
77 << "\n");
78 Buf += Reloc.Reloc.Offset;
79 int64_t ExistingValue;
80 switch (Reloc.Reloc.Type) {
81 case R_WEBASSEMBLY_TYPE_INDEX_LEB:
82 case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
83 ExistingValue = decodeULEB128(Buf);
84 if (ExistingValue != Reloc.Reloc.Index) {
85 DEBUG(dbgs() << "existing value: " << decodeULEB128(Buf) << "\n");
86 assert(decodeULEB128(Buf) == Reloc.Reloc.Index);
87 }
88 LLVM_FALLTHROUGH;
89 case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
90 case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
91 encodeULEB128(Reloc.Value, Buf, 5);
92 break;
93 case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
94 ExistingValue = decodeSLEB128(Buf);
95 if (ExistingValue != Reloc.Reloc.Index) {
96 DEBUG(dbgs() << "existing value: " << decodeSLEB128(Buf) << "\n");
97 assert(decodeSLEB128(Buf) == Reloc.Reloc.Index);
98 }
99 LLVM_FALLTHROUGH;
100 case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
101 encodeSLEB128(static_cast<int32_t>(Reloc.Value), Buf, 5);
102 break;
103 case R_WEBASSEMBLY_TABLE_INDEX_I32:
104 case R_WEBASSEMBLY_MEMORY_ADDR_I32:
105 support::endian::write32<support::little>(Buf, Reloc.Value);
106 break;
107 default:
108 llvm_unreachable("unknown relocation type");
109 }
110}
111
112static void applyRelocations(uint8_t *Buf,
113 const std::vector<OutputRelocation> &Relocs) {
114 log("applyRelocations: count=" + Twine(Relocs.size()));
115 for (const OutputRelocation &Reloc : Relocs) {
116 applyRelocation(Buf, Reloc);
117 }
118}
119
120// Relocations contain an index into the function, global or table index
121// space of the input file. This function takes a relocation and returns the
122// relocated index (i.e. translates from the input index space to the output
123// index space).
124static uint32_t calcNewIndex(const ObjFile &File, const WasmRelocation &Reloc) {
125 switch (Reloc.Type) {
126 case R_WEBASSEMBLY_TYPE_INDEX_LEB:
127 return File.relocateTypeIndex(Reloc.Index);
128 case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
129 return File.relocateFunctionIndex(Reloc.Index);
130 case R_WEBASSEMBLY_TABLE_INDEX_I32:
131 case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
132 return File.relocateTableIndex(Reloc.Index);
133 case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
134 case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
135 case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
136 case R_WEBASSEMBLY_MEMORY_ADDR_I32:
137 return File.relocateGlobalIndex(Reloc.Index);
138 default:
139 llvm_unreachable("unknown relocation type");
140 }
141}
142
143// Take a vector of relocations from an input file and create output
144// relocations based on them. Calculates the updated index and offset for
145// each relocation as well as the value to write out in the final binary.
146static void calcRelocations(const ObjFile &File,
147 ArrayRef<WasmRelocation> Relocs,
148 std::vector<OutputRelocation> &OutputRelocs,
149 int32_t OutputOffset) {
150 log("calcRelocations: " + File.getName() + " offset=" + Twine(OutputOffset));
151 for (const WasmRelocation &Reloc : Relocs) {
152 int64_t NewIndex = calcNewIndex(File, Reloc);
153 OutputRelocation NewReloc;
154 NewReloc.Reloc = Reloc;
155 NewReloc.Reloc.Offset += OutputOffset;
156 NewReloc.NewIndex = NewIndex;
157 DEBUG(dbgs() << "reloc: type=" << Reloc.Type << " index=" << Reloc.Index
158 << " offset=" << Reloc.Offset << " new=" << NewIndex
159 << " newOffset=" << NewReloc.Reloc.Offset << "\n");
160
161 switch (Reloc.Type) {
162 case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
163 case R_WEBASSEMBLY_MEMORY_ADDR_I32:
164 case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
165 NewReloc.Value = File.getRelocatedAddress(Reloc.Index);
166 if (NewReloc.Value != UINT32_MAX)
167 NewReloc.Value += Reloc.Addend;
168 break;
169 default:
170 NewReloc.Value = NewIndex;
171 }
172
173 OutputRelocs.emplace_back(NewReloc);
174 }
175}
176
177void OutputSection::createHeader(size_t BodySize) {
178 raw_string_ostream OS(Header);
179 debugWrite(OS.tell(),
180 "section type [" + Twine(sectionTypeToString(Type)) + "]");
181 writeUleb128(OS, Type, nullptr);
182 writeUleb128(OS, BodySize, "section size");
183 OS.flush();
184 log("createHeader: " + toString(this) + " body=" + Twine(BodySize) +
185 " total=" + Twine(getSize()));
186}
187
188CodeSection::CodeSection(uint32_t NumFunctions, std::vector<ObjFile *> &Objs)
189 : OutputSection(WASM_SEC_CODE), InputObjects(Objs) {
190 raw_string_ostream OS(CodeSectionHeader);
191 writeUleb128(OS, NumFunctions, "function count");
192 OS.flush();
193 BodySize = CodeSectionHeader.size();
194
195 for (ObjFile *File : InputObjects) {
196 if (!File->CodeSection)
197 continue;
198
199 File->CodeOffset = BodySize;
200 ArrayRef<uint8_t> Content = File->CodeSection->Content;
201 unsigned HeaderSize = 0;
202 decodeULEB128(Content.data(), &HeaderSize);
203
204 calcRelocations(*File, File->CodeSection->Relocations,
205 File->CodeRelocations, BodySize - HeaderSize);
206
207 size_t PayloadSize = Content.size() - HeaderSize;
208 BodySize += PayloadSize;
209 }
210
211 createHeader(BodySize);
212}
213
214void CodeSection::writeTo(uint8_t *Buf) {
215 log("writing " + toString(this));
216 log(" size=" + Twine(getSize()));
217 Buf += Offset;
218
219 // Write section header
220 memcpy(Buf, Header.data(), Header.size());
221 Buf += Header.size();
222
223 uint8_t *ContentsStart = Buf;
224
225 // Write code section headers
226 memcpy(Buf, CodeSectionHeader.data(), CodeSectionHeader.size());
227 Buf += CodeSectionHeader.size();
228
229 // Write code section bodies
230 parallelForEach(InputObjects, [ContentsStart](ObjFile *File) {
231 if (!File->CodeSection)
232 return;
233
234 ArrayRef<uint8_t> Content(File->CodeSection->Content);
235
236 // Payload doesn't include the initial header (function count)
237 unsigned HeaderSize = 0;
238 decodeULEB128(Content.data(), &HeaderSize);
239
240 size_t PayloadSize = Content.size() - HeaderSize;
241 memcpy(ContentsStart + File->CodeOffset, Content.data() + HeaderSize,
242 PayloadSize);
243
244 log("applying relocations for: " + File->getName());
245 if (File->CodeRelocations.size())
246 applyRelocations(ContentsStart, File->CodeRelocations);
247 });
248}
249
250uint32_t CodeSection::numRelocations() const {
251 uint32_t Count = 0;
252 for (ObjFile *File : InputObjects)
253 Count += File->CodeRelocations.size();
254 return Count;
255}
256
257void CodeSection::writeRelocations(raw_ostream &OS) const {
258 for (ObjFile *File : InputObjects)
259 for (const OutputRelocation &Reloc : File->CodeRelocations)
260 writeReloc(OS, Reloc);
261}
262
263DataSection::DataSection(std::vector<OutputSegment *> &Segments)
264 : OutputSection(WASM_SEC_DATA), Segments(Segments) {
265 raw_string_ostream OS(DataSectionHeader);
266
267 writeUleb128(OS, Segments.size(), "data segment count");
268 OS.flush();
269 BodySize = DataSectionHeader.size();
270
271 for (OutputSegment *Segment : Segments) {
272 raw_string_ostream OS(Segment->Header);
273 writeUleb128(OS, 0, "memory index");
274 writeUleb128(OS, WASM_OPCODE_I32_CONST, "opcode:i32const");
275 writeSleb128(OS, Segment->StartVA, "memory offset");
276 writeUleb128(OS, WASM_OPCODE_END, "opcode:end");
277 writeUleb128(OS, Segment->Size, "segment size");
278 OS.flush();
279 Segment->setSectionOffset(BodySize);
280 BodySize += Segment->Header.size();
281 log("Data segment: size=" + Twine(Segment->Size));
282 for (const InputSegment *InputSeg : Segment->InputSegments) {
283 uint32_t InputOffset = InputSeg->getInputSectionOffset();
284 uint32_t OutputOffset = Segment->getSectionOffset() +
285 Segment->Header.size() +
286 InputSeg->getOutputSegmentOffset();
287 calcRelocations(*InputSeg->File, InputSeg->Relocations, Relocations,
288 OutputOffset - InputOffset);
289 }
290 BodySize += Segment->Size;
291 }
292
293 createHeader(BodySize);
294}
295
296void DataSection::writeTo(uint8_t *Buf) {
297 log("writing " + toString(this) + " size=" + Twine(getSize()) +
298 " body=" + Twine(BodySize));
299 Buf += Offset;
300
301 // Write section header
302 memcpy(Buf, Header.data(), Header.size());
303 Buf += Header.size();
304
305 uint8_t *ContentsStart = Buf;
306
307 // Write data section headers
308 memcpy(Buf, DataSectionHeader.data(), DataSectionHeader.size());
309
310 parallelForEach(Segments, [ContentsStart](const OutputSegment *Segment) {
311 // Write data segment header
312 uint8_t *SegStart = ContentsStart + Segment->getSectionOffset();
313 memcpy(SegStart, Segment->Header.data(), Segment->Header.size());
314
315 // Write segment data payload
316 for (const InputSegment *Input : Segment->InputSegments) {
317 ArrayRef<uint8_t> Content(Input->Segment->Data.Content);
318 memcpy(SegStart + Segment->Header.size() +
319 Input->getOutputSegmentOffset(),
320 Content.data(), Content.size());
321 }
322 });
323
324 applyRelocations(ContentsStart, Relocations);
325}
326
327void DataSection::writeRelocations(raw_ostream &OS) const {
328 for (const OutputRelocation &Reloc : Relocations)
329 writeReloc(OS, Reloc);
330}