add_subdirectory(COFF)
add_subdirectory(ELF)
add_subdirectory(MinGW)
-
+add_subdirectory(wasm)
E: lhames@gmail.com, kledzik@apple.com
D: Mach-O backend
+N: Sam Clegg
+E: sbc@chromium.org
+D: WebAssembly backend (wasm/*)
-The ELF and COFF Linkers
-========================
+The ELF, COFF and Wasm Linkers
+==============================
The ELF Linker as a Library
---------------------------
We implemented the linkers as native linkers for each file format.
- The two linkers share the same design but do not share code.
+ The linkers share the same design but share very little code.
Sharing code makes sense if the benefit is worth its cost.
- In our case, ELF and COFF are different enough that we thought the layer to
- abstract the differences wouldn't worth its complexity and run-time cost.
- Elimination of the abstract layer has greatly simplified the implementation.
+ In our case, the object formats are different enough that we thought the layer
+ to abstract the differences wouldn't be worth its complexity and run-time
+ cost. Elimination of the abstract layer has greatly simplified the
+ implementation.
* Speed by design
--- /dev/null
+WebAssembly lld port
+====================
+
+Note: The WebAssembly port is still a work in progress and is be lacking
+certain features.
+
+The WebAssembly version of lld takes WebAssembly binaries as inputs and produces
+a WebAssembly binary as its output. For the most part this port tried to mimic
+the behaviour of traditional ELF linkers and specifically the ELF lld port.
+Where possible that command line flags and the semantics should be the same.
+
+
+Object file format
+------------------
+
+The format the input object files that lld expects is specified as part of the
+the WebAssembly tool conventions
+https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md.
+
+This is object format that the llvm will produce when run with the
+``wasm32-unknown-unknown-wasm`` target. To build llvm with WebAssembly support
+currently requires enabling the experimental backed using
+``-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly``.
+
+
+Missing features
+----------------
+
+There are several key features that are not yet implement in the WebAssembly
+ports:
+
+- 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.
+- Section start/end symbols. The synthetic symbols that mark the start and
+ of data regions are not yet created in the output file.
for system linkers and runs much faster than them. It also provides
features that are useful for toolchain developers.
-The linker supports ELF (Unix), PE/COFF (Windows) and Mach-O (macOS)
-in descending order of completeness. Internally, LLD consists of three
-different linkers. The ELF port is the one that will be described in
-this document. The PE/COFF port is almost complete except the lack of
-the Windows debug info (PDB) support. The Mach-O port is built based
-on a different architecture than the ELF or COFF ports. For the
-details about Mach-O, please read :doc:`AtomLLD`.
+The linker supports ELF (Unix), PE/COFF (Windows), Mach-O (macOS) and
+WebAssembly in descending order of completeness. Internally, LLD consists of
+several different linkers. The ELF port is the one that will be described in
+this document. The PE/COFF port is almost complete except the lack of the
+Windows debug info (PDB) support. The WebAssembly port is still a work in
+progress (See :doc:`WebAssembly`). The Mach-O port is built based on a
+different architecture than the others. For the details about Mach-O, please
+read :doc:`AtomLLD`.
Features
--------
NewLLD
AtomLLD
+ WebAssembly
windows_support
ReleaseNotes
bool link(llvm::ArrayRef<const char *> Args,
llvm::raw_ostream &Diag = llvm::errs());
}
+
+namespace wasm {
+bool link(llvm::ArrayRef<const char *> Args, bool CanExitEarly,
+ llvm::raw_ostream &Diag = llvm::errs());
+}
}
#endif
llvm_config.use_lld()
tool_patterns = [
- 'llvm-as', 'llvm-mc', 'llvm-nm',
+ 'llc', 'llvm-as', 'llvm-mc', 'llvm-nm',
'llvm-objdump', 'llvm-pdbutil', 'llvm-readobj', 'obj2yaml', 'yaml2obj']
llvm_config.add_tool_substitutions(tool_patterns)
'Mips': 'mips',
'PowerPC': 'ppc',
'Sparc': 'sparc',
+ 'WebAssembly': 'wasm',
'X86': 'x86'})
])
--- /dev/null
+declare i32 @bar() local_unnamed_addr #1
+
+define i32 @foo() local_unnamed_addr #0 {
+entry:
+ %call = tail call i32 @bar() #2
+ ret i32 %call
+}
--- /dev/null
+declare i32 @foo() local_unnamed_addr #1
+
+define i32 @bar() local_unnamed_addr #0 {
+entry:
+ %call = tail call i32 @foo() #2
+ ret i32 %call
+}
--- /dev/null
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown-wasm"
+
+@indirect_bar = hidden local_unnamed_addr global i32 ()* @bar, align 4
+
+; Function Attrs: norecurse nounwind readnone
+define hidden i32 @bar() #0 {
+entry:
+ ret i32 1
+}
+
+; Function Attrs: nounwind
+define hidden void @call_bar_indirect() local_unnamed_addr #1 {
+entry:
+ %0 = load i32 ()*, i32 ()** @indirect_bar, align 4
+ %call = tail call i32 %0() #2
+ ret void
+}
--- /dev/null
+; Wasm module generated from the following C code:
+; void puts(const char*);
+; void hello() { puts("hello\n"); }
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown-wasm"
+
+@hello_str = unnamed_addr constant [7 x i8] c"hello\0A\00", align 1
+
+; Function Attrs: nounwind
+define hidden void @hello() local_unnamed_addr #0 {
+entry:
+ tail call void @puts(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @hello_str, i32 0, i32 0))
+ ret void
+}
+
+; Function Attrs: nounwind
+declare void @puts(i8* nocapture readonly) local_unnamed_addr #1
--- /dev/null
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown-wasm"
+
+@g0 = global i32 1, align 4
+@foo = global i32 1, align 4
+
+define i32 @f1() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f2() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f3() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f4() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f5() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f6() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f7() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f8() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f9() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f10() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f11() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f12() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f13() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f14() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f15() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f16() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f17() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f18() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f19() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f20() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f21() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f22() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f23() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f24() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f25() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f26() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f27() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f28() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f29() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f30() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f31() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f32() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f33() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f34() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f35() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f36() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f37() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f38() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f39() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f40() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f41() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f42() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f43() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f44() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f45() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f46() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f47() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f48() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f49() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f50() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f51() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f52() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f53() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f54() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f55() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f56() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f57() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f58() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f59() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f60() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f61() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f62() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f63() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f64() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f65() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f66() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f67() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f68() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f69() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f70() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f71() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f72() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f73() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f74() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f75() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f76() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f77() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f78() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f79() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f80() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f81() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f82() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f83() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f84() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f85() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f86() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f87() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f88() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f89() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f90() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f91() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f92() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f93() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f94() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f95() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f96() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f97() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f98() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f99() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f100() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f101() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f102() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f103() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f104() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f105() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f106() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f107() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f108() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f109() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f110() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f111() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f112() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f113() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f114() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f115() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f116() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f117() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f118() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f119() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f120() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f121() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f122() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f123() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f124() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f125() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f126() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f127() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f128() {
+entry:
+ %0 = load i32, i32* @g0, align 4
+ ret i32 %0
+}
+
+define i32 @f129() {
+entry:
+ %0 = load i32, i32* @g0, align 4
+ ret i32 %0
+}
--- /dev/null
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown-wasm"
+
+; Function Attrs: norecurse nounwind readnone
+define hidden i32 @ret32(float %arg) #0 {
+entry:
+ ret i32 0
+ ; ptrtoint (i32 (float)* @ret32 to i32)
+}
--- /dev/null
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown-wasm"
+
+define hidden i64 @ret64(double %arg) local_unnamed_addr #0 {
+entry:
+ ret i64 1
+}
--- /dev/null
+define i32 @foo() #0 {
+entry:
+ ret i32 0
+}
+
+@bar = weak alias i32 (), i32 ()* @foo
+
+define hidden i32 @call_bar() #0 {
+entry:
+ %call = call i32 @bar()
+ ret i32 %call
+}
--- /dev/null
+; Verify that multually dependant object files in an archive is handled
+; correctly.
+;
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %s -o %t.o
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %S/Inputs/archive1.ll -o %t2.o
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %S/Inputs/archive2.ll -o %t3.o
+; RUN: llvm-ar rcs %t.a %t2.o %t3.o
+; RUN: lld -flavor wasm %t.a %t.o -o %t.wasm
+; RUN: llvm-nm -a %t.wasm | FileCheck %s
+
+; Specifying the same archive twice is allowed.
+; RUN: lld -flavor wasm %t.a %t.a %t.o -o %t.wasm
+
+declare i32 @foo() local_unnamed_addr #1
+
+define i32 @_start() local_unnamed_addr #0 {
+entry:
+ %call = tail call i32 @foo() #2
+ ret i32 %call
+}
+
+; CHECK: T _start
+; CHECK: T bar
+; CHECK: T foo
--- /dev/null
+; RUN: llc -filetype=obj %p/Inputs/call-indirect.ll -o %t2.o
+; RUN: llc -filetype=obj %s -o %t.o
+; RUN: lld -flavor wasm -o %t.wasm %t2.o %t.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; bitcode generated from the following C code:
+; int foo(void) { return 1; }
+; int (*indirect_func)(void) = &foo;
+; void _start(void) { indirect_func(); }
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown-wasm"
+
+@indirect_func = hidden local_unnamed_addr global i32 ()* @foo, align 4
+
+; Function Attrs: norecurse nounwind readnone
+define hidden i32 @foo() #0 {
+entry:
+ ret i32 1
+}
+
+; Function Attrs: nounwind
+define hidden void @_start() local_unnamed_addr #1 {
+entry:
+ %0 = load i32 ()*, i32 ()** @indirect_func, align 4
+ %call = tail call i32 %0() #2
+ ret void
+}
+
+; CHECK: !WASM
+; CHECK-NEXT: FileHeader:
+; CHECK-NEXT: Version: 0x00000001
+; CHECK-NEXT: Sections:
+; CHECK-NEXT: - Type: TYPE
+; CHECK-NEXT: Signatures:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: ReturnType: NORESULT
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Type: FUNCTION
+; CHECK-NEXT: FunctionTypes: [ 0, 1, 0, 1 ]
+; CHECK-NEXT: - Type: TABLE
+; CHECK-NEXT: Tables:
+; CHECK-NEXT: - ElemType: ANYFUNC
+; CHECK-NEXT: Limits:
+; CHECK-NEXT: Flags: 0x00000001
+; CHECK-NEXT: Initial: 0x00000003
+; CHECK-NEXT: Maximum: 0x00000003
+; CHECK-NEXT: - Type: MEMORY
+; CHECK-NEXT: Memories:
+; CHECK-NEXT: - Initial: 0x00000002
+; CHECK-NEXT: - Type: GLOBAL
+; CHECK-NEXT: Globals:
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: true
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 66576
+; CHECK-NEXT: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: memory
+; CHECK-NEXT: Kind: MEMORY
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: bar
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: call_bar_indirect
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: - Name: foo
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 2
+; CHECK-NEXT: - Name: _start
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 3
+; CHECK: - Type: ELEM
+; CHECK-NEXT: Segments:
+; CHECK-NEXT: - Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1
+; CHECK-NEXT: Functions: [ 0, 2 ]
+; CHECK-NEXT: - Type: CODE
+; CHECK-NEXT: Functions:
+; CHECK: - Locals:
+; CHECK: - Locals:
+; CHECK: - Locals:
+; CHECK: - Type: DATA
+; CHECK-NEXT: Segments:
+; CHECK-NEXT: - SectionOffset: 7
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1024
+; CHECK-NEXT: Content: '0100000002000000'
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 8
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: name
+; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: bar
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: call_bar_indirect
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: foo
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Name: _start
--- /dev/null
+# RUN: llc -filetype=obj %p/Inputs/ret32.ll -o %t.ret32.o
+# RUN: not lld -flavor wasm -o %t.wasm %t.ret32.o %t.ret32.o 2>&1 | FileCheck %s
+
+# CHECK: duplicate symbol: ret32
+# CHECK-NEXT: >>> defined in {{.*}}conflict.test.tmp.ret32.o
+# CHECK-NEXT: >>> defined in {{.*}}conflict.test.tmp.ret32.o
--- /dev/null
+; RUN: llc -filetype=obj %p/Inputs/hello.ll -o %t.hello.o
+; RUN: llc -filetype=obj %s -o %t.o
+; RUN: lld -flavor wasm --emit-relocs --allow-undefined -o %t.wasm %t.o %t.hello.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown-wasm"
+
+@foo = hidden global i32 1, align 4
+@aligned_bar = hidden global i32 3, align 16
+
+@hello_str = external global i8*
+@external_ref = global i8** @hello_str, align 8
+
+; CHECK: - Type: GLOBAL
+; CHECK-NEXT: Globals:
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: true
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 66608
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1024
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1040
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1048
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1052
+
+; CHECK: - Type: DATA
+; CHECK-NEXT: Relocations:
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_I32
+; CHECK-NEXT: Index: 4
+; CHECK-NEXT: Offset: 0x0000001F
+; CHECK-NEXT: Segments:
+; CHECK-NEXT: - SectionOffset: 7
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1024
+; CHECK-NEXT: Content: 0100000000000000000000000000000003000000000000001C040000
+; CHECK-NEXT: - SectionOffset: 41
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1052
+; CHECK-NEXT: Content: 68656C6C6F0A00
+
+; CHECK: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 35
--- /dev/null
+; RUN: llc -filetype=obj %s -o %t.o
+; RUN: lld -flavor wasm -e entry -o %t.wasm %t.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+; RUN: lld -flavor wasm --entry=entry -o %t.wasm %t.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown-wasm"
+
+define hidden void @entry() local_unnamed_addr #0 {
+entry:
+ ret void
+}
+
+; CHECK: - Type: EXPORT
+; CHECK: Exports:
+; CHECK: - Name: memory
+; CHECK: Kind: MEMORY
+; CHECK: Index: 0
+; CHECK: - Name: entry
+; CHECK: Kind: FUNCTION
+; CHECK: Index: 0
--- /dev/null
+; RUN: llc -filetype=obj %p/Inputs/ret32.ll -o %t.ret32.o
+; RUN: llc -filetype=obj %s -o %t.o
+; RUN: lld -flavor wasm -o %t.wasm %t.o %t.ret32.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown-wasm"
+
+; Function Attrs: nounwind
+define hidden void @_start() local_unnamed_addr #0 {
+entry:
+ %call = tail call i32 @ret32(float 0.000000e+00) #2
+ ret void
+}
+
+declare i32 @ret32(float) local_unnamed_addr #1
+
+; CHECK: - Type: TYPE
+; CHECK: Signatures:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: NORESULT
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - F32
+; CHECK: - Type: FUNCTION
+; CHECK-NEXT: FunctionTypes: [ 0, 1 ]
+; CHECK: - Type: CODE
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 43000000001081808080001A0B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41000B
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 0
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: name
+; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: _start
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: ret32
+; CHECK-NEXT: ...
--- /dev/null
+; RUN: llc -filetype=obj %p/Inputs/ret32.ll -o %t.ret32.o
+; RUN: llc -filetype=obj %s -o %t.o
+; RUN: lld -flavor wasm -o %t.wasm %t.ret32.o %t.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown-wasm"
+
+; Function Attrs: nounwind
+define hidden void @_start() local_unnamed_addr #0 {
+entry:
+ %call = tail call i32 @ret32(float 0.000000e+00) #2
+ ret void
+}
+
+declare i32 @ret32(float) local_unnamed_addr #1
+
+; CHECK: Sections:
+; CHECK: - Type: TYPE
+; CHECK-NEXT: Signatures:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - F32
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: ReturnType: NORESULT
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Type: FUNCTION
+; CHECK-NEXT: FunctionTypes: [ 0, 1 ]
+; CHECK: - Type: CODE
+; CHECK-NEXT: Functions:
+; CHECK: - Locals:
+; CHECK: - Locals:
+; CHECK: Name: name
+; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: ret32
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: _start
+; CHECK-NEXT: ...
--- /dev/null
+# RUN: llc -filetype=obj %p/Inputs/ret32.ll -o %t.ret32.o
+# RUN: llc -filetype=obj %p/Inputs/ret64.ll -o %t.ret64.o
+# RUN: lld -flavor wasm -r -o %t.wasm %t.ret32.o %t.ret64.o
+# RUN: obj2yaml %t.wasm | FileCheck %s
+
+CHECK: Sections:
+CHECK: - Type: TYPE
+CHECK: Signatures:
+CHECK: - Index: 0
+CHECK: ReturnType: I32
+CHECK: ParamTypes:
+CHECK: - F32
+CHECK: - Index: 1
+CHECK: ReturnType: I64
+CHECK: ParamTypes:
+CHECK: - F64
+CHECK: - Type: FUNCTION
+CHECK: FunctionTypes: [ 0, 1 ]
--- /dev/null
+# RUN: llc -filetype=obj %p/Inputs/ret32.ll -o %t.ret32.o
+# RUN: lld -flavor wasm -entry ret32 --import-memory -o %t.wasm %t.ret32.o
+# RUN: obj2yaml %t.wasm | FileCheck %s
+
+# Verify the --import-memory flag creates a memory import
+
+# CHECK: - Type: IMPORT
+# CHECK-NEXT: Imports:
+# CHECK-NEXT: - Module: env
+# CHECK-NEXT: Field: memory
+# CHECK-NEXT: Kind: MEMORY
+# CHECK-NEXT: Memory:
+# CHECK-NEXT: Initial: 0x00000002
--- /dev/null
+; RUN: llc -mtriple wasm32-unknown-unknown-wasm -filetype=obj %s -o %t.o
+; RUN: not lld -flavor wasm -o %t.wasm -z stack-size=1 %t.o 2>&1 | FileCheck %s
+
+define i32 @_start() local_unnamed_addr #1 {
+entry:
+ ret i32 0
+}
+
+; CHECK: error: stack size must be 16-byte aligned
--- /dev/null
+if 'wasm' not in config.available_features:
+ config.unsupported = True
+
+config.suffixes = ['.test', '.yaml', '.ll']
--- /dev/null
+; RUN: llc -filetype=obj %s -o %t.o
+; RUN: lld -flavor wasm -o %t.wasm %t.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown-wasm"
+
+@foo = default global i32 1, align 4
+@bar = internal default global i32 3, align 4
+
+define internal i32 @baz() local_unnamed_addr {
+entry:
+ ret i32 2
+}
+
+define i32 @_start() local_unnamed_addr {
+entry:
+ ret i32 1
+}
+
+; CHECK: --- !WASM
+; CHECK-NEXT: FileHeader:
+; CHECK-NEXT: Version: 0x00000001
+; CHECK-NEXT: Sections:
+; CHECK-NEXT: - Type: TYPE
+; CHECK-NEXT: Signatures:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Type: FUNCTION
+; CHECK-NEXT: FunctionTypes: [ 0, 0 ]
+; CHECK-NEXT: - Type: TABLE
+; CHECK-NEXT: Tables:
+; CHECK-NEXT: - ElemType: ANYFUNC
+; CHECK-NEXT: Limits:
+; CHECK-NEXT: Flags: 0x00000001
+; CHECK-NEXT: Initial: 0x00000001
+; CHECK-NEXT: Maximum: 0x00000001
+; CHECK-NEXT: - Type: MEMORY
+; CHECK-NEXT: Memories:
+; CHECK-NEXT: - Initial: 0x00000002
+; CHECK-NEXT: - Type: GLOBAL
+; CHECK-NEXT: Globals:
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: true
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 66576
+; CHECK-NEXT: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: memory
+; CHECK-NEXT: Kind: MEMORY
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: _start
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: - Type: CODE
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41020B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41010B
+; CHECK-NEXT: - Type: DATA
+; CHECK-NEXT: Segments:
+; CHECK-NEXT: - SectionOffset: 7
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1024
+; CHECK-NEXT: Content: '0100000003000000'
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 8
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: name
+; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: baz
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: _start
+; CHECK-NEXT: ...
--- /dev/null
+; RUN: llc -filetype=obj %p/Inputs/many-funcs.ll -o %t.many.o
+; RUN: llc -filetype=obj %s -o %t.o
+; RUN: lld -flavor wasm -r -o %t.wasm %t.many.o %t.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; Test that relocations within the CODE section correctly handle
+; linking object with different header sizes. many-funcs.ll has
+; 128 function and so the final output requires a 2-byte LEB in
+; the CODE section header to store the function count.
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown-wasm"
+
+define i32 @func() {
+entry:
+ %call = tail call i32 @func()
+ ret i32 %call
+}
+
+; CHECK: - Type: CODE
+; CHECK-NEXT: Relocations:
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000008
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000014
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000020
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000002C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000038
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000044
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000050
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000005C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000068
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000074
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000080
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000008C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000098
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000000A4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000000B0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000000BC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000000C8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000000D4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000000E0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000000EC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000000F8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000104
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000110
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000011C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000128
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000134
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000140
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000014C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000158
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000164
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000170
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000017C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000188
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000194
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000001A0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000001AC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000001B8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000001C4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000001D0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000001DC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000001E8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000001F4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000200
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000020C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000218
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000224
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000230
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000023C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000248
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000254
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000260
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000026C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000278
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000284
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000290
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000029C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000002A8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000002B4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000002C0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000002CC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000002D8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000002E4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000002F0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000002FC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000308
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000314
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000320
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000032C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000338
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000344
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000350
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000035C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000368
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000374
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000380
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000038C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000398
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000003A4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000003B0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000003BC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000003C8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000003D4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000003E0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000003EC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000003F8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000404
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000410
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000041C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000428
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000434
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000440
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000044C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000458
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000464
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000470
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000047C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000488
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000494
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000004A0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000004AC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000004B8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000004C4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000004D0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000004DC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000004E8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000004F4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000500
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000050C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000518
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000524
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000530
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000053C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000548
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000554
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000560
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000056C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000578
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000584
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000590
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000059C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000005A8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000005B4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000005C0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000005CC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000005D8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000005E4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000005F0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Offset: 0x000005FC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Offset: 0x00000608
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_FUNCTION_INDEX_LEB
+; CHECK-NEXT: Index: 129
+; CHECK-NEXT: Offset: 0x00000611
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280280808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280280808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 1081818080000B
+; CHECK-NEXT: - Type: DATA
+; CHECK-NEXT: Segments:
+; CHECK-NEXT: - SectionOffset: 6
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 0
+; CHECK-NEXT: Content: '01000000'
+; CHECK-NEXT: - SectionOffset: 15
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 4
+; CHECK-NEXT: Content: '01000000'
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 8
+; CHECK-NEXT: SegmentInfo:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: .data.g0
+; CHECK-NEXT: Alignment: 4
+; CHECK-NEXT: Flags: 0
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: .data.foo
+; CHECK-NEXT: Alignment: 4
+; CHECK-NEXT: Flags: 0
+
--- /dev/null
+; RUN: llc -filetype=obj %p/Inputs/hello.ll -o %t.hello.o
+; RUN: llc -filetype=obj %s -o %t.o
+; RUN: lld -flavor wasm -r -o %t.wasm %t.hello.o %t.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown-wasm"
+
+; Function Attrs: nounwind
+define hidden i32 @my_func() local_unnamed_addr {
+entry:
+ %call = tail call i32 @foo_import()
+ ret i32 1
+}
+
+declare i32 @foo_import() local_unnamed_addr
+@data_import = external global i64
+
+@func_addr1 = hidden global i32()* @my_func, align 4
+@func_addr2 = hidden global i32()* @foo_import, align 4
+@data_addr1 = hidden global i64* @data_import, align 8
+
+; CHECK: --- !WASM
+; CHECK-NEXT: FileHeader:
+; CHECK-NEXT: Version: 0x00000001
+; CHECK-NEXT: Sections:
+; CHECK-NEXT: - Type: TYPE
+; CHECK-NEXT: Signatures:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: NORESULT
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: ReturnType: NORESULT
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - I32
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Type: IMPORT
+; CHECK-NEXT: Imports:
+; CHECK-NEXT: - Module: env
+; CHECK-NEXT: Field: puts
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: SigIndex: 1
+; CHECK-NEXT: - Module: env
+; CHECK-NEXT: Field: foo_import
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: SigIndex: 2
+; CHECK-NEXT: - Module: env
+; CHECK-NEXT: Field: data_import
+; CHECK-NEXT: Kind: GLOBAL
+; CHECK-NEXT: GlobalType: I32
+; CHECK-NEXT: GlobalMutable: false
+; CHECK-NEXT: - Type: FUNCTION
+; CHECK-NEXT: FunctionTypes: [ 0, 2 ]
+; CHECK-NEXT: - Type: TABLE
+; CHECK-NEXT: Tables:
+; CHECK-NEXT: - ElemType: ANYFUNC
+; CHECK-NEXT: Limits:
+; CHECK-NEXT: Flags: 0x00000001
+; CHECK-NEXT: Initial: 0x00000002
+; CHECK-NEXT: Maximum: 0x00000002
+; CHECK-NEXT: - Type: MEMORY
+; CHECK-NEXT: Memories:
+; CHECK-NEXT: - Initial: 0x00000001
+; CHECK-NEXT: - Type: GLOBAL
+; CHECK-NEXT: Globals:
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 0
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 8
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 12
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 16
+; CHECK-NEXT: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: hello
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 2
+; CHECK-NEXT: - Name: my_func
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 3
+; CHECK-NEXT: - Type: ELEM
+; CHECK-NEXT: Segments:
+; CHECK-NEXT: - Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 0
+; CHECK-NEXT: Functions: [ 3, 1 ]
+; CHECK-NEXT: - Type: CODE
+; CHECK-NEXT: Relocations:
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_SLEB
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Offset: 0x00000004
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_FUNCTION_INDEX_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000000A
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_FUNCTION_INDEX_LEB
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Offset: 0x00000013
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4180808080001080808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 1081808080001A41010B
+; CHECK-NEXT: - Type: DATA
+; CHECK-NEXT: Relocations:
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_TABLE_INDEX_I32
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000012
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_TABLE_INDEX_I32
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Offset: 0x0000001B
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_I32
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000024
+; CHECK-NEXT: Segments:
+; CHECK-NEXT: - SectionOffset: 6
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 0
+; CHECK-NEXT: Content: 68656C6C6F0A00
+; CHECK-NEXT: - SectionOffset: 18
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 8
+; CHECK-NEXT: Content: '00000000'
+; CHECK-NEXT: - SectionOffset: 27
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 12
+; CHECK-NEXT: Content: '01000000'
+; CHECK-NEXT: - SectionOffset: 36
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 16
+; CHECK-NEXT: Content: FFFFFFFF
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 20
+; CHECK-NEXT: SegmentInfo:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: .rodata.hello_str
+; CHECK-NEXT: Alignment: 1
+; CHECK-NEXT: Flags: 0
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: .data.func_addr1
+; CHECK-NEXT: Alignment: 4
+; CHECK-NEXT: Flags: 0
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: .data.func_addr2
+; CHECK-NEXT: Alignment: 4
+; CHECK-NEXT: Flags: 0
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Name: .data.data_addr1
+; CHECK-NEXT: Alignment: 8
+; CHECK-NEXT: Flags: 0
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: name
+; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: puts
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: foo_import
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: hello
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Name: my_func
+; CHECK-NEXT: ...
--- /dev/null
+RUN: llc -filetype=obj %p/Inputs/ret32.ll -o %t.ret32.o
+RUN: lld -flavor wasm --strip-debug --entry=ret32 -o %t.wasm %t.ret32.o
+RUN: obj2yaml %t.wasm | FileCheck %s
+
+# Check that there is no name section
+CHECK-NOT: Name: name
--- /dev/null
+; RUN: llc -filetype=obj %s -o %t.o
+; RUN: llc -filetype=obj %p/Inputs/ret32.ll -o %t.ret32.o
+; RUN: not lld -flavor wasm -o %t.wasm %t.o %t.ret32.o 2>&1 | FileCheck %s
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown-wasm"
+
+@ret32 = extern_weak global i32, align 4
+
+; CHECK: error: symbol type mismatch: ret32
+; CHECK: >>> defined as Global in {{.*}}symtol-type-mismatch.ll.tmp.o
+; CHECK: >>> defined as Function in {{.*}}.ret32.o
--- /dev/null
+RUN: llc -filetype=obj %p/Inputs/ret32.ll -o %t.ret32.o
+RUN: not lld -flavor wasm -o %t.wasm %t.ret32.o 2>&1 | FileCheck %s
+
+CHECK: error: undefined symbol: _start
--- /dev/null
+; RUN: llc -filetype=obj %s -o %t.o
+; RUN: lld -flavor wasm --allow-undefined -o %t.wasm %t.o
+
+; Fails due to undefined 'foo'
+; RUN: not lld -flavor wasm -o %t.wasm %t.o 2>&1 | FileCheck %s
+; CHECK: error: {{.*}}.o: undefined symbol: foo
+
+; But succeeds if we pass a file containing 'foo' as --allow-undefined-file.
+; RUN: echo 'foo' > %t.txt
+; RUN: lld -flavor wasm --allow-undefined-file=%t.txt -o %t.wasm %t.o
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown-wasm"
+
+; Takes the address of the external foo() resulting in undefined external
+@bar = hidden local_unnamed_addr global i8* bitcast (i32 ()* @foo to i8*), align 4
+
+declare i32 @foo() #0
+
+define hidden void @_start() local_unnamed_addr #0 {
+entry:
+ ret void
+}
--- /dev/null
+; RUN: llc -filetype=obj %s -o %t.o
+; RUN: lld -flavor wasm -o %t.wasm %t.o
+; RUN: llvm-readobj -file-headers %t.wasm | FileCheck %s
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown-wasm"
+
+define hidden void @_start() local_unnamed_addr #0 {
+entry:
+ ret void
+}
+
+; CHECK: Format: WASM
+; CHECK: Arch: wasm32
+; CHECK: AddressSize: 32bit
+; CHECK: Version: 0x1
--- /dev/null
+; RUN: llc -mtriple wasm32-unknown-unknown-wasm -filetype=obj -o %t.o %s
+; RUN: llc -mtriple=wasm32-unknown-unknown-wasm -filetype=obj %S/Inputs/weak-alias.ll -o %t2.o
+; RUN: lld -flavor wasm %t.o %t2.o -o %t.wasm
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; Test that the strongly defined bar is used correctly despite the existence
+; of the weak alias
+
+define i32 @bar() local_unnamed_addr #1 {
+ ret i32 1
+}
+
+; Function Attrs: nounwind uwtable
+define void @_start() local_unnamed_addr #1 {
+entry:
+ %call = tail call i32 @bar() #2
+ ret void
+}
+
+; CHECK: --- !WASM
+; CHECK-NEXT: FileHeader:
+; CHECK-NEXT: Version: 0x00000001
+; CHECK-NEXT: Sections:
+; CHECK-NEXT: - Type: TYPE
+; CHECK-NEXT: Signatures:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: ReturnType: NORESULT
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Type: FUNCTION
+; CHECK-NEXT: FunctionTypes: [ 0, 1, 0, 0 ]
+; CHECK-NEXT: - Type: TABLE
+; CHECK-NEXT: Tables:
+; CHECK-NEXT: - ElemType: ANYFUNC
+; CHECK-NEXT: Limits:
+; CHECK-NEXT: Flags: 0x00000001
+; CHECK-NEXT: Initial: 0x00000001
+; CHECK-NEXT: Maximum: 0x00000001
+; CHECK-NEXT: - Type: MEMORY
+; CHECK-NEXT: Memories:
+; CHECK-NEXT: - Initial: 0x00000002
+; CHECK-NEXT: - Type: GLOBAL
+; CHECK-NEXT: Globals:
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: true
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 66560
+; CHECK-NEXT: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: memory
+; CHECK-NEXT: Kind: MEMORY
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: bar
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: _start
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: - Name: foo
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 2
+; CHECK-NEXT: - Name: call_bar
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 3
+; CHECK-NEXT: - Type: CODE
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41010B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 1080808080001A0B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 1080808080000B
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 0
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: name
+; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: bar
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: _start
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: foo
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Name: call_bar
+; CHECK-NEXT: ...
--- /dev/null
+; RUN: llc -mtriple wasm32-unknown-unknown-wasm -filetype=obj -o %t.o %s
+; RUN: llc -mtriple=wasm32-unknown-unknown-wasm -filetype=obj %S/Inputs/weak-alias.ll -o %t2.o
+; RUN: lld -flavor wasm %t.o %t2.o -o %t.wasm
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; Test that weak aliases (bar is a weak alias of foo) are linked correctly
+
+declare i32 @bar() local_unnamed_addr #1
+
+; Function Attrs: nounwind uwtable
+define i32 @_start() local_unnamed_addr #1 {
+entry:
+ %call = tail call i32 @bar() #2
+ ret i32 %call
+}
+
+; CHECK: --- !WASM
+; CHECK-NEXT: FileHeader:
+; CHECK-NEXT: Version: 0x00000001
+; CHECK-NEXT: Sections:
+; CHECK-NEXT: - Type: TYPE
+; CHECK-NEXT: Signatures:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Type: FUNCTION
+; CHECK-NEXT: FunctionTypes: [ 0, 0, 0 ]
+; CHECK-NEXT: - Type: TABLE
+; CHECK-NEXT: Tables:
+; CHECK-NEXT: - ElemType: ANYFUNC
+; CHECK-NEXT: Limits:
+; CHECK-NEXT: Flags: 0x00000001
+; CHECK-NEXT: Initial: 0x00000001
+; CHECK-NEXT: Maximum: 0x00000001
+; CHECK-NEXT: - Type: MEMORY
+; CHECK-NEXT: Memories:
+; CHECK-NEXT: - Initial: 0x00000002
+; CHECK-NEXT: - Type: GLOBAL
+; CHECK-NEXT: Globals:
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: true
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 66560
+; CHECK-NEXT: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: memory
+; CHECK-NEXT: Kind: MEMORY
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: bar
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: - Name: _start
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: foo
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: - Name: call_bar
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 2
+; CHECK-NEXT: - Type: CODE
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 1081808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 1081808080000B
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 0
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: name
+; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: _start
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: foo
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: call_bar
+; CHECK-NEXT: ...
--- /dev/null
+; RUN: llc -mtriple wasm32-unknown-unknown-wasm -filetype=obj -o %t.o %s
+; RUN: lld -flavor wasm -strip-debug %t.o -o %t.wasm
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; Test that undefined weak externals (global_var) and (foo) don't cause
+; link failures and resolve to zero.
+
+@global_var = extern_weak global i32, align 4
+
+declare extern_weak i32 @foo()
+
+define hidden i8* @get_address_of_foo() #0 {
+entry:
+ ret i8* bitcast (i32 ()* @foo to i8*)
+}
+
+define hidden i32* @get_address_of_global_var() #0 {
+ ret i32* @global_var
+}
+
+define hidden i32 @_start() #0 {
+entry:
+ %0 = load i32, i32* @global_var, align 4
+ ret i32 %0
+}
+
+; CHECK: --- !WASM
+; CHECK-NEXT: FileHeader:
+; CHECK-NEXT: Version: 0x00000001
+; CHECK-NEXT: Sections:
+; CHECK-NEXT: - Type: TYPE
+; CHECK-NEXT: Signatures:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Type: FUNCTION
+; CHECK-NEXT: FunctionTypes: [ 0, 0, 0 ]
+; CHECK-NEXT: - Type: TABLE
+; CHECK-NEXT: Tables:
+; CHECK-NEXT: - ElemType: ANYFUNC
+; CHECK-NEXT: Limits:
+; CHECK-NEXT: Flags: 0x00000001
+; CHECK-NEXT: Initial: 0x00000002
+; CHECK-NEXT: Maximum: 0x00000002
+; CHECK-NEXT: - Type: MEMORY
+; CHECK-NEXT: Memories:
+; CHECK-NEXT: - Initial: 0x00000002
+; CHECK-NEXT: - Type: GLOBAL
+; CHECK-NEXT: Globals:
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: true
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 66560
+; CHECK-NEXT: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: memory
+; CHECK-NEXT: Kind: MEMORY
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: get_address_of_foo
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: get_address_of_global_var
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: - Name: _start
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 2
+; CHECK-NEXT: - Type: ELEM
+; CHECK-NEXT: Segments:
+; CHECK-NEXT: - Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1
+; CHECK-NEXT: Functions: [ 0 ]
+; CHECK-NEXT: - Type: CODE
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4181808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41FFFFFFFF7F0B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41002802FFFFFFFF0F0B
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 0
+; CHECK-NEXT: ...
lldDriver
lldELF
lldMinGW
+ lldWasm
)
install(TARGETS lld
RUNTIME DESTINATION bin)
if(NOT LLD_SYMLINKS_TO_CREATE)
- set(LLD_SYMLINKS_TO_CREATE lld-link ld.lld ld64.lld)
+ set(LLD_SYMLINKS_TO_CREATE lld-link ld.lld ld64.lld wasm-ld)
endif()
foreach(link ${LLD_SYMLINKS_TO_CREATE})
Gnu, // -flavor gnu
WinLink, // -flavor link
Darwin, // -flavor darwin
+ Wasm, // -flavor wasm
};
LLVM_ATTRIBUTE_NORETURN static void die(const Twine &S) {
static Flavor getFlavor(StringRef S) {
return StringSwitch<Flavor>(S)
.CasesLower("ld", "ld.lld", "gnu", Gnu)
+ .CasesLower("wasm", "ld-wasm", Wasm)
.CaseLower("link", WinLink)
.CasesLower("ld64", "ld64.lld", "darwin", Darwin)
.Default(Invalid);
return !coff::link(Args, true);
case Darwin:
return !mach_o::link(Args);
+ case Wasm:
+ return !wasm::link(Args, true);
default:
die("lld is a generic driver.\n"
"Invoke ld.lld (Unix), ld (macOS) or lld-link (Windows) instead.");
--- /dev/null
+set(LLVM_TARGET_DEFINITIONS Options.td)
+tablegen(LLVM Options.inc -gen-opt-parser-defs)
+add_public_tablegen_target(WasmOptionsTableGen)
+
+add_lld_library(lldWasm
+ Driver.cpp
+ InputFiles.cpp
+ InputSegment.cpp
+ OutputSections.cpp
+ Strings.cpp
+ SymbolTable.cpp
+ Symbols.cpp
+ Writer.cpp
+ WriterUtils.cpp
+
+ LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ BinaryFormat
+ Core
+ Demangle
+ Object
+ Option
+ Support
+
+ LINK_LIBS
+ lldCommon
+ lldCore
+ )
--- /dev/null
+//===- Config.h -------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_CONFIG_H
+#define LLD_WASM_CONFIG_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/Wasm.h"
+
+#include "Symbols.h"
+
+using llvm::wasm::WasmGlobal;
+
+#include <set>
+
+namespace lld {
+namespace wasm {
+
+struct Configuration {
+ bool AllowUndefined = false;
+ bool Demangle = true;
+ bool EmitRelocs = false;
+ bool ImportMemory = false;
+ bool Relocatable = false;
+ bool StripDebug = false;
+ bool StripAll = false;
+ uint32_t ZStackSize = 0;
+ uint32_t MaxMemory = 0;
+ uint32_t GlobalBase = 0;
+ uint32_t InitialMemory = 0;
+ llvm::StringRef Entry;
+ llvm::StringRef Sysroot;
+ llvm::StringRef OutputFile;
+
+ std::vector<llvm::StringRef> SearchPaths;
+ std::set<llvm::StringRef> AllowUndefinedSymbols;
+ std::vector<std::pair<Symbol *, WasmGlobal>> SyntheticGlobals;
+};
+
+// The only instance of Configuration struct.
+extern Configuration *Config;
+
+} // namespace wasm
+} // namespace lld
+
+#endif
--- /dev/null
+//===- Driver.cpp ---------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Common/Driver.h"
+#include "Config.h"
+#include "Memory.h"
+#include "SymbolTable.h"
+#include "Writer.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Threads.h"
+#include "lld/Common/Version.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Object/Wasm.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+
+using namespace llvm;
+using namespace llvm::sys;
+using namespace llvm::wasm;
+using llvm::sys::Process;
+
+using namespace lld;
+using namespace lld::wasm;
+
+namespace {
+
+// Parses command line options.
+class WasmOptTable : public llvm::opt::OptTable {
+public:
+ WasmOptTable();
+ llvm::opt::InputArgList parse(ArrayRef<const char *> Argv);
+};
+
+// Create enum with OPT_xxx values for each option in Options.td
+enum {
+ OPT_INVALID = 0,
+#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID,
+#include "Options.inc"
+#undef OPTION
+};
+
+class LinkerDriver {
+public:
+ void link(ArrayRef<const char *> ArgsArr);
+
+private:
+ void createFiles(llvm::opt::InputArgList &Args);
+ void addFile(StringRef Path);
+ void addLibrary(StringRef Name);
+ std::vector<InputFile *> Files;
+};
+
+} // anonymous namespace
+
+std::vector<SpecificAllocBase *> lld::wasm::SpecificAllocBase::Instances;
+Configuration *lld::wasm::Config;
+BumpPtrAllocator lld::wasm::BAlloc;
+
+bool lld::wasm::link(ArrayRef<const char *> Args, bool CanExitEarly,
+ raw_ostream &Error) {
+ errorHandler().LogName = Args[0];
+ errorHandler().ErrorOS = &Error;
+ errorHandler().ColorDiagnostics = Error.has_colors();
+ errorHandler().ErrorLimitExceededMsg =
+ "too many errors emitted, stopping now (use "
+ "-error-limit=0 to see all errors)";
+
+ Config = make<Configuration>();
+ Symtab = make<SymbolTable>();
+
+ LinkerDriver().link(Args);
+
+ // Exit immediately if we don't need to return to the caller.
+ // This saves time because the overhead of calling destructors
+ // for all globally-allocated objects is not negligible.
+ if (CanExitEarly)
+ exitLld(errorCount() ? 1 : 0);
+
+ freeArena();
+ return !errorCount();
+}
+
+// Create OptTable
+
+// Create prefix string literals used in Options.td
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Options.inc"
+#undef PREFIX
+
+// Create table mapping all options defined in Options.td
+static const opt::OptTable::Info OptInfo[] = {
+#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \
+ {X1, X2, X10, X11, OPT_##ID, opt::Option::KIND##Class, \
+ X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12},
+#include "Options.inc"
+#undef OPTION
+};
+
+static std::vector<StringRef> getArgs(opt::InputArgList &Args, int Id) {
+ std::vector<StringRef> V;
+ for (auto *Arg : Args.filtered(Id))
+ V.push_back(Arg->getValue());
+ return V;
+}
+
+static int getInteger(opt::InputArgList &Args, unsigned Key, int Default) {
+ int V = Default;
+ if (auto *Arg = Args.getLastArg(Key)) {
+ StringRef S = Arg->getValue();
+ if (S.getAsInteger(10, V))
+ error(Arg->getSpelling() + ": number expected, but got " + S);
+ }
+ return V;
+}
+
+static uint64_t getZOptionValue(opt::InputArgList &Args, StringRef Key,
+ uint64_t Default) {
+ for (auto *Arg : Args.filtered(OPT_z)) {
+ StringRef Value = Arg->getValue();
+ size_t Pos = Value.find("=");
+ if (Pos != StringRef::npos && Key == Value.substr(0, Pos)) {
+ Value = Value.substr(Pos + 1);
+ uint64_t Res;
+ if (Value.getAsInteger(0, Res))
+ error("invalid " + Key + ": " + Value);
+ return Res;
+ }
+ }
+ return Default;
+}
+
+static std::vector<StringRef> getLines(MemoryBufferRef MB) {
+ SmallVector<StringRef, 0> Arr;
+ MB.getBuffer().split(Arr, '\n');
+
+ std::vector<StringRef> Ret;
+ for (StringRef S : Arr) {
+ S = S.trim();
+ if (!S.empty() && S[0] != '#')
+ Ret.push_back(S);
+ }
+ return Ret;
+}
+
+// Set color diagnostics according to -color-diagnostics={auto,always,never}
+// or -no-color-diagnostics flags.
+static void handleColorDiagnostics(opt::InputArgList &Args) {
+ auto *Arg = Args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
+ OPT_no_color_diagnostics);
+ if (!Arg)
+ return;
+
+ if (Arg->getOption().getID() == OPT_color_diagnostics)
+ errorHandler().ColorDiagnostics = true;
+ else if (Arg->getOption().getID() == OPT_no_color_diagnostics)
+ errorHandler().ColorDiagnostics = false;
+ else {
+ StringRef S = Arg->getValue();
+ if (S == "always")
+ errorHandler().ColorDiagnostics = true;
+ if (S == "never")
+ errorHandler().ColorDiagnostics = false;
+ if (S != "auto")
+ error("unknown option: -color-diagnostics=" + S);
+ }
+}
+
+// Find a file by concatenating given paths.
+static Optional<std::string> findFile(StringRef Path1, const Twine &Path2) {
+ SmallString<128> S;
+ path::append(S, Path1, Path2);
+ if (fs::exists(S))
+ return S.str().str();
+ return None;
+}
+
+// Inject a new wasm global into the output binary with the given value.
+// Wasm global are used in relocatable object files to model symbol imports
+// and exports. In the final exectuable the only use of wasm globals is the
+// for the exlicit stack pointer (__stack_pointer).
+static void addSyntheticGlobal(StringRef Name, int32_t Value) {
+ log("injecting global: " + Name);
+ Symbol *S = Symtab->addDefinedGlobal(Name);
+ S->setOutputIndex(Config->SyntheticGlobals.size());
+
+ WasmGlobal Global;
+ Global.Mutable = true;
+ Global.Type = WASM_TYPE_I32;
+ Global.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
+ Global.InitExpr.Value.Int32 = Value;
+ Config->SyntheticGlobals.emplace_back(S, Global);
+}
+
+// Inject a new undefined symbol into the link. This will cause the link to
+// fail unless this symbol can be found.
+static void addSyntheticUndefinedFunction(StringRef Name) {
+ log("injecting undefined func: " + Name);
+ Symtab->addUndefinedFunction(Name);
+}
+
+static void printHelp(const char *Argv0) {
+ WasmOptTable Table;
+ Table.PrintHelp(outs(), Argv0, "LLVM Linker", false);
+}
+
+WasmOptTable::WasmOptTable() : OptTable(OptInfo) {}
+
+opt::InputArgList WasmOptTable::parse(ArrayRef<const char *> Argv) {
+ SmallVector<const char *, 256> Vec(Argv.data(), Argv.data() + Argv.size());
+
+ unsigned MissingIndex;
+ unsigned MissingCount;
+ opt::InputArgList Args = this->ParseArgs(Vec, MissingIndex, MissingCount);
+
+ handleColorDiagnostics(Args);
+ for (auto *Arg : Args.filtered(OPT_UNKNOWN))
+ error("unknown argument: " + Arg->getSpelling());
+ return Args;
+}
+
+void LinkerDriver::addFile(StringRef Path) {
+ Optional<MemoryBufferRef> Buffer = readFile(Path);
+ if (!Buffer.hasValue())
+ return;
+ MemoryBufferRef MBRef = *Buffer;
+
+ if (identify_magic(MBRef.getBuffer()) == file_magic::archive)
+ Files.push_back(make<ArchiveFile>(MBRef));
+ else
+ Files.push_back(make<ObjFile>(MBRef));
+}
+
+// Add a given library by searching it from input search paths.
+void LinkerDriver::addLibrary(StringRef Name) {
+ for (StringRef Dir : Config->SearchPaths) {
+ if (Optional<std::string> S = findFile(Dir, "lib" + Name + ".a")) {
+ addFile(*S);
+ return;
+ }
+ }
+
+ error("unable to find library -l" + Name);
+}
+
+void LinkerDriver::createFiles(opt::InputArgList &Args) {
+ for (auto *Arg : Args) {
+ switch (Arg->getOption().getUnaliasedOption().getID()) {
+ case OPT_l:
+ addLibrary(Arg->getValue());
+ break;
+ case OPT_INPUT:
+ addFile(Arg->getValue());
+ break;
+ }
+ }
+
+ if (Files.empty())
+ error("no input files");
+}
+
+void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
+ WasmOptTable Parser;
+ opt::InputArgList Args = Parser.parse(ArgsArr.slice(1));
+
+ // Handle --help
+ if (Args.hasArg(OPT_help)) {
+ printHelp(ArgsArr[0]);
+ return;
+ }
+
+ // Parse and evaluate -mllvm options.
+ std::vector<const char *> V;
+ V.push_back("lld-link (LLVM option parsing)");
+ for (auto *Arg : Args.filtered(OPT_mllvm))
+ V.push_back(Arg->getValue());
+ cl::ParseCommandLineOptions(V.size(), V.data());
+
+ errorHandler().ErrorLimit = getInteger(Args, OPT_error_limit, 20);
+
+ if (Args.hasArg(OPT_version) || Args.hasArg(OPT_v)) {
+ outs() << getLLDVersion() << "\n";
+ return;
+ }
+
+ Config->AllowUndefined = Args.hasArg(OPT_allow_undefined);
+ Config->EmitRelocs = Args.hasArg(OPT_emit_relocs);
+ Config->Entry = Args.getLastArgValue(OPT_entry);
+ Config->ImportMemory = Args.hasArg(OPT_import_memory);
+ Config->OutputFile = Args.getLastArgValue(OPT_o);
+ Config->Relocatable = Args.hasArg(OPT_relocatable);
+ Config->SearchPaths = getArgs(Args, OPT_L);
+ Config->StripAll = Args.hasArg(OPT_strip_all);
+ Config->StripDebug = Args.hasArg(OPT_strip_debug);
+ Config->Sysroot = Args.getLastArgValue(OPT_sysroot);
+ errorHandler().Verbose = Args.hasArg(OPT_verbose);
+ ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true);
+
+ Config->InitialMemory = getInteger(Args, OPT_initial_memory, 0);
+ Config->GlobalBase = getInteger(Args, OPT_global_base, 1024);
+ Config->MaxMemory = getInteger(Args, OPT_max_memory, 0);
+ Config->ZStackSize = getZOptionValue(Args, "stack-size", WasmPageSize);
+
+ if (auto *Arg = Args.getLastArg(OPT_allow_undefined_file))
+ if (Optional<MemoryBufferRef> Buf = readFile(Arg->getValue()))
+ for (StringRef Sym : getLines(*Buf))
+ Config->AllowUndefinedSymbols.insert(Sym);
+
+ if (Config->OutputFile.empty())
+ error("no output file specified");
+
+ if (!Args.hasArg(OPT_INPUT))
+ error("no input files");
+
+ if (Config->Relocatable && !Config->Entry.empty())
+ error("entry point specified for relocatable output file");
+
+ if (!Config->Relocatable) {
+ if (Config->Entry.empty())
+ Config->Entry = "_start";
+ addSyntheticUndefinedFunction(Config->Entry);
+
+ addSyntheticGlobal("__stack_pointer", 0);
+ }
+
+ createFiles(Args);
+ if (errorCount())
+ return;
+
+ // Add all files to the symbol table. This will add almost all
+ // symbols that we need to the symbol table.
+ for (InputFile *F : Files)
+ Symtab->addFile(F);
+
+ // Make sure we have resolved all symbols.
+ if (!Config->Relocatable && !Config->AllowUndefined) {
+ Symtab->reportRemainingUndefines();
+ if (errorCount())
+ return;
+ }
+
+ if (!Config->Entry.empty()) {
+ Symbol *Sym = Symtab->find(Config->Entry);
+ if (!Sym->isFunction())
+ fatal("entry point is not a function: " + Sym->getName());
+ }
+
+ // Write the result to the file.
+ writeResult();
+}
--- /dev/null
+//===- InputFiles.cpp -----------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InputFiles.h"
+
+#include "Config.h"
+#include "InputSegment.h"
+#include "Memory.h"
+#include "Strings.h"
+#include "SymbolTable.h"
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/Wasm.h"
+#include "llvm/Support/raw_ostream.h"
+
+#define DEBUG_TYPE "lld"
+
+using namespace lld;
+using namespace lld::wasm;
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::wasm;
+
+Optional<MemoryBufferRef> lld::wasm::readFile(StringRef Path) {
+ log("Loading: " + Path);
+
+ auto MBOrErr = MemoryBuffer::getFile(Path);
+ if (auto EC = MBOrErr.getError()) {
+ error("cannot open " + Path + ": " + EC.message());
+ return None;
+ }
+ std::unique_ptr<MemoryBuffer> &MB = *MBOrErr;
+ MemoryBufferRef MBRef = MB->getMemBufferRef();
+ make<std::unique_ptr<MemoryBuffer>>(std::move(MB)); // take MB ownership
+
+ return MBRef;
+}
+
+void ObjFile::dumpInfo() const {
+ log("reloc info for: " + getName() + "\n" +
+ " FunctionIndexOffset : " + Twine(FunctionIndexOffset) + "\n" +
+ " NumFunctionImports : " + Twine(NumFunctionImports()) + "\n" +
+ " TableIndexOffset : " + Twine(TableIndexOffset) + "\n" +
+ " GlobalIndexOffset : " + Twine(GlobalIndexOffset) + "\n" +
+ " NumGlobalImports : " + Twine(NumGlobalImports()) + "\n");
+}
+
+bool ObjFile::isImportedFunction(uint32_t Index) const {
+ return Index < NumFunctionImports();
+}
+
+const Symbol *ObjFile::getFunctionSymbol(uint32_t Index) const {
+ return FunctionSymbols[Index];
+}
+
+const Symbol *ObjFile::getGlobalSymbol(uint32_t Index) const {
+ return GlobalSymbols[Index];
+}
+
+uint32_t ObjFile::getRelocatedAddress(uint32_t Index) const {
+ return getGlobalSymbol(Index)->getVirtualAddress();
+}
+
+uint32_t ObjFile::relocateFunctionIndex(uint32_t Original) const {
+ DEBUG(dbgs() << "relocateFunctionIndex: " << Original);
+ const Symbol *Sym = getFunctionSymbol(Original);
+ uint32_t Index;
+ if (Sym)
+ Index = Sym->getOutputIndex();
+ else
+ Index = Original + FunctionIndexOffset;
+
+ DEBUG(dbgs() << " -> " << Index << "\n");
+ return Index;
+}
+
+uint32_t ObjFile::relocateTypeIndex(uint32_t Original) const {
+ return TypeMap[Original];
+}
+
+uint32_t ObjFile::relocateTableIndex(uint32_t Original) const {
+ return Original + TableIndexOffset;
+}
+
+uint32_t ObjFile::relocateGlobalIndex(uint32_t Original) const {
+ DEBUG(dbgs() << "relocateGlobalIndex: " << Original);
+ uint32_t Index;
+ const Symbol *Sym = getGlobalSymbol(Original);
+ if (Sym)
+ Index = Sym->getOutputIndex();
+ else
+ Index = Original + GlobalIndexOffset;
+
+ DEBUG(dbgs() << " -> " << Index << "\n");
+ return Index;
+}
+
+void ObjFile::parse() {
+ // Parse a memory buffer as a wasm file.
+ DEBUG(dbgs() << "Parsing object: " << toString(this) << "\n");
+ std::unique_ptr<Binary> Bin = check(createBinary(MB), toString(this));
+
+ auto *Obj = dyn_cast<WasmObjectFile>(Bin.get());
+ if (!Obj)
+ fatal(toString(this) + ": not a wasm file");
+ if (!Obj->isRelocatableObject())
+ fatal(toString(this) + ": not a relocatable wasm file");
+
+ Bin.release();
+ WasmObj.reset(Obj);
+
+ // Find the code and data sections. Wasm objects can have at most one code
+ // and one data section.
+ for (const SectionRef &Sec : WasmObj->sections()) {
+ const WasmSection &Section = WasmObj->getWasmSection(Sec);
+ if (Section.Type == WASM_SEC_CODE)
+ CodeSection = &Section;
+ else if (Section.Type == WASM_SEC_DATA)
+ DataSection = &Section;
+ }
+
+ initializeSymbols();
+}
+
+// Return the InputSegment in which a given symbol is defined.
+InputSegment *ObjFile::getSegment(const WasmSymbol &WasmSym) {
+ uint32_t Address = WasmObj->getWasmSymbolValue(WasmSym);
+ for (InputSegment *Segment : Segments) {
+ if (Address >= Segment->startVA() && Address < Segment->endVA()) {
+ DEBUG(dbgs() << "Found symbol in segment: " << WasmSym.Name << " -> "
+ << Segment->getName() << "\n");
+
+ return Segment;
+ }
+ }
+ error("Symbol not found in any segment: " + WasmSym.Name);
+ return nullptr;
+}
+
+void ObjFile::initializeSymbols() {
+ Symbols.reserve(WasmObj->getNumberOfSymbols());
+
+ for (const WasmImport &Import : WasmObj->imports()) {
+ switch (Import.Kind) {
+ case WASM_EXTERNAL_FUNCTION:
+ ++FunctionImports;
+ break;
+ case WASM_EXTERNAL_GLOBAL:
+ ++GlobalImports;
+ break;
+ }
+ }
+
+ FunctionSymbols.resize(FunctionImports + WasmObj->functions().size());
+ GlobalSymbols.resize(GlobalImports + WasmObj->globals().size());
+
+ for (const WasmSegment &Seg : WasmObj->dataSegments())
+ Segments.emplace_back(make<InputSegment>(&Seg, this));
+
+ Symbol *S;
+ for (const SymbolRef &Sym : WasmObj->symbols()) {
+ const WasmSymbol &WasmSym = WasmObj->getWasmSymbol(Sym.getRawDataRefImpl());
+ switch (WasmSym.Type) {
+ case WasmSymbol::SymbolType::FUNCTION_IMPORT:
+ case WasmSymbol::SymbolType::GLOBAL_IMPORT:
+ S = createUndefined(WasmSym);
+ break;
+ case WasmSymbol::SymbolType::GLOBAL_EXPORT:
+ S = createDefined(WasmSym, getSegment(WasmSym));
+ break;
+ case WasmSymbol::SymbolType::FUNCTION_EXPORT:
+ S = createDefined(WasmSym);
+ break;
+ case WasmSymbol::SymbolType::DEBUG_FUNCTION_NAME:
+ // These are for debugging only, no need to create linker symbols for them
+ continue;
+ }
+
+ Symbols.push_back(S);
+ if (WasmSym.isFunction()) {
+ DEBUG(dbgs() << "Function: " << WasmSym.ElementIndex << " -> "
+ << toString(*S) << "\n");
+ FunctionSymbols[WasmSym.ElementIndex] = S;
+ } else {
+ DEBUG(dbgs() << "Global: " << WasmSym.ElementIndex << " -> "
+ << toString(*S) << "\n");
+ GlobalSymbols[WasmSym.ElementIndex] = S;
+ }
+ }
+
+ DEBUG(dbgs() << "Functions: " << FunctionSymbols.size() << "\n");
+ DEBUG(dbgs() << "Globals : " << GlobalSymbols.size() << "\n");
+}
+
+Symbol *ObjFile::createUndefined(const WasmSymbol &Sym) {
+ return Symtab->addUndefined(this, &Sym);
+}
+
+Symbol *ObjFile::createDefined(const WasmSymbol &Sym,
+ const InputSegment *Segment) {
+ Symbol *S;
+ if (Sym.isLocal()) {
+ S = make<Symbol>(Sym.Name, true);
+ Symbol::Kind Kind;
+ if (Sym.Type == WasmSymbol::SymbolType::FUNCTION_EXPORT)
+ Kind = Symbol::Kind::DefinedFunctionKind;
+ else if (Sym.Type == WasmSymbol::SymbolType::GLOBAL_EXPORT)
+ Kind = Symbol::Kind::DefinedGlobalKind;
+ else
+ llvm_unreachable("invalid local symbol type");
+ S->update(Kind, this, &Sym, Segment);
+ return S;
+ }
+ return Symtab->addDefined(this, &Sym, Segment);
+}
+
+void ArchiveFile::parse() {
+ // Parse a MemoryBufferRef as an archive file.
+ DEBUG(dbgs() << "Parsing library: " << toString(this) << "\n");
+ File = check(Archive::create(MB), toString(this));
+
+ // Read the symbol table to construct Lazy symbols.
+ int Count = 0;
+ for (const Archive::Symbol &Sym : File->symbols()) {
+ Symtab->addLazy(this, &Sym);
+ ++Count;
+ }
+ DEBUG(dbgs() << "Read " << Count << " symbols\n");
+}
+
+void ArchiveFile::addMember(const Archive::Symbol *Sym) {
+ const Archive::Child &C =
+ check(Sym->getMember(),
+ "could not get the member for symbol " + Sym->getName());
+
+ // Don't try to load the same member twice (this can happen when members
+ // mutually reference each other).
+ if (!Seen.insert(C.getChildOffset()).second)
+ return;
+
+ DEBUG(dbgs() << "loading lazy: " << displayName(Sym->getName()) << "\n");
+ DEBUG(dbgs() << "from archive: " << toString(this) << "\n");
+
+ MemoryBufferRef MB =
+ check(C.getMemoryBufferRef(),
+ "could not get the buffer for the member defining symbol " +
+ Sym->getName());
+
+ if (identify_magic(MB.getBuffer()) != file_magic::wasm_object) {
+ error("unknown file type: " + MB.getBufferIdentifier());
+ return;
+ }
+
+ InputFile *Obj = make<ObjFile>(MB);
+ Obj->ParentName = ParentName;
+ Symtab->addFile(Obj);
+}
+
+// Returns a string in the format of "foo.o" or "foo.a(bar.o)".
+std::string lld::toString(wasm::InputFile *File) {
+ if (!File)
+ return "<internal>";
+
+ if (File->ParentName.empty())
+ return File->getName();
+
+ return (File->ParentName + "(" + File->getName() + ")").str();
+}
--- /dev/null
+//===- InputFiles.h ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_INPUT_FILES_H
+#define LLD_WASM_INPUT_FILES_H
+
+#include "lld/Common/LLVM.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/Wasm.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+#include "WriterUtils.h"
+
+#include <vector>
+
+using llvm::object::WasmObjectFile;
+using llvm::object::WasmSection;
+using llvm::object::WasmSymbol;
+using llvm::object::Archive;
+using llvm::wasm::WasmImport;
+
+namespace lld {
+namespace wasm {
+
+class Symbol;
+class InputSegment;
+
+class InputFile {
+public:
+ enum Kind {
+ ObjectKind,
+ ArchiveKind,
+ };
+
+ virtual ~InputFile() {}
+
+ // Returns the filename.
+ StringRef getName() const { return MB.getBufferIdentifier(); }
+
+ // Reads a file (the constructor doesn't do that).
+ virtual void parse() = 0;
+
+ Kind kind() const { return FileKind; }
+
+ // An archive file name if this file is created from an archive.
+ StringRef ParentName;
+
+protected:
+ InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
+ MemoryBufferRef MB;
+
+private:
+ const Kind FileKind;
+};
+
+// .a file (ar archive)
+class ArchiveFile : public InputFile {
+public:
+ explicit ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
+ static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
+
+ void addMember(const Archive::Symbol *Sym);
+
+ void parse() override;
+
+private:
+ std::unique_ptr<Archive> File;
+ llvm::DenseSet<uint64_t> Seen;
+};
+
+// .o file (wasm object file)
+class ObjFile : public InputFile {
+public:
+ explicit ObjFile(MemoryBufferRef M) : InputFile(ObjectKind, M) {}
+ static bool classof(const InputFile *F) { return F->kind() == ObjectKind; }
+
+ void parse() override;
+
+ // Returns the underlying wasm file.
+ const WasmObjectFile *getWasmObj() const { return WasmObj.get(); }
+
+ void dumpInfo() const;
+
+ uint32_t relocateTypeIndex(uint32_t Original) const;
+ uint32_t relocateFunctionIndex(uint32_t Original) const;
+ uint32_t relocateGlobalIndex(uint32_t Original) const;
+ uint32_t relocateTableIndex(uint32_t Original) const;
+ uint32_t getRelocatedAddress(uint32_t Index) const;
+
+ // Returns true if the given function index is an imported function,
+ // as opposed to the locally defined function.
+ bool isImportedFunction(uint32_t Index) const;
+
+ size_t NumFunctionImports() const { return FunctionImports; }
+ size_t NumGlobalImports() const { return GlobalImports; }
+
+ int32_t FunctionIndexOffset = 0;
+ int32_t GlobalIndexOffset = 0;
+ int32_t TableIndexOffset = 0;
+ const WasmSection *CodeSection = nullptr;
+ std::vector<OutputRelocation> CodeRelocations;
+ int32_t CodeOffset = 0;
+ const WasmSection *DataSection = nullptr;
+
+ std::vector<uint32_t> TypeMap;
+ std::vector<InputSegment *> Segments;
+
+ const std::vector<Symbol *> &getSymbols() { return Symbols; }
+
+private:
+ Symbol *createDefined(const WasmSymbol &Sym,
+ const InputSegment *Segment = nullptr);
+ Symbol *createUndefined(const WasmSymbol &Sym);
+ void initializeSymbols();
+ InputSegment *getSegment(const WasmSymbol &WasmSym);
+ const Symbol *getFunctionSymbol(uint32_t Index) const;
+ const Symbol *getGlobalSymbol(uint32_t Index) const;
+
+ // List of all symbols referenced or defined by this file.
+ std::vector<Symbol *> Symbols;
+
+ // List of all function symbols indexed by the function index space
+ std::vector<const Symbol *> FunctionSymbols;
+
+ // List of all global symbols indexed by the global index space
+ std::vector<const Symbol *> GlobalSymbols;
+
+ uint32_t GlobalImports = 0;
+ uint32_t FunctionImports = 0;
+ std::unique_ptr<WasmObjectFile> WasmObj;
+};
+
+// Opens a given file.
+llvm::Optional<MemoryBufferRef> readFile(StringRef Path);
+
+} // namespace wasm
+
+std::string toString(wasm::InputFile *File);
+
+} // namespace lld
+
+#endif
--- /dev/null
+//===- InputSegment.cpp ---------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InputSegment.h"
+#include "OutputSegment.h"
+#include "lld/Common/LLVM.h"
+
+#define DEBUG_TYPE "lld"
+
+using namespace llvm;
+using namespace lld::wasm;
+
+uint32_t InputSegment::translateVA(uint32_t Address) const {
+ assert(Address >= startVA() && Address < endVA());
+ int32_t Delta = OutputSeg->StartVA + OutputSegmentOffset - startVA();
+ DEBUG(dbgs() << "translateVA: " << getName() << " Delta=" << Delta
+ << " Address=" << Address << "\n");
+ return Address + Delta;
+}
--- /dev/null
+//===- InputSegment.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Represents a WebAssembly data segment which can be included as part of
+// an output data segments. Note that in WebAssembly, unlike ELF and other
+// formats, used the term "data segment" to refer to the continous regions of
+// memory that make on the data section. See:
+// https://webassembly.github.io/spec/syntax/modules.html#syntax-data
+//
+// For example, by default, clang will produce a separate data section for
+// each global variable.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_INPUT_SEGMENT_H
+#define LLD_WASM_INPUT_SEGMENT_H
+
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/Object/Wasm.h"
+
+using llvm::object::WasmSegment;
+using llvm::wasm::WasmRelocation;
+
+namespace lld {
+namespace wasm {
+
+class ObjFile;
+class OutputSegment;
+
+class InputSegment {
+public:
+ InputSegment(const WasmSegment *Seg, const ObjFile *F)
+ : Segment(Seg), File(F) {}
+
+ // Translate an offset in the input segment to an offset in the output
+ // segment.
+ uint32_t translateVA(uint32_t Address) const;
+
+ const OutputSegment *getOutputSegment() const { return OutputSeg; }
+
+ uint32_t getOutputSegmentOffset() const { return OutputSegmentOffset; }
+
+ uint32_t getInputSectionOffset() const { return Segment->SectionOffset; }
+
+ void setOutputSegment(const OutputSegment *Segment, uint32_t Offset) {
+ OutputSeg = Segment;
+ OutputSegmentOffset = Offset;
+ }
+
+ uint32_t getSize() const { return Segment->Data.Content.size(); }
+ uint32_t getAlignment() const { return Segment->Data.Alignment; }
+ uint32_t startVA() const { return Segment->Data.Offset.Value.Int32; }
+ uint32_t endVA() const { return startVA() + getSize(); }
+ StringRef getName() const { return Segment->Data.Name; }
+
+ const WasmSegment *Segment;
+ const ObjFile *File;
+ std::vector<WasmRelocation> Relocations;
+
+protected:
+ const OutputSegment *OutputSeg = nullptr;
+ uint32_t OutputSegmentOffset = 0;
+};
+
+} // namespace wasm
+} // namespace lld
+
+#endif // LLD_WASM_INPUT_SEGMENT_H
--- /dev/null
+//===- Memory.h -------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// See WASM/Memory.h
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_MEMORY_H
+#define LLD_WASM_MEMORY_H
+
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/StringSaver.h"
+#include <vector>
+
+namespace lld {
+namespace wasm {
+
+extern llvm::BumpPtrAllocator BAlloc;
+extern llvm::StringSaver Saver;
+
+struct SpecificAllocBase {
+ SpecificAllocBase() { Instances.push_back(this); }
+ virtual ~SpecificAllocBase() = default;
+ virtual void reset() = 0;
+ static std::vector<SpecificAllocBase *> Instances;
+};
+
+template <class T> struct SpecificAlloc : public SpecificAllocBase {
+ void reset() override { Alloc.DestroyAll(); }
+ llvm::SpecificBumpPtrAllocator<T> Alloc;
+};
+
+template <typename T, typename... U> T *make(U &&... Args) {
+ static SpecificAlloc<T> Alloc;
+ return new (Alloc.Alloc.Allocate()) T(std::forward<U>(Args)...);
+}
+
+inline void freeArena() {
+ for (SpecificAllocBase *Alloc : SpecificAllocBase::Instances)
+ Alloc->reset();
+ BAlloc.Reset();
+}
+} // namespace wasm
+} // namespace lld
+
+#endif
--- /dev/null
+include "llvm/Option/OptParser.td"
+
+// For options whose names are multiple letters, either one dash or
+// two can precede the option name except those that start with 'o'.
+class F<string name>: Flag<["--", "-"], name>;
+class J<string name>: Joined<["--", "-"], name>;
+class S<string name>: Separate<["--", "-"], name>;
+
+def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"<dir>">,
+ HelpText<"Add a directory to the library search path">;
+
+def color_diagnostics: F<"color-diagnostics">,
+ HelpText<"Use colors in diagnostics">;
+
+def color_diagnostics_eq: J<"color-diagnostics=">,
+ HelpText<"Use colors in diagnostics">;
+
+// The follow flags are shared with the ELF linker
+def help: F<"help">, HelpText<"Print option help">;
+
+def l: JoinedOrSeparate<["-"], "l">, MetaVarName<"<libName>">,
+ HelpText<"Root name of library to use">;
+
+def mllvm: S<"mllvm">, HelpText<"Options to pass to LLVM">;
+
+def no_threads: F<"no-threads">,
+ HelpText<"Do not run the linker multi-threaded">;
+
+def no_color_diagnostics: F<"no-color-diagnostics">,
+ HelpText<"Do not use colors in diagnostics">;
+
+def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"<path>">,
+ HelpText<"Path to file to write output">;
+
+def threads: F<"threads">, HelpText<"Run the linker multi-threaded">;
+
+def v: Flag<["-"], "v">, HelpText<"Display the version number">;
+
+def version: F<"version">, HelpText<"Display the version number and exit">;
+
+def verbose: F<"verbose">, HelpText<"Verbose mode">;
+
+def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">;
+
+def emit_relocs: F<"emit-relocs">, HelpText<"Generate relocations in output">;
+
+def strip_all: F<"strip-all">, HelpText<"Strip all symbols">;
+
+def strip_debug: F<"strip-debug">, HelpText<"Strip debugging information">;
+
+def sysroot: J<"sysroot=">, HelpText<"Set the system root">;
+
+def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
+ HelpText<"Linker option extensions">;
+
+def entry: S<"entry">, MetaVarName<"<entry>">,
+ HelpText<"Name of entry point symbol">;
+
+def error_limit: J<"error-limit=">,
+ HelpText<"Maximum number of errors to emit before stopping (0 = no limit)">;
+
+// The follow flags are unique to wasm
+
+def global_base: J<"global-base=">,
+ HelpText<"Where to start to place global data">;
+
+def initial_memory: J<"initial-memory=">,
+ HelpText<"Initial size of the linear memory">;
+
+def max_memory: J<"max-memory=">,
+ HelpText<"Maximum size of the linear memory">;
+
+def import_memory: F<"import-memory">,
+ HelpText<"Import memory from the environment">;
+
+def allow_undefined: F<"allow-undefined">,
+ HelpText<"Allow undefined symbols in linked binary">;
+
+def allow_undefined_file: J<"allow-undefined-file=">,
+ HelpText<"Allow symbols listed in <file> to be undefined in linked binary">;
+
+def allow_undefined_file_s: Separate<["-"], "allow-undefined-file">, Alias<allow_undefined_file>;
+
+// Aliases
+def alias_initial_memory_i: Flag<["-"], "i">, Alias<initial_memory>;
+def alias_max_memory_m: Flag<["-"], "m">, Alias<max_memory>;
+def alias_relocatable_r: Flag<["-"], "r">, Alias<relocatable>;
+def alias_entry_e: JoinedOrSeparate<["-"], "e">, Alias<entry>;
+def alias_entry_entry: J<"entry=">, Alias<entry>;
--- /dev/null
+//===- OutputSections.cpp -------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "OutputSections.h"
+
+#include "Config.h"
+#include "InputFiles.h"
+#include "Memory.h"
+#include "OutputSegment.h"
+#include "SymbolTable.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Threads.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/LEB128.h"
+
+#define DEBUG_TYPE "lld"
+
+using namespace llvm;
+using namespace llvm::wasm;
+using namespace lld;
+using namespace lld::wasm;
+
+enum class RelocEncoding {
+ Uleb128,
+ Sleb128,
+ I32,
+};
+
+static StringRef sectionTypeToString(uint32_t SectionType) {
+ switch (SectionType) {
+ case WASM_SEC_CUSTOM:
+ return "CUSTOM";
+ case WASM_SEC_TYPE:
+ return "TYPE";
+ case WASM_SEC_IMPORT:
+ return "IMPORT";
+ case WASM_SEC_FUNCTION:
+ return "FUNCTION";
+ case WASM_SEC_TABLE:
+ return "TABLE";
+ case WASM_SEC_MEMORY:
+ return "MEMORY";
+ case WASM_SEC_GLOBAL:
+ return "GLOBAL";
+ case WASM_SEC_EXPORT:
+ return "EXPORT";
+ case WASM_SEC_START:
+ return "START";
+ case WASM_SEC_ELEM:
+ return "ELEM";
+ case WASM_SEC_CODE:
+ return "CODE";
+ case WASM_SEC_DATA:
+ return "DATA";
+ default:
+ fatal("invalid section type");
+ }
+}
+
+std::string lld::toString(OutputSection *Section) {
+ std::string rtn = sectionTypeToString(Section->Type);
+ if (!Section->Name.empty())
+ rtn += "(" + Section->Name + ")";
+ return rtn;
+}
+
+static void applyRelocation(uint8_t *Buf, const OutputRelocation &Reloc) {
+ DEBUG(dbgs() << "write reloc: type=" << Reloc.Reloc.Type
+ << " index=" << Reloc.Reloc.Index << " new=" << Reloc.NewIndex
+ << " value=" << Reloc.Value << " offset=" << Reloc.Reloc.Offset
+ << "\n");
+ Buf += Reloc.Reloc.Offset;
+ int64_t ExistingValue;
+ switch (Reloc.Reloc.Type) {
+ case R_WEBASSEMBLY_TYPE_INDEX_LEB:
+ case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
+ ExistingValue = decodeULEB128(Buf);
+ if (ExistingValue != Reloc.Reloc.Index) {
+ DEBUG(dbgs() << "existing value: " << decodeULEB128(Buf) << "\n");
+ assert(decodeULEB128(Buf) == Reloc.Reloc.Index);
+ }
+ LLVM_FALLTHROUGH;
+ case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
+ case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
+ encodeULEB128(Reloc.Value, Buf, 5);
+ break;
+ case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
+ ExistingValue = decodeSLEB128(Buf);
+ if (ExistingValue != Reloc.Reloc.Index) {
+ DEBUG(dbgs() << "existing value: " << decodeSLEB128(Buf) << "\n");
+ assert(decodeSLEB128(Buf) == Reloc.Reloc.Index);
+ }
+ LLVM_FALLTHROUGH;
+ case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
+ encodeSLEB128(static_cast<int32_t>(Reloc.Value), Buf, 5);
+ break;
+ case R_WEBASSEMBLY_TABLE_INDEX_I32:
+ case R_WEBASSEMBLY_MEMORY_ADDR_I32:
+ support::endian::write32<support::little>(Buf, Reloc.Value);
+ break;
+ default:
+ llvm_unreachable("unknown relocation type");
+ }
+}
+
+static void applyRelocations(uint8_t *Buf,
+ const std::vector<OutputRelocation> &Relocs) {
+ log("applyRelocations: count=" + Twine(Relocs.size()));
+ for (const OutputRelocation &Reloc : Relocs) {
+ applyRelocation(Buf, Reloc);
+ }
+}
+
+// Relocations contain an index into the function, global or table index
+// space of the input file. This function takes a relocation and returns the
+// relocated index (i.e. translates from the input index space to the output
+// index space).
+static uint32_t calcNewIndex(const ObjFile &File, const WasmRelocation &Reloc) {
+ switch (Reloc.Type) {
+ case R_WEBASSEMBLY_TYPE_INDEX_LEB:
+ return File.relocateTypeIndex(Reloc.Index);
+ case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
+ return File.relocateFunctionIndex(Reloc.Index);
+ case R_WEBASSEMBLY_TABLE_INDEX_I32:
+ case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
+ return File.relocateTableIndex(Reloc.Index);
+ case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
+ case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
+ case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
+ case R_WEBASSEMBLY_MEMORY_ADDR_I32:
+ return File.relocateGlobalIndex(Reloc.Index);
+ default:
+ llvm_unreachable("unknown relocation type");
+ }
+}
+
+// Take a vector of relocations from an input file and create output
+// relocations based on them. Calculates the updated index and offset for
+// each relocation as well as the value to write out in the final binary.
+static void calcRelocations(const ObjFile &File,
+ ArrayRef<WasmRelocation> Relocs,
+ std::vector<OutputRelocation> &OutputRelocs,
+ int32_t OutputOffset) {
+ log("calcRelocations: " + File.getName() + " offset=" + Twine(OutputOffset));
+ for (const WasmRelocation &Reloc : Relocs) {
+ int64_t NewIndex = calcNewIndex(File, Reloc);
+ OutputRelocation NewReloc;
+ NewReloc.Reloc = Reloc;
+ NewReloc.Reloc.Offset += OutputOffset;
+ NewReloc.NewIndex = NewIndex;
+ DEBUG(dbgs() << "reloc: type=" << Reloc.Type << " index=" << Reloc.Index
+ << " offset=" << Reloc.Offset << " new=" << NewIndex
+ << " newOffset=" << NewReloc.Reloc.Offset << "\n");
+
+ switch (Reloc.Type) {
+ case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
+ case R_WEBASSEMBLY_MEMORY_ADDR_I32:
+ case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
+ NewReloc.Value = File.getRelocatedAddress(Reloc.Index);
+ if (NewReloc.Value != UINT32_MAX)
+ NewReloc.Value += Reloc.Addend;
+ break;
+ default:
+ NewReloc.Value = NewIndex;
+ }
+
+ OutputRelocs.emplace_back(NewReloc);
+ }
+}
+
+void OutputSection::createHeader(size_t BodySize) {
+ raw_string_ostream OS(Header);
+ debugWrite(OS.tell(),
+ "section type [" + Twine(sectionTypeToString(Type)) + "]");
+ writeUleb128(OS, Type, nullptr);
+ writeUleb128(OS, BodySize, "section size");
+ OS.flush();
+ log("createHeader: " + toString(this) + " body=" + Twine(BodySize) +
+ " total=" + Twine(getSize()));
+}
+
+CodeSection::CodeSection(uint32_t NumFunctions, std::vector<ObjFile *> &Objs)
+ : OutputSection(WASM_SEC_CODE), InputObjects(Objs) {
+ raw_string_ostream OS(CodeSectionHeader);
+ writeUleb128(OS, NumFunctions, "function count");
+ OS.flush();
+ BodySize = CodeSectionHeader.size();
+
+ for (ObjFile *File : InputObjects) {
+ if (!File->CodeSection)
+ continue;
+
+ File->CodeOffset = BodySize;
+ ArrayRef<uint8_t> Content = File->CodeSection->Content;
+ unsigned HeaderSize = 0;
+ decodeULEB128(Content.data(), &HeaderSize);
+
+ calcRelocations(*File, File->CodeSection->Relocations,
+ File->CodeRelocations, BodySize - HeaderSize);
+
+ size_t PayloadSize = Content.size() - HeaderSize;
+ BodySize += PayloadSize;
+ }
+
+ createHeader(BodySize);
+}
+
+void CodeSection::writeTo(uint8_t *Buf) {
+ log("writing " + toString(this));
+ log(" size=" + Twine(getSize()));
+ Buf += Offset;
+
+ // Write section header
+ memcpy(Buf, Header.data(), Header.size());
+ Buf += Header.size();
+
+ uint8_t *ContentsStart = Buf;
+
+ // Write code section headers
+ memcpy(Buf, CodeSectionHeader.data(), CodeSectionHeader.size());
+ Buf += CodeSectionHeader.size();
+
+ // Write code section bodies
+ parallelForEach(InputObjects, [ContentsStart](ObjFile *File) {
+ if (!File->CodeSection)
+ return;
+
+ ArrayRef<uint8_t> Content(File->CodeSection->Content);
+
+ // Payload doesn't include the initial header (function count)
+ unsigned HeaderSize = 0;
+ decodeULEB128(Content.data(), &HeaderSize);
+
+ size_t PayloadSize = Content.size() - HeaderSize;
+ memcpy(ContentsStart + File->CodeOffset, Content.data() + HeaderSize,
+ PayloadSize);
+
+ log("applying relocations for: " + File->getName());
+ if (File->CodeRelocations.size())
+ applyRelocations(ContentsStart, File->CodeRelocations);
+ });
+}
+
+uint32_t CodeSection::numRelocations() const {
+ uint32_t Count = 0;
+ for (ObjFile *File : InputObjects)
+ Count += File->CodeRelocations.size();
+ return Count;
+}
+
+void CodeSection::writeRelocations(raw_ostream &OS) const {
+ for (ObjFile *File : InputObjects)
+ for (const OutputRelocation &Reloc : File->CodeRelocations)
+ writeReloc(OS, Reloc);
+}
+
+DataSection::DataSection(std::vector<OutputSegment *> &Segments)
+ : OutputSection(WASM_SEC_DATA), Segments(Segments) {
+ raw_string_ostream OS(DataSectionHeader);
+
+ writeUleb128(OS, Segments.size(), "data segment count");
+ OS.flush();
+ BodySize = DataSectionHeader.size();
+
+ 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");
+ writeUleb128(OS, Segment->Size, "segment size");
+ OS.flush();
+ Segment->setSectionOffset(BodySize);
+ BodySize += Segment->Header.size();
+ log("Data segment: size=" + Twine(Segment->Size));
+ for (const InputSegment *InputSeg : Segment->InputSegments) {
+ uint32_t InputOffset = InputSeg->getInputSectionOffset();
+ uint32_t OutputOffset = Segment->getSectionOffset() +
+ Segment->Header.size() +
+ InputSeg->getOutputSegmentOffset();
+ calcRelocations(*InputSeg->File, InputSeg->Relocations, Relocations,
+ OutputOffset - InputOffset);
+ }
+ BodySize += Segment->Size;
+ }
+
+ createHeader(BodySize);
+}
+
+void DataSection::writeTo(uint8_t *Buf) {
+ log("writing " + toString(this) + " size=" + Twine(getSize()) +
+ " body=" + Twine(BodySize));
+ Buf += Offset;
+
+ // Write section header
+ memcpy(Buf, Header.data(), Header.size());
+ Buf += Header.size();
+
+ uint8_t *ContentsStart = Buf;
+
+ // Write data section headers
+ memcpy(Buf, DataSectionHeader.data(), DataSectionHeader.size());
+
+ parallelForEach(Segments, [ContentsStart](const OutputSegment *Segment) {
+ // Write data segment header
+ uint8_t *SegStart = ContentsStart + Segment->getSectionOffset();
+ memcpy(SegStart, Segment->Header.data(), Segment->Header.size());
+
+ // Write segment data payload
+ for (const InputSegment *Input : Segment->InputSegments) {
+ ArrayRef<uint8_t> Content(Input->Segment->Data.Content);
+ memcpy(SegStart + Segment->Header.size() +
+ Input->getOutputSegmentOffset(),
+ Content.data(), Content.size());
+ }
+ });
+
+ applyRelocations(ContentsStart, Relocations);
+}
+
+void DataSection::writeRelocations(raw_ostream &OS) const {
+ for (const OutputRelocation &Reloc : Relocations)
+ writeReloc(OS, Reloc);
+}
--- /dev/null
+//===- OutputSections.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_OUTPUT_SECTIONS_H
+#define LLD_WASM_OUTPUT_SECTIONS_H
+
+#include "InputSegment.h"
+#include "WriterUtils.h"
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/ADT/DenseMap.h"
+
+using llvm::raw_ostream;
+using llvm::raw_string_ostream;
+
+namespace lld {
+
+namespace wasm {
+class OutputSection;
+}
+std::string toString(wasm::OutputSection *Section);
+
+namespace wasm {
+
+class OutputSegment;
+class ObjFile;
+
+class OutputSection {
+public:
+ OutputSection(uint32_t Type, std::string Name = "")
+ : Type(Type), Name(Name) {}
+
+ virtual ~OutputSection() = default;
+
+ void setOffset(size_t NewOffset) {
+ log("setOffset: " + toString(this) + " -> " + Twine(NewOffset));
+ Offset = NewOffset;
+ }
+
+ void createHeader(size_t BodySize);
+ virtual size_t getSize() const = 0;
+ virtual void writeTo(uint8_t *Buf) = 0;
+ virtual void finalizeContents() {}
+
+ std::string Header;
+ uint32_t Type;
+ std::string Name;
+
+ virtual uint32_t numRelocations() const { return 0; }
+ virtual void writeRelocations(raw_ostream &OS) const {}
+
+protected:
+ size_t Offset = 0;
+};
+
+class SyntheticSection : public OutputSection {
+public:
+ SyntheticSection(uint32_t Type, std::string Name = "")
+ : OutputSection(Type, Name), BodyOutputStream(Body) {
+ if (!Name.empty())
+ writeStr(BodyOutputStream, Name);
+ }
+
+ void writeTo(uint8_t *Buf) override {
+ assert(Offset);
+ log("writing " + toString(this));
+ memcpy(Buf + Offset, Header.data(), Header.size());
+ memcpy(Buf + Offset + Header.size(), Body.data(), Body.size());
+ }
+
+ size_t getSize() const override { return Header.size() + Body.size(); }
+
+ void finalizeContents() override {
+ BodyOutputStream.flush();
+ createHeader(Body.size());
+ }
+
+ raw_ostream &getStream() { return BodyOutputStream; }
+
+ std::string Body;
+
+protected:
+ raw_string_ostream BodyOutputStream;
+};
+
+// Some synthetic sections (e.g. "name" and "linking") have subsections.
+// Just like the synthetic sections themselves these need to be created before
+// they can be written out (since they are preceded by their length). This
+// class is used to create subsections and then write them into the stream
+// of the parent section.
+class SubSection : public SyntheticSection {
+public:
+ explicit SubSection(uint32_t Type) : SyntheticSection(Type) {}
+
+ void writeToStream(raw_ostream &OS) {
+ writeBytes(OS, Header.data(), Header.size());
+ writeBytes(OS, Body.data(), Body.size());
+ }
+};
+
+class CodeSection : public OutputSection {
+public:
+ explicit CodeSection(uint32_t NumFunctions, std::vector<ObjFile *> &Objs);
+ size_t getSize() const override { return Header.size() + BodySize; }
+ void writeTo(uint8_t *Buf) override;
+ uint32_t numRelocations() const override;
+ void writeRelocations(raw_ostream &OS) const override;
+
+protected:
+ std::vector<ObjFile *> &InputObjects;
+ std::string CodeSectionHeader;
+ size_t BodySize = 0;
+};
+
+class DataSection : public OutputSection {
+public:
+ explicit DataSection(std::vector<OutputSegment *> &Segments);
+ size_t getSize() const override { return Header.size() + BodySize; }
+ void writeTo(uint8_t *Buf) override;
+ uint32_t numRelocations() const override { return Relocations.size(); }
+ void writeRelocations(raw_ostream &OS) const override;
+
+protected:
+ std::vector<OutputRelocation> Relocations;
+ std::vector<OutputSegment *> &Segments;
+ std::string DataSectionHeader;
+ size_t BodySize = 0;
+};
+
+} // namespace wasm
+} // namespace lld
+
+#endif // LLD_WASM_OUTPUT_SECTIONS_H
--- /dev/null
+//===- OutputSegment.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_OUTPUT_SEGMENT_H
+#define LLD_WASM_OUTPUT_SEGMENT_H
+
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/Object/Wasm.h"
+
+namespace lld {
+namespace wasm {
+
+class InputSegment;
+
+class OutputSegment {
+public:
+ OutputSegment(StringRef N) : Name(N) {}
+
+ void addInputSegment(InputSegment *Segment) {
+ Alignment = std::max(Alignment, Segment->getAlignment());
+ InputSegments.push_back(Segment);
+ Size = llvm::alignTo(Size, Segment->getAlignment());
+ ;
+ Segment->setOutputSegment(this, Size);
+ Size += Segment->getSize();
+ }
+
+ uint32_t getSectionOffset() const { return SectionOffset; }
+
+ void setSectionOffset(uint32_t Offset) { SectionOffset = Offset; }
+
+ StringRef Name;
+ uint32_t Alignment = 0;
+ uint32_t StartVA = 0;
+ std::vector<const InputSegment *> InputSegments;
+
+ // Sum of the size of the all the input segments
+ uint32_t Size = 0;
+
+ // Segment header
+ std::string Header;
+
+private:
+ uint32_t SectionOffset = 0;
+};
+
+} // namespace wasm
+} // namespace lld
+
+#endif // LLD_WASM_OUTPUT_SEGMENT_H
--- /dev/null
+//===- Strings.cpp -------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Strings.h"
+#include "Config.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Demangle/Demangle.h"
+
+using namespace llvm;
+
+// Returns the demangled C++ symbol name for Name.
+Optional<std::string> lld::wasm::demangle(StringRef Name) {
+ // itaniumDemangle can be used to demangle strings other than symbol
+ // names which do not necessarily start with "_Z". Name can be
+ // either a C or C++ symbol. Don't call itaniumDemangle if the name
+ // does not look like a C++ symbol name to avoid getting unexpected
+ // result for a C symbol that happens to match a mangled type name.
+ if (!Name.startswith("_Z"))
+ return None;
+
+ char *Buf = itaniumDemangle(Name.str().c_str(), nullptr, nullptr, nullptr);
+ if (!Buf)
+ return None;
+ std::string S(Buf);
+ free(Buf);
+ return S;
+}
+
+std::string lld::wasm::displayName(StringRef Name) {
+ if (Config->Demangle)
+ if (Optional<std::string> S = demangle(Name))
+ return "`" + *S + "'";
+ return Name;
+}
--- /dev/null
+//===- Strings.h ------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_STRINGS_H
+#define LLD_WASM_STRINGS_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+namespace lld {
+namespace wasm {
+
+// Returns a demangled C++ symbol name. If Name is not a mangled
+// name, it returns Optional::None.
+llvm::Optional<std::string> demangle(llvm::StringRef Name);
+
+std::string displayName(llvm::StringRef Name);
+
+} // namespace wasm
+} // namespace lld
+
+#endif
--- /dev/null
+//===- SymbolTable.cpp ----------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolTable.h"
+
+#include "Config.h"
+#include "Memory.h"
+#include "Strings.h"
+#include "lld/Common/ErrorHandler.h"
+
+#include <unordered_set>
+
+#define DEBUG_TYPE "lld"
+
+using namespace llvm;
+using namespace lld;
+using namespace lld::wasm;
+
+SymbolTable *lld::wasm::Symtab;
+
+void SymbolTable::addFile(InputFile *File) {
+ log("Processing: " + toString(File));
+ File->parse();
+
+ if (auto *F = dyn_cast<ObjFile>(File))
+ ObjectFiles.push_back(F);
+}
+
+void SymbolTable::reportRemainingUndefines() {
+ std::unordered_set<Symbol *> Undefs;
+ for (auto &I : Symtab) {
+ Symbol *Sym = I.second;
+ if (Sym->isUndefined() && !Sym->isWeak() &&
+ Config->AllowUndefinedSymbols.count(Sym->getName()) == 0) {
+ Undefs.insert(Sym);
+ }
+ }
+
+ if (Undefs.empty())
+ return;
+
+ for (ObjFile *File : ObjectFiles)
+ for (Symbol *Sym : File->getSymbols())
+ if (Undefs.count(Sym))
+ error(toString(File) + ": undefined symbol: " + toString(*Sym));
+
+ for (Symbol *Sym : Undefs)
+ if (!Sym->getFile())
+ error("undefined symbol: " + toString(*Sym));
+}
+
+Symbol *SymbolTable::find(StringRef Name) {
+ auto It = Symtab.find(CachedHashStringRef(Name));
+ if (It == Symtab.end())
+ return nullptr;
+ return It->second;
+}
+
+std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
+ Symbol *&Sym = Symtab[CachedHashStringRef(Name)];
+ if (Sym)
+ return {Sym, false};
+ Sym = make<Symbol>(Name, false);
+ return {Sym, true};
+}
+
+void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) {
+ error("duplicate symbol: " + toString(*Existing) + "\n>>> defined in " +
+ toString(Existing->getFile()) + "\n>>> defined in " +
+ (NewFile ? toString(NewFile) : "<internal>"));
+}
+
+static void checkSymbolTypes(Symbol *Existing, InputFile *F,
+ const WasmSymbol *New) {
+ if (Existing->isLazy())
+ return;
+
+ bool NewIsFunction = New->Type == WasmSymbol::SymbolType::FUNCTION_EXPORT ||
+ New->Type == WasmSymbol::SymbolType::FUNCTION_IMPORT;
+ if (Existing->isFunction() == NewIsFunction)
+ return;
+
+ std::string Filename = "<builtin>";
+ if (Existing->getFile())
+ Filename = toString(Existing->getFile());
+ error("symbol type mismatch: " + New->Name + "\n>>> defined as " +
+ (Existing->isFunction() ? "Function" : "Global") + " in " + Filename +
+ "\n>>> defined as " + (NewIsFunction ? "Function" : "Global") + " in " +
+ F->getName());
+}
+
+Symbol *SymbolTable::addDefinedGlobal(StringRef Name) {
+ DEBUG(dbgs() << "addDefinedGlobal: " << Name << "\n");
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(Name);
+ if (WasInserted)
+ S->update(Symbol::DefinedGlobalKind);
+ else if (!S->isGlobal())
+ error("symbol type mismatch: " + Name);
+ return S;
+}
+
+Symbol *SymbolTable::addDefined(InputFile *F, const WasmSymbol *Sym,
+ const InputSegment *Segment) {
+ DEBUG(dbgs() << "addDefined: " << Sym->Name << "\n");
+ Symbol *S;
+ bool WasInserted;
+ Symbol::Kind Kind = Symbol::DefinedFunctionKind;
+ if (Sym->Type == WasmSymbol::SymbolType::GLOBAL_EXPORT)
+ Kind = Symbol::DefinedGlobalKind;
+
+ std::tie(S, WasInserted) = insert(Sym->Name);
+ if (WasInserted) {
+ S->update(Kind, F, Sym, Segment);
+ } else if (!S->isDefined()) {
+ // The existing symbol table entry is undefined. The new symbol replaces
+ // it
+ DEBUG(dbgs() << "resolving existing undefined symbol: " << Sym->Name
+ << "\n");
+ checkSymbolTypes(S, F, Sym);
+ S->update(Kind, F, Sym, Segment);
+ } else if (Sym->isWeak()) {
+ // the new symbol is weak we can ignore it
+ DEBUG(dbgs() << "existing symbol takes precensence\n");
+ } else if (S->isWeak()) {
+ // the new symbol is not weak and the existing symbol is, so we replace
+ // it
+ DEBUG(dbgs() << "replacing existing weak symbol\n");
+ S->update(Kind, F, Sym, Segment);
+ } else {
+ // niether symbol is week. They conflict.
+ reportDuplicate(S, F);
+ }
+ return S;
+}
+
+Symbol *SymbolTable::addUndefinedFunction(StringRef Name) {
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(Name);
+ if (WasInserted)
+ S->update(Symbol::UndefinedFunctionKind);
+ else if (!S->isFunction())
+ error("symbol type mismatch: " + Name);
+ return S;
+}
+
+Symbol *SymbolTable::addUndefined(InputFile *F, const WasmSymbol *Sym) {
+ DEBUG(dbgs() << "addUndefined: " << displayName(Sym->Name) << "\n");
+ Symbol *S;
+ bool WasInserted;
+ Symbol::Kind Kind = Symbol::UndefinedFunctionKind;
+ if (Sym->Type == WasmSymbol::SymbolType::GLOBAL_IMPORT)
+ Kind = Symbol::UndefinedGlobalKind;
+ std::tie(S, WasInserted) = insert(Sym->Name);
+ if (WasInserted) {
+ S->update(Kind, F, Sym);
+ } else if (S->isLazy()) {
+ DEBUG(dbgs() << "resolved by existing lazy\n");
+ auto *AF = cast<ArchiveFile>(S->getFile());
+ AF->addMember(&S->getArchiveSymbol());
+ } else if (S->isDefined()) {
+ DEBUG(dbgs() << "resolved by existing\n");
+ checkSymbolTypes(S, F, Sym);
+ }
+ return S;
+}
+
+void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol *Sym) {
+ DEBUG(dbgs() << "addLazy: " << displayName(Sym->getName()) << "\n");
+ StringRef Name = Sym->getName();
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(Name);
+ if (WasInserted) {
+ S->update(Symbol::LazyKind, F);
+ S->setArchiveSymbol(*Sym);
+ } else if (S->isUndefined()) {
+ // There is an existing undefined symbol. The can load from the
+ // archive.
+ DEBUG(dbgs() << "replacing existing undefined\n");
+ F->addMember(Sym);
+ }
+}
--- /dev/null
+//===- SymbolTable.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_SYMBOL_TABLE_H
+#define LLD_WASM_SYMBOL_TABLE_H
+
+#include "InputFiles.h"
+#include "Symbols.h"
+
+#include "llvm/ADT/CachedHashString.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/raw_ostream.h"
+
+using llvm::object::WasmSymbol;
+
+namespace lld {
+namespace wasm {
+
+class InputSegment;
+
+// SymbolTable is a bucket of all known symbols, including defined,
+// undefined, or lazy symbols (the last one is symbols in archive
+// files whose archive members are not yet loaded).
+//
+// We put all symbols of all files to a SymbolTable, and the
+// SymbolTable selects the "best" symbols if there are name
+// conflicts. For example, obviously, a defined symbol is better than
+// an undefined symbol. Or, if there's a conflict between a lazy and a
+// undefined, it'll read an archive member to read a real definition
+// to replace the lazy symbol. The logic is implemented in the
+// add*() functions, which are called by input files as they are parsed.
+// There is one add* function per symbol type.
+class SymbolTable {
+public:
+ void addFile(InputFile *File);
+
+ std::vector<ObjFile *> ObjectFiles;
+ std::vector<Symbol *> SyntheticSymbols;
+
+ void reportDuplicate(Symbol *Existing, InputFile *NewFile);
+ void reportRemainingUndefines();
+
+ Symbol *find(StringRef Name);
+
+ Symbol *addDefined(InputFile *F, const WasmSymbol *Sym,
+ const InputSegment *Segment = nullptr);
+ Symbol *addUndefined(InputFile *F, const WasmSymbol *Sym);
+ Symbol *addUndefinedFunction(StringRef Name);
+ Symbol *addDefinedGlobal(StringRef Name);
+ void addLazy(ArchiveFile *F, const Archive::Symbol *Sym);
+
+private:
+ std::pair<Symbol *, bool> insert(StringRef Name);
+
+ llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> Symtab;
+};
+
+extern SymbolTable *Symtab;
+
+} // namespace wasm
+} // namespace lld
+
+#endif
--- /dev/null
+//===- Symbols.cpp --------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Symbols.h"
+
+#include "Config.h"
+#include "InputFiles.h"
+#include "InputSegment.h"
+#include "Strings.h"
+#include "lld/Common/ErrorHandler.h"
+
+#define DEBUG_TYPE "lld"
+
+using namespace llvm;
+using namespace lld;
+using namespace lld::wasm;
+
+uint32_t Symbol::getGlobalIndex() const {
+ assert(!Sym->isFunction());
+ return Sym->ElementIndex;
+}
+
+uint32_t Symbol::getFunctionIndex() const {
+ assert(Sym->isFunction());
+ return Sym->ElementIndex;
+}
+
+uint32_t Symbol::getFunctionTypeIndex() const {
+ assert(Sym->isFunction());
+ ObjFile *Obj = cast<ObjFile>(File);
+ if (Obj->isImportedFunction(Sym->ElementIndex)) {
+ const WasmImport &Import = Obj->getWasmObj()->imports()[Sym->ImportIndex];
+ DEBUG(dbgs() << "getFunctionTypeIndex: import: " << Sym->ImportIndex
+ << " -> " << Import.SigIndex << "\n");
+ return Import.SigIndex;
+ }
+ DEBUG(dbgs() << "getFunctionTypeIndex: non import: " << Sym->ElementIndex
+ << "\n");
+ uint32_t FuntionIndex = Sym->ElementIndex - Obj->NumFunctionImports();
+ return Obj->getWasmObj()->functionTypes()[FuntionIndex];
+}
+
+uint32_t Symbol::getVirtualAddress() const {
+ assert(isGlobal());
+ DEBUG(dbgs() << "getVirtualAddress: " << getName() << "\n");
+ if (isUndefined())
+ return UINT32_MAX;
+
+ assert(Sym != nullptr);
+ ObjFile *Obj = cast<ObjFile>(File);
+ const WasmGlobal &Global =
+ Obj->getWasmObj()->globals()[getGlobalIndex() - Obj->NumGlobalImports()];
+ assert(Global.Type == llvm::wasm::WASM_TYPE_I32);
+ assert(Segment);
+ return Segment->translateVA(Global.InitExpr.Value.Int32);
+}
+
+uint32_t Symbol::getOutputIndex() const {
+ if (isUndefined() && isWeak())
+ return 0;
+ return OutputIndex.getValue();
+}
+
+void Symbol::setOutputIndex(uint32_t Index) {
+ DEBUG(dbgs() << "setOutputIndex " << Name << " -> " << Index << "\n");
+ assert(!hasOutputIndex());
+ OutputIndex = Index;
+}
+
+void Symbol::update(Kind K, InputFile *F, const WasmSymbol *WasmSym,
+ const InputSegment *Seg) {
+ SymbolKind = K;
+ File = F;
+ Sym = WasmSym;
+ Segment = Seg;
+}
+
+bool Symbol::isWeak() const { return Sym && Sym->isWeak(); }
+
+std::string lld::toString(wasm::Symbol &Sym) {
+ return wasm::displayName(Sym.getName());
+}
+
+std::string lld::toString(wasm::Symbol::Kind &Kind) {
+ switch (Kind) {
+ case wasm::Symbol::DefinedFunctionKind:
+ return "DefinedFunction";
+ case wasm::Symbol::DefinedGlobalKind:
+ return "DefinedGlobal";
+ case wasm::Symbol::UndefinedFunctionKind:
+ return "UndefinedFunction";
+ case wasm::Symbol::UndefinedGlobalKind:
+ return "UndefinedGlobal";
+ case wasm::Symbol::LazyKind:
+ return "LazyKind";
+ }
+ llvm_unreachable("Invalid symbol kind!");
+}
--- /dev/null
+//===- Symbols.h ------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_SYMBOLS_H
+#define LLD_WASM_SYMBOLS_H
+
+#include "lld/Common/LLVM.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/Wasm.h"
+
+using llvm::object::Archive;
+using llvm::object::WasmSymbol;
+using llvm::wasm::WasmImport;
+using llvm::wasm::WasmExport;
+
+namespace lld {
+namespace wasm {
+
+class InputFile;
+class InputSegment;
+
+class Symbol {
+public:
+ enum Kind {
+ DefinedFunctionKind,
+ DefinedGlobalKind,
+
+ LazyKind,
+ UndefinedFunctionKind,
+ UndefinedGlobalKind,
+
+ LastDefinedKind = DefinedGlobalKind,
+ InvalidKind,
+ };
+
+ Symbol(StringRef Name, bool IsLocal)
+ : WrittenToSymtab(0), WrittenToNameSec(0), Name(Name), IsLocal(IsLocal) {}
+
+ Kind getKind() const { return SymbolKind; }
+
+ bool isLazy() const { return SymbolKind == LazyKind; }
+ bool isDefined() const { return SymbolKind <= LastDefinedKind; }
+ bool isUndefined() const {
+ return SymbolKind == UndefinedGlobalKind ||
+ SymbolKind == UndefinedFunctionKind;
+ }
+ bool isFunction() const {
+ return SymbolKind == DefinedFunctionKind ||
+ SymbolKind == UndefinedFunctionKind;
+ }
+ bool isGlobal() const { return !isFunction(); }
+ bool isLocal() const { return IsLocal; }
+ bool isWeak() const;
+
+ // Returns the symbol name.
+ StringRef getName() const { return Name; }
+
+ // Returns the file from which this symbol was created.
+ InputFile *getFile() const { return File; }
+
+ uint32_t getGlobalIndex() const;
+ uint32_t getFunctionIndex() const;
+ uint32_t getFunctionTypeIndex() const;
+ uint32_t getOutputIndex() const;
+
+ // Returns the virtual address of a defined global.
+ // Only works for globals, not functions.
+ uint32_t getVirtualAddress() const;
+
+ // Returns true if an output index has been set for this symbol
+ bool hasOutputIndex() { return OutputIndex.hasValue(); }
+
+ // Set the output index of the symbol (in the function or global index
+ // space of the output object.
+ void setOutputIndex(uint32_t Index);
+
+ void update(Kind K, InputFile *F = nullptr, const WasmSymbol *Sym = nullptr,
+ const InputSegment *Segment = nullptr);
+
+ void setArchiveSymbol(const Archive::Symbol &Sym) { ArchiveSymbol = Sym; }
+ const Archive::Symbol &getArchiveSymbol() { return ArchiveSymbol; }
+
+ // This bit is used by Writer::writeNameSection() to prevent
+ // symbols from being written to the symbol table more than once.
+ unsigned WrittenToSymtab : 1;
+ unsigned WrittenToNameSec : 1;
+
+protected:
+ StringRef Name;
+ bool IsLocal;
+ Archive::Symbol ArchiveSymbol = {nullptr, 0, 0};
+ Kind SymbolKind = InvalidKind;
+ InputFile *File = nullptr;
+ const WasmSymbol *Sym = nullptr;
+ const InputSegment *Segment = nullptr;
+ llvm::Optional<uint32_t> OutputIndex;
+};
+
+} // namespace wasm
+
+// Returns a symbol name for an error message.
+std::string toString(wasm::Symbol &Sym);
+std::string toString(wasm::Symbol::Kind &Kind);
+
+} // namespace lld
+
+#endif
--- /dev/null
+//===- Writer.cpp ---------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Writer.h"
+
+#include "Config.h"
+#include "Memory.h"
+#include "OutputSections.h"
+#include "OutputSegment.h"
+#include "SymbolTable.h"
+#include "WriterUtils.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Threads.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/LEB128.h"
+
+#include <cstdarg>
+
+#define DEBUG_TYPE "lld"
+
+using namespace llvm;
+using namespace llvm::wasm;
+using namespace lld;
+using namespace lld::wasm;
+
+static constexpr int kStackAlignment = 16;
+
+namespace {
+
+// Needed for WasmSignatureDenseMapInfo
+bool operator==(const WasmSignature &LHS, const WasmSignature &RHS) {
+ return LHS.ReturnType == RHS.ReturnType && LHS.ParamTypes == RHS.ParamTypes;
+}
+
+// Traits for using WasmSignature in a DenseMap.
+struct WasmSignatureDenseMapInfo {
+ static WasmSignature getEmptyKey() {
+ WasmSignature Sig;
+ Sig.ReturnType = 1;
+ return Sig;
+ }
+ static WasmSignature getTombstoneKey() {
+ WasmSignature Sig;
+ Sig.ReturnType = 2;
+ return Sig;
+ }
+ static unsigned getHashValue(const WasmSignature &Sig) {
+ uintptr_t Value = 0;
+ Value += DenseMapInfo<int32_t>::getHashValue(Sig.ReturnType);
+ for (int32_t Param : Sig.ParamTypes)
+ Value += DenseMapInfo<int32_t>::getHashValue(Param);
+ return Value;
+ }
+ static bool isEqual(const WasmSignature &LHS, const WasmSignature &RHS) {
+ return LHS == RHS;
+ }
+};
+
+// The writer writes a SymbolTable result to a file.
+class Writer {
+public:
+ void run();
+
+private:
+ void openFile();
+
+ void assignSymbolIndexes();
+ void calculateImports();
+ void calculateOffsets();
+ void calculateTypes();
+ void createOutputSegments();
+ void layoutMemory();
+ void createHeader();
+ void createSections();
+ SyntheticSection *createSyntheticSection(uint32_t Type,
+ std::string Name = "");
+
+ // Builtin sections
+ void createTypeSection();
+ void createFunctionSection();
+ void createTableSection();
+ void createGlobalSection();
+ void createExportSection();
+ void createImportSection();
+ void createMemorySection();
+ void createElemSection();
+ void createStartSection();
+ void createCodeSection();
+ void createDataSection();
+
+ // Custom sections
+ void createRelocSections();
+ void createLinkingSection();
+ void createNameSection();
+
+ void writeHeader();
+ void writeSections();
+
+ uint64_t FileSize = 0;
+ uint32_t DataSize = 0;
+ uint32_t NumFunctions = 0;
+ uint32_t NumGlobals = 0;
+ uint32_t NumMemoryPages = 0;
+ uint32_t NumTableElems = 0;
+ uint32_t NumElements = 0;
+ uint32_t InitialTableOffset = 0;
+
+ std::vector<const WasmSignature *> Types;
+ DenseMap<WasmSignature, int32_t, WasmSignatureDenseMapInfo> TypeIndices;
+ std::vector<Symbol *> FunctionImports;
+ std::vector<Symbol *> GlobalImports;
+
+ // Elements that are used to construct the final output
+ std::string Header;
+ std::vector<OutputSection *> OutputSections;
+
+ std::unique_ptr<FileOutputBuffer> Buffer;
+
+ std::vector<OutputSegment *> Segments;
+ llvm::SmallDenseMap<StringRef, OutputSegment *> SegmentMap;
+};
+
+} // anonymous namespace
+
+static void debugPrint(const char *fmt, ...) {
+ if (!errorHandler().Verbose)
+ return;
+ fprintf(stderr, "lld: ");
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+void Writer::createImportSection() {
+ uint32_t NumImports = FunctionImports.size() + GlobalImports.size();
+ if (Config->ImportMemory)
+ ++NumImports;
+
+ if (NumImports == 0)
+ return;
+
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_IMPORT);
+ raw_ostream &OS = Section->getStream();
+
+ writeUleb128(OS, NumImports, "import count");
+
+ for (Symbol *Sym : FunctionImports) {
+ WasmImport Import;
+ Import.Module = "env";
+ Import.Field = Sym->getName();
+ Import.Kind = WASM_EXTERNAL_FUNCTION;
+ auto *Obj = cast<ObjFile>(Sym->getFile());
+ Import.SigIndex = Obj->relocateTypeIndex(Sym->getFunctionTypeIndex());
+ writeImport(OS, Import);
+ }
+
+ if (Config->ImportMemory) {
+ WasmImport Import;
+ Import.Module = "env";
+ Import.Field = "memory";
+ Import.Kind = WASM_EXTERNAL_MEMORY;
+ Import.Memory.Flags = 0;
+ Import.Memory.Initial = NumMemoryPages;
+ writeImport(OS, Import);
+ }
+
+ for (Symbol *Sym : GlobalImports) {
+ WasmImport Import;
+ Import.Module = "env";
+ Import.Field = Sym->getName();
+ Import.Kind = WASM_EXTERNAL_GLOBAL;
+ Import.Global.Mutable = false;
+ assert(isa<ObjFile>(Sym->getFile()));
+ // TODO(sbc): Set type of this import
+ // ObjFile* Obj = dyn_cast<ObjFile>(Sym->getFile());
+ Import.Global.Type = WASM_TYPE_I32; // Sym->getGlobalType();
+ writeImport(OS, Import);
+ }
+}
+
+void Writer::createTypeSection() {
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_TYPE);
+ raw_ostream &OS = Section->getStream();
+ writeUleb128(OS, Types.size(), "type count");
+ for (const WasmSignature *Sig : Types) {
+ writeSig(OS, *Sig);
+ }
+}
+
+void Writer::createFunctionSection() {
+ if (!NumFunctions)
+ return;
+
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_FUNCTION);
+ raw_ostream &OS = Section->getStream();
+
+ writeUleb128(OS, NumFunctions, "function count");
+ for (ObjFile *File : Symtab->ObjectFiles) {
+ for (uint32_t Sig : File->getWasmObj()->functionTypes()) {
+ writeUleb128(OS, File->relocateTypeIndex(Sig), "sig index");
+ }
+ }
+}
+
+void Writer::createMemorySection() {
+ if (Config->ImportMemory)
+ return;
+
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_MEMORY);
+ raw_ostream &OS = Section->getStream();
+
+ writeUleb128(OS, 1, "memory count");
+ writeUleb128(OS, 0, "memory limits flags");
+ writeUleb128(OS, NumMemoryPages, "initial pages");
+}
+
+void Writer::createGlobalSection() {
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_GLOBAL);
+ raw_ostream &OS = Section->getStream();
+
+ writeUleb128(OS, NumGlobals, "global count");
+ for (auto &Pair : Config->SyntheticGlobals) {
+ WasmGlobal &Global = Pair.second;
+ writeGlobal(OS, Global);
+ }
+
+ if (Config->Relocatable || Config->EmitRelocs) {
+ for (ObjFile *File : Symtab->ObjectFiles) {
+ uint32_t GlobalIndex = File->NumGlobalImports();
+ for (const WasmGlobal &Global : File->getWasmObj()->globals()) {
+ WasmGlobal RelocatedGlobal(Global);
+ if (Global.Type != WASM_TYPE_I32)
+ fatal("unsupported global type: " + Twine(Global.Type));
+ if (Global.InitExpr.Opcode != WASM_OPCODE_I32_CONST)
+ fatal("unsupported global init opcode: " +
+ Twine(Global.InitExpr.Opcode));
+ RelocatedGlobal.InitExpr.Value.Int32 =
+ File->getRelocatedAddress(GlobalIndex);
+ writeGlobal(OS, RelocatedGlobal);
+ ++GlobalIndex;
+ }
+ }
+ }
+}
+
+void Writer::createTableSection() {
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_TABLE);
+ raw_ostream &OS = Section->getStream();
+
+ writeUleb128(OS, 1, "table count");
+ writeSleb128(OS, WASM_TYPE_ANYFUNC, "table type");
+ writeUleb128(OS, WASM_LIMITS_FLAG_HAS_MAX, "table flags");
+ writeUleb128(OS, NumTableElems, "table initial size");
+ writeUleb128(OS, NumTableElems, "table max size");
+}
+
+void Writer::createExportSection() {
+ // Memory is and main function are exported for executables.
+ bool ExportMemory = !Config->Relocatable && !Config->ImportMemory;
+ bool ExportMain = !Config->Relocatable;
+ bool ExportOther = true; // Config->Relocatable;
+
+ uint32_t NumExports = 0;
+
+ if (ExportMemory)
+ ++NumExports;
+
+ if (ExportMain && !ExportOther)
+ ++NumExports;
+
+ if (ExportOther) {
+ for (ObjFile *File : Symtab->ObjectFiles) {
+ for (Symbol *Sym : File->getSymbols()) {
+ if (!Sym->isFunction() || Sym->isLocal() || Sym->isUndefined() ||
+ Sym->WrittenToSymtab)
+ continue;
+ Sym->WrittenToSymtab = true;
+ ++NumExports;
+ }
+ }
+ }
+
+ if (!NumExports)
+ return;
+
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_EXPORT);
+ raw_ostream &OS = Section->getStream();
+
+ writeUleb128(OS, NumExports, "export count");
+
+ if (ExportMemory) {
+ WasmExport MemoryExport;
+ MemoryExport.Name = "memory";
+ MemoryExport.Kind = WASM_EXTERNAL_MEMORY;
+ MemoryExport.Index = 0;
+ writeExport(OS, MemoryExport);
+ }
+
+ if (ExportMain) {
+ Symbol *Sym = Symtab->find(Config->Entry);
+ if (Sym->isDefined()) {
+ if (!Sym->isFunction())
+ fatal("entry point is not a function: " + Sym->getName());
+
+ if (!ExportOther) {
+ WasmExport MainExport;
+ MainExport.Name = Config->Entry;
+ MainExport.Kind = WASM_EXTERNAL_FUNCTION;
+ MainExport.Index = Sym->getOutputIndex();
+ writeExport(OS, MainExport);
+ }
+ }
+ }
+
+ if (ExportOther) {
+ for (ObjFile *File : Symtab->ObjectFiles) {
+ for (Symbol *Sym : File->getSymbols()) {
+ if (!Sym->isFunction() || Sym->isLocal() | Sym->isUndefined() ||
+ !Sym->WrittenToSymtab)
+ continue;
+ Sym->WrittenToSymtab = false;
+ log("Export: " + Sym->getName());
+ WasmExport Export;
+ Export.Name = Sym->getName();
+ Export.Index = Sym->getOutputIndex();
+ if (Sym->isFunction())
+ Export.Kind = WASM_EXTERNAL_FUNCTION;
+ else
+ Export.Kind = WASM_EXTERNAL_GLOBAL;
+ writeExport(OS, Export);
+ }
+ }
+
+ // TODO(sbc): Export local symbols too, Even though they are not part
+ // of the symbol table?
+ }
+}
+
+void Writer::createStartSection() {}
+
+void Writer::createElemSection() {
+ if (!NumElements)
+ return;
+
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_ELEM);
+ raw_ostream &OS = Section->getStream();
+
+ writeUleb128(OS, 1, "segment count");
+ writeUleb128(OS, 0, "table index");
+ WasmInitExpr InitExpr;
+ InitExpr.Opcode = WASM_OPCODE_I32_CONST;
+ InitExpr.Value.Int32 = InitialTableOffset;
+ writeInitExpr(OS, InitExpr);
+ writeUleb128(OS, NumElements, "elem count");
+
+ for (ObjFile *File : Symtab->ObjectFiles)
+ for (const WasmElemSegment &Segment : File->getWasmObj()->elements())
+ for (uint64_t FunctionIndex : Segment.Functions)
+ writeUleb128(OS, File->relocateFunctionIndex(FunctionIndex),
+ "function index");
+}
+
+void Writer::createCodeSection() {
+ if (!NumFunctions)
+ return;
+
+ log("createCodeSection");
+
+ auto Section = make<CodeSection>(NumFunctions, Symtab->ObjectFiles);
+ OutputSections.push_back(Section);
+}
+
+void Writer::createDataSection() {
+ if (!Segments.size())
+ return;
+
+ log("createDataSection");
+ auto Section = make<DataSection>(Segments);
+ OutputSections.push_back(Section);
+}
+
+// Create reloctions sections in the final output.
+// These are only created when relocatable output is requested.
+void Writer::createRelocSections() {
+ log("createRelocSections");
+ // Don't use iterator here since we are adding to OutputSection
+ size_t OrigSize = OutputSections.size();
+ for (size_t i = 0; i < OrigSize; i++) {
+ OutputSection *S = OutputSections[i];
+ const char *name;
+ uint32_t Count = S->numRelocations();
+ if (!Count)
+ continue;
+
+ if (S->Type == WASM_SEC_DATA)
+ name = "reloc.DATA";
+ else if (S->Type == WASM_SEC_CODE)
+ name = "reloc.CODE";
+ else
+ llvm_unreachable("relocations only support for code and data");
+
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_CUSTOM, name);
+ raw_ostream &OS = Section->getStream();
+ writeUleb128(OS, S->Type, "reloc section");
+ writeUleb128(OS, Count, "reloc count");
+ S->writeRelocations(OS);
+ }
+}
+
+// Create the custome "linking" section containing linker metadata.
+// This is only created when relocatable output is requested.
+void Writer::createLinkingSection() {
+ SyntheticSection *Section =
+ createSyntheticSection(WASM_SEC_CUSTOM, "linking");
+ raw_ostream &OS = Section->getStream();
+
+ SubSection DataSizeSubSection(WASM_DATA_SIZE);
+ writeUleb128(DataSizeSubSection.getStream(), DataSize, "data size");
+ DataSizeSubSection.finalizeContents();
+ DataSizeSubSection.writeToStream(OS);
+
+ if (Segments.size() && Config->Relocatable) {
+ SubSection SubSection(WASM_SEGMENT_INFO);
+ writeUleb128(SubSection.getStream(), Segments.size(), "num data segments");
+ for (const OutputSegment *S : Segments) {
+ writeStr(SubSection.getStream(), S->Name, "segment name");
+ writeUleb128(SubSection.getStream(), S->Alignment, "alignment");
+ writeUleb128(SubSection.getStream(), 0, "flags");
+ }
+ SubSection.finalizeContents();
+ SubSection.writeToStream(OS);
+ }
+}
+
+// Create the custom "name" section containing debug symbol names.
+void Writer::createNameSection() {
+ // Create an array of all function sorted by function index space
+ std::vector<const Symbol *> Names;
+
+ for (ObjFile *File : Symtab->ObjectFiles) {
+ Names.reserve(Names.size() + File->getSymbols().size());
+ for (Symbol *S : File->getSymbols()) {
+ if (!S->isFunction() || S->isWeak() || S->WrittenToNameSec)
+ continue;
+ S->WrittenToNameSec = true;
+ Names.emplace_back(S);
+ }
+ }
+
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_CUSTOM, "name");
+
+ std::sort(Names.begin(), Names.end(), [](const Symbol *A, const Symbol *B) {
+ return A->getOutputIndex() < B->getOutputIndex();
+ });
+
+ SubSection FunctionSubsection(WASM_NAMES_FUNCTION);
+ raw_ostream &OS = FunctionSubsection.getStream();
+ writeUleb128(OS, Names.size(), "name count");
+
+ // We have to iterate through the inputs twice so that all the imports
+ // appear first before any of the local function names.
+ for (const Symbol *S : Names) {
+ writeUleb128(OS, S->getOutputIndex(), "func index");
+ writeStr(OS, S->getName(), "symbol name");
+ }
+
+ FunctionSubsection.finalizeContents();
+ FunctionSubsection.writeToStream(Section->getStream());
+}
+
+void Writer::writeHeader() {
+ memcpy(Buffer->getBufferStart(), Header.data(), Header.size());
+}
+
+void Writer::writeSections() {
+ uint8_t *Buf = Buffer->getBufferStart();
+ parallelForEach(OutputSections, [Buf](OutputSection *S) { S->writeTo(Buf); });
+}
+
+// Fix the memory layout of the output binary. This assigns memory offsets
+// to each of the intput data sections as well as the explicit stack region.
+void Writer::layoutMemory() {
+ uint32_t MemoryPtr = 0;
+ if (!Config->Relocatable) {
+ MemoryPtr = Config->GlobalBase;
+ debugPrint("mem: global base = %d\n", Config->GlobalBase);
+ }
+
+ createOutputSegments();
+
+ // Static data comes first
+ for (OutputSegment *Seg : Segments) {
+ MemoryPtr = alignTo(MemoryPtr, Seg->Alignment);
+ Seg->StartVA = MemoryPtr;
+ debugPrint("mem: %-10s offset=%-8d size=%-4d align=%d\n",
+ Seg->Name.str().c_str(), MemoryPtr, Seg->Size, Seg->Alignment);
+ MemoryPtr += Seg->Size;
+ }
+
+ DataSize = MemoryPtr;
+ if (!Config->Relocatable)
+ DataSize -= Config->GlobalBase;
+ debugPrint("mem: static data = %d\n", DataSize);
+
+ // Stack comes after static data
+ if (!Config->Relocatable) {
+ MemoryPtr = alignTo(MemoryPtr, kStackAlignment);
+ if (Config->ZStackSize != alignTo(Config->ZStackSize, kStackAlignment))
+ error("stack size must be " + Twine(kStackAlignment) + "-byte aligned");
+ debugPrint("mem: stack size = %d\n", Config->ZStackSize);
+ debugPrint("mem: stack base = %d\n", MemoryPtr);
+ MemoryPtr += Config->ZStackSize;
+ Config->SyntheticGlobals[0].second.InitExpr.Value.Int32 = MemoryPtr;
+ debugPrint("mem: stack top = %d\n", MemoryPtr);
+ }
+
+ uint32_t MemSize = alignTo(MemoryPtr, WasmPageSize);
+ NumMemoryPages = MemSize / WasmPageSize;
+ debugPrint("mem: total pages = %d\n", NumMemoryPages);
+}
+
+SyntheticSection *Writer::createSyntheticSection(uint32_t Type,
+ std::string Name) {
+ auto Sec = make<SyntheticSection>(Type, Name);
+ log("createSection: " + toString(Sec));
+ OutputSections.push_back(Sec);
+ return Sec;
+}
+
+void Writer::createSections() {
+ // Known sections
+ createTypeSection();
+ createImportSection();
+ createFunctionSection();
+ createTableSection();
+ createMemorySection();
+ createGlobalSection();
+ createExportSection();
+ createStartSection();
+ createElemSection();
+ createCodeSection();
+ createDataSection();
+
+ // Custom sections
+ if (Config->EmitRelocs || Config->Relocatable)
+ createRelocSections();
+ createLinkingSection();
+ if (!Config->StripDebug && !Config->StripAll)
+ createNameSection();
+
+ for (OutputSection *S : OutputSections) {
+ S->setOffset(FileSize);
+ S->finalizeContents();
+ FileSize += S->getSize();
+ }
+}
+
+void Writer::calculateOffsets() {
+ NumGlobals = Config->SyntheticGlobals.size();
+ NumTableElems = InitialTableOffset;
+
+ for (ObjFile *File : Symtab->ObjectFiles) {
+ const WasmObjectFile *WasmFile = File->getWasmObj();
+
+ // Function Index
+ File->FunctionIndexOffset =
+ FunctionImports.size() - File->NumFunctionImports() + NumFunctions;
+ NumFunctions += WasmFile->functions().size();
+
+ // Global Index
+ if (Config->Relocatable || Config->EmitRelocs) {
+ File->GlobalIndexOffset =
+ GlobalImports.size() - File->NumGlobalImports() + NumGlobals;
+ NumGlobals += WasmFile->globals().size();
+ }
+
+ // Memory
+ if (WasmFile->memories().size()) {
+ if (WasmFile->memories().size() > 1) {
+ fatal(File->getName() + ": contains more than one memory");
+ }
+ }
+
+ // Table
+ uint32_t TableCount = WasmFile->tables().size();
+ if (TableCount) {
+ if (TableCount > 1)
+ fatal(File->getName() + ": contains more than one table");
+ File->TableIndexOffset = NumTableElems;
+ NumTableElems += WasmFile->tables()[0].Limits.Initial;
+ }
+
+ // Elem
+ uint32_t SegmentCount = WasmFile->elements().size();
+ if (SegmentCount) {
+ if (SegmentCount > 1) {
+ fatal(File->getName() + ": contains more than element segment");
+ } else {
+ const WasmElemSegment &Segment = WasmFile->elements()[0];
+ if (Segment.TableIndex != 0)
+ fatal(File->getName() + ": unsupported table index");
+ else if (Segment.Offset.Value.Int32 != 0)
+ fatal(File->getName() + ": unsupported segment offset");
+ else
+ NumElements += Segment.Functions.size();
+ }
+ }
+ }
+}
+
+void Writer::calculateImports() {
+ for (ObjFile *File : Symtab->ObjectFiles) {
+ for (Symbol *Sym : File->getSymbols()) {
+ if (Sym->hasOutputIndex() || Sym->isDefined() || Sym->isWeak())
+ continue;
+
+ if (Sym->isFunction()) {
+ Sym->setOutputIndex(FunctionImports.size());
+ FunctionImports.push_back(Sym);
+ } else {
+ Sym->setOutputIndex(GlobalImports.size());
+ GlobalImports.push_back(Sym);
+ }
+ }
+ }
+}
+
+void Writer::calculateTypes() {
+ for (ObjFile *File : Symtab->ObjectFiles) {
+ File->TypeMap.reserve(File->getWasmObj()->types().size());
+ for (const WasmSignature &Sig : File->getWasmObj()->types()) {
+ auto Pair = TypeIndices.insert(std::make_pair(Sig, Types.size()));
+ if (Pair.second)
+ Types.push_back(&Sig);
+
+ // Now we map the input files index to the index in the linked output
+ File->TypeMap.push_back(Pair.first->second);
+ }
+ }
+}
+
+void Writer::assignSymbolIndexes() {
+ for (ObjFile *File : Symtab->ObjectFiles) {
+ DEBUG(dbgs() << "assignSymbolIndexes: " << File->getName() << "\n");
+ for (Symbol *Sym : File->getSymbols()) {
+ if (Sym->hasOutputIndex() || !Sym->isDefined())
+ continue;
+
+ if (Sym->getFile() && isa<ObjFile>(Sym->getFile())) {
+ auto *Obj = cast<ObjFile>(Sym->getFile());
+ if (Sym->isFunction())
+ Sym->setOutputIndex(Obj->FunctionIndexOffset +
+ Sym->getFunctionIndex());
+ else
+ Sym->setOutputIndex(Obj->GlobalIndexOffset + Sym->getGlobalIndex());
+ }
+ }
+ }
+}
+
+static StringRef getOutputDataSegmentName(StringRef Name) {
+ if (Config->Relocatable)
+ return Name;
+
+ for (StringRef V :
+ {".text.", ".rodata.", ".data.rel.ro.", ".data.", ".bss.rel.ro.",
+ ".bss.", ".init_array.", ".fini_array.", ".ctors.", ".dtors.", ".tbss.",
+ ".gcc_except_table.", ".tdata.", ".ARM.exidx.", ".ARM.extab."}) {
+ StringRef Prefix = V.drop_back();
+ if (Name.startswith(V) || Name == Prefix)
+ return Prefix;
+ }
+
+ return Name;
+}
+
+void Writer::createOutputSegments() {
+ for (ObjFile *File : Symtab->ObjectFiles) {
+ for (InputSegment *Segment : File->Segments) {
+ StringRef Name = getOutputDataSegmentName(Segment->getName());
+ OutputSegment *&S = SegmentMap[Name];
+ if (S == nullptr) {
+ DEBUG(dbgs() << "new segment: " << Name << "\n");
+ S = make<OutputSegment>(Name);
+ Segments.push_back(S);
+ }
+ S->addInputSegment(Segment);
+ DEBUG(dbgs() << "added data: " << Name << ": " << S->Size << "\n");
+ for (const WasmRelocation &R : File->DataSection->Relocations) {
+ if (R.Offset >= Segment->getInputSectionOffset() &&
+ R.Offset < Segment->getInputSectionOffset() + Segment->getSize()) {
+ Segment->Relocations.push_back(R);
+ }
+ }
+ }
+ }
+}
+
+void Writer::run() {
+ if (!Config->Relocatable)
+ InitialTableOffset = 1;
+
+ log("-- calculateTypes");
+ calculateTypes();
+ log("-- calculateImports");
+ calculateImports();
+ log("-- calculateOffsets");
+ calculateOffsets();
+
+ if (errorHandler().Verbose) {
+ log("NumFunctions : " + Twine(NumFunctions));
+ log("NumGlobals : " + Twine(NumGlobals));
+ log("NumImports : " +
+ Twine(FunctionImports.size() + GlobalImports.size()));
+ log("FunctionImports : " + Twine(FunctionImports.size()));
+ log("GlobalImports : " + Twine(GlobalImports.size()));
+ for (ObjFile *File : Symtab->ObjectFiles)
+ File->dumpInfo();
+ }
+
+ log("-- assignSymbolIndexes");
+ assignSymbolIndexes();
+ log("-- layoutMemory");
+ layoutMemory();
+
+ createHeader();
+ log("-- createSections");
+ createSections();
+
+ log("-- openFile");
+ openFile();
+ if (errorCount())
+ return;
+
+ writeHeader();
+
+ log("-- writeSections");
+ writeSections();
+ if (errorCount())
+ return;
+
+ if (Error E = Buffer->commit())
+ fatal("failed to write the output file: " + toString(std::move(E)));
+}
+
+// Open a result file.
+void Writer::openFile() {
+ log("writing: " + Config->OutputFile);
+ ::remove(Config->OutputFile.str().c_str());
+
+ Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
+ FileOutputBuffer::create(Config->OutputFile, FileSize,
+ FileOutputBuffer::F_executable);
+
+ if (!BufferOrErr)
+ error("failed to open " + Config->OutputFile + ": " +
+ toString(BufferOrErr.takeError()));
+ else
+ Buffer = std::move(*BufferOrErr);
+}
+
+void Writer::createHeader() {
+ raw_string_ostream OS(Header);
+ writeBytes(OS, WasmMagic, sizeof(WasmMagic), "wasm magic");
+ writeU32(OS, WasmVersion, "wasm version");
+ OS.flush();
+ FileSize += Header.size();
+}
+
+void lld::wasm::writeResult() { Writer().run(); }
--- /dev/null
+//===- Writer.h -------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_WRITER_H
+#define LLD_WASM_WRITER_H
+
+namespace lld {
+namespace wasm {
+
+void writeResult();
+
+} // namespace wasm
+} // namespace lld
+
+#endif
--- /dev/null
+//===- WriterUtils.cpp ----------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "WriterUtils.h"
+
+#include "lld/Common/ErrorHandler.h"
+
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/EndianStream.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/LEB128.h"
+
+#define DEBUG_TYPE "lld"
+
+using namespace llvm;
+using namespace llvm::wasm;
+using namespace lld::wasm;
+
+static const char *valueTypeToString(int32_t Type) {
+ switch (Type) {
+ case WASM_TYPE_I32:
+ return "i32";
+ case WASM_TYPE_I64:
+ return "i64";
+ case WASM_TYPE_F32:
+ return "f32";
+ case WASM_TYPE_F64:
+ return "f64";
+ default:
+ llvm_unreachable("invalid value type");
+ }
+}
+
+namespace lld {
+
+void wasm::debugWrite(uint64_t offset, Twine msg) {
+ DEBUG(dbgs() << format(" | %08" PRIx64 ": ", offset) << msg << "\n");
+}
+
+void wasm::writeUleb128(raw_ostream &OS, uint32_t Number, const char *msg) {
+ if (msg)
+ debugWrite(OS.tell(), msg + formatv(" [{0:x}]", Number));
+ encodeULEB128(Number, OS);
+}
+
+void wasm::writeSleb128(raw_ostream &OS, int32_t Number, const char *msg) {
+ if (msg)
+ debugWrite(OS.tell(), msg + formatv(" [{0:x}]", Number));
+ encodeSLEB128(Number, OS);
+}
+
+void wasm::writeBytes(raw_ostream &OS, const char *bytes, size_t count,
+ const char *msg) {
+ if (msg)
+ debugWrite(OS.tell(), msg + formatv(" [data[{0}]]", count));
+ OS.write(bytes, count);
+}
+
+void wasm::writeStr(raw_ostream &OS, const StringRef String, const char *msg) {
+ if (msg)
+ debugWrite(OS.tell(),
+ msg + formatv(" [str[{0}]: {1}]", String.size(), String));
+ writeUleb128(OS, String.size(), nullptr);
+ writeBytes(OS, String.data(), String.size());
+}
+
+void wasm::writeU8(raw_ostream &OS, uint8_t byte, const char *msg) {
+ OS << byte;
+}
+
+void wasm::writeU32(raw_ostream &OS, uint32_t Number, const char *msg) {
+ debugWrite(OS.tell(), msg + formatv("[{0:x}]", Number));
+ support::endian::Writer<support::little>(OS).write(Number);
+}
+
+void wasm::writeValueType(raw_ostream &OS, int32_t Type, const char *msg) {
+ debugWrite(OS.tell(), msg + formatv("[type: {0}]", valueTypeToString(Type)));
+ writeSleb128(OS, Type, nullptr);
+}
+
+void wasm::writeSig(raw_ostream &OS, const WasmSignature &Sig) {
+ writeSleb128(OS, WASM_TYPE_FUNC, "signature type");
+ writeUleb128(OS, Sig.ParamTypes.size(), "param count");
+ for (int32_t ParamType : Sig.ParamTypes) {
+ writeValueType(OS, ParamType, "param type");
+ }
+ if (Sig.ReturnType == WASM_TYPE_NORESULT) {
+ writeUleb128(OS, 0, "result count");
+ } else {
+ writeUleb128(OS, 1, "result count");
+ writeValueType(OS, Sig.ReturnType, "result type");
+ }
+}
+
+void wasm::writeInitExpr(raw_ostream &OS, const WasmInitExpr &InitExpr) {
+ writeU8(OS, InitExpr.Opcode, "opcode");
+ switch (InitExpr.Opcode) {
+ case WASM_OPCODE_I32_CONST:
+ writeSleb128(OS, InitExpr.Value.Int32, "literal (i32)");
+ break;
+ case WASM_OPCODE_I64_CONST:
+ writeSleb128(OS, InitExpr.Value.Int64, "literal (i64)");
+ break;
+ case WASM_OPCODE_GET_GLOBAL:
+ writeUleb128(OS, InitExpr.Value.Global, "literal (global index)");
+ break;
+ default:
+ fatal("unknown opcode in init expr: " + Twine(InitExpr.Opcode));
+ break;
+ }
+ writeU8(OS, WASM_OPCODE_END, "opcode:end");
+}
+
+void wasm::writeLimits(raw_ostream &OS, const WasmLimits &Limits) {
+ writeUleb128(OS, Limits.Flags, "limits flags");
+ writeUleb128(OS, Limits.Initial, "limits initial");
+ if (Limits.Flags & WASM_LIMITS_FLAG_HAS_MAX)
+ writeUleb128(OS, Limits.Maximum, "limits max");
+}
+
+void wasm::writeGlobal(raw_ostream &OS, const WasmGlobal &Global) {
+ writeValueType(OS, Global.Type, "global type");
+ writeUleb128(OS, Global.Mutable, "global mutable");
+ writeInitExpr(OS, Global.InitExpr);
+}
+
+void wasm::writeImport(raw_ostream &OS, const WasmImport &Import) {
+ writeStr(OS, Import.Module, "import module name");
+ writeStr(OS, Import.Field, "import field name");
+ writeU8(OS, Import.Kind, "import kind");
+ switch (Import.Kind) {
+ case WASM_EXTERNAL_FUNCTION:
+ writeUleb128(OS, Import.SigIndex, "import sig index");
+ break;
+ case WASM_EXTERNAL_GLOBAL:
+ writeValueType(OS, Import.Global.Type, "import global type");
+ writeUleb128(OS, Import.Global.Mutable, "import global mutable");
+ break;
+ case WASM_EXTERNAL_MEMORY:
+ writeLimits(OS, Import.Memory);
+ break;
+ default:
+ fatal("unsupported import type: " + Twine(Import.Kind));
+ break;
+ }
+}
+
+void wasm::writeExport(raw_ostream &OS, const WasmExport &Export) {
+ writeStr(OS, Export.Name, "export name");
+ writeU8(OS, Export.Kind, "export kind");
+ switch (Export.Kind) {
+ case WASM_EXTERNAL_FUNCTION:
+ writeUleb128(OS, Export.Index, "function index");
+ break;
+ case WASM_EXTERNAL_GLOBAL:
+ writeUleb128(OS, Export.Index, "global index");
+ break;
+ case WASM_EXTERNAL_MEMORY:
+ writeUleb128(OS, Export.Index, "memory index");
+ break;
+ default:
+ fatal("unsupported export type: " + Twine(Export.Kind));
+ break;
+ }
+}
+
+void wasm::writeReloc(raw_ostream &OS, const OutputRelocation &Reloc) {
+ writeUleb128(OS, Reloc.Reloc.Type, "reloc type");
+ writeUleb128(OS, Reloc.Reloc.Offset, "reloc offset");
+ writeUleb128(OS, Reloc.NewIndex, "reloc index");
+
+ switch (Reloc.Reloc.Type) {
+ case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
+ case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
+ case R_WEBASSEMBLY_MEMORY_ADDR_I32:
+ writeUleb128(OS, Reloc.Reloc.Addend, "reloc addend");
+ break;
+ default:
+ break;
+ }
+}
+
+} // namespace lld
--- /dev/null
+//===- WriterUtils.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_WRITERUTILS_H
+#define LLD_WASM_WRITERUTILS_H
+
+#include "llvm/ADT/Twine.h"
+#include "llvm/Object/Wasm.h"
+#include "llvm/Support/raw_ostream.h"
+
+using llvm::raw_ostream;
+
+namespace lld {
+namespace wasm {
+
+struct OutputRelocation {
+ llvm::wasm::WasmRelocation Reloc;
+ uint32_t NewIndex;
+ uint32_t Value;
+};
+
+void debugWrite(uint64_t offset, llvm::Twine msg);
+
+void writeUleb128(raw_ostream &OS, uint32_t Number, const char *msg);
+
+void writeSleb128(raw_ostream &OS, int32_t Number, const char *msg);
+
+void writeBytes(raw_ostream &OS, const char *bytes, size_t count,
+ const char *msg = nullptr);
+
+void writeStr(raw_ostream &OS, const llvm::StringRef String,
+ const char *msg = nullptr);
+
+void writeU8(raw_ostream &OS, uint8_t byte, const char *msg);
+
+void writeU32(raw_ostream &OS, uint32_t Number, const char *msg);
+
+void writeValueType(raw_ostream &OS, int32_t Type, const char *msg);
+
+void writeSig(raw_ostream &OS, const llvm::wasm::WasmSignature &Sig);
+
+void writeInitExpr(raw_ostream &OS, const llvm::wasm::WasmInitExpr &InitExpr);
+
+void writeLimits(raw_ostream &OS, const llvm::wasm::WasmLimits &Limits);
+
+void writeGlobal(raw_ostream &OS, const llvm::wasm::WasmGlobal &Global);
+
+void writeImport(raw_ostream &OS, const llvm::wasm::WasmImport &Import);
+
+void writeExport(raw_ostream &OS, const llvm::wasm::WasmExport &Export);
+
+void writeReloc(raw_ostream &OS, const OutputRelocation &Reloc);
+
+} // namespace wasm
+} // namespace lld
+
+#endif // LLD_WASM_WRITERUTILS_H