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.
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.
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>
26 // These will be paths to the appropriate source directories.
30 const TempDir = "/tmp"
31 const IncludeDir = TempDir + "/include" // To hold our C headers
32 const BuildDir = TempDir + "/build" // To hold intermediate build files
34 const GOOS = "linux" // Only for Linux targets
35 const BuildArch = "amd64" // Must be built on this architecture
36 const MinKernel = "2.6.23" // https://golang.org/doc/install#requirements
39 GoArch string // Architecture name according to Go
40 LinuxArch string // Architecture name according to the Linux Kernel
41 GNUArch string // Architecture name according to GNU tools (https://wiki.debian.org/Multiarch/Tuples)
42 BigEndian bool // Default Little Endian
43 SignedChar bool // Is -fsigned-char needed (default no)
47 // List of the 11 Linux targets supported by the go compiler. sparc64 is not
48 // currently supported, though a port is in progress.
49 var targets = []target{
53 GNUArch: "i686-linux-gnu", // Note "i686" not "i386"
59 GNUArch: "x86_64-linux-gnu",
65 GNUArch: "aarch64-linux-gnu",
72 GNUArch: "arm-linux-gnueabi",
78 GNUArch: "mips-linux-gnu",
85 GNUArch: "mipsel-linux-gnu",
91 GNUArch: "mips64-linux-gnuabi64",
98 GNUArch: "mips64el-linux-gnuabi64",
103 LinuxArch: "powerpc",
104 GNUArch: "powerpc64-linux-gnu",
110 LinuxArch: "powerpc",
111 GNUArch: "powerpc64le-linux-gnu",
117 GNUArch: "s390x-linux-gnu",
123 // GoArch: "sparc64",
124 // LinuxArch: "sparc",
125 // GNUArch: "sparc64-linux-gnu",
132 if runtime.GOOS != GOOS || runtime.GOARCH != BuildArch {
133 fmt.Printf("Build system has GOOS_GOARCH = %s_%s, need %s_%s\n",
134 runtime.GOOS, runtime.GOARCH, GOOS, BuildArch)
138 // Check that we are using the new build system if we should
139 if os.Getenv("GOLANG_SYS_BUILD") != "docker" {
140 fmt.Println("In the new build system, mkall.go should not be called directly.")
141 fmt.Println("See README.md")
145 // Parse the command line options
146 if len(os.Args) != 3 {
147 fmt.Println("USAGE: go run linux/mkall.go <linux_dir> <glibc_dir>")
150 LinuxDir = os.Args[1]
151 GlibcDir = os.Args[2]
153 for _, t := range targets {
154 fmt.Printf("----- GENERATING: %s -----\n", t.GoArch)
155 if err := t.generateFiles(); err != nil {
156 fmt.Printf("%v\n***** FAILURE: %s *****\n\n", err, t.GoArch)
158 fmt.Printf("----- SUCCESS: %s -----\n\n", t.GoArch)
163 // Makes an exec.Cmd with Stderr attached to os.Stderr
164 func makeCommand(name string, args ...string) *exec.Cmd {
165 cmd := exec.Command(name, args...)
166 cmd.Stderr = os.Stderr
170 // Runs the command, pipes output to a formatter, pipes that to an output file.
171 func (t *target) commandFormatOutput(formatter string, outputFile string,
172 name string, args ...string) (err error) {
173 mainCmd := makeCommand(name, args...)
175 fmtCmd := makeCommand(formatter)
176 if formatter == "mkpost" {
177 fmtCmd = makeCommand("go", "run", "mkpost.go")
178 // Set GOARCH_TARGET so mkpost knows what GOARCH is..
179 fmtCmd.Env = append(os.Environ(), "GOARCH_TARGET="+t.GoArch)
180 // Set GOARCH to host arch for mkpost, so it can run natively.
181 for i, s := range fmtCmd.Env {
182 if strings.HasPrefix(s, "GOARCH=") {
183 fmtCmd.Env[i] = "GOARCH=" + BuildArch
188 // mainCmd | fmtCmd > outputFile
189 if fmtCmd.Stdin, err = mainCmd.StdoutPipe(); err != nil {
192 if fmtCmd.Stdout, err = os.Create(outputFile); err != nil {
196 // Make sure the formatter eventually closes
197 if err = fmtCmd.Start(); err != nil {
201 fmtErr := fmtCmd.Wait()
210 // Generates all the files for a Linux target
211 func (t *target) generateFiles() error {
212 // Setup environment variables
213 os.Setenv("GOOS", GOOS)
214 os.Setenv("GOARCH", t.GoArch)
216 // Get appropriate compiler and emulator (unless on x86)
217 if t.LinuxArch != "x86" {
218 // Check/Setup cross compiler
219 compiler := t.GNUArch + "-gcc"
220 if _, err := exec.LookPath(compiler); err != nil {
223 os.Setenv("CC", compiler)
225 // Check/Setup emulator (usually first component of GNUArch)
226 qemuArchName := t.GNUArch[:strings.Index(t.GNUArch, "-")]
227 if t.LinuxArch == "powerpc" {
228 qemuArchName = t.GoArch
230 os.Setenv("GORUN", "qemu-"+qemuArchName)
232 os.Setenv("CC", "gcc")
235 // Make the include directory and fill it with headers
236 if err := os.MkdirAll(IncludeDir, os.ModePerm); err != nil {
239 defer os.RemoveAll(IncludeDir)
240 if err := t.makeHeaders(); err != nil {
241 return fmt.Errorf("could not make header files: %v", err)
243 fmt.Println("header files generated")
245 // Make each of the four files
246 if err := t.makeZSysnumFile(); err != nil {
247 return fmt.Errorf("could not make zsysnum file: %v", err)
249 fmt.Println("zsysnum file generated")
251 if err := t.makeZSyscallFile(); err != nil {
252 return fmt.Errorf("could not make zsyscall file: %v", err)
254 fmt.Println("zsyscall file generated")
256 if err := t.makeZTypesFile(); err != nil {
257 return fmt.Errorf("could not make ztypes file: %v", err)
259 fmt.Println("ztypes file generated")
261 if err := t.makeZErrorsFile(); err != nil {
262 return fmt.Errorf("could not make zerrors file: %v", err)
264 fmt.Println("zerrors file generated")
269 // Create the Linux and glibc headers in the include directory.
270 func (t *target) makeHeaders() error {
271 // Make the Linux headers we need for this architecture
272 linuxMake := makeCommand("make", "headers_install", "ARCH="+t.LinuxArch, "INSTALL_HDR_PATH="+TempDir)
273 linuxMake.Dir = LinuxDir
274 if err := linuxMake.Run(); err != nil {
278 // A Temporary build directory for glibc
279 if err := os.MkdirAll(BuildDir, os.ModePerm); err != nil {
282 defer os.RemoveAll(BuildDir)
284 // Make the glibc headers we need for this architecture
285 confScript := filepath.Join(GlibcDir, "configure")
286 glibcConf := makeCommand(confScript, "--prefix="+TempDir, "--host="+t.GNUArch, "--enable-kernel="+MinKernel)
287 glibcConf.Dir = BuildDir
288 if err := glibcConf.Run(); err != nil {
291 glibcMake := makeCommand("make", "install-headers")
292 glibcMake.Dir = BuildDir
293 if err := glibcMake.Run(); err != nil {
296 // We only need an empty stubs file
297 stubsFile := filepath.Join(IncludeDir, "gnu/stubs.h")
298 if file, err := os.Create(stubsFile); err != nil {
307 // makes the zsysnum_linux_$GOARCH.go file
308 func (t *target) makeZSysnumFile() error {
309 zsysnumFile := fmt.Sprintf("zsysnum_linux_%s.go", t.GoArch)
310 unistdFile := filepath.Join(IncludeDir, "asm/unistd.h")
312 args := append(t.cFlags(), unistdFile)
313 return t.commandFormatOutput("gofmt", zsysnumFile, "linux/mksysnum.pl", args...)
316 // makes the zsyscall_linux_$GOARCH.go file
317 func (t *target) makeZSyscallFile() error {
318 zsyscallFile := fmt.Sprintf("zsyscall_linux_%s.go", t.GoArch)
319 // Find the correct architecture syscall file (might end with x.go)
320 archSyscallFile := fmt.Sprintf("syscall_linux_%s.go", t.GoArch)
321 if _, err := os.Stat(archSyscallFile); os.IsNotExist(err) {
322 shortArch := strings.TrimSuffix(t.GoArch, "le")
323 archSyscallFile = fmt.Sprintf("syscall_linux_%sx.go", shortArch)
326 args := append(t.mksyscallFlags(), "-tags", "linux,"+t.GoArch,
327 "syscall_linux.go", archSyscallFile)
328 return t.commandFormatOutput("gofmt", zsyscallFile, "./mksyscall.pl", args...)
331 // makes the zerrors_linux_$GOARCH.go file
332 func (t *target) makeZErrorsFile() error {
333 zerrorsFile := fmt.Sprintf("zerrors_linux_%s.go", t.GoArch)
335 return t.commandFormatOutput("gofmt", zerrorsFile, "./mkerrors.sh", t.cFlags()...)
338 // makes the ztypes_linux_$GOARCH.go file
339 func (t *target) makeZTypesFile() error {
340 ztypesFile := fmt.Sprintf("ztypes_linux_%s.go", t.GoArch)
342 args := []string{"tool", "cgo", "-godefs", "--"}
343 args = append(args, t.cFlags()...)
344 args = append(args, "linux/types.go")
345 return t.commandFormatOutput("mkpost", ztypesFile, "go", args...)
348 // Flags that should be given to gcc and cgo for this target
349 func (t *target) cFlags() []string {
350 // Compile statically to avoid cross-architecture dynamic linking.
351 flags := []string{"-Wall", "-Werror", "-static", "-I" + IncludeDir}
353 // Architecture-specific flags
355 flags = append(flags, "-fsigned-char")
357 if t.LinuxArch == "x86" {
358 flags = append(flags, fmt.Sprintf("-m%d", t.Bits))
364 // Flags that should be given to mksyscall for this target
365 func (t *target) mksyscallFlags() (flags []string) {
368 flags = append(flags, "-b32")
370 flags = append(flags, "-l32")
374 // This flag menas a 64-bit value should use (even, odd)-pair.
375 if t.GoArch == "arm" || (t.LinuxArch == "mips" && t.Bits == 32) {
376 flags = append(flags, "-arm")