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:
parent
dc9755c2a2
commit
d92a3606f5
@ -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
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user