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
}
}
+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.
Sxxx = iota
STEXT
SELFRXSECT
+
STYPE
SSTRING
SGOSTRING
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
*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 {
/* 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
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))
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))
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))
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
}
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
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
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
argv = append(argv, "-dynamiclib")
} else {
argv = append(argv, "-Wl,-Bsymbolic")
+ if UseRelro() {
+ argv = append(argv, "-Wl,-z,relro")
+ }
argv = append(argv, "-shared")
}
case BuildmodeShared:
// 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 {
obj.SGOSTRING,
obj.SGOFUNC,
obj.SGCBITS,
+ obj.STYPERELRO,
+ obj.SSTRINGRELRO,
+ obj.SGOSTRINGRELRO,
+ obj.SGOFUNCRELRO,
+ obj.SGCBITSRELRO,
+ obj.SRODATARELRO,
obj.SWINDOWS:
if !s.Reachable {
continue
// 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)
symgcbits := s
symtypelink := Linklookup(Ctxt, "runtime.typelink", 0)
+ symtypelink.Type = obj.STYPELINK
symt = Linklookup(Ctxt, "runtime.symtab", 0)
symt.Local = true
}
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.") {