Skip to content

Commit 5cbca8d

Browse files
committedSep 8, 2015
cmd/internal/ld: put read-only relocated data into .data.rel.ro when 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. The change in genasmsym to add STYPELINK to the switch should fix things on darwin/arm64. Fixes #10914 Updates #9210 Change-Id: Iab4a6678dd04cec6114e683caac5cf31b1063309 Reviewed-on: https://go-review.googlesource.com/14306 Run-TryBot: Michael Hudson-Doyle <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 14f9195 commit 5cbca8d

File tree

13 files changed

+207
-18
lines changed

13 files changed

+207
-18
lines changed
 

‎misc/cgo/testcshared/test.bash

+7
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ GOPATH=$(pwd) go install -buildmode=c-shared $suffix libgo
8181
GOPATH=$(pwd) go build -buildmode=c-shared $suffix -o libgo.$libext src/libgo/libgo.go
8282
binpush libgo.$libext
8383

84+
if [ "$goos" == "linux" ]; then
85+
if readelf -d libgo.$libext | grep TEXTREL >/dev/null; then
86+
echo "libgo.$libext has TEXTREL set"
87+
exit 1
88+
fi
89+
fi
90+
8491
# test0: exported symbols in shared lib are accessible.
8592
# TODO(iant): using _shared here shouldn't really be necessary.
8693
$(go env CC) $(go env GOGCCFLAGS) -I ${installdir} -o testp main0.c libgo.$libext

‎misc/cgo/testshared/shared_test.go

+39
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,45 @@ func TestSOBuilt(t *testing.T) {
163163
}
164164
}
165165

166+
func hasDynTag(f *elf.File, tag elf.DynTag) bool {
167+
ds := f.SectionByType(elf.SHT_DYNAMIC)
168+
if ds == nil {
169+
return false
170+
}
171+
d, err := ds.Data()
172+
if err != nil {
173+
return false
174+
}
175+
for len(d) > 0 {
176+
var t elf.DynTag
177+
switch f.Class {
178+
case elf.ELFCLASS32:
179+
t = elf.DynTag(f.ByteOrder.Uint32(d[0:4]))
180+
d = d[8:]
181+
case elf.ELFCLASS64:
182+
t = elf.DynTag(f.ByteOrder.Uint64(d[0:8]))
183+
d = d[16:]
184+
}
185+
if t == tag {
186+
return true
187+
}
188+
}
189+
return false
190+
}
191+
192+
// The shared library does not have relocations against the text segment.
193+
func TestNoTextrel(t *testing.T) {
194+
sopath := filepath.Join(gorootInstallDir, soname)
195+
f, err := elf.Open(sopath)
196+
if err != nil {
197+
t.Fatal("elf.Open failed: ", err)
198+
}
199+
defer f.Close()
200+
if hasDynTag(f, elf.DT_TEXTREL) {
201+
t.Errorf("%s has DT_TEXTREL set", soname)
202+
}
203+
}
204+
166205
// The install command should have created a "shlibname" file for the
167206
// listed packages (and runtime/cgo) indicating the name of the shared
168207
// library containing it.

‎src/cmd/internal/obj/link.go

+20
Original file line numberDiff line numberDiff line change
@@ -334,13 +334,33 @@ const (
334334
Sxxx = iota
335335
STEXT
336336
SELFRXSECT
337+
337338
STYPE
338339
SSTRING
339340
SGOSTRING
340341
SGOFUNC
341342
SGCBITS
342343
SRODATA
343344
SFUNCTAB
345+
346+
// Types STYPE-SFUNCTAB above are written to the .rodata section by default.
347+
// When linking a shared object, some conceptually "read only" types need to
348+
// be written to by relocations and putting them in a section called
349+
// ".rodata" interacts poorly with the system linkers. The GNU linkers
350+
// support this situation by arranging for sections of the name
351+
// ".data.rel.ro.XXX" to be mprotected read only by the dynamic linker after
352+
// relocations have applied, so when the Go linker is creating a shared
353+
// object it checks all objects of the above types and bumps any object that
354+
// has a relocation to it to the corresponding type below, which are then
355+
// written to sections with appropriate magic names.
356+
STYPERELRO
357+
SSTRINGRELRO
358+
SGOSTRINGRELRO
359+
SGOFUNCRELRO
360+
SGCBITSRELRO
361+
SRODATARELRO
362+
SFUNCTABRELRO
363+
344364
STYPELINK
345365
SSYMTAB
346366
SPCLNTAB

‎src/cmd/link/internal/ld/data.go

+72-5
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,31 @@ func dodata() {
12071207

12081208
*l = nil
12091209

1210+
if UseRelro() {
1211+
// "read only" data with relocations needs to go in its own section
1212+
// when building a shared library. We do this by boosting objects of
1213+
// type SXXX with relocations to type SXXXRELRO.
1214+
for s := datap; s != nil; s = s.Next {
1215+
if (s.Type >= obj.STYPE && s.Type <= obj.SFUNCTAB && len(s.R) > 0) || s.Type == obj.SGOSTRING {
1216+
s.Type += (obj.STYPERELRO - obj.STYPE)
1217+
if s.Outer != nil {
1218+
s.Outer.Type = s.Type
1219+
}
1220+
}
1221+
}
1222+
// Check that we haven't made two symbols with the same .Outer into
1223+
// different types (because references two symbols with non-nil Outer
1224+
// become references to the outer symbol + offset it's vital that the
1225+
// symbol and the outer end up in the same section).
1226+
for s := datap; s != nil; s = s.Next {
1227+
if s.Outer != nil && s.Outer.Type != s.Type {
1228+
Diag("inconsistent types for %s and its Outer %s (%d != %d)",
1229+
s.Name, s.Outer.Name, s.Type, s.Outer.Type)
1230+
}
1231+
}
1232+
1233+
}
1234+
12101235
datap = listsort(datap, datcmp, listnextp)
12111236

12121237
if Iself {
@@ -1465,12 +1490,12 @@ func dodata() {
14651490
/* read-only data */
14661491
sect = addsection(segro, ".rodata", 04)
14671492

1468-
sect.Align = maxalign(s, obj.STYPELINK-1)
1493+
sect.Align = maxalign(s, obj.STYPERELRO-1)
14691494
datsize = Rnd(datsize, int64(sect.Align))
14701495
sect.Vaddr = 0
14711496
Linklookup(Ctxt, "runtime.rodata", 0).Sect = sect
14721497
Linklookup(Ctxt, "runtime.erodata", 0).Sect = sect
1473-
for ; s != nil && s.Type < obj.STYPELINK; s = s.Next {
1498+
for ; s != nil && s.Type < obj.STYPERELRO; s = s.Next {
14741499
datsize = aligndatsize(datsize, s)
14751500
s.Sect = sect
14761501
s.Type = obj.SRODATA
@@ -1480,8 +1505,45 @@ func dodata() {
14801505

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

1508+
// There is some data that are conceptually read-only but are written to by
1509+
// relocations. On GNU systems, we can arrange for the dynamic linker to
1510+
// mprotect sections after relocations are applied by giving them write
1511+
// permissions in the object file and calling them ".data.rel.ro.FOO". We
1512+
// divide the .rodata section between actual .rodata and .data.rel.ro.rodata,
1513+
// but for the other sections that this applies to, we just write a read-only
1514+
// .FOO section or a read-write .data.rel.ro.FOO section depending on the
1515+
// situation.
1516+
// TODO(mwhudson): It would make sense to do this more widely, but it makes
1517+
// the system linker segfault on darwin.
1518+
relro_perms := 04
1519+
relro_prefix := ""
1520+
1521+
if UseRelro() {
1522+
relro_perms = 06
1523+
relro_prefix = ".data.rel.ro"
1524+
/* data only written by relocations */
1525+
sect = addsection(segro, ".data.rel.ro", 06)
1526+
1527+
sect.Align = maxalign(s, obj.STYPELINK-1)
1528+
datsize = Rnd(datsize, int64(sect.Align))
1529+
sect.Vaddr = 0
1530+
for ; s != nil && s.Type < obj.STYPELINK; s = s.Next {
1531+
datsize = aligndatsize(datsize, s)
1532+
if s.Outer != nil && s.Outer.Sect != nil && s.Outer.Sect != sect {
1533+
Diag("s.Outer (%s) in different section from s (%s)", s.Outer.Name, s.Name)
1534+
}
1535+
s.Sect = sect
1536+
s.Type = obj.SRODATA
1537+
s.Value = int64(uint64(datsize) - sect.Vaddr)
1538+
growdatsize(&datsize, s)
1539+
}
1540+
1541+
sect.Length = uint64(datsize) - sect.Vaddr
1542+
1543+
}
1544+
14831545
/* typelink */
1484-
sect = addsection(segro, ".typelink", 04)
1546+
sect = addsection(segro, relro_prefix+".typelink", relro_perms)
14851547

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

15011563
/* gosymtab */
1502-
sect = addsection(segro, ".gosymtab", 04)
1564+
sect = addsection(segro, relro_prefix+".gosymtab", relro_perms)
15031565

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

15191581
/* gopclntab */
1520-
sect = addsection(segro, ".gopclntab", 04)
1582+
sect = addsection(segro, relro_prefix+".gopclntab", relro_perms)
15211583

15221584
sect.Align = maxalign(s, obj.SELFROSECT-1)
15231585
datsize = Rnd(datsize, int64(sect.Align))
@@ -1723,6 +1785,11 @@ func address() {
17231785
rodata = text.Next
17241786
}
17251787
typelink := rodata.Next
1788+
if UseRelro() {
1789+
// There is another section (.data.rel.ro) when building a shared
1790+
// object on elf systems.
1791+
typelink = typelink.Next
1792+
}
17261793
symtab := typelink.Next
17271794
pclntab := symtab.Next
17281795

‎src/cmd/link/internal/ld/elf.go

+24-9
Original file line numberDiff line numberDiff line change
@@ -1690,9 +1690,18 @@ func doelf() {
16901690
}
16911691
Addstring(shstrtab, ".elfdata")
16921692
Addstring(shstrtab, ".rodata")
1693-
Addstring(shstrtab, ".typelink")
1694-
Addstring(shstrtab, ".gosymtab")
1695-
Addstring(shstrtab, ".gopclntab")
1693+
if Buildmode == BuildmodeShared || Buildmode == BuildmodeCShared {
1694+
Addstring(shstrtab, ".data.rel.ro")
1695+
}
1696+
// See the comment about data.rel.ro.FOO section names in data.go.
1697+
relro_prefix := ""
1698+
1699+
if UseRelro() {
1700+
relro_prefix = ".data.rel.ro"
1701+
}
1702+
Addstring(shstrtab, relro_prefix+".typelink")
1703+
Addstring(shstrtab, relro_prefix+".gosymtab")
1704+
Addstring(shstrtab, relro_prefix+".gopclntab")
16961705

16971706
if Linkmode == LinkExternal {
16981707
Debug['d'] = 1
@@ -1701,20 +1710,26 @@ func doelf() {
17011710
case '6', '7', '9':
17021711
Addstring(shstrtab, ".rela.text")
17031712
Addstring(shstrtab, ".rela.rodata")
1704-
Addstring(shstrtab, ".rela.typelink")
1705-
Addstring(shstrtab, ".rela.gosymtab")
1706-
Addstring(shstrtab, ".rela.gopclntab")
1713+
Addstring(shstrtab, ".rela"+relro_prefix+".typelink")
1714+
Addstring(shstrtab, ".rela"+relro_prefix+".gosymtab")
1715+
Addstring(shstrtab, ".rela"+relro_prefix+".gopclntab")
17071716
Addstring(shstrtab, ".rela.noptrdata")
17081717
Addstring(shstrtab, ".rela.data")
1718+
if UseRelro() {
1719+
Addstring(shstrtab, ".rela.data.rel.ro")
1720+
}
17091721

17101722
default:
17111723
Addstring(shstrtab, ".rel.text")
17121724
Addstring(shstrtab, ".rel.rodata")
1713-
Addstring(shstrtab, ".rel.typelink")
1714-
Addstring(shstrtab, ".rel.gosymtab")
1715-
Addstring(shstrtab, ".rel.gopclntab")
1725+
Addstring(shstrtab, ".rel"+relro_prefix+".typelink")
1726+
Addstring(shstrtab, ".rel"+relro_prefix+".gosymtab")
1727+
Addstring(shstrtab, ".rel"+relro_prefix+".gopclntab")
17161728
Addstring(shstrtab, ".rel.noptrdata")
17171729
Addstring(shstrtab, ".rel.data")
1730+
if UseRelro() {
1731+
Addstring(shstrtab, ".rel.data.rel.ro")
1732+
}
17181733
}
17191734

17201735
// add a .note.GNU-stack section to mark the stack as non-executable

‎src/cmd/link/internal/ld/lib.go

+20-1
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,12 @@ func DynlinkingGo() bool {
174174
return Buildmode == BuildmodeShared || Linkshared
175175
}
176176

177+
// UseRelro returns whether to make use of "read only relocations" aka
178+
// relro.
179+
func UseRelro() bool {
180+
return (Buildmode == BuildmodeCShared || Buildmode == BuildmodeShared) && Iself
181+
}
182+
177183
var (
178184
Thestring string
179185
Thelinkarch *LinkArch
@@ -980,6 +986,9 @@ func hostlink() {
980986
argv = append(argv, "-dynamiclib")
981987
} else {
982988
argv = append(argv, "-Wl,-Bsymbolic")
989+
if UseRelro() {
990+
argv = append(argv, "-Wl,-z,relro")
991+
}
983992
argv = append(argv, "-shared")
984993
}
985994
case BuildmodeShared:
@@ -991,7 +1000,10 @@ func hostlink() {
9911000
// think we may well end up wanting to use -Bsymbolic here
9921001
// anyway.
9931002
argv = append(argv, "-Wl,-Bsymbolic-functions")
994-
argv = append(argv, "-shared")
1003+
if UseRelro() {
1004+
argv = append(argv, "-shared")
1005+
}
1006+
argv = append(argv, "-Wl,-z,relro")
9951007
}
9961008

9971009
if Linkshared && Iself {
@@ -1771,6 +1783,13 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) {
17711783
obj.SGOSTRING,
17721784
obj.SGOFUNC,
17731785
obj.SGCBITS,
1786+
obj.STYPERELRO,
1787+
obj.SSTRINGRELRO,
1788+
obj.SGOSTRINGRELRO,
1789+
obj.SGOFUNCRELRO,
1790+
obj.SGCBITSRELRO,
1791+
obj.SRODATARELRO,
1792+
obj.STYPELINK,
17741793
obj.SWINDOWS:
17751794
if !s.Reachable {
17761795
continue

‎src/cmd/link/internal/ld/symtab.go

+25-3
Original file line numberDiff line numberDiff line change
@@ -350,13 +350,29 @@ func symtab() {
350350

351351
// pseudo-symbols to mark locations of type, string, and go string data.
352352
var symtype *LSym
353-
if !DynlinkingGo() {
353+
var symtyperel *LSym
354+
if UseRelro() && Buildmode == BuildmodeCShared {
355+
s = Linklookup(Ctxt, "type.*", 0)
356+
357+
s.Type = obj.STYPE
358+
s.Size = 0
359+
s.Reachable = true
360+
symtype = s
361+
362+
s = Linklookup(Ctxt, "typerel.*", 0)
363+
364+
s.Type = obj.STYPERELRO
365+
s.Size = 0
366+
s.Reachable = true
367+
symtyperel = s
368+
} else if !DynlinkingGo() {
354369
s = Linklookup(Ctxt, "type.*", 0)
355370

356371
s.Type = obj.STYPE
357372
s.Size = 0
358373
s.Reachable = true
359374
symtype = s
375+
symtyperel = s
360376
}
361377

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

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

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

402419
if strings.HasPrefix(s.Name, "type.") && !DynlinkingGo() {
403-
s.Type = obj.STYPE
404420
s.Hide = 1
405-
s.Outer = symtype
421+
if UseRelro() && len(s.R) > 0 {
422+
s.Type = obj.STYPERELRO
423+
s.Outer = symtyperel
424+
} else {
425+
s.Type = obj.STYPE
426+
s.Outer = symtype
427+
}
406428
}
407429

408430
if strings.HasPrefix(s.Name, "go.typelink.") {
40 Bytes
Binary file not shown.

‎src/cmd/newlink/testdata/autoweak.6

40 Bytes
Binary file not shown.

‎src/cmd/newlink/testdata/dead.6

40 Bytes
Binary file not shown.

‎src/cmd/newlink/testdata/hello.6

40 Bytes
Binary file not shown.

‎src/cmd/newlink/testdata/layout.6

40 Bytes
Binary file not shown.

‎src/cmd/newlink/testdata/pclntab.6

40 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)
Please sign in to comment.