1// Copyright 2017 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// linux/mkall.go - Generates all Linux zsysnum, zsyscall, zerror, and ztype
6// files for all 11 linux architectures supported by the go compiler. See
7// README.md for more information about the build system.
8
9// To run it you must have a git checkout of the Linux kernel and glibc. Once
10// the appropriate sources are ready, the program is run as:
11//     go run linux/mkall.go <linux_dir> <glibc_dir>
12
13// +build ignore
14
15package main
16
17import (
18	"bufio"
19	"bytes"
20	"fmt"
21	"io"
22	"io/ioutil"
23	"os"
24	"os/exec"
25	"path/filepath"
26	"runtime"
27	"strings"
28	"unicode"
29)
30
31// These will be paths to the appropriate source directories.
32var LinuxDir string
33var GlibcDir string
34
35const TempDir = "/tmp"
36const IncludeDir = TempDir + "/include" // To hold our C headers
37const BuildDir = TempDir + "/build"     // To hold intermediate build files
38
39const GOOS = "linux"       // Only for Linux targets
40const BuildArch = "amd64"  // Must be built on this architecture
41const MinKernel = "2.6.23" // https://golang.org/doc/install#requirements
42
43type target struct {
44	GoArch     string // Architecture name according to Go
45	LinuxArch  string // Architecture name according to the Linux Kernel
46	GNUArch    string // Architecture name according to GNU tools (https://wiki.debian.org/Multiarch/Tuples)
47	BigEndian  bool   // Default Little Endian
48	SignedChar bool   // Is -fsigned-char needed (default no)
49	Bits       int
50}
51
52// List of the 11 Linux targets supported by the go compiler. sparc64 is not
53// currently supported, though a port is in progress.
54var targets = []target{
55	{
56		GoArch:    "386",
57		LinuxArch: "x86",
58		GNUArch:   "i686-linux-gnu", // Note "i686" not "i386"
59		Bits:      32,
60	},
61	{
62		GoArch:    "amd64",
63		LinuxArch: "x86",
64		GNUArch:   "x86_64-linux-gnu",
65		Bits:      64,
66	},
67	{
68		GoArch:     "arm64",
69		LinuxArch:  "arm64",
70		GNUArch:    "aarch64-linux-gnu",
71		SignedChar: true,
72		Bits:       64,
73	},
74	{
75		GoArch:    "arm",
76		LinuxArch: "arm",
77		GNUArch:   "arm-linux-gnueabi",
78		Bits:      32,
79	},
80	{
81		GoArch:    "mips",
82		LinuxArch: "mips",
83		GNUArch:   "mips-linux-gnu",
84		BigEndian: true,
85		Bits:      32,
86	},
87	{
88		GoArch:    "mipsle",
89		LinuxArch: "mips",
90		GNUArch:   "mipsel-linux-gnu",
91		Bits:      32,
92	},
93	{
94		GoArch:    "mips64",
95		LinuxArch: "mips",
96		GNUArch:   "mips64-linux-gnuabi64",
97		BigEndian: true,
98		Bits:      64,
99	},
100	{
101		GoArch:    "mips64le",
102		LinuxArch: "mips",
103		GNUArch:   "mips64el-linux-gnuabi64",
104		Bits:      64,
105	},
106	{
107		GoArch:    "ppc64",
108		LinuxArch: "powerpc",
109		GNUArch:   "powerpc64-linux-gnu",
110		BigEndian: true,
111		Bits:      64,
112	},
113	{
114		GoArch:    "ppc64le",
115		LinuxArch: "powerpc",
116		GNUArch:   "powerpc64le-linux-gnu",
117		Bits:      64,
118	},
119	{
120		GoArch:     "s390x",
121		LinuxArch:  "s390",
122		GNUArch:    "s390x-linux-gnu",
123		BigEndian:  true,
124		SignedChar: true,
125		Bits:       64,
126	},
127	// {
128	// 	GoArch:    "sparc64",
129	// 	LinuxArch: "sparc",
130	// 	GNUArch:   "sparc64-linux-gnu",
131	// 	BigEndian: true,
132	// 	Bits:      64,
133	// },
134}
135
136// ptracePairs is a list of pairs of targets that can, in some cases,
137// run each other's binaries.
138var ptracePairs = []struct{ a1, a2 string }{
139	{"386", "amd64"},
140	{"arm", "arm64"},
141	{"mips", "mips64"},
142	{"mipsle", "mips64le"},
143}
144
145func main() {
146	if runtime.GOOS != GOOS || runtime.GOARCH != BuildArch {
147		fmt.Printf("Build system has GOOS_GOARCH = %s_%s, need %s_%s\n",
148			runtime.GOOS, runtime.GOARCH, GOOS, BuildArch)
149		return
150	}
151
152	// Check that we are using the new build system if we should
153	if os.Getenv("GOLANG_SYS_BUILD") != "docker" {
154		fmt.Println("In the new build system, mkall.go should not be called directly.")
155		fmt.Println("See README.md")
156		return
157	}
158
159	// Parse the command line options
160	if len(os.Args) != 3 {
161		fmt.Println("USAGE: go run linux/mkall.go <linux_dir> <glibc_dir>")
162		return
163	}
164	LinuxDir = os.Args[1]
165	GlibcDir = os.Args[2]
166
167	for _, t := range targets {
168		fmt.Printf("----- GENERATING: %s -----\n", t.GoArch)
169		if err := t.generateFiles(); err != nil {
170			fmt.Printf("%v\n***** FAILURE:    %s *****\n\n", err, t.GoArch)
171		} else {
172			fmt.Printf("----- SUCCESS:    %s -----\n\n", t.GoArch)
173		}
174	}
175
176	fmt.Printf("----- GENERATING ptrace pairs -----\n")
177	ok := true
178	for _, p := range ptracePairs {
179		if err := generatePtracePair(p.a1, p.a2); err != nil {
180			fmt.Printf("%v\n***** FAILURE: %s/%s *****\n\n", err, p.a1, p.a2)
181			ok = false
182		}
183	}
184	if ok {
185		fmt.Printf("----- SUCCESS ptrace pairs    -----\n\n")
186	}
187}
188
189// Makes an exec.Cmd with Stderr attached to os.Stderr
190func makeCommand(name string, args ...string) *exec.Cmd {
191	cmd := exec.Command(name, args...)
192	cmd.Stderr = os.Stderr
193	return cmd
194}
195
196// Runs the command, pipes output to a formatter, pipes that to an output file.
197func (t *target) commandFormatOutput(formatter string, outputFile string,
198	name string, args ...string) (err error) {
199	mainCmd := makeCommand(name, args...)
200
201	fmtCmd := makeCommand(formatter)
202	if formatter == "mkpost" {
203		fmtCmd = makeCommand("go", "run", "mkpost.go")
204		// Set GOARCH_TARGET so mkpost knows what GOARCH is..
205		fmtCmd.Env = append(os.Environ(), "GOARCH_TARGET="+t.GoArch)
206		// Set GOARCH to host arch for mkpost, so it can run natively.
207		for i, s := range fmtCmd.Env {
208			if strings.HasPrefix(s, "GOARCH=") {
209				fmtCmd.Env[i] = "GOARCH=" + BuildArch
210			}
211		}
212	}
213
214	// mainCmd | fmtCmd > outputFile
215	if fmtCmd.Stdin, err = mainCmd.StdoutPipe(); err != nil {
216		return
217	}
218	if fmtCmd.Stdout, err = os.Create(outputFile); err != nil {
219		return
220	}
221
222	// Make sure the formatter eventually closes
223	if err = fmtCmd.Start(); err != nil {
224		return
225	}
226	defer func() {
227		fmtErr := fmtCmd.Wait()
228		if err == nil {
229			err = fmtErr
230		}
231	}()
232
233	return mainCmd.Run()
234}
235
236// Generates all the files for a Linux target
237func (t *target) generateFiles() error {
238	// Setup environment variables
239	os.Setenv("GOOS", GOOS)
240	os.Setenv("GOARCH", t.GoArch)
241
242	// Get appropriate compiler and emulator (unless on x86)
243	if t.LinuxArch != "x86" {
244		// Check/Setup cross compiler
245		compiler := t.GNUArch + "-gcc"
246		if _, err := exec.LookPath(compiler); err != nil {
247			return err
248		}
249		os.Setenv("CC", compiler)
250
251		// Check/Setup emulator (usually first component of GNUArch)
252		qemuArchName := t.GNUArch[:strings.Index(t.GNUArch, "-")]
253		if t.LinuxArch == "powerpc" {
254			qemuArchName = t.GoArch
255		}
256		os.Setenv("GORUN", "qemu-"+qemuArchName)
257	} else {
258		os.Setenv("CC", "gcc")
259	}
260
261	// Make the include directory and fill it with headers
262	if err := os.MkdirAll(IncludeDir, os.ModePerm); err != nil {
263		return err
264	}
265	defer os.RemoveAll(IncludeDir)
266	if err := t.makeHeaders(); err != nil {
267		return fmt.Errorf("could not make header files: %v", err)
268	}
269	fmt.Println("header files generated")
270
271	// Make each of the four files
272	if err := t.makeZSysnumFile(); err != nil {
273		return fmt.Errorf("could not make zsysnum file: %v", err)
274	}
275	fmt.Println("zsysnum file generated")
276
277	if err := t.makeZSyscallFile(); err != nil {
278		return fmt.Errorf("could not make zsyscall file: %v", err)
279	}
280	fmt.Println("zsyscall file generated")
281
282	if err := t.makeZTypesFile(); err != nil {
283		return fmt.Errorf("could not make ztypes file: %v", err)
284	}
285	fmt.Println("ztypes file generated")
286
287	if err := t.makeZErrorsFile(); err != nil {
288		return fmt.Errorf("could not make zerrors file: %v", err)
289	}
290	fmt.Println("zerrors file generated")
291
292	return nil
293}
294
295// Create the Linux and glibc headers in the include directory.
296func (t *target) makeHeaders() error {
297	// Make the Linux headers we need for this architecture
298	linuxMake := makeCommand("make", "headers_install", "ARCH="+t.LinuxArch, "INSTALL_HDR_PATH="+TempDir)
299	linuxMake.Dir = LinuxDir
300	if err := linuxMake.Run(); err != nil {
301		return err
302	}
303
304	// A Temporary build directory for glibc
305	if err := os.MkdirAll(BuildDir, os.ModePerm); err != nil {
306		return err
307	}
308	defer os.RemoveAll(BuildDir)
309
310	// Make the glibc headers we need for this architecture
311	confScript := filepath.Join(GlibcDir, "configure")
312	glibcConf := makeCommand(confScript, "--prefix="+TempDir, "--host="+t.GNUArch, "--enable-kernel="+MinKernel)
313	glibcConf.Dir = BuildDir
314	if err := glibcConf.Run(); err != nil {
315		return err
316	}
317	glibcMake := makeCommand("make", "install-headers")
318	glibcMake.Dir = BuildDir
319	if err := glibcMake.Run(); err != nil {
320		return err
321	}
322	// We only need an empty stubs file
323	stubsFile := filepath.Join(IncludeDir, "gnu/stubs.h")
324	if file, err := os.Create(stubsFile); err != nil {
325		return err
326	} else {
327		file.Close()
328	}
329
330	return nil
331}
332
333// makes the zsysnum_linux_$GOARCH.go file
334func (t *target) makeZSysnumFile() error {
335	zsysnumFile := fmt.Sprintf("zsysnum_linux_%s.go", t.GoArch)
336	unistdFile := filepath.Join(IncludeDir, "asm/unistd.h")
337
338	args := append(t.cFlags(), unistdFile)
339	return t.commandFormatOutput("gofmt", zsysnumFile, "linux/mksysnum.pl", args...)
340}
341
342// makes the zsyscall_linux_$GOARCH.go file
343func (t *target) makeZSyscallFile() error {
344	zsyscallFile := fmt.Sprintf("zsyscall_linux_%s.go", t.GoArch)
345	// Find the correct architecture syscall file (might end with x.go)
346	archSyscallFile := fmt.Sprintf("syscall_linux_%s.go", t.GoArch)
347	if _, err := os.Stat(archSyscallFile); os.IsNotExist(err) {
348		shortArch := strings.TrimSuffix(t.GoArch, "le")
349		archSyscallFile = fmt.Sprintf("syscall_linux_%sx.go", shortArch)
350	}
351
352	args := append(t.mksyscallFlags(), "-tags", "linux,"+t.GoArch,
353		"syscall_linux.go", archSyscallFile)
354	return t.commandFormatOutput("gofmt", zsyscallFile, "./mksyscall.pl", args...)
355}
356
357// makes the zerrors_linux_$GOARCH.go file
358func (t *target) makeZErrorsFile() error {
359	zerrorsFile := fmt.Sprintf("zerrors_linux_%s.go", t.GoArch)
360
361	return t.commandFormatOutput("gofmt", zerrorsFile, "./mkerrors.sh", t.cFlags()...)
362}
363
364// makes the ztypes_linux_$GOARCH.go file
365func (t *target) makeZTypesFile() error {
366	ztypesFile := fmt.Sprintf("ztypes_linux_%s.go", t.GoArch)
367
368	args := []string{"tool", "cgo", "-godefs", "--"}
369	args = append(args, t.cFlags()...)
370	args = append(args, "linux/types.go")
371	return t.commandFormatOutput("mkpost", ztypesFile, "go", args...)
372}
373
374// Flags that should be given to gcc and cgo for this target
375func (t *target) cFlags() []string {
376	// Compile statically to avoid cross-architecture dynamic linking.
377	flags := []string{"-Wall", "-Werror", "-static", "-I" + IncludeDir}
378
379	// Architecture-specific flags
380	if t.SignedChar {
381		flags = append(flags, "-fsigned-char")
382	}
383	if t.LinuxArch == "x86" {
384		flags = append(flags, fmt.Sprintf("-m%d", t.Bits))
385	}
386
387	return flags
388}
389
390// Flags that should be given to mksyscall for this target
391func (t *target) mksyscallFlags() (flags []string) {
392	if t.Bits == 32 {
393		if t.BigEndian {
394			flags = append(flags, "-b32")
395		} else {
396			flags = append(flags, "-l32")
397		}
398	}
399
400	// This flag menas a 64-bit value should use (even, odd)-pair.
401	if t.GoArch == "arm" || (t.LinuxArch == "mips" && t.Bits == 32) {
402		flags = append(flags, "-arm")
403	}
404	return
405}
406
407// generatePtracePair takes a pair of GOARCH values that can run each
408// other's binaries, such as 386 and amd64. It extracts the PtraceRegs
409// type for each one. It writes a new file defining the types
410// PtraceRegsArch1 and PtraceRegsArch2 and the corresponding functions
411// Ptrace{Get,Set}Regs{arch1,arch2}. This permits debugging the other
412// binary on a native system.
413func generatePtracePair(arch1, arch2 string) error {
414	def1, err := ptraceDef(arch1)
415	if err != nil {
416		return err
417	}
418	def2, err := ptraceDef(arch2)
419	if err != nil {
420		return err
421	}
422	f, err := os.Create(fmt.Sprintf("zptrace%s_linux.go", arch1))
423	if err != nil {
424		return err
425	}
426	buf := bufio.NewWriter(f)
427	fmt.Fprintf(buf, "// Code generated by linux/mkall.go generatePtracePair(%s, %s). DO NOT EDIT.\n", arch1, arch2)
428	fmt.Fprintf(buf, "\n")
429	fmt.Fprintf(buf, "// +build linux\n")
430	fmt.Fprintf(buf, "// +build %s %s\n", arch1, arch2)
431	fmt.Fprintf(buf, "\n")
432	fmt.Fprintf(buf, "package unix\n")
433	fmt.Fprintf(buf, "\n")
434	fmt.Fprintf(buf, "%s\n", `import "unsafe"`)
435	fmt.Fprintf(buf, "\n")
436	writeOnePtrace(buf, arch1, def1)
437	fmt.Fprintf(buf, "\n")
438	writeOnePtrace(buf, arch2, def2)
439	if err := buf.Flush(); err != nil {
440		return err
441	}
442	if err := f.Close(); err != nil {
443		return err
444	}
445	return nil
446}
447
448// ptraceDef returns the definition of PtraceRegs for arch.
449func ptraceDef(arch string) (string, error) {
450	filename := fmt.Sprintf("ztypes_linux_%s.go", arch)
451	data, err := ioutil.ReadFile(filename)
452	if err != nil {
453		return "", fmt.Errorf("reading %s: %v", filename, err)
454	}
455	start := bytes.Index(data, []byte("type PtraceRegs struct"))
456	if start < 0 {
457		return "", fmt.Errorf("%s: no definition of PtraceRegs", filename)
458	}
459	data = data[start:]
460	end := bytes.Index(data, []byte("\n}\n"))
461	if end < 0 {
462		return "", fmt.Errorf("%s: can't find end of PtraceRegs definition", filename)
463	}
464	return string(data[:end+2]), nil
465}
466
467// writeOnePtrace writes out the ptrace definitions for arch.
468func writeOnePtrace(w io.Writer, arch, def string) {
469	uarch := string(unicode.ToUpper(rune(arch[0]))) + arch[1:]
470	fmt.Fprintf(w, "// PtraceRegs%s is the registers used by %s binaries.\n", uarch, arch)
471	fmt.Fprintf(w, "%s\n", strings.Replace(def, "PtraceRegs", "PtraceRegs"+uarch, 1))
472	fmt.Fprintf(w, "\n")
473	fmt.Fprintf(w, "// PtraceGetRegs%s fetches the registers used by %s binaries.\n", uarch, arch)
474	fmt.Fprintf(w, "func PtraceGetRegs%s(pid int, regsout *PtraceRegs%s) error {\n", uarch, uarch)
475	fmt.Fprintf(w, "\treturn ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout)))\n")
476	fmt.Fprintf(w, "}\n")
477	fmt.Fprintf(w, "\n")
478	fmt.Fprintf(w, "// PtraceSetRegs%s sets the registers used by %s binaries.\n", uarch, arch)
479	fmt.Fprintf(w, "func PtraceSetRegs%s(pid int, regs *PtraceRegs%s) error {\n", uarch, uarch)
480	fmt.Fprintf(w, "\treturn ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs)))\n")
481	fmt.Fprintf(w, "}\n")
482}
483