]> git.proxmox.com Git - rustc.git/blame - src/binaryen/src/wasm-linker.cpp
New upstream version 1.25.0+dfsg1
[rustc.git] / src / binaryen / src / wasm-linker.cpp
CommitLineData
abe05a73
XL
1/*
2 * Copyright 2016 WebAssembly Community Group participants
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "wasm-linker.h"
18#include "asm_v_wasm.h"
19#include "ir/utils.h"
20#include "s2wasm.h"
21#include "support/utilities.h"
22#include "wasm-builder.h"
23#include "wasm-emscripten.h"
24#include "wasm-printing.h"
25
26using namespace wasm;
27
28// Name of the dummy function to prevent erroneous nullptr comparisons.
29static constexpr const char* dummyFunction = "__wasm_nullptr";
30static constexpr const char* stackPointer = "__stack_pointer";
31
32void Linker::placeStackPointer(Address stackAllocation) {
33 // ensure this is the first allocation
34 assert(nextStatic == globalBase || nextStatic == 1);
35 const Address pointerSize = 4;
36 // Unconditionally allocate space for the stack pointer. Emscripten
37 // allocates the stack itself, and initializes the stack pointer itself.
38 out.addStatic(pointerSize, pointerSize, stackPointer);
39 if (stackAllocation) {
40 // If we are allocating the stack, set up a relocation to initialize the
41 // stack pointer to point to one past-the-end of the stack allocation.
42 std::vector<char> raw;
43 raw.resize(pointerSize);
44 auto relocation = new LinkerObject::Relocation(
45 LinkerObject::Relocation::kData, (uint32_t*)&raw[0], ".stack", stackAllocation);
46 out.addRelocation(relocation);
47 assert(out.wasm.memory.segments.empty());
48 out.addSegment(stackPointer, raw);
49 }
50}
51
52void Linker::ensureFunctionImport(Name target, std::string signature) {
53 if (!out.wasm.getImportOrNull(target)) {
54 auto import = new Import;
55 import->name = import->base = target;
56 import->module = ENV;
57 import->functionType = ensureFunctionType(signature, &out.wasm)->name;
58 import->kind = ExternalKind::Function;
59 out.wasm.addImport(import);
60 }
61}
62
63void Linker::ensureObjectImport(Name target) {
64 if (!out.wasm.getImportOrNull(target)) {
65 auto import = new Import;
66 import->name = import->base = target;
67 import->module = ENV;
68 import->kind = ExternalKind::Global;
69 import->globalType = i32;
70 out.wasm.addImport(import);
71 }
72}
73
74void Linker::layout() {
75 // Convert calls to undefined functions to call_imports
76 for (const auto& f : out.undefinedFunctionCalls) {
77 Name target = f.first;
78 if (!out.symbolInfo.undefinedFunctions.count(target)) continue;
79 // Create an import for the target if necessary.
80 ensureFunctionImport(target, getSig(*f.second.begin()));
81 // Change each call. The target is the same since it's still the name.
82 // Delete and re-allocate the Expression as CallImport to avoid undefined
83 // behavior.
84 for (auto* call : f.second) {
85 auto type = call->type;
86 ExpressionList operands(out.wasm.allocator);
87 operands.swap(call->operands);
88 auto target = call->target;
89 CallImport* newCall = ExpressionManipulator::convert<Call, CallImport>(call, out.wasm.allocator);
90 newCall->type = type;
91 newCall->operands.swap(operands);
92 newCall->target = target;
93 }
94 }
95
96 // Allocate all user statics
97 for (const auto& obj : out.staticObjects) {
98 allocateStatic(obj.allocSize, obj.alignment, obj.name);
99 }
100
101 // Update the segments with their addresses now that they have been allocated.
102 for (const auto& seg : out.segments) {
103 Address address = staticAddresses[seg.first];
104 out.wasm.memory.segments[seg.second].offset = out.wasm.allocator.alloc<Const>()->set(Literal(uint32_t(address)));
105 segmentsByAddress[address] = seg.second;
106 }
107
108 // Place the stack after the user's static data, to keep those addresses
109 // small.
110 if (stackAllocation) allocateStatic(stackAllocation, 16, ".stack");
111
112 // The minimum initial memory size is the amount of static variables we have
113 // allocated. Round it up to a page, and update the page-increment versions
114 // of initial and max
115 Address initialMem = roundUpToPageSize(nextStatic);
116 if (userInitialMemory) {
117 if (initialMem > userInitialMemory) {
118 Fatal() << "Specified initial memory size " << userInitialMemory <<
119 " is smaller than required size " << initialMem;
120 }
121 out.wasm.memory.initial = userInitialMemory / Memory::kPageSize;
122 } else {
123 out.wasm.memory.initial = initialMem / Memory::kPageSize;
124 }
125 out.wasm.memory.exists = true;
126
127 if (userMaxMemory) out.wasm.memory.max = userMaxMemory / Memory::kPageSize;
128
129 if (importMemory) {
130 auto memoryImport = make_unique<Import>();
131 memoryImport->name = MEMORY;
132 memoryImport->module = ENV;
133 memoryImport->base = MEMORY;
134 memoryImport->kind = ExternalKind::Memory;
135 out.wasm.memory.imported = true;
136 out.wasm.addImport(memoryImport.release());
137 } else {
138 auto memoryExport = make_unique<Export>();
139 memoryExport->name = MEMORY;
140 memoryExport->value = Name::fromInt(0);
141 memoryExport->kind = ExternalKind::Memory;
142 out.wasm.addExport(memoryExport.release());
143 }
144
145 // Add imports for any imported objects
146 for (const auto& obj : out.symbolInfo.importedObjects) {
147 ensureObjectImport(obj);
148 }
149
150 // XXX For now, export all functions marked .globl.
151 for (Name name : out.globls) exportFunction(out.wasm, name, false);
152 for (Name name : out.initializerFunctions) exportFunction(out.wasm, name, true);
153
154 // Pad the indirect function table with a dummy function
155 makeDummyFunction();
156 // Always create a table, even if it's empty.
157 out.wasm.table.exists = true;
158
159 // Pre-assign the function indexes
160 for (auto& pair : out.indirectIndexes) {
161 if (functionIndexes.count(pair.first) != 0 ||
162 functionNames.count(pair.second) != 0) {
163 Fatal() << "Function " << pair.first << " already has an index " <<
164 functionIndexes[pair.first] << " while setting index " << pair.second;
165 }
166 if (debug) {
167 std::cerr << "pre-assigned function index: " << pair.first << ": "
168 << pair.second << '\n';
169 }
170 functionIndexes[pair.first] = pair.second;
171 functionNames[pair.second] = pair.first;
172 }
173
174 // Emit the pre-assigned function names in sorted order
175 for (const auto& P : functionNames) {
176 ensureTableSegment();
177 getTableDataRef().push_back(P.second);
178 }
179
180 for (auto& relocation : out.relocations) {
181 // TODO: Handle weak symbols properly, instead of always taking the weak definition.
182 auto *alias = out.getAlias(relocation->symbol, relocation->kind);
183 Name name = relocation->symbol;
184
185 if (debug) std::cerr << "fix relocation " << name << '\n';
186
187 if (alias) {
188 name = alias->symbol;
189 relocation->addend += alias->offset;
190 }
191
192 if (relocation->kind == LinkerObject::Relocation::kData) {
193 const auto& symbolAddress = staticAddresses.find(name);
194 if (symbolAddress == staticAddresses.end()) Fatal() << "Unknown relocation: " << name << '\n';
195 *(relocation->data) = symbolAddress->second + relocation->addend;
196 if (debug) std::cerr << " ==> " << *(relocation->data) << '\n';
197 } else {
198 // function address
199 if (!out.wasm.getFunctionOrNull(name)) {
200 if (FunctionType* f = out.getExternType(name)) {
201 // Address of an imported function is taken, but imports do not have addresses in wasm.
202 // Generate a thunk to forward to the call_import.
203 Function* thunk = getImportThunk(name, f);
204 *(relocation->data) = getFunctionIndex(thunk->name) + relocation->addend;
205 } else {
206 std::cerr << "Unknown symbol: " << name << '\n';
207 if (!ignoreUnknownSymbols) Fatal() << "undefined reference\n";
208 *(relocation->data) = 0;
209 }
210 } else {
211 *(relocation->data) = getFunctionIndex(name) + relocation->addend;
212 }
213 }
214 }
215 out.relocations.clear();
216
217 if (!!startFunction) {
218 if (out.symbolInfo.implementedFunctions.count(startFunction) == 0) {
219 Fatal() << "Unknown start function: `" << startFunction << "`\n";
220 }
221 const auto *target = out.wasm.getFunction(startFunction);
222 Name start("_start");
223 if (out.symbolInfo.implementedFunctions.count(start) != 0) {
224 Fatal() << "Start function already present: `" << start << "`\n";
225 }
226 auto* func = new Function;
227 func->name = start;
228 out.wasm.addFunction(func);
229 out.wasm.addStart(start);
230 Builder builder(out.wasm);
231 auto* block = builder.makeBlock();
232 func->body = block;
233 {
234 // Create the call, matching its parameters.
235 // TODO allow calling with non-default values.
236 std::vector<Expression*> args;
237 Index paramNum = 0;
238 for (WasmType type : target->params) {
239 Name name = Name::fromInt(paramNum++);
240 Builder::addVar(func, name, type);
241 auto* param = builder.makeGetLocal(func->getLocalIndex(name), type);
242 args.push_back(param);
243 }
244 auto* call = builder.makeCall(startFunction, args, target->result);
245 Expression* e = call;
246 if (target->result != none) e = builder.makeDrop(call);
247 block->list.push_back(e);
248 block->finalize();
249 }
250 }
251
252 // ensure an explicit function type for indirect call targets
253 for (auto& segment : out.wasm.table.segments) {
254 for (auto& name : segment.data) {
255 auto* func = out.wasm.getFunction(name);
256 func->type = ensureFunctionType(getSig(func), &out.wasm)->name;
257 }
258 }
259
260 // Export malloc/realloc/free/memalign whenever availble. JavsScript version of
261 // malloc has some issues and it cannot be called once _sbrk() is called, and
262 // JS glue code does not have realloc(). TODO This should get the list of
263 // exported functions from emcc.py - it has EXPORTED_FUNCTION metadata to keep
264 // track of this. Get the list of exported functions using a command-line
265 // argument from emcc.py and export all of them.
266 for (auto function : {"malloc", "free", "realloc", "memalign"}) {
267 if (out.symbolInfo.implementedFunctions.count(function)) {
268 exportFunction(out.wasm, function, true);
269 }
270 }
271
272 // finalize function table
273 unsigned int tableSize = getTableData().size();
274 if (tableSize > 0) {
275 out.wasm.table.initial = out.wasm.table.max = tableSize;
276 }
277}
278
279bool Linker::linkObject(S2WasmBuilder& builder) {
280 LinkerObject::SymbolInfo *newSymbols = builder.getSymbolInfo();
281 // check for multiple definitions
282 for (const Name& symbol : newSymbols->implementedFunctions) {
283 if (out.symbolInfo.implementedFunctions.count(symbol)) {
284 // TODO: Figure out error handling for library-style pieces
285 // TODO: give LinkerObjects (or builders) names for better errors.
286 std::cerr << "Error: multiple definition of symbol " << symbol << "\n";
287 return false;
288 }
289 }
290 // Allow duplicate aliases only if they refer to the same name. For now we
291 // do not expect aliases in compiler-rt files.
292 // TODO: figure out what the semantics of merging aliases should be.
293 for (const auto& alias : newSymbols->aliasedSymbols) {
294 if (out.symbolInfo.aliasedSymbols.count(alias.first) &&
295 (out.symbolInfo.aliasedSymbols.at(alias.first).symbol != alias.second.symbol ||
296 out.symbolInfo.aliasedSymbols.at(alias.first).kind != alias.second.kind)) {
297 std::cerr << "Error: conflicting definitions for alias "
298 << alias.first.c_str() << "of type " << alias.second.kind << "\n";
299 return false;
300 }
301 }
302 out.symbolInfo.merge(*newSymbols);
303 builder.build(&out);
304 return true;
305}
306
307bool Linker::linkArchive(Archive& archive) {
308 bool selected;
309 do {
310 selected = false;
311 for (auto child = archive.child_begin(), end = archive.child_end();
312 child != end; ++child) {
313 Archive::SubBuffer memberBuf = child->getBuffer();
314 // S2WasmBuilder expects its input to be NUL-terminated. Archive members
315 // are
316 // not NUL-terminated. So we have to copy the contents out before parsing.
317 std::vector<char> memberString(memberBuf.len + 1);
318 memcpy(memberString.data(), memberBuf.data, memberBuf.len);
319 memberString[memberBuf.len] = '\0';
320 S2WasmBuilder memberBuilder(memberString.data(), false);
321 auto* memberSymbols = memberBuilder.getSymbolInfo();
322 for (const Name& symbol : memberSymbols->implementedFunctions) {
323 if (out.symbolInfo.undefinedFunctions.count(symbol)) {
324 if (!linkObject(memberBuilder)) return false;
325 selected = true;
326 break;
327 }
328 }
329 }
330 // If we selected an archive member, it may depend on another archive member
331 // so continue to make passes over the members until no more are added.
332 } while (selected);
333 return true;
334}
335
336Address Linker::getStaticBump() const {
337 return nextStatic - globalBase;
338}
339
340void Linker::ensureTableSegment() {
341 if (out.wasm.table.segments.size() == 0) {
342 auto emptySegment = out.wasm.allocator.alloc<Const>()->set(Literal(uint32_t(0)));
343 out.wasm.table.segments.emplace_back(emptySegment);
344 }
345}
346
347std::vector<Name>& Linker::getTableDataRef() {
348 assert(out.wasm.table.segments.size() == 1);
349 return out.wasm.table.segments[0].data;
350}
351
352std::vector<Name> Linker::getTableData() {
353 if (out.wasm.table.segments.size() > 0) {
354 return getTableDataRef();
355 }
356 return {};
357}
358
359Index Linker::getFunctionIndex(Name name) {
360 if (!functionIndexes.count(name)) {
361 ensureTableSegment();
362 functionIndexes[name] = getTableData().size();
363 getTableDataRef().push_back(name);
364 if (debug) {
365 std::cerr << "function index: " << name << ": "
366 << functionIndexes[name] << '\n';
367 }
368 }
369 return functionIndexes[name];
370}
371
372void Linker::makeDummyFunction() {
373 bool create = false;
374 // Check if there are address-taken functions
375 for (auto& relocation : out.relocations) {
376 if (relocation->kind == LinkerObject::Relocation::kFunction) {
377 create = true;
378 break;
379 }
380 }
381
382 if (!create) return;
383 wasm::Builder wasmBuilder(out.wasm);
384 Expression *unreachable = wasmBuilder.makeUnreachable();
385 Function *dummy = wasmBuilder.makeFunction(Name(dummyFunction), {}, WasmType::none, {}, unreachable);
386 out.wasm.addFunction(dummy);
387 getFunctionIndex(dummy->name);
388}
389
390Function* Linker::getImportThunk(Name name, const FunctionType* funcType) {
391 Name thunkName = std::string("__importThunk_") + name.c_str();
392 if (Function* thunk = out.wasm.getFunctionOrNull(thunkName)) return thunk;
393 ensureFunctionImport(name, getSig(funcType));
394 wasm::Builder wasmBuilder(out.wasm);
395 std::vector<NameType> params;
396 Index p = 0;
397 for (const auto& ty : funcType->params) params.emplace_back(std::to_string(p++), ty);
398 Function *f = wasmBuilder.makeFunction(thunkName, std::move(params), funcType->result, {});
399 std::vector<Expression*> args;
400 for (Index i = 0; i < funcType->params.size(); ++i) {
401 args.push_back(wasmBuilder.makeGetLocal(i, funcType->params[i]));
402 }
403 Expression* call = wasmBuilder.makeCallImport(name, args, funcType->result);
404 f->body = call;
405 out.wasm.addFunction(f);
406 return f;
407}
408
409Address Linker::getStackPointerAddress() const {
410 return Address(staticAddresses.at(stackPointer));
411}