Note: the original README.md has been moved here.
Because Golang should be as ubiquitous as C :)
A github issue was created back in 2017.
In the golang-dev thread "how to port golang to a new architecture", Aram Hăvărneanu posted a plan for porting Golang:
I've done many ports now, so the strategy I use
is this (very simplified):
1. Add GOOS/GOARCH support to the toolchain
2. Add some support for GOARCH in cmd/internal/obj
3. Add some support for GOARCH in cmd/asm
4. Add some support for GOOS/GOARCH in cmd/link
5. Iterate through 2-3-4 until you can produse some kind
of binaries from assembly files. Depending on the specifics
of GOOS/GOARCH you might, or might not need to use external
linking.
6. Once you can produce binaries, thoroughly test them (link
just assembly programs, without runtime). Basically make
sure the low-level toolchain works.
7. Start working on the Go compiler for GOARCH.
8. Write a minimal alternative runtime for Go. The runtime
is much too complicated as a first test Go program. Basically
write your own runtime in assembly that is just stubbed
out, but can run a good bulk of the programs in go/test.
9. Once that works well enough, start using the real runtime.
This requires implementing a lot of assembly, but you can
use the lessons learned from #8.
10. Make all the tests in go/test work.
11. Make all the stdlib tests work. You are still working
amd64 now, and executing on GOARCH with go_GOOS_GOARCH_exec.
12. Run bootstrap.bash
13. Move over the artifacts on GOOS/GOARCH machine.
14. Make sure make.bash works. You will still likely have
to fix problems to make this N-generation compiler work.
15. Make sure all.bash works.
16. Done.
As you can see, steps 1-14 are done on amd64 (or some other supported
platform), and only step 15 is done on the target architecture.
Let's start at the beginning:
$ cd src
$ cat > build-linux-ppc.sh <<EOF
#!/bin/bash
set -e
export GOOS=linux
export GOARCH=ppc
export GOROOT_FINAL=/opt/go-linux-ppc
./make.bash -v
EOF
$ chmod +x build-linux-ppc.sh
$ ./build-linux-ppc.sh
Our first error:
$ ./build-linux-ppc.sh
cmd/go: unsupported GOOS/GOARCH pair linux/ppc
Building Go cmd/dist using /usr/local/go. ()
cmd/dist
go tool dist: unknown $GOARCH ppc
The first error comes from src/cmd/go/internal/work/action.go
, in func CheckGOOSARCHPair
.
In src/cmd/dist/build.go
, add ppc
to the okgoarch
array.
Next failure:
Building packages and commands for target, linux/ppc.
cmd/go: unsupported GOOS/GOARCH pair linux/ppc
go tool dist: FAILED: /home/cell/github/pepaslabs/go-ppc/pkg/tool/linux_amd64/go_bootstrap install -gcflags=all= -ldflags=all= -v std cmd: exit status 2
CheckGOOSARCHPair
checks the map OSArchSupportsCgo
in src/cmd/go/internal/cfg/zosarch.go
.
We need to add "linux/ppc": true
to this map, but the first line of the file notes Code generated by go tool dist; DO NOT EDIT..
zosarch.go
is generated by the function mkzosarch
in src/cmd/dist/buildgo.go
.
Running go tool dist
gets us:
$ go tool dist
usage: go tool dist [command]
Commands are:
banner print installation banner
bootstrap rebuild everything
clean deletes all built files
env [-p] print environment (-p: include $PATH)
install [dir] install individual directory
list [-json] list all supported platforms
test [-h] run Go test(s)
version print Go version
All commands take -v flags to emit extra information.
Let's try go tool dist bootstrap
:
$ go tool dist bootstrap -v
go tool dist: mkdir /usr/local/go/pkg/obj/go-build: permission denied
Perhaps as root?
# go tool dist bootstrap -v
Building Go toolchain1 using /root/go1.4.
go tool dist: FAILED: /root/go1.4/bin/go install -gcflags=-l -tags=math_big_pure_go compiler_bootstrap -v bootstrap/cmd/...: fork/exec /root/go1.4/bin/go: no such file or directory
Perhaps I just need to run make.bash
?
$ ./make.bash
Building Go cmd/dist using /usr/local/go. (go1.14.2 linux/amd64)
Building Go toolchain1 using /usr/local/go.
Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1.
Building Go toolchain2 using go_bootstrap and Go toolchain1.
Building Go toolchain3 using go_bootstrap and Go toolchain2.
Building packages and commands for linux/amd64.
---
Installed Go for linux/amd64 in /home/cell/github/pepaslabs/go-ppc
Installed commands in /home/cell/github/pepaslabs/go-ppc/bin
That seemed to work...
But zosarch.go still hasn't been updated:
$ cat cmd/go/internal/cfg/zosarch.go | grep ppc
"aix/ppc64": true,
"linux/ppc64": false,
"linux/ppc64le": true,
Welp, let's try editing zosarch.go
by hand.
$ ./build-linux-ppc.sh
...
Building packages and commands for target, linux/ppc.
cmd/go: unsupported GOOS/GOARCH pair linux/ppc
go tool dist: FAILED: /home/cell/github/pepaslabs/go-ppc/pkg/tool/linux_amd64/go_bootstrap install -gcflags=all= -ldflags=all= -v std cmd: exit status 2
Hmm.
Ok, so my current go
binary doesn't support linux/ppc
, and it is what is failing:
$ go tool dist list | grep ppc
aix/ppc64
linux/ppc64
linux/ppc64le
So first I need to build a go
which doesn't abort as soon as it sees a linux/ppc
GOOS/GOARCH
.
Let's try GOOS=linux GOARCH=ppc ./bootstrap.bash
.
$ GOOS=linux GOARCH=ppc ./bootstrap.bash
#### Copying to ../../go-linux-ppc-bootstrap
#### Cleaning ../../go-linux-ppc-bootstrap
Removing VERSION.cache
Removing bin/
Removing pkg/
Removing src/cmd/cgo/zdefaultcc.go
Removing src/cmd/dist/dist
Removing src/cmd/go/internal/cfg/zdefaultcc.go
Removing src/cmd/go/internal/cfg/zosarch.go
Removing src/cmd/internal/objabi/zbootstrap.go
Removing src/go/build/zcgo.go
Removing src/runtime/internal/sys/zversion.go
#### Building ../../go-linux-ppc-bootstrap
cmd/go: unsupported GOOS/GOARCH pair linux/ppc
Building Go cmd/dist using /usr/local/go. ()
Building Go toolchain1 using /usr/local/go.
Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1.
Building Go toolchain2 using go_bootstrap and Go toolchain1.
Building Go toolchain3 using go_bootstrap and Go toolchain2.
Building packages and commands for host, linux/amd64.
Building packages and commands for target, linux/ppc.
cmd/go: unsupported GOOS/GOARCH pair linux/ppc
go tool dist: FAILED: /home/cell/github/pepaslabs/go-linux-ppc-bootstrap/pkg/tool/linux_amd64/go_bootstrap install -gcflags=all= -ldflags=all= std cmd: exit status 2
Also, zosarch.go
has been reset and no longer contains linux/ppc
:
$ cat cmd/go/internal/cfg/zosarch.go | grep ppc
"aix/ppc64": true,
"linux/ppc64": false,
"linux/ppc64le": true,
Ah, in src/cmd/dist/build.go
, we see this:
// Cannot use go/build directly because cmd/dist for a new release
// builds against an old release's go/build, which may be out of sync.
// To reduce duplication, we generate the list for go/build from this.
//
// We list all supported platforms in this list, so that this is the
// single point of truth for supported platforms. This list is used
// by 'go tool dist list'.
var cgoEnabled = map[string]bool{
Now let's try bootstrapping again.
$ GOOS=linux GOARCH=ppc ./bootstrap.bash
../../go-linux-ppc-bootstrap already exists; remove before continuing
Ok, let's clean that up and try again.
$ rm -rf ../../go-linux-ppc-bootstrap
$ GOOS=linux GOARCH=ppc ./bootstrap.bash
Building packages and commands for host, linux/amd64.
Building packages and commands for target, linux/ppc.
go tool compile: exit status 2
compile: unknown architecture "ppc"
go tool dist: FAILED: /home/cell/github/pepaslabs/go-linux-ppc-bootstrap/pkg/tool/linux_amd64/go_bootstrap install -gcflags=all= -ldflags=all= std cmd: exit status 1
Ok, perhaps I first need to bootstrap go
for my own platform (with updated cgoEnabled
map),
and then use that to build go
for linux/ppc
.
$ GOOS=linux GOARCH=amd64 ./bootstrap.bash
...
Bootstrap toolchain for linux/amd64 installed in /home/cell/github/pepaslabs/go-linux-amd64-bootstrap.
Building tbz.
-rw-r--r-- 1 cell cell 122118427 May 2 15:30 /home/cell/github/pepaslabs/go-linux-amd64-bootstrap.tbz
$ ../../go-linux-amd64-bootstrap/bin/go tool dist list | grep ppc
aix/ppc64
linux/ppc
linux/ppc64
linux/ppc64le
Ok! Got past the first build error :)
I'll prepend that bootstrap toolchain to my $PATH
and use it while working on this project.
$ export PATH="/home/cell/github/pepaslabs/go-linux-amd64-bootstrap/bin:$PATH"
$ which go
/home/cell/github/pepaslabs/go-linux-amd64-bootstrap/bin/go
$ go tool dist list | grep ppc
aix/ppc64
linux/ppc
linux/ppc64
linux/ppc64le
Ok, next failure:
$ ./build-linux-ppc.sh
...
Building packages and commands for target, linux/ppc.
go tool compile: exit status 2
compile: unknown architecture "ppc"
go tool compile: exit status 2
compile: unknown architecture "ppc"
go tool dist: FAILED: /home/cell/github/pepaslabs/go-ppc/pkg/tool/linux_amd64/go_bootstrap install -gcflags=all= -ldflags=all= -v std cmd: exit status 1
This comes from cmd/compile/main.go
:
$ grep -n -r 'compile: unknown architec' .
./cmd/compile/main.go:48: fmt.Fprintf(os.Stderr, "compile: unknown architecture %q\n", objabi.GOARCH)
Oh, hmm, I can now run ./make.bash
, which installs a go
into ./bin
which has linux/ppc
in its cgoEnabled
map:
$ ./make.bash
...
$ ../bin/go tool dist list | grep ppc
aix/ppc64
linux/ppc
linux/ppc64
linux/ppc64le
So now I no longer need the bootstrap toolchain in go-linux-amd64-bootstrap
.
I'll start a new shell and export PATH="/home/cell/github/pepaslabs/go-ppc/bin:$PATH"
.
$ export PATH="/home/cell/github/pepaslabs/go-ppc/bin:$PATH"
$ go tool dist list | grep ppc
aix/ppc64
linux/ppc
linux/ppc64
linux/ppc64le
Now I should be able to edit the golang source and simply run ./make.bash
.
Returning to our error, we need to add an entry to the archInits
map in src/cmd/compile/main.go
:
"ppc": ppc.Init,
We also need to add an import entry to the same file:
import (
"cmd/compile/internal/ppc"
...
We also need to create a new go module at src/cmd/compile/internal/ppc
.
We'll start by copying the ppc64
directory.
Edit each of the files in src/cmd/compile/internal/ppc
,
making the obvious trivial ppc64
to ppc
translations.
In src/cmd/compile/internal/ppc/galign.go
, we need to figure out what to use for arch.MAXWIDTH
.
Let's take a look at what the other arches do:
$ grep MAXWIDTH */galign.go
amd64/galign.go: arch.MAXWIDTH = 1 << 50
arm64/galign.go: arch.MAXWIDTH = 1 << 50
arm/galign.go: arch.MAXWIDTH = (1 << 32) - 1
mips64/galign.go: arch.MAXWIDTH = 1 << 50
mips/galign.go: arch.MAXWIDTH = (1 << 31) - 1
ppc64/galign.go: arch.MAXWIDTH = 1 << 60
ppc/galign.go: arch.MAXWIDTH = 1 << 60
riscv64/galign.go: arch.MAXWIDTH = 1 << 50
s390x/galign.go: arch.MAXWIDTH = 1 << 50
x86/galign.go: arch.MAXWIDTH = (1 << 32) - 1
Interesting, I wonder why mips is shifted only 31 times, while arm and x86 are shifted 32 times? I'll assum ppc should be the same as x86 and arm.
Ok, next error:
$ GOOS=linux GOARCH=ppc GOROOT_FINAL=/opt/go-linux-ppc ./make.bash -v
cmd/go: unsupported GOOS/GOARCH pair linux/ppc
Building Go cmd/dist using /usr/local/go. ()
cmd/dist
Building Go toolchain1 using /usr/local/go.
../../../../src/cmd/compile/main.go:14: package bootstrap/cmd/compile/internal/ppc is not in GOROOT (/usr/local/go/src/bootstrap/cmd/compile/internal/ppc)
go tool dist: FAILED: /usr/local/go/bin/go install -gcflags=-l -tags=math_big_pure_go compiler_bootstrap -v bootstrap/cmd/...: exit status 1
Ok, we need to similarly clone src/cmd/internal/obj/ppc64
to ppc
.