[WebAssembly] Implement @llvm.global_ctors and @llvm.global_dtors
Summary:
- lowers @llvm.global_dtors by adding @llvm.global_ctors
functions which register the destructors with `__cxa_atexit`.
- impements @llvm.global_ctors with wasm start functions and linker metadata
See [here](https://github.com/WebAssembly/tool-conventions/issues/25) for more background.
Subscribers: jfb, dschuff, mgorny, jgravelle-google, aheejin, sunfish
Differential Revision: https://reviews.llvm.org/D41211
llvm-svn: 320774
diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp
index b2c5b14..6e76c5f 100644
--- a/llvm/lib/MC/WasmObjectWriter.cpp
+++ b/llvm/lib/MC/WasmObjectWriter.cpp
@@ -284,7 +284,8 @@
void writeDataRelocSection();
void writeLinkingMetaDataSection(
ArrayRef<WasmDataSegment> Segments, uint32_t DataSize,
- SmallVector<std::pair<StringRef, uint32_t>, 4> SymbolFlags);
+ const SmallVector<std::pair<StringRef, uint32_t>, 4> &SymbolFlags,
+ const SmallVector<std::pair<uint16_t, uint32_t>, 2> &InitFuncs);
uint32_t getProvisionalValue(const WasmRelocationEntry &RelEntry);
void applyRelocations(ArrayRef<WasmRelocationEntry> Relocations,
@@ -366,6 +367,10 @@
uint64_t FixupOffset = Layout.getFragmentOffset(Fragment) + Fixup.getOffset();
MCContext &Ctx = Asm.getContext();
+ // The .init_array isn't translated as data, so don't do relocations in it.
+ if (FixupSection.getSectionName().startswith(".init_array"))
+ return;
+
if (const MCSymbolRefExpr *RefB = Target.getSymB()) {
assert(RefB->getKind() == MCSymbolRefExpr::VK_None &&
"Should not have constructed this");
@@ -905,7 +910,8 @@
void WasmObjectWriter::writeLinkingMetaDataSection(
ArrayRef<WasmDataSegment> Segments, uint32_t DataSize,
- SmallVector<std::pair<StringRef, uint32_t>, 4> SymbolFlags) {
+ const SmallVector<std::pair<StringRef, uint32_t>, 4> &SymbolFlags,
+ const SmallVector<std::pair<uint16_t, uint32_t>, 2> &InitFuncs) {
SectionBookkeeping Section;
startSection(Section, wasm::WASM_SEC_CUSTOM, "linking");
SectionBookkeeping SubSection;
@@ -937,6 +943,16 @@
endSection(SubSection);
}
+ if (!InitFuncs.empty()) {
+ startSection(SubSection, wasm::WASM_INIT_FUNCS);
+ encodeULEB128(InitFuncs.size(), getStream());
+ for (auto &StartFunc : InitFuncs) {
+ encodeULEB128(StartFunc.first, getStream()); // priority
+ encodeULEB128(StartFunc.second, getStream()); // function index
+ }
+ endSection(SubSection);
+ }
+
endSection(Section);
}
@@ -977,6 +993,7 @@
SmallVector<WasmImport, 4> Imports;
SmallVector<WasmExport, 4> Exports;
SmallVector<std::pair<StringRef, uint32_t>, 4> SymbolFlags;
+ SmallVector<std::pair<uint16_t, uint32_t>, 2> InitFuncs;
SmallPtrSet<const MCSymbolWasm *, 4> IsAddressTaken;
unsigned NumFuncImports = 0;
SmallVector<WasmDataSegment, 4> DataSegments;
@@ -1132,6 +1149,10 @@
if (!Section.isWasmData())
continue;
+ // .init_array sections are handled specially elsewhere.
+ if (cast<MCSectionWasm>(Sec).getSectionName().startswith(".init_array"))
+ continue;
+
DataSize = alignTo(DataSize, Section.getAlignment());
DataSegments.emplace_back();
WasmDataSegment &Segment = DataSegments.back();
@@ -1291,6 +1312,56 @@
registerFunctionType(*Fixup.Symbol);
}
+ // Translate .init_array section contents into start functions.
+ for (const MCSection &S : Asm) {
+ const auto &WS = static_cast<const MCSectionWasm &>(S);
+ if (WS.getSectionName().startswith(".fini_array"))
+ report_fatal_error(".fini_array sections are unsupported");
+ if (!WS.getSectionName().startswith(".init_array"))
+ continue;
+ if (WS.getFragmentList().empty())
+ continue;
+ if (WS.getFragmentList().size() != 2)
+ report_fatal_error("only one .init_array section fragment supported");
+ const MCFragment &AlignFrag = *WS.begin();
+ if (AlignFrag.getKind() != MCFragment::FT_Align)
+ report_fatal_error(".init_array section should be aligned");
+ if (cast<MCAlignFragment>(AlignFrag).getAlignment() != (is64Bit() ? 8 : 4))
+ report_fatal_error(".init_array section should be aligned for pointers");
+ const MCFragment &Frag = *std::next(WS.begin());
+ if (Frag.hasInstructions() || Frag.getKind() != MCFragment::FT_Data)
+ report_fatal_error("only data supported in .init_array section");
+ uint16_t Priority = UINT16_MAX;
+ if (WS.getSectionName().size() != 11) {
+ if (WS.getSectionName()[11] != '.')
+ report_fatal_error(".init_array section priority should start with '.'");
+ if (WS.getSectionName().substr(12).getAsInteger(10, Priority))
+ report_fatal_error("invalid .init_array section priority");
+ }
+ const auto &DataFrag = cast<MCDataFragment>(Frag);
+ const SmallVectorImpl<char> &Contents = DataFrag.getContents();
+ for (const uint8_t *p = (const uint8_t *)Contents.data(),
+ *end = (const uint8_t *)Contents.data() + Contents.size();
+ p != end; ++p) {
+ if (*p != 0)
+ report_fatal_error("non-symbolic data in .init_array section");
+ }
+ for (const MCFixup &Fixup : DataFrag.getFixups()) {
+ assert(Fixup.getKind() == MCFixup::getKindForSize(is64Bit() ? 8 : 4, false));
+ const MCExpr *Expr = Fixup.getValue();
+ auto *Sym = dyn_cast<MCSymbolRefExpr>(Expr);
+ if (!Sym)
+ report_fatal_error("fixups in .init_array should be symbol references");
+ if (Sym->getKind() != MCSymbolRefExpr::VK_WebAssembly_FUNCTION)
+ report_fatal_error("symbols in .init_array should be for functions");
+ auto I = SymbolIndices.find(cast<MCSymbolWasm>(&Sym->getSymbol()));
+ if (I == SymbolIndices.end())
+ report_fatal_error("symbols in .init_array should be defined");
+ uint32_t Index = I->second;
+ InitFuncs.push_back(std::make_pair(Priority, Index));
+ }
+ }
+
// Write out the Wasm header.
writeHeader(Asm);
@@ -1301,14 +1372,14 @@
// Skip the "memory" section; we import the memory instead.
writeGlobalSection();
writeExportSection(Exports);
- // TODO: Start Section
writeElemSection(TableElems);
writeCodeSection(Asm, Layout, Functions);
writeDataSection(DataSegments);
writeNameSection(Functions, Imports, NumFuncImports);
writeCodeRelocSection();
writeDataRelocSection();
- writeLinkingMetaDataSection(DataSegments, DataSize, SymbolFlags);
+ writeLinkingMetaDataSection(DataSegments, DataSize, SymbolFlags,
+ InitFuncs);
// TODO: Translate the .comment section to the output.
// TODO: Translate debug sections to the output.