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-emscripten.h"
18
19 #include <sstream>
20
21 #include "asm_v_wasm.h"
22 #include "asmjs/shared-constants.h"
23 #include "ir/import-utils.h"
24 #include "ir/literal-utils.h"
25 #include "ir/module-utils.h"
26 #include "ir/table-utils.h"
27 #include "shared-constants.h"
28 #include "support/debug.h"
29 #include "wasm-builder.h"
30 #include "wasm-traversal.h"
31 #include "wasm.h"
32
33 #define DEBUG_TYPE "emscripten"
34
35 namespace wasm {
36
37 cashew::IString EM_ASM_PREFIX("emscripten_asm_const");
38 cashew::IString EM_JS_PREFIX("__em_js__");
39
40 static Name STACK_INIT("stack$init");
41 static Name POST_INSTANTIATE("__post_instantiate");
42
addExportedFunction(Module & wasm,Function * function)43 void addExportedFunction(Module& wasm, Function* function) {
44 wasm.addFunction(function);
45 auto export_ = new Export;
46 export_->name = export_->value = function->name;
47 export_->kind = ExternalKind::Function;
48 wasm.addExport(export_);
49 }
50
51 // TODO(sbc): There should probably be a better way to do this.
isExported(Module & wasm,Name name)52 bool isExported(Module& wasm, Name name) {
53 for (auto& ex : wasm.exports) {
54 if (ex->value == name) {
55 return true;
56 }
57 }
58 return false;
59 }
60
getStackPointerGlobal(Module & wasm)61 Global* getStackPointerGlobal(Module& wasm) {
62 // Assumption: The stack pointer is either imported as __stack_pointer or
63 // its the first non-imported and non-exported global.
64 // TODO(sbc): Find a better way to discover the stack pointer. Perhaps the
65 // linker could export it by name?
66 for (auto& g : wasm.globals) {
67 if (g->imported()) {
68 if (g->base == STACK_POINTER) {
69 return g.get();
70 }
71 } else if (!isExported(wasm, g->name)) {
72 return g.get();
73 }
74 }
75 return nullptr;
76 }
77
78 // For emscripten SIDE_MODULE we generate a single exported function called
79 // __post_instantiate which calls two functions:
80 //
81 // - __assign_got_enties
82 // - __wasm_call_ctors
83 //
84 // The former is function we generate here which calls imported g$XXX functions
85 // order to assign values to any globals imported from GOT.func or GOT.mem.
86 // These globals hold address of functions and globals respectively.
87 //
88 // The later is the constructor function generaed by lld which performs any
89 // fixups on the memory section and calls static constructors.
generatePostInstantiateFunction()90 void EmscriptenGlueGenerator::generatePostInstantiateFunction() {
91 BYN_TRACE("generatePostInstantiateFunction\n");
92 Builder builder(wasm);
93 Function* post_instantiate = builder.makeFunction(
94 POST_INSTANTIATE, std::vector<NameType>{}, Type::none, {});
95 wasm.addFunction(post_instantiate);
96
97 if (Function* F = wasm.getFunctionOrNull(ASSIGN_GOT_ENTRIES)) {
98 // call __assign_got_enties from post_instantiate
99 Expression* call = builder.makeCall(F->name, {}, Type::none);
100 post_instantiate->body = builder.blockify(post_instantiate->body, call);
101 }
102
103 // The names of standard imports/exports used by lld doesn't quite match that
104 // expected by emscripten.
105 // TODO(sbc): Unify these
106 if (auto* e = wasm.getExportOrNull(WASM_CALL_CTORS)) {
107 Expression* call = builder.makeCall(e->value, {}, Type::none);
108 post_instantiate->body = builder.blockify(post_instantiate->body, call);
109 wasm.removeExport(WASM_CALL_CTORS);
110 }
111
112 auto* ex = new Export();
113 ex->value = post_instantiate->name;
114 ex->name = POST_INSTANTIATE;
115 ex->kind = ExternalKind::Function;
116 wasm.addExport(ex);
117 }
118
119 // lld can sometimes produce a build with an imported mutable __stack_pointer
120 // (i.e. when linking with -fpie). This method internalizes the
121 // __stack_pointer and initializes it from an immutable global instead.
122 // For -shared builds we instead call replaceStackPointerGlobal.
internalizeStackPointerGlobal()123 void EmscriptenGlueGenerator::internalizeStackPointerGlobal() {
124 Global* stackPointer = getStackPointerGlobal(wasm);
125 if (!stackPointer || !stackPointer->imported() || !stackPointer->mutable_) {
126 return;
127 }
128
129 Name internalName = stackPointer->name;
130 Name externalName = internalName.c_str() + std::string("_import");
131
132 // Rename the imported global, and make it immutable
133 stackPointer->name = externalName;
134 stackPointer->mutable_ = false;
135 wasm.updateMaps();
136
137 // Create a new global with the old name that is not imported.
138 Builder builder(wasm);
139 auto* init = builder.makeGlobalGet(externalName, stackPointer->type);
140 auto* sp = builder.makeGlobal(
141 internalName, stackPointer->type, init, Builder::Mutable);
142 wasm.addGlobal(sp);
143 }
144
145 const Address UNKNOWN_OFFSET(uint32_t(-1));
146
getSegmentOffsets(Module & wasm)147 std::vector<Address> getSegmentOffsets(Module& wasm) {
148 std::unordered_map<Index, Address> passiveOffsets;
149 if (wasm.features.hasBulkMemory()) {
150 // Fetch passive segment offsets out of memory.init instructions
151 struct OffsetSearcher : PostWalker<OffsetSearcher> {
152 std::unordered_map<Index, Address>& offsets;
153 OffsetSearcher(std::unordered_map<unsigned, Address>& offsets)
154 : offsets(offsets) {}
155 void visitMemoryInit(MemoryInit* curr) {
156 auto* dest = curr->dest->dynCast<Const>();
157 if (!dest) {
158 return;
159 }
160 auto it = offsets.find(curr->segment);
161 if (it != offsets.end()) {
162 Fatal() << "Cannot get offset of passive segment initialized "
163 "multiple times";
164 }
165 offsets[curr->segment] = dest->value.geti32();
166 }
167 } searcher(passiveOffsets);
168 searcher.walkModule(&wasm);
169 }
170 std::vector<Address> segmentOffsets;
171 for (unsigned i = 0; i < wasm.memory.segments.size(); ++i) {
172 auto& segment = wasm.memory.segments[i];
173 if (segment.isPassive) {
174 auto it = passiveOffsets.find(i);
175 if (it != passiveOffsets.end()) {
176 segmentOffsets.push_back(it->second);
177 } else {
178 // This was a non-constant offset (perhaps TLS)
179 segmentOffsets.push_back(UNKNOWN_OFFSET);
180 }
181 } else if (auto* addrConst = segment.offset->dynCast<Const>()) {
182 auto address = addrConst->value.geti32();
183 segmentOffsets.push_back(address);
184 } else {
185 // TODO(sbc): Wasm shared libraries have data segments with non-const
186 // offset.
187 segmentOffsets.push_back(0);
188 }
189 }
190 return segmentOffsets;
191 }
192
escape(const char * input)193 std::string escape(const char* input) {
194 std::string code = input;
195 // replace newlines quotes with escaped newlines
196 size_t curr = 0;
197 while ((curr = code.find("\\n", curr)) != std::string::npos) {
198 code = code.replace(curr, 2, "\\\\n");
199 curr += 3; // skip this one
200 }
201 // replace double quotes with escaped single quotes
202 curr = 0;
203 while ((curr = code.find('"', curr)) != std::string::npos) {
204 if (curr == 0 || code[curr - 1] != '\\') {
205 code = code.replace(curr,
206 1,
207 "\\"
208 "\"");
209 curr += 2; // skip this one
210 } else { // already escaped, escape the slash as well
211 code = code.replace(curr,
212 1,
213 "\\"
214 "\\"
215 "\"");
216 curr += 3; // skip this one
217 }
218 }
219 return code;
220 }
221
stringAtAddr(Module & wasm,std::vector<Address> const & segmentOffsets,Address address)222 const char* stringAtAddr(Module& wasm,
223 std::vector<Address> const& segmentOffsets,
224 Address address) {
225 for (unsigned i = 0; i < wasm.memory.segments.size(); ++i) {
226 Memory::Segment& segment = wasm.memory.segments[i];
227 Address offset = segmentOffsets[i];
228 if (offset != UNKNOWN_OFFSET && address >= offset &&
229 address < offset + segment.data.size()) {
230 return &segment.data[address - offset];
231 }
232 }
233 return nullptr;
234 }
235
codeForConstAddr(Module & wasm,std::vector<Address> const & segmentOffsets,int32_t address)236 std::string codeForConstAddr(Module& wasm,
237 std::vector<Address> const& segmentOffsets,
238 int32_t address) {
239 const char* str = stringAtAddr(wasm, segmentOffsets, address);
240 if (!str) {
241 // If we can't find the segment corresponding with the address, then we
242 // omitted the segment and the address points to an empty string.
243 return escape("");
244 }
245 return escape(str);
246 }
247
248 enum class Proxying {
249 None,
250 Sync,
251 Async,
252 };
253
proxyingSuffix(Proxying proxy)254 std::string proxyingSuffix(Proxying proxy) {
255 switch (proxy) {
256 case Proxying::None:
257 return "";
258 case Proxying::Sync:
259 return "sync_on_main_thread_";
260 case Proxying::Async:
261 return "async_on_main_thread_";
262 }
263 WASM_UNREACHABLE("invalid prozy type");
264 }
265
266 struct AsmConstWalker : public LinearExecutionWalker<AsmConstWalker> {
267 Module& wasm;
268 bool minimizeWasmChanges;
269 std::vector<Address> segmentOffsets; // segment index => address offset
270
271 struct AsmConst {
272 std::set<Signature> sigs;
273 Address id;
274 std::string code;
275 Proxying proxy;
276 };
277
278 std::vector<AsmConst> asmConsts;
279 std::set<std::pair<Signature, Proxying>> allSigs;
280 // last sets in the current basic block, per index
281 std::map<Index, LocalSet*> sets;
282
AsmConstWalkerwasm::AsmConstWalker283 AsmConstWalker(Module& _wasm, bool minimizeWasmChanges)
284 : wasm(_wasm), minimizeWasmChanges(minimizeWasmChanges),
285 segmentOffsets(getSegmentOffsets(wasm)) {}
286
287 void noteNonLinear(Expression* curr);
288
289 void visitLocalSet(LocalSet* curr);
290 void visitCall(Call* curr);
291
292 void process();
293
294 private:
295 void createAsmConst(uint32_t id, std::string code, Signature sig, Name name);
296 Signature asmConstSig(Signature baseSig);
297 Name nameForImportWithSig(Signature sig, Proxying proxy);
298 void queueImport(Name importName, Signature baseSig);
299 void addImports();
300 Proxying proxyType(Name name);
301
302 std::vector<std::unique_ptr<Function>> queuedImports;
303 };
304
noteNonLinear(Expression * curr)305 void AsmConstWalker::noteNonLinear(Expression* curr) {
306 // End of this basic block; clear sets.
307 sets.clear();
308 }
309
visitLocalSet(LocalSet * curr)310 void AsmConstWalker::visitLocalSet(LocalSet* curr) { sets[curr->index] = curr; }
311
visitCall(Call * curr)312 void AsmConstWalker::visitCall(Call* curr) {
313 auto* import = wasm.getFunction(curr->target);
314 // Find calls to emscripten_asm_const* functions whose first argument is
315 // is always a string constant.
316 if (!import->imported()) {
317 return;
318 }
319 auto importName = import->base;
320 if (!importName.hasSubstring(EM_ASM_PREFIX)) {
321 return;
322 }
323
324 auto baseSig = wasm.getFunction(curr->target)->sig;
325 auto sig = asmConstSig(baseSig);
326 auto* arg = curr->operands[0];
327 while (!arg->dynCast<Const>()) {
328 if (auto* get = arg->dynCast<LocalGet>()) {
329 // The argument may be a local.get, in which case, the last set in this
330 // basic block has the value.
331 auto* set = sets[get->index];
332 if (set) {
333 assert(set->index == get->index);
334 arg = set->value;
335 } else {
336 Fatal() << "local.get of unknown in arg0 of call to " << importName
337 << " (used by EM_ASM* macros) in function "
338 << getFunction()->name
339 << ".\nThis might be caused by aggressive compiler "
340 "transformations. Consider using EM_JS instead.";
341 }
342 continue;
343 }
344
345 if (auto* setlocal = arg->dynCast<LocalSet>()) {
346 // The argument may be a local.tee, in which case we take first child
347 // which is the value being copied into the local.
348 if (setlocal->isTee()) {
349 arg = setlocal->value;
350 continue;
351 }
352 }
353
354 if (auto* bin = arg->dynCast<Binary>()) {
355 if (bin->op == AddInt32) {
356 // In the dynamic linking case the address of the string constant
357 // is the result of adding its offset to __memory_base.
358 // In this case are only looking for the offset from __memory_base
359 // the RHS of the addition is just what we want.
360 arg = bin->right;
361 continue;
362 }
363 }
364
365 Fatal() << "Unexpected arg0 type (" << getExpressionName(arg)
366 << ") in call to: " << importName;
367 }
368
369 auto* value = arg->cast<Const>();
370 int32_t address = value->value.geti32();
371 auto code = codeForConstAddr(wasm, segmentOffsets, address);
372 createAsmConst(address, code, sig, importName);
373 }
374
proxyType(Name name)375 Proxying AsmConstWalker::proxyType(Name name) {
376 if (name.hasSubstring("_sync_on_main_thread")) {
377 return Proxying::Sync;
378 } else if (name.hasSubstring("_async_on_main_thread")) {
379 return Proxying::Async;
380 }
381 return Proxying::None;
382 }
383
process()384 void AsmConstWalker::process() {
385 // Find and queue necessary imports
386 walkModule(&wasm);
387 // Add them after the walk, to avoid iterator invalidation on
388 // the list of functions.
389 addImports();
390 }
391
createAsmConst(uint32_t id,std::string code,Signature sig,Name name)392 void AsmConstWalker::createAsmConst(uint32_t id,
393 std::string code,
394 Signature sig,
395 Name name) {
396 AsmConst asmConst;
397 asmConst.id = id;
398 asmConst.code = code;
399 asmConst.sigs.insert(sig);
400 asmConst.proxy = proxyType(name);
401 asmConsts.push_back(asmConst);
402 }
403
asmConstSig(Signature baseSig)404 Signature AsmConstWalker::asmConstSig(Signature baseSig) {
405 assert(baseSig.params.size() >= 1);
406 // Omit the signature of the "code" parameter, taken as a string, as the
407 // first argument
408 return Signature(
409 Type(std::vector<Type>(baseSig.params.begin() + 1, baseSig.params.end())),
410 baseSig.results);
411 }
412
nameForImportWithSig(Signature sig,Proxying proxy)413 Name AsmConstWalker::nameForImportWithSig(Signature sig, Proxying proxy) {
414 std::string fixedTarget = EM_ASM_PREFIX.str + std::string("_") +
415 proxyingSuffix(proxy) +
416 getSig(sig.results, sig.params);
417 return Name(fixedTarget.c_str());
418 }
419
queueImport(Name importName,Signature baseSig)420 void AsmConstWalker::queueImport(Name importName, Signature baseSig) {
421 auto import = new Function;
422 import->name = import->base = importName;
423 import->module = ENV;
424 import->sig = baseSig;
425 queuedImports.push_back(std::unique_ptr<Function>(import));
426 }
427
addImports()428 void AsmConstWalker::addImports() {
429 for (auto& import : queuedImports) {
430 wasm.addFunction(import.release());
431 }
432 }
433
fixEmAsmConstsAndReturnWalker(Module & wasm,bool minimizeWasmChanges)434 static AsmConstWalker fixEmAsmConstsAndReturnWalker(Module& wasm,
435 bool minimizeWasmChanges) {
436 AsmConstWalker walker(wasm, minimizeWasmChanges);
437 walker.process();
438 return walker;
439 }
440
441 struct EmJsWalker : public PostWalker<EmJsWalker> {
442 Module& wasm;
443 std::vector<Address> segmentOffsets; // segment index => address offset
444
445 std::map<std::string, std::string> codeByName;
446
EmJsWalkerwasm::EmJsWalker447 EmJsWalker(Module& _wasm)
448 : wasm(_wasm), segmentOffsets(getSegmentOffsets(wasm)) {}
449
visitExportwasm::EmJsWalker450 void visitExport(Export* curr) {
451 if (curr->kind != ExternalKind::Function) {
452 return;
453 }
454 if (!curr->name.startsWith(EM_JS_PREFIX.str)) {
455 return;
456 }
457 auto* func = wasm.getFunction(curr->value);
458 auto funcName = std::string(curr->name.stripPrefix(EM_JS_PREFIX.str));
459 // An EM_JS has a single const in the body. Typically it is just returned,
460 // but in unoptimized code it might be stored to a local and loaded from
461 // there, and in relocatable code it might get added to __memory_base etc.
462 FindAll<Const> consts(func->body);
463 if (consts.list.size() != 1) {
464 Fatal() << "Unexpected generated __em_js__ function body: " << curr->name;
465 }
466 auto* addrConst = consts.list[0];
467 int32_t address = addrConst->value.geti32();
468 auto code = codeForConstAddr(wasm, segmentOffsets, address);
469 codeByName[funcName] = code;
470 }
471 };
472
fixEmJsFuncsAndReturnWalker(Module & wasm)473 EmJsWalker fixEmJsFuncsAndReturnWalker(Module& wasm) {
474 EmJsWalker walker(wasm);
475 walker.walkModule(&wasm);
476
477 std::vector<Name> toRemove;
478 for (auto& func : wasm.functions) {
479 if (func->name.startsWith(EM_JS_PREFIX.str)) {
480 toRemove.push_back(func->name);
481 }
482 }
483 for (auto funcName : toRemove) {
484 wasm.removeFunction(funcName);
485 wasm.removeExport(funcName);
486 }
487 return walker;
488 }
489
printSignatures(std::ostream & o,const std::set<Signature> & c)490 void printSignatures(std::ostream& o, const std::set<Signature>& c) {
491 o << "[";
492 bool first = true;
493 for (auto& sig : c) {
494 if (first) {
495 first = false;
496 } else {
497 o << ",";
498 }
499 o << '"' << getSig(sig.results, sig.params) << '"';
500 }
501 o << "]";
502 }
503
generateEmscriptenMetadata(Address staticBump,std::vector<Name> const & initializerFunctions)504 std::string EmscriptenGlueGenerator::generateEmscriptenMetadata(
505 Address staticBump, std::vector<Name> const& initializerFunctions) {
506 bool commaFirst;
507 auto nextElement = [&commaFirst]() {
508 if (commaFirst) {
509 commaFirst = false;
510 return "\n ";
511 } else {
512 return ",\n ";
513 }
514 };
515
516 std::stringstream meta;
517 meta << "{\n";
518
519 AsmConstWalker emAsmWalker =
520 fixEmAsmConstsAndReturnWalker(wasm, minimizeWasmChanges);
521
522 // print
523 commaFirst = true;
524 if (!emAsmWalker.asmConsts.empty()) {
525 meta << " \"asmConsts\": {";
526 for (auto& asmConst : emAsmWalker.asmConsts) {
527 meta << nextElement();
528 meta << '"' << asmConst.id << "\": [\"" << asmConst.code << "\", ";
529 printSignatures(meta, asmConst.sigs);
530 meta << ", [\"" << proxyingSuffix(asmConst.proxy) << "\"]";
531
532 meta << "]";
533 }
534 meta << "\n },\n";
535 }
536
537 EmJsWalker emJsWalker = fixEmJsFuncsAndReturnWalker(wasm);
538 if (!emJsWalker.codeByName.empty()) {
539 meta << " \"emJsFuncs\": {";
540 commaFirst = true;
541 for (auto& pair : emJsWalker.codeByName) {
542 auto& name = pair.first;
543 auto& code = pair.second;
544 meta << nextElement();
545 meta << '"' << name << "\": \"" << code << '"';
546 }
547 meta << "\n },\n";
548 }
549
550 meta << " \"staticBump\": " << staticBump << ",\n";
551 meta << " \"tableSize\": " << wasm.table.initial.addr << ",\n";
552
553 if (!initializerFunctions.empty()) {
554 meta << " \"initializers\": [";
555 commaFirst = true;
556 for (const auto& func : initializerFunctions) {
557 meta << nextElement();
558 meta << "\"" << func.c_str() << "\"";
559 }
560 meta << "\n ],\n";
561 }
562
563 // Avoid adding duplicate imports to `declares' or `invokeFuncs`. Even
564 // though we might import the same function multiple times (i.e. with
565 // different sigs) we only need to list is in the metadata once.
566 std::set<std::string> declares;
567 std::set<std::string> invokeFuncs;
568
569 // We use the `base` rather than the `name` of the imports here and below
570 // becasue this is the externally visible name that the embedder (JS) will
571 // see.
572 meta << " \"declares\": [";
573 commaFirst = true;
574 ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) {
575 if (emJsWalker.codeByName.count(import->base.str) == 0 &&
576 !import->base.startsWith("invoke_")) {
577 if (declares.insert(import->base.str).second) {
578 meta << nextElement() << '"' << import->base.str << '"';
579 }
580 }
581 });
582 meta << "\n ],\n";
583
584 meta << " \"externs\": [";
585 commaFirst = true;
586 ModuleUtils::iterImportedGlobals(wasm, [&](Global* import) {
587 if (!(import->module == ENV && import->name == STACK_INIT)) {
588 meta << nextElement() << "\"_" << import->base.str << '"';
589 }
590 });
591 meta << "\n ],\n";
592
593 if (!wasm.exports.empty()) {
594 meta << " \"exports\": [";
595 commaFirst = true;
596 for (const auto& ex : wasm.exports) {
597 if (ex->kind == ExternalKind::Function) {
598 meta << nextElement() << '"' << ex->name.str << '"';
599 }
600 }
601 meta << "\n ],\n";
602
603 meta << " \"namedGlobals\": {";
604 commaFirst = true;
605 for (const auto& ex : wasm.exports) {
606 if (ex->kind == ExternalKind::Global) {
607 const Global* g = wasm.getGlobal(ex->value);
608 assert(g->type == Type::i32);
609 Const* init = g->init->cast<Const>();
610 uint32_t addr = init->value.geti32();
611 meta << nextElement() << '"' << ex->name.str << "\" : \"" << addr
612 << '"';
613 }
614 }
615 meta << "\n },\n";
616 }
617
618 meta << " \"invokeFuncs\": [";
619 commaFirst = true;
620 ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) {
621 if (import->module == ENV && import->base.startsWith("invoke_")) {
622 if (invokeFuncs.insert(import->base.str).second) {
623 meta << nextElement() << '"' << import->base.str << '"';
624 }
625 }
626 });
627 meta << "\n ],\n";
628
629 // In normal mode we attempt to determine if main takes argumnts or not
630 // In standalone mode we export _start instead and rely on the presence
631 // of the __wasi_args_get and __wasi_args_sizes_get syscalls allow us to
632 // DCE to the argument handling JS code instead.
633 if (!standalone) {
634 auto mainReadsParams = false;
635 auto* exp = wasm.getExportOrNull("main");
636 if (!exp) {
637 exp = wasm.getExportOrNull("__main_argc_argv");
638 }
639 if (exp) {
640 if (exp->kind == ExternalKind::Function) {
641 auto* main = wasm.getFunction(exp->value);
642 mainReadsParams = true;
643 // If main does not read its parameters, it will just be a stub that
644 // calls __original_main (which has no parameters).
645 if (auto* call = main->body->dynCast<Call>()) {
646 if (call->operands.empty()) {
647 mainReadsParams = false;
648 }
649 }
650 }
651 }
652 meta << " \"mainReadsParams\": " << int(mainReadsParams) << ",\n";
653 }
654
655 meta << " \"features\": [";
656 commaFirst = true;
657 wasm.features.iterFeatures([&](FeatureSet::Feature f) {
658 meta << nextElement() << "\"--enable-" << FeatureSet::toString(f) << '"';
659 });
660 meta << "\n ]\n";
661
662 meta << "}\n";
663
664 return meta.str();
665 }
666
separateDataSegments(Output * outfile,Address base)667 void EmscriptenGlueGenerator::separateDataSegments(Output* outfile,
668 Address base) {
669 size_t lastEnd = 0;
670 for (Memory::Segment& seg : wasm.memory.segments) {
671 if (seg.isPassive) {
672 Fatal() << "separating passive segments not implemented";
673 }
674 if (!seg.offset->is<Const>()) {
675 Fatal() << "separating relocatable segments not implemented";
676 }
677 size_t offset = seg.offset->cast<Const>()->value.geti32();
678 offset -= base;
679 size_t fill = offset - lastEnd;
680 if (fill > 0) {
681 std::vector<char> buf(fill);
682 outfile->write(buf.data(), fill);
683 }
684 outfile->write(seg.data.data(), seg.data.size());
685 lastEnd = offset + seg.data.size();
686 }
687 wasm.memory.segments.clear();
688 }
689
renameMainArgcArgv()690 void EmscriptenGlueGenerator::renameMainArgcArgv() {
691 // If an export call ed __main_argc_argv exists rename it to main
692 Export* ex = wasm.getExportOrNull("__main_argc_argv");
693 if (!ex) {
694 BYN_TRACE("renameMain: __main_argc_argv not found\n");
695 return;
696 }
697 ex->name = "main";
698 wasm.updateMaps();
699 ModuleUtils::renameFunction(wasm, "__main_argc_argv", "main");
700 }
701
702 } // namespace wasm
703