[lld] r346918 - [WebAssembly] Initial support for shared objects (-shared)
Sam Clegg via llvm-commits
llvm-commits at lists.llvm.org
Wed Nov 14 16:37:22 PST 2018
Author: sbc
Date: Wed Nov 14 16:37:21 2018
New Revision: 346918
URL: http://llvm.org/viewvc/llvm-project?rev=346918&view=rev
Log:
[WebAssembly] Initial support for shared objects (-shared)
Based on the initial spec proposal:
https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
The llvm/codegen side of this is still missing but I believe this change is
still worth landing as an incremental step
Differential Revision: https://reviews.llvm.org/D54249
Added:
lld/trunk/test/wasm/shared.ll
Modified:
lld/trunk/docs/ReleaseNotes.rst
lld/trunk/docs/WebAssembly.rst
lld/trunk/wasm/Config.h
lld/trunk/wasm/Driver.cpp
lld/trunk/wasm/LTO.cpp
lld/trunk/wasm/Options.td
lld/trunk/wasm/OutputSections.cpp
lld/trunk/wasm/Symbols.cpp
lld/trunk/wasm/Symbols.h
lld/trunk/wasm/Writer.cpp
Modified: lld/trunk/docs/ReleaseNotes.rst
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/docs/ReleaseNotes.rst?rev=346918&r1=346917&r2=346918&view=diff
==============================================================================
--- lld/trunk/docs/ReleaseNotes.rst (original)
+++ lld/trunk/docs/ReleaseNotes.rst Wed Nov 14 16:37:21 2018
@@ -65,3 +65,11 @@ MachO Improvements
------------------
* Item 1.
+
+WebAssembly Improvements
+------------------------
+
+* Add initial support for creating shared libraries (-shared).
+ Note: The shared library format is still under active development and may
+ undergo significant changes in future versions.
+ See: https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
Modified: lld/trunk/docs/WebAssembly.rst
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/docs/WebAssembly.rst?rev=346918&r1=346917&r2=346918&view=diff
==============================================================================
--- lld/trunk/docs/WebAssembly.rst (original)
+++ lld/trunk/docs/WebAssembly.rst Wed Nov 14 16:37:21 2018
@@ -29,6 +29,8 @@ Missing features
There are several key features that are not yet implement in the WebAssembly
ports:
+- Support for building shared libraries via ``-shared`` is still as work in
+ progress.
- COMDAT support. This means that support for C++ is still very limited.
- Function stripping. Currently there is no support for ``--gc-sections`` so
functions and data from a given object will linked as a unit.
Added: lld/trunk/test/wasm/shared.ll
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/wasm/shared.ll?rev=346918&view=auto
==============================================================================
--- lld/trunk/test/wasm/shared.ll (added)
+++ lld/trunk/test/wasm/shared.ll Wed Nov 14 16:37:21 2018
@@ -0,0 +1,70 @@
+; RUN: llc -O0 -filetype=obj %s -o %t.o
+; RUN: wasm-ld -shared -o %t.wasm %t.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+target triple = "wasm32-unknown-unknown"
+
+ at used_data = hidden global i32 2, align 4
+ at indirect_func = local_unnamed_addr global void ()* @foo, align 4
+
+define default void @foo() {
+entry:
+ %0 = load i32, i32* @used_data, align 4
+ %1 = load void ()*, void ()** @indirect_func, align 4
+ call void %1()
+ ret void
+}
+
+; check for dylink section at start
+
+; CHECK: Sections:
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: dylink
+; CHECK-NEXT: MemorySize: 4
+; CHECK-NEXT: MemoryAlignment: 2
+; CHECK-NEXT: TableSize: 1
+; CHECK-NEXT: TableAlignment: 0
+
+; check for import of __table_base and __memory_base globals
+
+; CHECK: - Type: IMPORT
+; CHECK-NEXT: Imports:
+; CHECK-NEXT: - Module: env
+; CHECK-NEXT: Field: __indirect_function_table
+; CHECK-NEXT: Kind: TABLE
+; CHECK-NEXT: Table:
+; CHECK-NEXT: ElemType: ANYFUNC
+; CHECK-NEXT: Limits:
+; CHECK-NEXT: Flags: [ HAS_MAX ]
+; CHECK-NEXT: Initial: 0x00000001
+; CHECK-NEXT: Maximum: 0x00000001
+; CHECK-NEXT: - Module: env
+; CHECK-NEXT: Field: __memory_base
+; CHECK-NEXT: Kind: GLOBAL
+; CHECK-NEXT: GlobalType: I32
+; CHECK-NEXT: GlobalMutable: false
+; CHECK-NEXT: - Module: env
+; CHECK-NEXT: Field: __table_base
+; CHECK-NEXT: Kind: GLOBAL
+; CHECK-NEXT: GlobalType: I32
+; CHECK-NEXT: GlobalMutable: false
+
+; check for elem segment initialized with __table_base global as offset
+
+; CHECK: - Type: ELEM
+; CHECK-NEXT: Segments:
+; CHECK-NEXT: - Offset:
+; CHECK-NEXT: Opcode: GET_GLOBAL
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Functions: [ 1 ]
+
+; check the data segment initialized with __memory_base global as offset
+
+; CHECK: - Type: DATA
+; CHECK-NEXT: Segments:
+; CHECK-NEXT: - SectionOffset: 6
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: GET_GLOBAL
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Content: '00000000'
Modified: lld/trunk/wasm/Config.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/Config.h?rev=346918&r1=346917&r2=346918&view=diff
==============================================================================
--- lld/trunk/wasm/Config.h (original)
+++ lld/trunk/wasm/Config.h Wed Nov 14 16:37:21 2018
@@ -31,9 +31,11 @@ struct Configuration {
bool SharedMemory;
bool ImportTable;
bool MergeDataSegments;
+ bool Pie;
bool PrintGcSections;
bool Relocatable;
bool SaveTemps;
+ bool Shared;
bool StripAll;
bool StripDebug;
bool StackFirst;
@@ -52,6 +54,9 @@ struct Configuration {
llvm::StringSet<> AllowUndefinedSymbols;
std::vector<llvm::StringRef> SearchPaths;
llvm::CachePruningPolicy ThinLTOCachePolicy;
+
+ // True if we are creating position-independent code.
+ bool Pic;
};
// The only instance of Configuration struct.
Modified: lld/trunk/wasm/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/Driver.cpp?rev=346918&r1=346917&r2=346918&view=diff
==============================================================================
--- lld/trunk/wasm/Driver.cpp (original)
+++ lld/trunk/wasm/Driver.cpp Wed Nov 14 16:37:21 2018
@@ -369,6 +369,7 @@ void LinkerDriver::link(ArrayRef<const c
errorHandler().ErrorLimit = args::getInteger(Args, OPT_error_limit, 20);
Config->AllowUndefined = Args.hasArg(OPT_allow_undefined);
+ Config->CompressRelocations = Args.hasArg(OPT_compress_relocations);
Config->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true);
Config->DisableVerify = Args.hasArg(OPT_disable_verify);
Config->Entry = getEntry(Args, Args.hasArg(OPT_relocatable) ? "" : "_start");
@@ -391,13 +392,14 @@ void LinkerDriver::link(ArrayRef<const c
Config->MergeDataSegments =
Args.hasFlag(OPT_merge_data_segments, OPT_no_merge_data_segments,
!Config->Relocatable);
+ Config->Pie = Args.hasFlag(OPT_pie, OPT_no_pie, false);
Config->PrintGcSections =
Args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false);
Config->SaveTemps = Args.hasArg(OPT_save_temps);
Config->SearchPaths = args::getStrings(Args, OPT_L);
+ Config->Shared = Args.hasArg(OPT_shared);
Config->StripAll = Args.hasArg(OPT_strip_all);
Config->StripDebug = Args.hasArg(OPT_strip_debug);
- Config->CompressRelocations = Args.hasArg(OPT_compress_relocations);
Config->StackFirst = Args.hasArg(OPT_stack_first);
Config->ThinLTOCacheDir = Args.getLastArgValue(OPT_thinlto_cache_dir);
Config->ThinLTOCachePolicy = CHECK(
@@ -432,6 +434,9 @@ void LinkerDriver::link(ArrayRef<const c
return;
}
+ if (Config->Pie && Config->Shared)
+ error("-shared and -pie may not be used together");
+
if (Config->OutputFile.empty())
error("no output file specified");
@@ -447,8 +452,21 @@ void LinkerDriver::link(ArrayRef<const c
error("-r -and --compress-relocations may not be used together");
if (Args.hasArg(OPT_undefined))
error("-r -and --undefined may not be used together");
+ if (Config->Pie)
+ error("-r and -pie may not be used together");
+ }
+
+ Config->Pic = Config->Pie || Config->Shared;
+
+ if (Config->Pic) {
+ if (Config->ExportTable)
+ error("-shared/-pie is incompatible with --export-table");
+ Config->ImportTable = true;
}
+ if (Config->Shared)
+ Config->ExportDynamic = true;
+
Symbol *EntrySym = nullptr;
if (!Config->Relocatable) {
llvm::wasm::WasmGlobal Global;
@@ -475,6 +493,28 @@ void LinkerDriver::link(ArrayRef<const c
"__dso_handle", WASM_SYMBOL_VISIBILITY_HIDDEN);
WasmSym::DataEnd = Symtab->addSyntheticDataSymbol("__data_end", 0);
+ if (Config->Pic) {
+ // For PIC code, we import two global variables (__memory_base and
+ // __table_base) from the environment and use these as the offset at
+ // which to load our static data and function table.
+ // See: https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
+ static llvm::wasm::WasmGlobalType GlobalTypeI32 = {WASM_TYPE_I32, false};
+
+ WasmSym::MemoryBase = Symtab->addUndefinedGlobal(
+ "__memory_base", WASM_SYMBOL_VISIBILITY_HIDDEN, nullptr,
+ &GlobalTypeI32);
+ Config->AllowUndefinedSymbols.insert(WasmSym::MemoryBase->getName());
+ WasmSym::MemoryBase->IsUsedInRegularObj = true;
+ WasmSym::MemoryBase->markLive();
+
+ WasmSym::TableBase = Symtab->addUndefinedGlobal(
+ "__table_base", WASM_SYMBOL_VISIBILITY_HIDDEN, nullptr,
+ &GlobalTypeI32);
+ Config->AllowUndefinedSymbols.insert(WasmSym::TableBase->getName());
+ WasmSym::TableBase->IsUsedInRegularObj = true;
+ WasmSym::TableBase->markLive();
+ }
+
// These two synthetic symbols exist purely for the embedder so we always
// want to export them.
WasmSym::HeapBase->ForceExport = true;
@@ -511,7 +551,7 @@ void LinkerDriver::link(ArrayRef<const c
// Add synthetic dummies for weak undefined functions.
handleWeakUndefines();
- if (!Config->Entry.empty()) {
+ if (!Config->Shared && !Config->Entry.empty()) {
EntrySym = handleUndefined(Config->Entry);
if (EntrySym && EntrySym->isDefined())
EntrySym->ForceExport = true;
Modified: lld/trunk/wasm/LTO.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/LTO.cpp?rev=346918&r1=346917&r2=346918&view=diff
==============================================================================
--- lld/trunk/wasm/LTO.cpp (original)
+++ lld/trunk/wasm/LTO.cpp Wed Nov 14 16:37:21 2018
@@ -57,6 +57,13 @@ static std::unique_ptr<lto::LTO> createL
C.OptLevel = Config->LTOO;
C.MAttrs = GetMAttrs();
+ if (Config->Relocatable)
+ C.RelocModel = None;
+ else if (Config->Pic)
+ C.RelocModel = Reloc::PIC_;
+ else
+ C.RelocModel = Reloc::Static;
+
if (Config->SaveTemps)
checkError(C.addSaveTemps(Config->OutputFile.str() + ".",
/*UseInputModulePath*/ true));
Modified: lld/trunk/wasm/Options.td
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/Options.td?rev=346918&r1=346917&r2=346918&view=diff
==============================================================================
--- lld/trunk/wasm/Options.td (original)
+++ lld/trunk/wasm/Options.td Wed Nov 14 16:37:21 2018
@@ -17,7 +17,7 @@ multiclass B<string name, string help1,
def no_ # NAME: Flag<["--", "-"], "no-" # name>, HelpText<help2>;
}
-// The follow flags are shared with the ELF linker
+// The following flags are shared with the ELF linker
def color_diagnostics: F<"color-diagnostics">,
HelpText<"Use colors in diagnostics">;
@@ -75,12 +75,18 @@ def o: JoinedOrSeparate<["-"], "o">, Met
def O: JoinedOrSeparate<["-"], "O">, HelpText<"Optimize output file size">;
+defm pie: B<"pie",
+ "Create a position independent executable",
+ "Do not create a position independent executable (default)">;
+
defm print_gc_sections: B<"print-gc-sections",
"List removed unused sections",
"Do not list removed unused sections">;
def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">;
+def shared: F<"shared">, HelpText<"Build a shared object">;
+
def strip_all: F<"strip-all">, HelpText<"Strip all symbols">;
def strip_debug: F<"strip-debug">, HelpText<"Strip debugging information">;
Modified: lld/trunk/wasm/OutputSections.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/OutputSections.cpp?rev=346918&r1=346917&r2=346918&view=diff
==============================================================================
--- lld/trunk/wasm/OutputSections.cpp (original)
+++ lld/trunk/wasm/OutputSections.cpp Wed Nov 14 16:37:21 2018
@@ -136,9 +136,18 @@ DataSection::DataSection(ArrayRef<Output
for (OutputSegment *Segment : Segments) {
raw_string_ostream OS(Segment->Header);
writeUleb128(OS, 0, "memory index");
- writeUleb128(OS, WASM_OPCODE_I32_CONST, "opcode:i32const");
- writeSleb128(OS, Segment->StartVA, "memory offset");
- writeUleb128(OS, WASM_OPCODE_END, "opcode:end");
+ WasmInitExpr InitExpr;
+ if (Config->Pic) {
+ assert(Segments.size() <= 1 &&
+ "Currenly only a single data segment is supported in PIC mode");
+ InitExpr.Opcode = WASM_OPCODE_GET_GLOBAL;
+ InitExpr.Value.Global =
+ cast<GlobalSymbol>(WasmSym::MemoryBase)->getGlobalIndex();
+ } else {
+ InitExpr.Opcode = WASM_OPCODE_I32_CONST;
+ InitExpr.Value.Int32 = Segment->StartVA;
+ }
+ writeInitExpr(OS, InitExpr);
writeUleb128(OS, Segment->Size, "segment size");
OS.flush();
Modified: lld/trunk/wasm/Symbols.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/Symbols.cpp?rev=346918&r1=346917&r2=346918&view=diff
==============================================================================
--- lld/trunk/wasm/Symbols.cpp (original)
+++ lld/trunk/wasm/Symbols.cpp Wed Nov 14 16:37:21 2018
@@ -28,6 +28,8 @@ DefinedData *WasmSym::DsoHandle;
DefinedData *WasmSym::DataEnd;
DefinedData *WasmSym::HeapBase;
DefinedGlobal *WasmSym::StackPointer;
+Symbol *WasmSym::TableBase;
+Symbol *WasmSym::MemoryBase;
WasmSymbolType Symbol::getWasmType() const {
if (isa<FunctionSymbol>(this))
Modified: lld/trunk/wasm/Symbols.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/Symbols.h?rev=346918&r1=346917&r2=346918&view=diff
==============================================================================
--- lld/trunk/wasm/Symbols.h (original)
+++ lld/trunk/wasm/Symbols.h Wed Nov 14 16:37:21 2018
@@ -310,6 +310,14 @@ struct WasmSym {
// __dso_handle
// Symbol used in calls to __cxa_atexit to determine current DLL
static DefinedData *DsoHandle;
+
+ // __table_base
+ // Used in PIC code for offset of indirect function table
+ static Symbol *TableBase;
+
+ // __memory_base
+ // Used in PIC code for offset of global data
+ static Symbol *MemoryBase;
};
// A buffer class that is large enough to hold any Symbol-derived
Modified: lld/trunk/wasm/Writer.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/Writer.cpp?rev=346918&r1=346917&r2=346918&view=diff
==============================================================================
--- lld/trunk/wasm/Writer.cpp (original)
+++ lld/trunk/wasm/Writer.cpp Wed Nov 14 16:37:21 2018
@@ -39,7 +39,6 @@ using namespace lld;
using namespace lld::wasm;
static constexpr int kStackAlignment = 16;
-static constexpr int kInitialTableOffset = 1;
static constexpr const char *kFunctionTableName = "__indirect_function_table";
namespace {
@@ -90,6 +89,7 @@ private:
void createCustomSections();
// Custom sections
+ void createDylinkSection();
void createRelocSections();
void createLinkingSection();
void createNameSection();
@@ -98,8 +98,13 @@ private:
void writeSections();
uint64_t FileSize = 0;
+ uint32_t TableBase = 0;
uint32_t NumMemoryPages = 0;
uint32_t MaxMemoryPages = 0;
+ // Memory size and aligment. Written to the "dylink" section
+ // when build with -shared or -pie.
+ uint32_t MemAlign = 0;
+ uint32_t MemSize = 0;
std::vector<const WasmSignature *> Types;
DenseMap<WasmSignature, int32_t> TypeIndices;
@@ -161,7 +166,7 @@ void Writer::createImportSection() {
}
if (Config->ImportTable) {
- uint32_t TableSize = kInitialTableOffset + IndirectFunctions.size();
+ uint32_t TableSize = TableBase + IndirectFunctions.size();
WasmImport Import;
Import.Module = "env";
Import.Field = kFunctionTableName;
@@ -259,7 +264,7 @@ void Writer::createTableSection() {
// no address-taken function will fail at validation time since it is
// a validation error to include a call_indirect instruction if there
// is not table.
- uint32_t TableSize = kInitialTableOffset + IndirectFunctions.size();
+ uint32_t TableSize = TableBase + IndirectFunctions.size();
SyntheticSection *Section = createSyntheticSection(WASM_SEC_TABLE);
raw_ostream &OS = Section->getStream();
@@ -325,12 +330,18 @@ void Writer::createElemSection() {
writeUleb128(OS, 1, "segment count");
writeUleb128(OS, 0, "table index");
WasmInitExpr InitExpr;
- InitExpr.Opcode = WASM_OPCODE_I32_CONST;
- InitExpr.Value.Int32 = kInitialTableOffset;
+ if (Config->Pic) {
+ InitExpr.Opcode = WASM_OPCODE_GET_GLOBAL;
+ InitExpr.Value.Global =
+ cast<GlobalSymbol>(WasmSym::TableBase)->getGlobalIndex();
+ } else {
+ InitExpr.Opcode = WASM_OPCODE_I32_CONST;
+ InitExpr.Value.Int32 = TableBase;
+ }
writeInitExpr(OS, InitExpr);
writeUleb128(OS, IndirectFunctions.size(), "elem count");
- uint32_t TableIndex = kInitialTableOffset;
+ uint32_t TableIndex = TableBase;
for (const FunctionSymbol *Sym : IndirectFunctions) {
assert(Sym->getTableIndex() == TableIndex);
writeUleb128(OS, Sym->getFunctionIndex(), "function index");
@@ -425,6 +436,20 @@ public:
raw_string_ostream OS{Body};
};
+// Create the custom "dylink" section containing information for the dynamic
+// linker.
+// See
+// https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
+void Writer::createDylinkSection() {
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_CUSTOM, "dylink");
+ raw_ostream &OS = Section->getStream();
+
+ writeUleb128(OS, MemSize, "MemSize");
+ writeUleb128(OS, int(log2(MemAlign)), "MemAlign");
+ writeUleb128(OS, IndirectFunctions.size(), "TableSize");
+ writeUleb128(OS, 0, "TableAlign");
+}
+
// Create the custom "linking" section containing linker metadata.
// This is only created when relocatable output is requested.
void Writer::createLinkingSection() {
@@ -599,7 +624,7 @@ void Writer::layoutMemory() {
uint32_t MemoryPtr = 0;
auto PlaceStack = [&]() {
- if (Config->Relocatable)
+ if (Config->Relocatable || Config->Shared)
return;
MemoryPtr = alignTo(MemoryPtr, kStackAlignment);
if (Config->ZStackSize != alignTo(Config->ZStackSize, kStackAlignment))
@@ -625,7 +650,9 @@ void Writer::layoutMemory() {
if (WasmSym::DsoHandle)
WasmSym::DsoHandle->setVirtualAddress(DataStart);
+ MemAlign = 0;
for (OutputSegment *Seg : Segments) {
+ MemAlign = std::max(MemAlign, Seg->Alignment);
MemoryPtr = alignTo(MemoryPtr, Seg->Alignment);
Seg->StartVA = MemoryPtr;
log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}", Seg->Name,
@@ -658,8 +685,8 @@ void Writer::layoutMemory() {
else
MemoryPtr = Config->InitialMemory;
}
- uint32_t MemSize = alignTo(MemoryPtr, WasmPageSize);
- NumMemoryPages = MemSize / WasmPageSize;
+ MemSize = MemoryPtr;
+ NumMemoryPages = alignTo(MemoryPtr, WasmPageSize) / WasmPageSize;
log("mem: total pages = " + Twine(NumMemoryPages));
if (Config->MaxMemory != 0) {
@@ -682,6 +709,8 @@ SyntheticSection *Writer::createSyntheti
void Writer::createSections() {
// Known sections
+ if (Config->Pic)
+ createDylinkSection();
createTypeSection();
createImportSection();
createFunctionSection();
@@ -874,7 +903,7 @@ void Writer::assignIndexes() {
AddDefinedFunction(Func);
}
- uint32_t TableIndex = kInitialTableOffset;
+ uint32_t TableIndex = TableBase;
auto HandleRelocs = [&](InputChunk *Chunk) {
if (!Chunk->Live)
return;
@@ -926,6 +955,10 @@ void Writer::assignIndexes() {
}
static StringRef getOutputDataSegmentName(StringRef Name) {
+ // With PIC code we currently only support a single data segment since
+ // we only have a single __memory_base to use as our base address.
+ if (Config->Pic)
+ return "data";
if (!Config->MergeDataSegments)
return Name;
if (Name.startswith(".text."))
@@ -1010,9 +1043,14 @@ void Writer::calculateInitFunctions() {
}
void Writer::run() {
- if (Config->Relocatable)
+ if (Config->Relocatable || Config->Pic)
Config->GlobalBase = 0;
+ // For PIC code the table base is assigned dynamically by the loader.
+ // For non-PIC, we start at 1 so that accessing table index 0 always traps.
+ if (!Config->Pic)
+ TableBase = 1;
+
log("-- calculateImports");
calculateImports();
log("-- assignIndexes");
More information about the llvm-commits
mailing list