1
0
mirror of https://github.com/golang/go synced 2024-10-01 04:08:32 -06:00
go/misc/cgo/testshared/test.bash
Michael Hudson-Doyle 77fc03f4cd cmd/internal/ld, runtime: abort on shared library ABI mismatch
This:

1) Defines the ABI hash of a package (as the SHA1 of the __.PKGDEF)
2) Defines the ABI hash of a shared library (sort the packages by import
   path, concatenate the hashes of the packages and SHA1 that)
3) When building a shared library, compute the above value and define a
   global symbol that points to a go string that has the hash as its value.
4) When linking against a shared library, read the abi hash from the
   library and put both the value seen at link time and a reference
   to the global symbol into the moduledata.
5) During runtime initialization, check that the hash seen at link time
   still matches the hash the global symbol points to.

Change-Id: Iaa54c783790e6dde3057a2feadc35473d49614a5
Reviewed-on: https://go-review.googlesource.com/8773
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com>
2015-05-12 01:30:40 +00:00

138 lines
4.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# Copyright 2015 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
# Test that -buildmode=shared can produce a shared library and that
# -linkshared can link against it to produce a working executable.
set -eu
die () {
echo $@
exit 1
}
# Because go install -buildmode=shared $standard_library_package always
# installs into $GOROOT, here are some gymnastics to come up with a
# unique installsuffix to use in this test that we can clean up
# afterwards.
rootdir="$(dirname $(go list -f '{{.Target}}' runtime))"
template="${rootdir}_XXXXXXXX_dynlink"
std_install_dir=$(mktemp -d "$template")
scratch_dir=$(mktemp -d)
cp -a . $scratch_dir
opwd="$(pwd)"
cd $scratch_dir
export GOPATH="$(pwd)"
cleanup () {
rm -rf $std_install_dir $scratch_dir
}
trap cleanup EXIT
mysuffix=$(echo $std_install_dir | sed -e 's/.*_\([^_]*\)_dynlink/\1/')
# This is the smallest set of packages we can link into a shared
# library (runtime/cgo is built implicitly). Check they are built into
# a library with the expected name.
minpkgs="runtime sync/atomic"
soname=libruntime,sync-atomic.so
go install -installsuffix="$mysuffix" -buildmode=shared $minpkgs || die "install -buildmode=shared failed"
if [ ! -f "$std_install_dir/$soname" ]; then
echo "$std_install_dir/$soname not found!"
exit 1
fi
# The install command should have created a "shlibname" file for the
# listed packages (and runtime/cgo) indicating the name of the shared
# library containing it.
for pkg in $minpkgs runtime/cgo; do
if [ ! -f "$std_install_dir/$pkg.shlibname" ]; then
die "no shlibname file for $pkg"
fi
if [ "$(cat "$std_install_dir/$pkg.shlibname")" != "$soname" ]; then
die "shlibname file for $pkg has wrong contents"
fi
done
# Build a trivial program that links against the shared library we
# just made and check it runs.
go install -installsuffix="$mysuffix" -linkshared trivial || die "build -linkshared failed"
./bin/trivial || die "./bin/trivial failed"
# And check that it is actually dynamically linked against the library
# we hope it is linked against.
ensure_ldd () {
a="$(ldd $1)" || die "ldd $1 failed: $a"
{ echo "$a" | grep -q "$2"; } || die "$1 does not appear to be linked against $2"
}
ensure_ldd ./bin/trivial $std_install_dir/$soname
# Build a GOPATH package into a shared library that links against the above one.
rootdir="$(dirname $(go list -installsuffix="$mysuffix" -linkshared -f '{{.Target}}' dep))"
go install -installsuffix="$mysuffix" -buildmode=shared -linkshared dep
ensure_ldd $rootdir/libdep.so $std_install_dir/$soname
# And exe that links against both
go install -installsuffix="$mysuffix" -linkshared exe
./bin/exe || die "./bin/exe failed with code $?"
ensure_ldd ./bin/exe $rootdir/libdep.so
ensure_ldd ./bin/exe $std_install_dir/$soname
# Now, test rebuilding of shared libraries when they are stale.
will_check_rebuilt () {
for f in $@; do cp $f $f.bak; done
}
assert_rebuilt () {
find $1 -newer $1.bak | grep -q . || die "$1 was not rebuilt"
}
assert_not_rebuilt () {
find $1 -newer $1.bak | grep . && die "$1 was rebuilt" || true
}
# If the source is newer than both the .a file and the .so, both are rebuilt.
touch src/dep/dep.go
will_check_rebuilt $rootdir/libdep.so $rootdir/dep.a
go install -installsuffix="$mysuffix" -linkshared exe
assert_rebuilt $rootdir/dep.a
assert_rebuilt $rootdir/libdep.so
# If the .a file is newer than the .so, the .so is rebuilt (but not the .a)
touch $rootdir/dep.a
will_check_rebuilt $rootdir/libdep.so $rootdir/dep.a
go install -installsuffix="$mysuffix" -linkshared exe
assert_not_rebuilt $rootdir/dep.a
assert_rebuilt $rootdir/libdep.so
# If we make an ABI-breaking change to dep and rebuild libp.so but not exe, exe will
# abort with a complaint on startup.
# This assumes adding an exported function breaks ABI, which is not true in some
# senses but suffices for the narrow definition of ABI compatiblity the toolchain
# uses today.
echo "func ABIBreak() {}" >> src/dep/dep.go
go install -installsuffix="$mysuffix" -buildmode=shared -linkshared dep
output="$(./bin/exe 2>&1)" && die "exe succeeded after ABI break" || true
msg="abi mismatch detected between the executable and libdep.so"
{ echo "$output" | grep -q "$msg"; } || die "exe did not fail with expected message"
# Rebuilding exe makes it work again.
go install -installsuffix="$mysuffix" -linkshared exe
./bin/exe || die "exe failed after rebuild"
# If we make a change which does not break ABI (such as adding an
# unexported function) and rebuild libdep.so, exe still works.
echo "func noABIBreak() {}" >> src/dep/dep.go
go install -installsuffix="$mysuffix" -buildmode=shared -linkshared dep
./bin/exe || die "exe failed after non-ABI breaking change"