Skip to content

Commit 25cd982

Browse files
committed
main: optionally build with LLD
When building statically against LLVM, LLD is also included now. When included, the built in wasm-ld will automatically be used instead of the external command. There is also support for linking ELF files but because lld does not fully support armv6m this is not yet enabled (it produces a warning).
1 parent 9bbb233 commit 25cd982

File tree

7 files changed

+134
-25
lines changed

7 files changed

+134
-25
lines changed

BUILDING.md

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
# Building TinyGo
22

3-
TinyGo depends on LLVM and libclang, which are both big C++ libraries. There are
4-
two ways these can be linked: dynamically and statically. The default is dynamic
5-
linking because it is fast and works almost out of the box on Debian-based
6-
systems with the right libraries installed.
3+
TinyGo depends on LLVM and libclang, which are both big C++ libraries. It can
4+
also optionally use a built-in lld to ease cross compiling. There are two ways
5+
these can be linked: dynamically and statically. The default is dynamic linking
6+
because it is fast and works almost out of the box on Debian-based systems with
7+
the right libraries installed.
78

8-
This guide describes how to statically link TinyGo against LLVM and libclang so
9-
that the binary can be easily moved between systems.
9+
This guide describes how to statically link TinyGo against LLVM, libclang and
10+
lld so that the binary can be easily moved between systems.
1011

1112
## Dependencies
1213

13-
LLVM and Clang are both quite light on dependencies, requiring only standard
14+
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
1415
build tools to be built. Go is of course necessary to build TinyGo itself.
1516

1617
* Go (1.11+)
@@ -30,14 +31,15 @@ The first step is to get the source code. Place it in some directory, assuming
3031

3132
git clone -b release_70 https://github.com/llvm-mirror/llvm.git $HOME/src/llvm
3233
git clone -b release_70 https://github.com/llvm-mirror/clang.git $HOME/src/llvm/tools/clang
34+
git clone -b release_70 https://github.com/llvm-mirror/lld.git $HOME/src/llvm/tools/lld
3335
go get -d github.com/tinygo-org/tinygo
3436
cd $HOME/go/src/github.com/tinygo-org/tinygo
3537
dep ensure -vendor-only # download dependencies
3638

37-
Note that Clang must be placed inside the tools subdirectory of LLVM to be
38-
automatically built with the rest of the system.
39+
Note that Clang and LLD must be placed inside the tools subdirectory of LLVM to
40+
be automatically built with the rest of the system.
3941

40-
## Build LLVM and Clang
42+
## Build LLVM, Clang, LLD
4143

4244
Building LLVM is quite easy compared to some other software packages. However,
4345
the default configuration is _not_ optimized for distribution. It is optimized
@@ -86,10 +88,10 @@ This can take over an hour depending on the speed of your system.
8688
## Build TinyGo
8789

8890
Now that you have a working version of LLVM, build TinyGo using it. You need to
89-
specify the directories to the LLVM build directory and to the Clang source.
91+
specify the directories to the LLVM build directory and to the Clang and LLD source.
9092

9193
cd $HOME/go/src/github.com/tinygo-org/tinygo
92-
make static LLVM_BUILDDIR=$HOME/src/llvm-build CLANG_SRC=$HOME/src/llvm/tools/clang
94+
make static LLVM_BUILDDIR=$HOME/src/llvm-build CLANG_SRC=$HOME/src/llvm/tools/clang LLD_SRC=$HOME/src/llvm/tools/lld
9395

9496
## Verify TinyGo
9597

@@ -98,7 +100,7 @@ Try running TinyGo:
98100
./build/tinygo help
99101

100102
Also, make sure the `tinygo` binary really is statically linked. Check this
101-
using `ldd`:
103+
using `ldd` (not to be confused with `lld`):
102104

103105
ldd ./build/tinygo
104106

Makefile

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,17 @@ $(error Unknown target)
4040

4141
endif
4242

43-
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines debuginfodwarf executionengine instrumentation interpreter ipo irreader linker mc mcjit objcarcopts option profiledata scalaropts support target
43+
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines debuginfodwarf executionengine instrumentation interpreter ipo irreader linker lto mc mcjit objcarcopts option profiledata scalaropts support target
4444

4545
CLANG_LIBS = -Wl,--start-group $(abspath $(LLVM_BUILDDIR))/lib/libclang.a -lclangAnalysis -lclangARCMigrate -lclangAST -lclangASTMatchers -lclangBasic -lclangCodeGen -lclangCrossTU -lclangDriver -lclangDynamicASTMatchers -lclangEdit -lclangFormat -lclangFrontend -lclangFrontendTool -lclangHandleCXX -lclangHandleLLVM -lclangIndex -lclangLex -lclangParse -lclangRewrite -lclangRewriteFrontend -lclangSema -lclangSerialization -lclangStaticAnalyzerCheckers -lclangStaticAnalyzerCore -lclangStaticAnalyzerFrontend -lclangTooling -lclangToolingASTDiff -lclangToolingCore -lclangToolingInclusions -lclangToolingRefactor -Wl,--end-group -lstdc++
4646

47+
LLD_LIBS = -Wl,--start-group -llldCOFF -llldCommon -llldCore -llldDriver -llldELF -llldMachO -llldMinGW -llldReaderWriter -llldWasm -llldYAML -Wl,--end-group
48+
49+
4750
# For static linking.
48-
CGO_CPPFLAGS=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(CLANG_SRC))/include
51+
CGO_CPPFLAGS=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
4952
CGO_CXXFLAGS=-std=c++11
50-
CGO_LDFLAGS=-L$(LLVM_BUILDDIR)/lib $(CLANG_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS))
53+
CGO_LDFLAGS=-L$(LLVM_BUILDDIR)/lib $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS))
5154

5255

5356

linker-builtin.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// +build byollvm
2+
3+
package main
4+
5+
// This file provides a Link() function that uses the bundled lld if possible.
6+
7+
import (
8+
"errors"
9+
"os"
10+
"os/exec"
11+
"unsafe"
12+
)
13+
14+
/*
15+
#include <stdbool.h>
16+
#include <stdlib.h>
17+
bool tinygo_link_elf(int argc, char **argv);
18+
bool tinygo_link_wasm(int argc, char **argv);
19+
*/
20+
import "C"
21+
22+
// Link invokes a linker with the given name and flags.
23+
//
24+
// This version uses the built-in linker when trying to use lld.
25+
func Link(dir, linker string, flags ...string) error {
26+
switch linker {
27+
case "ld.lld", commands["ld.lld"]:
28+
flags = append([]string{"tinygo:" + linker}, flags...)
29+
var cflag *C.char
30+
buf := C.calloc(C.size_t(len(flags)), C.size_t(unsafe.Sizeof(cflag)))
31+
cflags := (*[1 << 10]*C.char)(unsafe.Pointer(buf))[:len(flags):len(flags)]
32+
for i, flag := range flags {
33+
cflag := C.CString(flag)
34+
cflags[i] = cflag
35+
defer C.free(unsafe.Pointer(cflag))
36+
}
37+
ok := C.tinygo_link_elf(C.int(len(flags)), (**C.char)(buf))
38+
if !ok {
39+
return errors.New("failed to link using built-in ld.lld")
40+
}
41+
return nil
42+
case "wasm-ld", commands["wasm-ld"]:
43+
flags = append([]string{"tinygo:" + linker}, flags...)
44+
var cflag *C.char
45+
buf := C.calloc(C.size_t(len(flags)), C.size_t(unsafe.Sizeof(cflag)))
46+
defer C.free(buf)
47+
cflags := (*[1 << 10]*C.char)(unsafe.Pointer(buf))[:len(flags):len(flags)]
48+
for i, flag := range flags {
49+
cflag := C.CString(flag)
50+
cflags[i] = cflag
51+
defer C.free(unsafe.Pointer(cflag))
52+
}
53+
ok := C.tinygo_link_wasm(C.int(len(flags)), (**C.char)(buf))
54+
if !ok {
55+
return errors.New("failed to link using built-in wasm-ld")
56+
}
57+
return nil
58+
default:
59+
// Fall back to external command.
60+
cmd := exec.Command(linker, flags...)
61+
cmd.Stdout = os.Stdout
62+
cmd.Stderr = os.Stderr
63+
cmd.Dir = dir
64+
return cmd.Run()
65+
}
66+
}

linker-external.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// +build !byollvm
2+
3+
package main
4+
5+
// This file provides a Link() function that always runs an external command. It
6+
// is provided for when tinygo is built without linking to liblld.
7+
8+
import (
9+
"os"
10+
"os/exec"
11+
)
12+
13+
// Link invokes a linker with the given name and arguments.
14+
//
15+
// This version always runs the linker as an external command.
16+
func Link(dir, linker string, flags ...string) error {
17+
cmd := exec.Command(linker, flags...)
18+
cmd.Stdout = os.Stdout
19+
cmd.Stderr = os.Stderr
20+
cmd.Dir = dir
21+
return cmd.Run()
22+
}

lld.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// +build byollvm
2+
3+
// This file provides C wrappers for liblld.
4+
5+
#include <lld/Common/Driver.h>
6+
7+
extern "C" {
8+
9+
bool tinygo_link_elf(int argc, char **argv) {
10+
std::vector<const char*> args(argv, argv + argc);
11+
return lld::elf::link(args, false);
12+
}
13+
14+
bool tinygo_link_wasm(int argc, char **argv) {
15+
std::vector<const char*> args(argv, argv + argc);
16+
return lld::wasm::link(args, false);
17+
}
18+
19+
} // external "C"

main.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ import (
2121
)
2222

2323
var commands = map[string]string{
24-
"ar": "ar",
25-
"clang": "clang-7",
24+
"ar": "ar",
25+
"clang": "clang-7",
26+
"ld.lld": "ld.lld-7",
27+
"wasm-ld": "wasm-ld-7",
2628
}
2729

2830
// commandError is an error type to wrap os/exec.Command errors. This provides
@@ -234,11 +236,7 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
234236
}
235237

236238
// Link the object files together.
237-
cmd := exec.Command(spec.Linker, ldflags...)
238-
cmd.Stdout = os.Stdout
239-
cmd.Stderr = os.Stderr
240-
cmd.Dir = sourceDir()
241-
err = cmd.Run()
239+
err = Link(sourceDir(), spec.Linker, ldflags...)
242240
if err != nil {
243241
return &commandError{"failed to link", executable, err}
244242
}

targets/wasm.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@
44
"goos": "js",
55
"goarch": "wasm",
66
"compiler": "clang-7",
7-
"linker": "ld.lld-7",
7+
"linker": "wasm-ld-7",
88
"cflags": [
99
"--target=wasm32",
1010
"-Oz"
1111
],
1212
"ldflags": [
13-
"-flavor", "wasm",
1413
"-allow-undefined"
1514
],
1615
"emulator": ["cwa"]

0 commit comments

Comments
 (0)