]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/internal/ld: put read-only relocated data into .data.rel.ro when making a shared...
authorMichael Hudson-Doyle <michael.hudson@canonical.com>
Thu, 21 May 2015 01:07:19 +0000 (13:07 +1200)
committerMichael Hudson-Doyle <michael.hudson@canonical.com>
Fri, 4 Sep 2015 05:25:10 +0000 (05:25 +0000)
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 #10914
Updates #9210

Change-Id: I7178daadc0ae87953d5a084aa3d580f4e3b46d47
Reviewed-on: https://go-review.googlesource.com/10300
Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
13 files changed:
misc/cgo/testcshared/test.bash
misc/cgo/testshared/shared_test.go
src/cmd/internal/obj/link.go
src/cmd/link/internal/ld/data.go
src/cmd/link/internal/ld/elf.go
src/cmd/link/internal/ld/lib.go
src/cmd/link/internal/ld/symtab.go
src/cmd/newlink/testdata/autosection.6
src/cmd/newlink/testdata/autoweak.6
src/cmd/newlink/testdata/dead.6
src/cmd/newlink/testdata/hello.6
src/cmd/newlink/testdata/layout.6
src/cmd/newlink/testdata/pclntab.6

index 57221bc36d4a93dda0b5e678d4b510e9a27ba7c1..1b7fec15493a7465f1cb9ba4202935410a023396 100755 (executable)
@@ -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
index 2e123641631cf94ed1595b4a56e265f5782b2b28..7f677d6a371999fc09ef61749b8fb7067467620e 100644 (file)
@@ -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.
index a5d622a70ab8da2910a7817180df6fe4e67cfe86..6066493bafd74035b0c63c76695da4515c67f455 100644 (file)
@@ -334,6 +334,7 @@ const (
        Sxxx = iota
        STEXT
        SELFRXSECT
+
        STYPE
        SSTRING
        SGOSTRING
@@ -341,6 +342,25 @@ const (
        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
index f1561d3c827eb704b1f81b5b23bf655de3cdff0a..4263e8cf2693ceb6b9c2e7ec0f2bfd152b56c733 100644 (file)
@@ -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 {
@@ -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
@@ -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))
@@ -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))
@@ -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))
@@ -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
 
index 187643e41b71652158560ed11091c2080f4e7c8c..a842cf6df46ea5240a13f372ea75ff3fb4a8baa0 100644 (file)
@@ -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
@@ -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
index 89f805d48343112a35c17944f47ae553d1408da7..6d265b2d304b7ec3a476dac52776590ee80c4d95 100644 (file)
@@ -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
@@ -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:
@@ -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 {
@@ -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
index 250c053143c220fbd192538e2723b7afb3d3839b..918ca8ac136ff145d7c71f23fc61ec530353df40 100644 (file)
@@ -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)
@@ -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
@@ -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.") {
index 3681f70db8d04528f734877f485a91960278e902..90c842792c6186def36e0c3029f75ee91f874f11 100644 (file)
Binary files a/src/cmd/newlink/testdata/autosection.6 and b/src/cmd/newlink/testdata/autosection.6 differ
index 99cf465928063a6a915efb4db536b9c56e79c236..c95dd20214f42d3dbc8c2a394ab5e2fc7049724c 100644 (file)
Binary files a/src/cmd/newlink/testdata/autoweak.6 and b/src/cmd/newlink/testdata/autoweak.6 differ
index 5b17ef1adb8ccbe8a482b46875f8ebfa6d2a4e02..a3ec719808aa235435539a6103b9fc5dab90239e 100644 (file)
Binary files a/src/cmd/newlink/testdata/dead.6 and b/src/cmd/newlink/testdata/dead.6 differ
index 1f08d2155eec7afe079e143ee53c45f0d5fd5c40..6731f525d08f5fd3fd3d302ccef0eb7efc730aa2 100644 (file)
Binary files a/src/cmd/newlink/testdata/hello.6 and b/src/cmd/newlink/testdata/hello.6 differ
index d1669866a4976400f78effb9de09191f4630d832..fcfbe1b3ded4daae150d6b3f5cab79720e57144e 100644 (file)
Binary files a/src/cmd/newlink/testdata/layout.6 and b/src/cmd/newlink/testdata/layout.6 differ
index dfe53ddc38d35ad73460c49f7246edb6b7c3ff1d..abc8aef0ab46445a79300453fbe00e1967d9052a 100644 (file)
Binary files a/src/cmd/newlink/testdata/pclntab.6 and b/src/cmd/newlink/testdata/pclntab.6 differ