Skip to content

Commit b0d175e

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 ee522e2 commit b0d175e

File tree

6 files changed

+37
-11
lines changed

6 files changed

+37
-11
lines changed

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,7 @@ var validLinkerFlags = []*lazyregexp.Regexp{
206206
re(`-Wl,-?-unresolved-symbols=[^,]+`),
207207
re(`-Wl,--(no-)?warn-([^,]+)`),
208208
re(`-Wl,-?-wrap[=,][^,@\-][^,]*`),
209-
re(`-Wl,-z,(no)?execstack`),
210-
re(`-Wl,-z,relro`),
209+
re(`-Wl(,-z,(relro|now|(no)?execstack))+`),
211210

212211
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)
213212
re(`\./.*\.(a|o|obj|dll|dylib|so|tbd)`),

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

+4
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

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ Flags:
4545
Disassemble output.
4646
-asan
4747
Link with C/C++ address sanitizer support.
48+
-bindnow
49+
Mark ELF object for immediate function binding (default false).
4850
-buildid id
4951
Record id as Go toolchain build id.
5052
-buildmode mode

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

+26-8
Original file line numberDiff line numberDiff line change
@@ -1044,10 +1044,16 @@ func elfdynhash(ctxt *Link) {
10441044
}
10451045

10461046
s = ldr.CreateSymForUpdate(".dynamic", 0)
1047-
if ctxt.BuildMode == BuildModePIE {
1048-
// https://github.com/bminor/glibc/blob/895ef79e04a953cac1493863bcae29ad85657ee1/elf/elf.h#L986
1049-
const DTFLAGS_1_PIE = 0x08000000
1050-
Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS_1, uint64(DTFLAGS_1_PIE))
1047+
if *flagBindNow {
1048+
Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS, uint64(elf.DF_BIND_NOW))
1049+
}
1050+
switch {
1051+
case ctxt.BuildMode == BuildModePIE && *flagBindNow:
1052+
Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS_1, uint64(elf.DF_1_PIE)|uint64(elf.DF_1_NOW))
1053+
case ctxt.BuildMode == BuildModePIE && !*flagBindNow:
1054+
Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS_1, uint64(elf.DF_1_PIE))
1055+
case ctxt.BuildMode != BuildModePIE && *flagBindNow:
1056+
Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS_1, uint64(elf.DF_1_NOW))
10511057
}
10521058
elfverneed = nfile
10531059
if elfverneed != 0 {
@@ -1095,6 +1101,7 @@ func elfphload(seg *sym.Segment) *ElfPhdr {
10951101
func elfphrelro(seg *sym.Segment) {
10961102
ph := newElfPhdr()
10971103
ph.Type = elf.PT_GNU_RELRO
1104+
ph.Flags = elf.PF_R
10981105
ph.Vaddr = seg.Vaddr
10991106
ph.Paddr = seg.Vaddr
11001107
ph.Memsz = seg.Length
@@ -1533,7 +1540,11 @@ func (ctxt *Link) doelf() {
15331540

15341541
/* global offset table */
15351542
got := ldr.CreateSymForUpdate(".got", 0)
1536-
got.SetType(sym.SELFGOT) // writable
1543+
if ctxt.UseRelro() {
1544+
got.SetType(sym.SRODATARELRO)
1545+
} else {
1546+
got.SetType(sym.SELFGOT) // writable
1547+
}
15371548

15381549
/* ppc64 glink resolver */
15391550
if ctxt.IsPPC64() {
@@ -1546,7 +1557,11 @@ func (ctxt *Link) doelf() {
15461557
hash.SetType(sym.SELFROSECT)
15471558

15481559
gotplt := ldr.CreateSymForUpdate(".got.plt", 0)
1549-
gotplt.SetType(sym.SELFSECT) // writable
1560+
if ctxt.UseRelro() && *flagBindNow {
1561+
gotplt.SetType(sym.SRODATARELRO)
1562+
} else {
1563+
gotplt.SetType(sym.SELFSECT) // writable
1564+
}
15501565

15511566
plt := ldr.CreateSymForUpdate(".plt", 0)
15521567
if ctxt.IsPPC64() {
@@ -1568,9 +1583,12 @@ func (ctxt *Link) doelf() {
15681583

15691584
/* define dynamic elf table */
15701585
dynamic := ldr.CreateSymForUpdate(".dynamic", 0)
1571-
if thearch.ELF.DynamicReadOnly {
1586+
switch {
1587+
case thearch.ELF.DynamicReadOnly:
15721588
dynamic.SetType(sym.SELFROSECT)
1573-
} else {
1589+
case ctxt.UseRelro():
1590+
dynamic.SetType(sym.SRODATARELRO)
1591+
default:
15741592
dynamic.SetType(sym.SELFSECT)
15751593
}
15761594

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -1558,12 +1558,14 @@ func (ctxt *Link) hostlink() {
15581558
}
15591559

15601560
var altLinker string
1561-
if ctxt.IsELF && ctxt.DynlinkingGo() {
1561+
if ctxt.IsELF && (ctxt.DynlinkingGo() || *flagBindNow) {
15621562
// We force all symbol resolution to be done at program startup
15631563
// because lazy PLT resolution can use large amounts of stack at
15641564
// times we cannot allow it to do so.
15651565
argv = append(argv, "-Wl,-z,now")
1566+
}
15661567

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

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

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ func init() {
6161
// Flags used by the linker. The exported flags are used by the architecture-specific packages.
6262
var (
6363
flagBuildid = flag.String("buildid", "", "record `id` as Go toolchain build id")
64+
flagBindNow = flag.Bool("bindnow", false, "mark ELF object for immediate function binding")
6465

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

0 commit comments

Comments
 (0)