Skip to content

Commit 520d6e6

Browse files
committed
cmd/link: add option to enable full RELRO for ELF
-bindnow linker option enables full RELRO on ELF targets. This options defaults to false and preserves current behavior - partial relro for buildmode=pie. Also, the following changes were made to align internal linker's behavior with external ELF linkers: - GNU_RELRO segment is marked Read-only - .dynamic is a relro section for partial and full RELRO - .got is a relro section for partial and full RELRO - .got.plt is a relro section for full RELRO only Supersedes #45681 (golang.org/cl/312509)
1 parent 600225f commit 520d6e6

File tree

6 files changed

+37
-11
lines changed

6 files changed

+37
-11
lines changed

src/cmd/go/internal/work/security.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,7 @@ var validLinkerFlags = []*lazyregexp.Regexp{
210210
re(`-Wl,-?-unresolved-symbols=[^,]+`),
211211
re(`-Wl,--(no-)?warn-([^,]+)`),
212212
re(`-Wl,-?-wrap[=,][^,@\-][^,]*`),
213-
re(`-Wl,-z,(no)?execstack`),
214-
re(`-Wl,-z,relro`),
213+
re(`-Wl(,-z,(relro|now|(no)?execstack))+`),
215214

216215
re(`[a-zA-Z0-9_/].*\.(a|o|obj|dll|dylib|so|tbd)`), // direct linker inputs: x.o or libfoo.so (but not -foo.o or @foo.o)
217216
re(`\./.*\.(a|o|obj|dll|dylib|so|tbd)`),

src/cmd/go/internal/work/security_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,10 @@ var goodLinkerFlags = [][]string{
167167
{"-Wl,-framework", "-Wl,Chocolate"},
168168
{"-Wl,-framework,Chocolate"},
169169
{"-Wl,-unresolved-symbols=ignore-all"},
170+
{"-Wl,-z,relro"},
171+
{"-Wl,-z,relro,-z,now"},
172+
{"-Wl,-z,now"},
173+
{"-Wl,-z,noexecstack"},
170174
{"libcgotbdtest.tbd"},
171175
{"./libcgotbdtest.tbd"},
172176
}

src/cmd/link/doc.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ Flags:
4747
Link with C/C++ address sanitizer support.
4848
-aslr
4949
Enable ASLR for buildmode=c-shared on windows (default true).
50+
-bindnow
51+
Mark ELF object for immediate function binding (default false).
5052
-buildid id
5153
Record id as Go toolchain build id.
5254
-buildmode mode

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

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,10 +1056,16 @@ func elfdynhash(ctxt *Link) {
10561056
}
10571057

10581058
s = ldr.CreateSymForUpdate(".dynamic", 0)
1059-
if ctxt.BuildMode == BuildModePIE {
1060-
// https://github.com/bminor/glibc/blob/895ef79e04a953cac1493863bcae29ad85657ee1/elf/elf.h#L986
1061-
const DTFLAGS_1_PIE = 0x08000000
1062-
Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS_1, uint64(DTFLAGS_1_PIE))
1059+
if *flagBindNow {
1060+
Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS, uint64(elf.DF_BIND_NOW))
1061+
}
1062+
switch {
1063+
case ctxt.BuildMode == BuildModePIE && *flagBindNow:
1064+
Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS_1, uint64(elf.DF_1_PIE)|uint64(elf.DF_1_NOW))
1065+
case ctxt.BuildMode == BuildModePIE && !*flagBindNow:
1066+
Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS_1, uint64(elf.DF_1_PIE))
1067+
case ctxt.BuildMode != BuildModePIE && *flagBindNow:
1068+
Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS_1, uint64(elf.DF_1_NOW))
10631069
}
10641070
elfverneed = nfile
10651071
if elfverneed != 0 {
@@ -1107,6 +1113,7 @@ func elfphload(seg *sym.Segment) *ElfPhdr {
11071113
func elfphrelro(seg *sym.Segment) {
11081114
ph := newElfPhdr()
11091115
ph.Type = elf.PT_GNU_RELRO
1116+
ph.Flags = elf.PF_R
11101117
ph.Vaddr = seg.Vaddr
11111118
ph.Paddr = seg.Vaddr
11121119
ph.Memsz = seg.Length
@@ -1556,7 +1563,11 @@ func (ctxt *Link) doelf() {
15561563

15571564
/* global offset table */
15581565
got := ldr.CreateSymForUpdate(".got", 0)
1559-
got.SetType(sym.SELFGOT) // writable
1566+
if ctxt.UseRelro() {
1567+
got.SetType(sym.SRODATARELRO)
1568+
} else {
1569+
got.SetType(sym.SELFGOT) // writable
1570+
}
15601571

15611572
/* ppc64 glink resolver */
15621573
if ctxt.IsPPC64() {
@@ -1569,7 +1580,11 @@ func (ctxt *Link) doelf() {
15691580
hash.SetType(sym.SELFROSECT)
15701581

15711582
gotplt := ldr.CreateSymForUpdate(".got.plt", 0)
1572-
gotplt.SetType(sym.SELFSECT) // writable
1583+
if ctxt.UseRelro() && *flagBindNow {
1584+
gotplt.SetType(sym.SRODATARELRO)
1585+
} else {
1586+
gotplt.SetType(sym.SELFSECT) // writable
1587+
}
15731588

15741589
plt := ldr.CreateSymForUpdate(".plt", 0)
15751590
if ctxt.IsPPC64() {
@@ -1591,9 +1606,12 @@ func (ctxt *Link) doelf() {
15911606

15921607
/* define dynamic elf table */
15931608
dynamic := ldr.CreateSymForUpdate(".dynamic", 0)
1594-
if thearch.ELF.DynamicReadOnly {
1609+
switch {
1610+
case thearch.ELF.DynamicReadOnly:
15951611
dynamic.SetType(sym.SELFROSECT)
1596-
} else {
1612+
case ctxt.UseRelro():
1613+
dynamic.SetType(sym.SRODATARELRO)
1614+
default:
15971615
dynamic.SetType(sym.SELFSECT)
15981616
}
15991617

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1599,12 +1599,14 @@ func (ctxt *Link) hostlink() {
15991599
}
16001600

16011601
var altLinker string
1602-
if ctxt.IsELF && ctxt.DynlinkingGo() {
1602+
if ctxt.IsELF && (ctxt.DynlinkingGo() || *flagBindNow) {
16031603
// We force all symbol resolution to be done at program startup
16041604
// because lazy PLT resolution can use large amounts of stack at
16051605
// times we cannot allow it to do so.
16061606
argv = append(argv, "-Wl,-z,now")
1607+
}
16071608

1609+
if ctxt.IsELF && ctxt.DynlinkingGo() {
16081610
// Do not let the host linker generate COPY relocations. These
16091611
// can move symbols out of sections that rely on stable offsets
16101612
// from the beginning of the section (like sym.STYPE).

src/cmd/link/internal/ld/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ func init() {
6363
// Flags used by the linker. The exported flags are used by the architecture-specific packages.
6464
var (
6565
flagBuildid = flag.String("buildid", "", "record `id` as Go toolchain build id")
66+
flagBindNow = flag.Bool("bindnow", false, "mark ELF object for immediate function binding")
6667

6768
flagOutfile = flag.String("o", "", "write output to `file`")
6869
flagPluginPath = flag.String("pluginpath", "", "full path name for plugin")

0 commit comments

Comments
 (0)