1
0
mirror of https://github.com/golang/go synced 2024-11-12 01:50:22 -07:00

cmd/go, cmd/link: build c-archive as position independent on ELF

This permits people to use -buildmode=c-archive to produce an archive
file that can be included in a PIE or shared library.

Change-Id: Ie340ee2f08bcff4f6fd1415f7d96d51ee3a7c9a1
Reviewed-on: https://go-review.googlesource.com/24180
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
Ian Lance Taylor 2016-06-16 12:59:09 -07:00
parent dc9755c2a2
commit d92a3606f5
6 changed files with 101 additions and 9 deletions

View File

@ -6,6 +6,7 @@ package carchive_test
import (
"bufio"
"debug/elf"
"fmt"
"io/ioutil"
"os"
@ -84,8 +85,13 @@ func init() {
cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
}
libgodir = GOOS + "_" + GOARCH
if GOOS == "darwin" && (GOARCH == "arm" || GOARCH == "arm64") {
libgodir = GOOS + "_" + GOARCH + "_shared"
switch GOOS {
case "darwin":
if GOARCH == "arm" || GOARCH == "arm64" {
libgodir += "_shared"
}
case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris":
libgodir += "_shared"
}
cc = append(cc, "-I", filepath.Join("pkg", libgodir))
@ -487,3 +493,71 @@ func TestExtar(t *testing.T) {
}
}
}
func TestPIE(t *testing.T) {
switch GOOS {
case "windows", "darwin", "plan9":
t.Skipf("skipping PIE test on %s", GOOS)
}
defer func() {
os.Remove("testp" + exeSuffix)
os.RemoveAll("pkg")
}()
cmd := exec.Command("go", "install", "-buildmode=c-archive", "libgo")
cmd.Env = gopathEnv
if out, err := cmd.CombinedOutput(); err != nil {
t.Logf("%s", out)
t.Fatal(err)
}
ccArgs := append(cc, "-fPIE", "-pie", "-o", "testp"+exeSuffix, "main.c", "main_unix.c", filepath.Join("pkg", libgodir, "libgo.a"))
if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
t.Logf("%s", out)
t.Fatal(err)
}
binArgs := append(bin, "arg1", "arg2")
if out, err := exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput(); err != nil {
t.Logf("%s", out)
t.Fatal(err)
}
f, err := elf.Open("testp" + exeSuffix)
if err != nil {
t.Fatal("elf.Open failed: ", err)
}
defer f.Close()
if hasDynTag(t, f, elf.DT_TEXTREL) {
t.Errorf("%s has DT_TEXTREL flag", "testp"+exeSuffix)
}
}
func hasDynTag(t *testing.T, f *elf.File, tag elf.DynTag) bool {
ds := f.SectionByType(elf.SHT_DYNAMIC)
if ds == nil {
t.Error("no SHT_DYNAMIC section")
return false
}
d, err := ds.Data()
if err != nil {
t.Errorf("can't read SHT_DYNAMIC contents: %v", err)
return false
}
for len(d) > 0 {
var t elf.DynTag
switch f.Class {
case elf.ELFCLASS32:
t = elf.DynTag(f.ByteOrder.Uint32(d[:4]))
d = d[8:]
case elf.ELFCLASS64:
t = elf.DynTag(f.ByteOrder.Uint64(d[:8]))
d = d[16:]
}
if t == tag {
return true
}
}
return false
}

View File

@ -338,6 +338,13 @@ func buildModeInit() {
case "darwin/arm", "darwin/arm64":
codegenArg = "-shared"
default:
switch goos {
case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris":
// Use -shared so that the result is
// suitable for inclusion in a PIE or
// shared library.
codegenArg = "-shared"
}
}
exeSuffix = ".a"
ldBuildmode = "c-archive"

View File

@ -163,7 +163,7 @@ func DynlinkingGo() bool {
// relro.
func UseRelro() bool {
switch Buildmode {
case BuildmodeCShared, BuildmodeShared, BuildmodePIE:
case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared, BuildmodePIE:
return Iself
default:
return *FlagLinkshared
@ -1642,7 +1642,7 @@ func stkcheck(ctxt *Link, up *Chain, depth int) int {
// onlyctxt.Diagnose the direct caller.
// TODO(mwhudson): actually think about this.
if depth == 1 && s.Type != obj.SXREF && !DynlinkingGo() &&
Buildmode != BuildmodePIE && Buildmode != BuildmodeCShared {
Buildmode != BuildmodeCArchive && Buildmode != BuildmodePIE && Buildmode != BuildmodeCShared {
ctxt.Diag("call to external function %s", s.Name)
}
return -1

View File

@ -155,7 +155,7 @@ func putelfsym(ctxt *Link, x *Symbol, s string, t int, addr int64, size int64, v
if x.Type&obj.SHIDDEN != 0 {
other = STV_HIDDEN
}
if (Buildmode == BuildmodePIE || DynlinkingGo()) && SysArch.Family == sys.PPC64 && type_ == STT_FUNC && x.Name != "runtime.duffzero" && x.Name != "runtime.duffcopy" {
if (Buildmode == BuildmodeCArchive || Buildmode == BuildmodePIE || DynlinkingGo()) && SysArch.Family == sys.PPC64 && type_ == STT_FUNC && x.Name != "runtime.duffzero" && x.Name != "runtime.duffcopy" {
// On ppc64 the top three bits of the st_other field indicate how
// many instructions separate the global and local entry points. In
// our case it is two instructions, indicated by the value 3.
@ -362,7 +362,7 @@ func (ctxt *Link) symtab() {
// pseudo-symbols to mark locations of type, string, and go string data.
var symtype *Symbol
var symtyperel *Symbol
if UseRelro() && (Buildmode == BuildmodeCShared || Buildmode == BuildmodePIE) {
if UseRelro() && (Buildmode == BuildmodeCArchive || Buildmode == BuildmodeCShared || Buildmode == BuildmodePIE) {
s = Linklookup(ctxt, "type.*", 0)
s.Type = obj.STYPE

View File

@ -51,9 +51,20 @@ func addcall(ctxt *ld.Link, s *ld.Symbol, t *ld.Symbol) {
}
func gentext(ctxt *ld.Link) {
if !ld.DynlinkingGo() && ld.Buildmode != ld.BuildmodePIE && ld.Buildmode != ld.BuildmodeCShared {
if ld.DynlinkingGo() {
// We need get_pc_thunk.
} else {
switch ld.Buildmode {
case ld.BuildmodeCArchive:
if !ld.Iself {
return
}
case ld.BuildmodePIE, ld.BuildmodeCShared:
// We need get_pc_thunk.
default:
return
}
}
// Generate little thunks that load the PC of the next instruction into a register.
for _, r := range [...]struct {

View File

@ -85,7 +85,7 @@ func archinit(ctxt *ld.Link) {
ld.Linkmode = ld.LinkInternal
}
if ld.Buildmode == ld.BuildmodeCShared || ld.Buildmode == ld.BuildmodePIE || ld.DynlinkingGo() {
if (ld.Buildmode == ld.BuildmodeCArchive && ld.Iself) || ld.Buildmode == ld.BuildmodeCShared || ld.Buildmode == ld.BuildmodePIE || ld.DynlinkingGo() {
ld.Linkmode = ld.LinkExternal
got := ld.Linklookup(ctxt, "_GLOBAL_OFFSET_TABLE_", 0)
got.Type = obj.SDYNIMPORT