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