mkall.go 14 KB

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