Flops
Making go1.12 fly on OS X Mavericks (10.9.5)
15 May 2019
If you are like me and just cannot live without having go1.12 run on your trusty Mavericks, you need
to install libMacportsLegacySupport
from project MacPorts. Check whether you already
have it installed:
$ ls -la /usr/local/lib/*cySu* -rw-r--r-- 1 root wheel 19848 May 11 07:47 /usr/local/lib/libMacportsLegacySupport.a -rwxr-xr-x 1 root wheel 19012 May 11 07:47 /usr/local/lib/libMacportsLegacySupport.dylib
I initially had not. As I am not letting gigabytes of useless crap to waste space on my hard disk,
I installed
macports-legacy-support
directly:
$ mkdir macports $ ( cd macports/ && git clone https://github.com/macports/macports-legacy-support ) Cloning into 'macports-legacy-support'... remote: Enumerating objects: 32, done. remote: Counting objects: 100% (32/32), done. remote: Compressing objects: 100% (28/28), done. remote: Total 625 (delta 14), reused 8 (delta 3), pack-reused 593 Receiving objects: 100% (625/625), 156.30 KiB | 0 bytes/s, done. Resolving deltas: 100% (394/394), done. Checking connectivity... done. $ ( cd macports/macports-legacy-support/ && sudo make install ) >& macports-legacy-support.install.log $ ls -la /usr/local/lib/*cySu*
Alternatively, you can consult with project MacPorts on how to install legacy support for OS X.
You need a working version of Golang to bootstrap go1.12
. I used /usr/local/go1.9.7
,
however your version may be different.
Examples that follow work best if you begin in your home directory, your shell is /bin/bash
and your GOHOME
is default $HOME/go
.
Parte 1
Now that libMacportsLegacySupport
was installed, I used
ticket and
patch from MacPorts to get working version of go1.12
for a starter. I created and downloaded a
fork of golang/go:
$ /usr/local/go1.9.7/bin/go get github.com/av86743/go package github.com/av86743/go: no Go files in /Users/ubuntu/go/src/github.com/av86743/go $
The patched code is now on the branch
go1.12.5-osx-legacy-take1
of this fork. After certain
tweaking of environment variables, suitable build incantation turned out to be this:
$ ( cd go/src/github.com/av86743/go/ && git checkout go1.12.5-osx-legacy-take1 ) Checking out files: 100% (10463/10463), done. Previous HEAD position was 6174b5e... go1 Branch go1.12.5-osx-legacy-take1 set up to track remote branch go1.12.5-osx-legacy-take1 from origin. Switched to a new branch 'go1.12.5-osx-legacy-take1' $ ( cd go/src/github.com/av86743/go/ && cd src && configure_ldflags='-L/usr/local/lib -lMacportsLegacySupport' && env GOROOT_BOOTSTRAP=/usr/local/go1.9.7 GO_EXTLINK_ENABLED=1 GO_LDFLAGS='"-extldflags='"${configure_ldflags}"'"' BOOT_GO_LDFLAGS='-extldflags='"${configure_ldflags}"'' ./make.bash ) Building Go cmd/dist using /usr/local/go1.9.7. Building Go toolchain1 using /usr/local/go1.9.7. Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1. clang: warning: argument unused during compilation: '-nopie' Building Go toolchain2 using go_bootstrap and Go toolchain1. Building Go toolchain3 using go_bootstrap and Go toolchain2. Building packages and commands for darwin/amd64. --- Installed Go for darwin/amd64 in /Users/ubuntu/go/src/github.com/av86743/go Installed commands in /Users/ubuntu/go/src/github.com/av86743/go/bin $
Everything seemed to be in order, so far.
Parte 2
Original instructions to install golang from sources actually invoke
all.bash
. all.bash
invokes make.bash
and after that run.bash
.
It is the latter that runs golang tests. I replaced make.bash
with
all.bash
, but this failed even before the tests started, because environment
variable BOOT_GO_LDFLAGS
was still set after make.bash
. This was one thing to fix.
MacPorts solution reqiures that golang environment variable GO_LDFLAGS
is set to
(or contains) a very specific value in order to link golang executable externally with
libMacportsLegacySupport
. This is not acceptable - consider the case when golang is invoked
from script beyond your control and already uses its own setting of GO_LDFLAGS
.
To fix this, I added code to always include libMacportsLegacySupport
when linking golang
executable externally. With this, GO_LDFLAGS
need not be involved at all.
The patched code is now on the branch
go1.12.5-osx-legacy-take2
of the fork. Build
incantation became simpler:
$ ( cd go/src/github.com/av86743/go/ && git checkout go1.12.5-osx-legacy-take2 ) Branch go1.12.5-osx-legacy-take2 set up to track remote branch go1.12.5-osx-legacy-take2 from origin. Switched to a new branch 'go1.12.5-osx-legacy-take2' $ ( cd go/src/github.com/av86743/go/ && cd src && configure_mplsldflags='-L/usr/local/lib -lMacportsLegacySupport' && env GOROOT_BOOTSTRAP=/usr/local/go1.9.7 ./all.bash ) Building Go cmd/dist using /usr/local/go1.9.7. Building Go toolchain1 using /usr/local/go1.9.7. Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1. clang: warning: argument unused during compilation: '-nopie' Building Go toolchain2 using go_bootstrap and Go toolchain1. Building Go toolchain3 using go_bootstrap and Go toolchain2. Building packages and commands for darwin/amd64. ##### Testing packages. ok archive/tar 0.111s ok archive/zip 3.264s <...boring...details...omitted...> ok cmd/vendor/golang.org/x/arch/x86/x86asm 0.138s ok cmd/vendor/golang.org/x/crypto/ssh/terminal 0.015s ok cmd/vendor/golang.org/x/sys/unix 2.333s ok cmd/vet 18.499s 2019/05/16 12:04:10 Failed: exit status 1 $
With that, all.bash
started to run tests. However, cross-compile tests were failing,
and on top of this several tests were giving segfault in FetchPEMRoots
.
Segfaulting? In golang own crypto/x509
? What the fkety-fk.
Intermezzo
I am a perfect tester. Whenever I give a try to another piece of some shitey half-baked software,
it instantly hits one of tripwires which I have naturally placed around. As luck had it this time,
"my problem" stemmed from user certificate present on my account. After a bit of boring bisecting,
I narrowed down segfault to this fragment of
isSSLPolicy()
:
CTypeRef value = NULL; if (CFDictionaryGetValueIfPresent(properties, kSecPolicyOid, (const void **)&value) { CFRelease(properties); return CFEqual(value, kSecPolicyAppleSSL); }
which obviously works much better when changed like this.
Here is a shapshot of the corresponding blame:
How very convenient. Evading detection for 9 months, and counting. So much for the most friendly code review.
Naturally, G shiteheads don't even bother to validate code which they write. And why would they - when they are so-o-o GOOGLE. Let's see how long does it take from them a) to notice that problem has been exposed and - if ever - b) to apply the fix.
In any case, if you are getting segfaults in FetchPEMRoots
, apply patch from
this branch of the fork and see if this does the trick.
Parte 3
Cross-compile tests were failing because MacPorts solution has second requirement: it forces
golang to always use external link mode using environment variable setting
GO_EXTLINK_ENABLED=1
. Conveniently, setting GO_EXTLINK_ENABLED=1
while building golang itself has an additional global effect of changing default value of
GO_EXTLINK_ENABLED
to 1. This breaks cross-compile tests as latter naturally expect
default link mode to be internal.
Setting default value of GO_EXTLINK_ENABLED
to 1 is of course a bad idea. But what is a
really bad idea, is to give an option to subtly change default value of GO_EXTLINK_ENABLED
.
Not a single one official golang build uses default setting of 1 for GO_EXTLINK_ENABLED
.
Consequently, golang tests were never ever run with default setting of 1. And I give you this
accurate prediction: no one at golang will ever bother to fix tests with regard to this. Indeed,
I will not be one to blame them for not doing it.
In order to verify that my understanding is correct, I additionally changed selection of link mode in
determineLinkMode()
to be always internal for cross-compiles,
even when link mode is forced to be external by setting variable GO_EXTLINK_ENABLED=1
(explicitly or by default). This is not acceptable as a permanent solution as it changes the default
behavior of golang with regard to setting of GO_EXTLINK_ENABLED
. However, at this
point I only wanted to see whether tests will pass after this modification.
The patched code is on the branch
go1.12.5-osx-legacy-take3
of the fork. Build
incantation remains the same:
$ ( cd go/src/github.com/av86743/go/ && git checkout go1.12.5-osx-legacy-take3 ) Branch go1.12.5-osx-legacy-take3 set up to track remote branch go1.12.5-osx-legacy-take3 from origin. Switched to a new branch 'go1.12.5-osx-legacy-take3' $ ( cd go/src/github.com/av86743/go/ && git show 2ce3919 | patch -p1 ) patching file src/crypto/x509/root_cgo_darwin.go $ ( cd go/src/github.com/av86743/go/ && cd src && configure_mplsldflags='-L/usr/local/lib -lMacportsLegacySupport' && env GOROOT_BOOTSTRAP=/usr/local/go1.9.7 ./all.bash ) Building Go cmd/dist using /usr/local/go1.9.7. Building Go toolchain1 using /usr/local/go1.9.7. Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1. clang: warning: argument unused during compilation: '-nopie' Building Go toolchain2 using go_bootstrap and Go toolchain1. Building Go toolchain3 using go_bootstrap and Go toolchain2. Building packages and commands for darwin/amd64. ##### Testing packages. ok archive/tar 0.107s ok archive/zip 2.884s <...boring...details...omitted...> ##### GOMAXPROCS=2 runtime -cpu=1,2,4 -quick ok runtime 13.517s ##### Testing without libgcc. dyld: Symbol not found: _unlinkat Referenced from: /var/folders/7c/vqbsk8sn48g90kh7fnmvpd080000gn/T/go-build075507559/b001/x509.test Expected in: flat namespace FAIL crypto/x509 1.361s 2019/05/16 15:18:35 Failed: exit status 1 dyld: Symbol not found: _unlinkat Referenced from: /var/folders/7c/vqbsk8sn48g90kh7fnmvpd080000gn/T/go-build124050274/b001/net.test Expected in: flat namespace FAIL net 5.617s 2019/05/16 15:18:35 Failed: exit status 1 ok os/user 0.012s ##### sync -cpu=10 ok sync 0.236s ##### Testing race detector skipped due to earlier error skipped due to earlier error skipped due to earlier error skipped due to earlier error skipped due to earlier error ##### ../misc/cgo/stdio skipped due to earlier error ##### ../misc/cgo/life skipped due to earlier error ##### ../misc/cgo/test skipped due to earlier error skipped due to earlier error skipped due to earlier error skipped due to earlier error 2019/05/16 15:18:42 FAILED $
Earlier tests have passed, but then more tests were run and some of them failed. This turned out to be end of the line for MacPorts solution as new failing tests require internal link mode.
Parte 4
At this point, the missing part was to figure out how to include libMacportsLegacySupport
for internal link mode. After looking under sufficient number of stones and flipping through
the content of some obscure tickets, I found a reference to the strange magic that I was looking
for - in the final lines of
runtime/sys_darwin.go
:
// Magic incantation to get libSystem actually dynamically linked. // TODO: Why does the code require this? See cmd/link/internal/ld/go.go //go:cgo_import_dynamic _ _ "/usr/lib/libSystem.B.dylib"
I added similar incantation as
runtime/sys_mpls_darwin_amd64.go
.
The final version of patched code is on the branch
go1.12.5-osx-legacy
of the fork. Build
incantation now simplifies to an original one:
$ ( cd go/src/github.com/av86743/go/ && git checkout go1.12.5-osx-legacy ) M src/crypto/x509/root_cgo_darwin.go Branch go1.12.5-osx-legacy set up to track remote branch go1.12.5-osx-legacy from origin. Switched to a new branch 'go1.12.5-osx-legacy' $ ( cd go/src/github.com/av86743/go/ && cd src && env GOROOT_BOOTSTRAP=/usr/local/go1.9.7 ./all.bash ) Building Go cmd/dist using /usr/local/go1.9.7. Building Go toolchain1 using /usr/local/go1.9.7. 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 darwin/amd64. ##### Testing packages. ok archive/tar 0.085s ok archive/zip 3.114s <...boring...details...omitted...> ##### ../test ##### API check Go version is "go1.12.5", ignoring -next /Users/ubuntu/go/src/github.com/av86743/go/api/next.txt ALL TESTS PASSED --- Installed Go for darwin/amd64 in /Users/ubuntu/go/src/github.com/av86743/go Installed commands in /Users/ubuntu/go/src/github.com/av86743/go/bin *** You need to add /Users/ubuntu/go/src/github.com/av86743/go/bin to your PATH. $
Now tests passed and all.bash
completed as indicated in the
original instructions.
Finale
The final version of patch is specialized for darwin/amd64
. It may work for
darwin/386
as well, however I do not have access to this platform in order to
verify this.
You may think that with libMacportsLegacySupport.a
size being under 20kb, it
would make sense to always link this library statically. If you decide to go down this
route, you will hit two walls.
First, libMacportsLegacySupport.a
is in
BSD ar format
, which golang does not support.
This is fixable at an expense of a couple of dozen lines perhaps, and functionality
bloat that this will bring in.
Second and much more uglier obstacle is that all external symbols that are resolved through
libMacportsLegacySupport
, are hardwired to be resolved through .dylib
.
They simply do not belong to the list of external symbols which are resolved by internal
link. If you add libMacportsLegacySupport.a
to the list of built-in packages,
its entry symbols will never actually be looked at.
In order to change this, a bunch of declarations for external functions has to be unwired from
being resolved strictly through .dylib
. Such change would be really intrusive
and likely real pain to rebase for future releases of golang. I did not bother myself with even
evaluating it.
If you have read this far, you probably have now a nicely working installation of go1.12 for your legacy OS X. Perhaps it was not a totally worthless reading, after all?
Disclaimer: No pigsels were f... er-r, hm-m, harmed in the course of this undertaking.