mirror of
https://github.com/golang/go
synced 2024-11-12 02:50:25 -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 (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"debug/elf"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
@ -84,8 +85,13 @@ func init() {
|
|||||||
cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
|
cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
|
||||||
}
|
}
|
||||||
libgodir = GOOS + "_" + GOARCH
|
libgodir = GOOS + "_" + GOARCH
|
||||||
if GOOS == "darwin" && (GOARCH == "arm" || GOARCH == "arm64") {
|
switch GOOS {
|
||||||
libgodir = GOOS + "_" + GOARCH + "_shared"
|
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))
|
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":
|
case "darwin/arm", "darwin/arm64":
|
||||||
codegenArg = "-shared"
|
codegenArg = "-shared"
|
||||||
default:
|
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"
|
exeSuffix = ".a"
|
||||||
ldBuildmode = "c-archive"
|
ldBuildmode = "c-archive"
|
||||||
|
@ -163,7 +163,7 @@ func DynlinkingGo() bool {
|
|||||||
// relro.
|
// relro.
|
||||||
func UseRelro() bool {
|
func UseRelro() bool {
|
||||||
switch Buildmode {
|
switch Buildmode {
|
||||||
case BuildmodeCShared, BuildmodeShared, BuildmodePIE:
|
case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared, BuildmodePIE:
|
||||||
return Iself
|
return Iself
|
||||||
default:
|
default:
|
||||||
return *FlagLinkshared
|
return *FlagLinkshared
|
||||||
@ -1642,7 +1642,7 @@ func stkcheck(ctxt *Link, up *Chain, depth int) int {
|
|||||||
// onlyctxt.Diagnose the direct caller.
|
// onlyctxt.Diagnose the direct caller.
|
||||||
// TODO(mwhudson): actually think about this.
|
// TODO(mwhudson): actually think about this.
|
||||||
if depth == 1 && s.Type != obj.SXREF && !DynlinkingGo() &&
|
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)
|
ctxt.Diag("call to external function %s", s.Name)
|
||||||
}
|
}
|
||||||
return -1
|
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 {
|
if x.Type&obj.SHIDDEN != 0 {
|
||||||
other = STV_HIDDEN
|
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
|
// On ppc64 the top three bits of the st_other field indicate how
|
||||||
// many instructions separate the global and local entry points. In
|
// many instructions separate the global and local entry points. In
|
||||||
// our case it is two instructions, indicated by the value 3.
|
// 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.
|
// pseudo-symbols to mark locations of type, string, and go string data.
|
||||||
var symtype *Symbol
|
var symtype *Symbol
|
||||||
var symtyperel *Symbol
|
var symtyperel *Symbol
|
||||||
if UseRelro() && (Buildmode == BuildmodeCShared || Buildmode == BuildmodePIE) {
|
if UseRelro() && (Buildmode == BuildmodeCArchive || Buildmode == BuildmodeCShared || Buildmode == BuildmodePIE) {
|
||||||
s = Linklookup(ctxt, "type.*", 0)
|
s = Linklookup(ctxt, "type.*", 0)
|
||||||
|
|
||||||
s.Type = obj.STYPE
|
s.Type = obj.STYPE
|
||||||
|
@ -51,9 +51,20 @@ func addcall(ctxt *ld.Link, s *ld.Symbol, t *ld.Symbol) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func gentext(ctxt *ld.Link) {
|
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
|
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.
|
// Generate little thunks that load the PC of the next instruction into a register.
|
||||||
for _, r := range [...]struct {
|
for _, r := range [...]struct {
|
||||||
|
@ -85,7 +85,7 @@ func archinit(ctxt *ld.Link) {
|
|||||||
ld.Linkmode = ld.LinkInternal
|
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
|
ld.Linkmode = ld.LinkExternal
|
||||||
got := ld.Linklookup(ctxt, "_GLOBAL_OFFSET_TABLE_", 0)
|
got := ld.Linklookup(ctxt, "_GLOBAL_OFFSET_TABLE_", 0)
|
||||||
got.Type = obj.SDYNIMPORT
|
got.Type = obj.SDYNIMPORT
|
||||||
|
Loading…
Reference in New Issue
Block a user