Skip to content

Commit

Permalink
cmd/internal/ld: put read-only relocated data into .data.rel.ro when …
Browse files Browse the repository at this point in the history
…making a shared object

Currently Go produces shared libraries that cannot be shared between processes
because they have relocations against the text segment (not text section). This
fixes this by moving some data to sections with magic names recognized by the
static linker.

Fixes golang#10914
Updates golang#9210

Change-Id: I7178daadc0ae87953d5a084aa3d580f4e3b46d47
Reviewed-on: https://go-review.googlesource.com/10300
Run-TryBot: Michael Hudson-Doyle <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
Reviewed-by: Ian Lance Taylor <[email protected]>
  • Loading branch information
mwhudson committed Sep 4, 2015
1 parent eaea5ad commit 2c2cbb6
Show file tree
Hide file tree
Showing 13 changed files with 206 additions and 18 deletions.
7 changes: 7 additions & 0 deletions misc/cgo/testcshared/test.bash
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ GOPATH=$(pwd) go install -buildmode=c-shared $suffix libgo
GOPATH=$(pwd) go build -buildmode=c-shared $suffix -o libgo.$libext src/libgo/libgo.go
binpush libgo.$libext

if [ "$goos" == "linux" ]; then
if readelf -d libgo.$libext | grep TEXTREL >/dev/null; then
echo "libgo.$libext has TEXTREL set"
exit 1
fi
fi

# test0: exported symbols in shared lib are accessible.
# TODO(iant): using _shared here shouldn't really be necessary.
$(go env CC) $(go env GOGCCFLAGS) -I ${installdir} -o testp main0.c libgo.$libext
Expand Down
39 changes: 39 additions & 0 deletions misc/cgo/testshared/shared_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,45 @@ func TestSOBuilt(t *testing.T) {
}
}

func hasDynTag(f *elf.File, tag elf.DynTag) bool {
ds := f.SectionByType(elf.SHT_DYNAMIC)
if ds == nil {
return false
}
d, err := ds.Data()
if err != nil {
return false
}
for len(d) > 0 {
var t elf.DynTag
switch f.Class {
case elf.ELFCLASS32:
t = elf.DynTag(f.ByteOrder.Uint32(d[0:4]))
d = d[8:]
case elf.ELFCLASS64:
t = elf.DynTag(f.ByteOrder.Uint64(d[0:8]))
d = d[16:]
}
if t == tag {
return true
}
}
return false
}

// The shared library does not have relocations against the text segment.
func TestNoTextrel(t *testing.T) {
sopath := filepath.Join(gorootInstallDir, soname)
f, err := elf.Open(sopath)
if err != nil {
t.Fatal("elf.Open failed: ", err)
}
defer f.Close()
if hasDynTag(f, elf.DT_TEXTREL) {
t.Errorf("%s has DT_TEXTREL set", soname)
}
}

// 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.
Expand Down
20 changes: 20 additions & 0 deletions src/cmd/internal/obj/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,13 +334,33 @@ const (
Sxxx = iota
STEXT
SELFRXSECT

STYPE
SSTRING
SGOSTRING
SGOFUNC
SGCBITS
SRODATA
SFUNCTAB

// Types STYPE-SFUNCTAB above are written to the .rodata section by default.
// When linking a shared object, some conceptually "read only" types need to
// be written to by relocations and putting them in a section called
// ".rodata" interacts poorly with the system linkers. The GNU linkers
// support this situation by arranging for sections of the name
// ".data.rel.ro.XXX" to be mprotected read only by the dynamic linker after
// relocations have applied, so when the Go linker is creating a shared
// object it checks all objects of the above types and bumps any object that
// has a relocation to it to the corresponding type below, which are then
// written to sections with appropriate magic names.
STYPERELRO
SSTRINGRELRO
SGOSTRINGRELRO
SGOFUNCRELRO
SGCBITSRELRO
SRODATARELRO
SFUNCTABRELRO

STYPELINK
SSYMTAB
SPCLNTAB
Expand Down
77 changes: 72 additions & 5 deletions src/cmd/link/internal/ld/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,31 @@ func dodata() {

*l = nil

if UseRelro() {
// "read only" data with relocations needs to go in its own section
// when building a shared library. We do this by boosting objects of
// type SXXX with relocations to type SXXXRELRO.
for s := datap; s != nil; s = s.Next {
if (s.Type >= obj.STYPE && s.Type <= obj.SFUNCTAB && len(s.R) > 0) || s.Type == obj.SGOSTRING {
s.Type += (obj.STYPERELRO - obj.STYPE)
if s.Outer != nil {
s.Outer.Type = s.Type
}
}
}
// Check that we haven't made two symbols with the same .Outer into
// different types (because references two symbols with non-nil Outer
// become references to the outer symbol + offset it's vital that the
// symbol and the outer end up in the same section).
for s := datap; s != nil; s = s.Next {
if s.Outer != nil && s.Outer.Type != s.Type {
Diag("inconsistent types for %s and its Outer %s (%d != %d)",
s.Name, s.Outer.Name, s.Type, s.Outer.Type)
}
}

}

datap = listsort(datap, datcmp, listnextp)

if Iself {
Expand Down Expand Up @@ -1465,12 +1490,12 @@ func dodata() {
/* read-only data */
sect = addsection(segro, ".rodata", 04)

sect.Align = maxalign(s, obj.STYPELINK-1)
sect.Align = maxalign(s, obj.STYPERELRO-1)
datsize = Rnd(datsize, int64(sect.Align))
sect.Vaddr = 0
Linklookup(Ctxt, "runtime.rodata", 0).Sect = sect
Linklookup(Ctxt, "runtime.erodata", 0).Sect = sect
for ; s != nil && s.Type < obj.STYPELINK; s = s.Next {
for ; s != nil && s.Type < obj.STYPERELRO; s = s.Next {
datsize = aligndatsize(datsize, s)
s.Sect = sect
s.Type = obj.SRODATA
Expand All @@ -1480,8 +1505,45 @@ func dodata() {

sect.Length = uint64(datsize) - sect.Vaddr

// There is some data that are conceptually read-only but are written to by
// relocations. On GNU systems, we can arrange for the dynamic linker to
// mprotect sections after relocations are applied by giving them write
// permissions in the object file and calling them ".data.rel.ro.FOO". We
// divide the .rodata section between actual .rodata and .data.rel.ro.rodata,
// but for the other sections that this applies to, we just write a read-only
// .FOO section or a read-write .data.rel.ro.FOO section depending on the
// situation.
// TODO(mwhudson): It would make sense to do this more widely, but it makes
// the system linker segfault on darwin.
relro_perms := 04
relro_prefix := ""

if UseRelro() {
relro_perms = 06
relro_prefix = ".data.rel.ro"
/* data only written by relocations */
sect = addsection(segro, ".data.rel.ro", 06)

sect.Align = maxalign(s, obj.STYPELINK-1)
datsize = Rnd(datsize, int64(sect.Align))
sect.Vaddr = 0
for ; s != nil && s.Type < obj.STYPELINK; s = s.Next {
datsize = aligndatsize(datsize, s)
if s.Outer != nil && s.Outer.Sect != nil && s.Outer.Sect != sect {
Diag("s.Outer (%s) in different section from s (%s)", s.Outer.Name, s.Name)
}
s.Sect = sect
s.Type = obj.SRODATA
s.Value = int64(uint64(datsize) - sect.Vaddr)
growdatsize(&datsize, s)
}

sect.Length = uint64(datsize) - sect.Vaddr

}

/* typelink */
sect = addsection(segro, ".typelink", 04)
sect = addsection(segro, relro_prefix+".typelink", relro_perms)

sect.Align = maxalign(s, obj.STYPELINK)
datsize = Rnd(datsize, int64(sect.Align))
Expand All @@ -1499,7 +1561,7 @@ func dodata() {
sect.Length = uint64(datsize) - sect.Vaddr

/* gosymtab */
sect = addsection(segro, ".gosymtab", 04)
sect = addsection(segro, relro_prefix+".gosymtab", relro_perms)

sect.Align = maxalign(s, obj.SPCLNTAB-1)
datsize = Rnd(datsize, int64(sect.Align))
Expand All @@ -1517,7 +1579,7 @@ func dodata() {
sect.Length = uint64(datsize) - sect.Vaddr

/* gopclntab */
sect = addsection(segro, ".gopclntab", 04)
sect = addsection(segro, relro_prefix+".gopclntab", relro_perms)

sect.Align = maxalign(s, obj.SELFROSECT-1)
datsize = Rnd(datsize, int64(sect.Align))
Expand Down Expand Up @@ -1723,6 +1785,11 @@ func address() {
rodata = text.Next
}
typelink := rodata.Next
if UseRelro() {
// There is another section (.data.rel.ro) when building a shared
// object on elf systems.
typelink = typelink.Next
}
symtab := typelink.Next
pclntab := symtab.Next

Expand Down
33 changes: 24 additions & 9 deletions src/cmd/link/internal/ld/elf.go
Original file line number Diff line number Diff line change
Expand Up @@ -1690,9 +1690,18 @@ func doelf() {
}
Addstring(shstrtab, ".elfdata")
Addstring(shstrtab, ".rodata")
Addstring(shstrtab, ".typelink")
Addstring(shstrtab, ".gosymtab")
Addstring(shstrtab, ".gopclntab")
if Buildmode == BuildmodeShared || Buildmode == BuildmodeCShared {
Addstring(shstrtab, ".data.rel.ro")
}
// See the comment about data.rel.ro.FOO section names in data.go.
relro_prefix := ""

if UseRelro() {
relro_prefix = ".data.rel.ro"
}
Addstring(shstrtab, relro_prefix+".typelink")
Addstring(shstrtab, relro_prefix+".gosymtab")
Addstring(shstrtab, relro_prefix+".gopclntab")

if Linkmode == LinkExternal {
Debug['d'] = 1
Expand All @@ -1701,20 +1710,26 @@ func doelf() {
case '6', '7', '9':
Addstring(shstrtab, ".rela.text")
Addstring(shstrtab, ".rela.rodata")
Addstring(shstrtab, ".rela.typelink")
Addstring(shstrtab, ".rela.gosymtab")
Addstring(shstrtab, ".rela.gopclntab")
Addstring(shstrtab, ".rela"+relro_prefix+".typelink")
Addstring(shstrtab, ".rela"+relro_prefix+".gosymtab")
Addstring(shstrtab, ".rela"+relro_prefix+".gopclntab")
Addstring(shstrtab, ".rela.noptrdata")
Addstring(shstrtab, ".rela.data")
if UseRelro() {
Addstring(shstrtab, ".rela.data.rel.ro")
}

default:
Addstring(shstrtab, ".rel.text")
Addstring(shstrtab, ".rel.rodata")
Addstring(shstrtab, ".rel.typelink")
Addstring(shstrtab, ".rel.gosymtab")
Addstring(shstrtab, ".rel.gopclntab")
Addstring(shstrtab, ".rel"+relro_prefix+".typelink")
Addstring(shstrtab, ".rel"+relro_prefix+".gosymtab")
Addstring(shstrtab, ".rel"+relro_prefix+".gopclntab")
Addstring(shstrtab, ".rel.noptrdata")
Addstring(shstrtab, ".rel.data")
if UseRelro() {
Addstring(shstrtab, ".rel.data.rel.ro")
}
}

// add a .note.GNU-stack section to mark the stack as non-executable
Expand Down
20 changes: 19 additions & 1 deletion src/cmd/link/internal/ld/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,12 @@ func DynlinkingGo() bool {
return Buildmode == BuildmodeShared || Linkshared
}

// UseRelro returns whether to make use of "read only relocations" aka
// relro.
func UseRelro() bool {
return (Buildmode == BuildmodeCShared || Buildmode == BuildmodeShared) && Iself
}

var (
Thestring string
Thelinkarch *LinkArch
Expand Down Expand Up @@ -980,6 +986,9 @@ func hostlink() {
argv = append(argv, "-dynamiclib")
} else {
argv = append(argv, "-Wl,-Bsymbolic")
if UseRelro() {
argv = append(argv, "-Wl,-z,relro")
}
argv = append(argv, "-shared")
}
case BuildmodeShared:
Expand All @@ -991,7 +1000,10 @@ func hostlink() {
// think we may well end up wanting to use -Bsymbolic here
// anyway.
argv = append(argv, "-Wl,-Bsymbolic-functions")
argv = append(argv, "-shared")
if UseRelro() {
argv = append(argv, "-shared")
}
argv = append(argv, "-Wl,-z,relro")
}

if Linkshared && Iself {
Expand Down Expand Up @@ -1771,6 +1783,12 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) {
obj.SGOSTRING,
obj.SGOFUNC,
obj.SGCBITS,
obj.STYPERELRO,
obj.SSTRINGRELRO,
obj.SGOSTRINGRELRO,
obj.SGOFUNCRELRO,
obj.SGCBITSRELRO,
obj.SRODATARELRO,
obj.SWINDOWS:
if !s.Reachable {
continue
Expand Down
28 changes: 25 additions & 3 deletions src/cmd/link/internal/ld/symtab.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,13 +350,29 @@ func symtab() {

// pseudo-symbols to mark locations of type, string, and go string data.
var symtype *LSym
if !DynlinkingGo() {
var symtyperel *LSym
if UseRelro() && Buildmode == BuildmodeCShared {
s = Linklookup(Ctxt, "type.*", 0)

s.Type = obj.STYPE
s.Size = 0
s.Reachable = true
symtype = s

s = Linklookup(Ctxt, "typerel.*", 0)

s.Type = obj.STYPERELRO
s.Size = 0
s.Reachable = true
symtyperel = s
} else if !DynlinkingGo() {
s = Linklookup(Ctxt, "type.*", 0)

s.Type = obj.STYPE
s.Size = 0
s.Reachable = true
symtype = s
symtyperel = s
}

s = Linklookup(Ctxt, "go.string.*", 0)
Expand All @@ -381,6 +397,7 @@ func symtab() {
symgcbits := s

symtypelink := Linklookup(Ctxt, "runtime.typelink", 0)
symtypelink.Type = obj.STYPELINK

symt = Linklookup(Ctxt, "runtime.symtab", 0)
symt.Local = true
Expand All @@ -400,9 +417,14 @@ func symtab() {
}

if strings.HasPrefix(s.Name, "type.") && !DynlinkingGo() {
s.Type = obj.STYPE
s.Hide = 1
s.Outer = symtype
if UseRelro() && len(s.R) > 0 {
s.Type = obj.STYPERELRO
s.Outer = symtyperel
} else {
s.Type = obj.STYPE
s.Outer = symtype
}
}

if strings.HasPrefix(s.Name, "go.typelink.") {
Expand Down
Binary file modified src/cmd/newlink/testdata/autosection.6
Binary file not shown.
Binary file modified src/cmd/newlink/testdata/autoweak.6
Binary file not shown.
Binary file modified src/cmd/newlink/testdata/dead.6
Binary file not shown.
Binary file modified src/cmd/newlink/testdata/hello.6
Binary file not shown.
Binary file modified src/cmd/newlink/testdata/layout.6
Binary file not shown.
Binary file modified src/cmd/newlink/testdata/pclntab.6
Binary file not shown.

0 comments on commit 2c2cbb6

Please sign in to comment.