1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
// Builder: a static busybox for the tolino, compiled with `zig cc`, so that
// `adb shell` has a shell and the everyday tools (ls, cat, ps, dmesg,
// mount, df). Nothing in the system runs it; only adbd's shell service.
//
// Inputs: zig, src (busybox tarball), target, cpu, sh, toolPath, plus
// gnutar/bzip2/gnumake.
package main
import (
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"drv"
)
func main() {
top := drv.Env("NIX_BUILD_TOP")
zig := drv.Env("zig") + "/bin/zig"
sh := drv.Env("sh")
cache := filepath.Join(top, "zig-cache")
os.Setenv("HOME", top)
os.Setenv("ZIG_GLOBAL_CACHE_DIR", cache)
os.Setenv("ZIG_LOCAL_CACHE_DIR", cache)
os.Setenv("PATH", drv.Env("toolPath"))
drv.RunIn(top, drv.Env("gnutar")+"/bin/tar", "-xjf", drv.Env("src"))
dirs := drv.Glob(top + "/busybox-*")
if len(dirs) != 1 {
drv.Fatalf("expected one busybox-* directory, got %v", dirs)
}
dir := dirs[0]
wrap := filepath.Join(top, "wrap")
target := "-target " + drv.Env("target") + " -mcpu=" + drv.Env("cpu")
// busybox's scripts/trylink passes GNU ld options zig's linker (lld)
// rejects outright; they only affect diagnostics and padding, so drop
// them. Everything else passes through.
drv.WriteShebang(wrap+"/cc", sh,
`for a; do`,
` shift`,
` case "$a" in`,
` -Wl,--warn-common|-Wl,--verbose|-Wl,-Map,*|-Wl,--sort-common|-Wl,--sort-section,*) ;;`,
` *) set -- "$@" "$a" ;;`,
` esac`,
`done`,
`exec `+zig+` cc `+target+` "$@"`)
drv.WriteShebang(wrap+"/ar", sh, `exec `+zig+` ar "$@"`)
drv.WriteShebang(wrap+"/strip", sh, `exit 0`)
// The host compiler, for busybox's build-time helpers (fixdep, kconfig).
drv.WriteShebang(wrap+"/hostcc", sh, `exec `+zig+` cc "$@"`)
mk := func(args ...string) {
jobs := drv.Env("NIX_BUILD_CORES")
if jobs == "0" {
jobs = strconv.Itoa(runtime.NumCPU())
}
base := []string{"-j" + jobs,
"CC=" + wrap + "/cc", "AR=" + wrap + "/ar", "STRIP=" + wrap + "/strip",
"HOSTCC=" + wrap + "/hostcc",
"CROSS_COMPILE=",
// Already stripped at link time (-s in EXTRA_CFLAGS); the strip
// stub would not produce the copy the Makefile expects.
"SKIP_STRIP=y",
}
drv.RunIn(dir, drv.Env("gnumake")+"/bin/make", append(base, args...)...)
}
mk("defconfig")
// Static, size-optimised, and without what does not build against
// musl or is meaningless here.
conf := filepath.Join(dir, ".config")
b, err := os.ReadFile(conf)
drv.Check(err)
c := string(b)
set := func(key, val string) {
lines := strings.Split(c, "\n")
found := false
for i, l := range lines {
if strings.HasPrefix(l, key+"=") || l == "# "+key+" is not set" {
lines[i] = key + "=" + val
if val == "n" {
lines[i] = "# " + key + " is not set"
}
found = true
}
}
if !found {
lines = append(lines, key+"="+val)
}
c = strings.Join(lines, "\n")
}
set("CONFIG_STATIC", "y")
// BB_GLOBAL_CONST: busybox declares its globals pointers (ptr_to_globals
// and friends) const, to let gcc keep them in a register, and assigns
// them once at applet start. zig cc is clang, which then reuses a load of
// the pointer from before the assignment: top crashed reading NULL+0x118.
// Empty makes them plain pointers.
set("CONFIG_EXTRA_CFLAGS", `"-Os -s -DBB_GLOBAL_CONST="`)
// tc and the console-tools ioctls need kernel headers musl/zig lack.
for _, k := range []string{"CONFIG_TC", "CONFIG_FEATURE_TC_INGRESS", "CONFIG_SELINUX",
"CONFIG_PAM", "CONFIG_FEATURE_HAVE_RPC", "CONFIG_FEATURE_INETD_RPC",
"CONFIG_FEATURE_SYSTEMD", "CONFIG_FEATURE_MOUNT_NFS",
// x86 SHA-NI code paths: defconfig enables them for the build host.
"CONFIG_SHA1_HWACCEL", "CONFIG_SHA256_HWACCEL"} {
set(k, "n")
}
drv.Write(conf, c, 0o644)
mk("oldconfig")
mk("busybox")
out := drv.Out()
drv.Mkdir(out + "/bin")
drv.CopyFile(dir+"/busybox", out+"/bin/busybox", 0o755)
// The applet list, one name per line, for the system builder to link.
// From the build's busybox.links: the binary is ARM and cannot run here.
mk("busybox.links")
al, err := os.ReadFile(dir + "/busybox.links")
drv.Check(err)
var names []string
for _, l := range strings.Fields(string(al)) {
names = append(names, filepath.Base(l))
}
drv.Write(out+"/applets", strings.Join(names, "\n")+"\n", 0o644)
}
|