From 276803d6111b46c66956c99d982d70f23820ba5d Mon Sep 17 00:00:00 2001 From: David Crawshaw Date: Mon, 5 Sep 2016 23:29:16 -0400 Subject: [PATCH] cmd/link: introduce a rel.ro segment When internally linking with using rel.ro sections, this segment covers the sections. To do this, move to other read-only sections, SELFROSECT and SMACHOPLT, out of the way. Part of adding PIE internal linking on linux/amd64. Change-Id: I4fb3d180e92f7e801789ab89864010faf5a2cb6d Reviewed-on: https://go-review.googlesource.com/28538 Run-TryBot: David Crawshaw TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- src/cmd/internal/obj/link.go | 11 +- src/cmd/link/internal/amd64/asm.go | 8 +- src/cmd/link/internal/arm/asm.go | 8 +- src/cmd/link/internal/arm64/asm.go | 8 +- src/cmd/link/internal/ld/data.go | 150 +++++++++++++++++----------- src/cmd/link/internal/ld/elf.go | 15 +++ src/cmd/link/internal/ld/lib.go | 9 +- src/cmd/link/internal/mips64/asm.go | 8 +- src/cmd/link/internal/ppc64/asm.go | 8 +- src/cmd/link/internal/s390x/asm.go | 8 +- src/cmd/link/internal/x86/asm.go | 7 ++ 11 files changed, 168 insertions(+), 72 deletions(-) diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index 513e00b825..614be6f74f 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -384,6 +384,7 @@ const ( STEXT SELFRXSECT + // Read-only sections. STYPE SSTRING SGOSTRING @@ -393,6 +394,11 @@ const ( SRODATA SFUNCTAB + SELFROSECT + SMACHOPLT + + // Read-only sections with relocations. + // // 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 @@ -412,12 +418,13 @@ const ( SRODATARELRO SFUNCTABRELRO + // Part of .data.rel.ro if it exists, otherwise part of .rodata. STYPELINK SITABLINK SSYMTAB SPCLNTAB - SELFROSECT - SMACHOPLT + + // Writable sections. SELFSECT SMACHO SMACHOGOT diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go index 639aae9fe7..5abf79f818 100644 --- a/src/cmd/link/internal/amd64/asm.go +++ b/src/cmd/link/internal/amd64/asm.go @@ -623,10 +623,16 @@ func asmb(ctxt *ld.Link) { if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f rodatblk\n", obj.Cputime()) } - ld.Cseek(int64(ld.Segrodata.Fileoff)) ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) } + if ld.Segrelrodata.Filelen > 0 { + if ctxt.Debugvlog != 0 { + ctxt.Logf("%5.2f relrodatblk\n", obj.Cputime()) + } + ld.Cseek(int64(ld.Segrelrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + } if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f datblk\n", obj.Cputime()) diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go index 881b559e68..ae6f35dde5 100644 --- a/src/cmd/link/internal/arm/asm.go +++ b/src/cmd/link/internal/arm/asm.go @@ -602,10 +602,16 @@ func asmb(ctxt *ld.Link) { if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f rodatblk\n", obj.Cputime()) } - ld.Cseek(int64(ld.Segrodata.Fileoff)) ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) } + if ld.Segrelrodata.Filelen > 0 { + if ctxt.Debugvlog != 0 { + ctxt.Logf("%5.2f relrodatblk\n", obj.Cputime()) + } + ld.Cseek(int64(ld.Segrelrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + } if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f datblk\n", obj.Cputime()) diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go index 70a6cfd1fa..66613d4686 100644 --- a/src/cmd/link/internal/arm64/asm.go +++ b/src/cmd/link/internal/arm64/asm.go @@ -411,10 +411,16 @@ func asmb(ctxt *ld.Link) { if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f rodatblk\n", obj.Cputime()) } - ld.Cseek(int64(ld.Segrodata.Fileoff)) ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) } + if ld.Segrelrodata.Filelen > 0 { + if ctxt.Debugvlog != 0 { + ctxt.Logf("%5.2f relrodatblk\n", obj.Cputime()) + } + ld.Cseek(int64(ld.Segrelrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + } if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f datblk\n", obj.Cputime()) diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 6496458061..07911e2328 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -1508,6 +1508,33 @@ func (ctxt *Link) dodata() { } sect.Length = uint64(datsize) - sect.Vaddr + /* read-only ELF, Mach-O sections */ + for _, s := range data[obj.SELFROSECT] { + sect = addsection(segro, s.Name, 04) + sect.Align = symalign(s) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + s.Sect = sect + s.Type = obj.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + datsize += s.Size + sect.Length = uint64(datsize) - sect.Vaddr + } + checkdatsize(ctxt, datsize, obj.SELFROSECT) + + for _, s := range data[obj.SMACHOPLT] { + sect = addsection(segro, s.Name, 04) + sect.Align = symalign(s) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + s.Sect = sect + s.Type = obj.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + datsize += s.Size + sect.Length = uint64(datsize) - sect.Vaddr + } + checkdatsize(ctxt, datsize, obj.SMACHOPLT) + // 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 @@ -1518,14 +1545,26 @@ func (ctxt *Link) dodata() { // situation. // TODO(mwhudson): It would make sense to do this more widely, but it makes // the system linker segfault on darwin. - relroPerms := 04 - relroPrefix := "" + addrelrosection := func(suffix string) *Section { + return addsection(segro, suffix, 04) + } if UseRelro() { - relroPerms = 06 - relroPrefix = ".data.rel.ro" + addrelrosection = func(suffix string) *Section { + seg := &Segrelrodata + if Linkmode == LinkExternal { + // Using a separate segment with an external + // linker results in some programs moving + // their data sections unexpectedly, which + // corrupts the moduledata. So we use the + // rodata segment and let the external linker + // sort out a rel.ro segment. + seg = &Segrodata + } + return addsection(seg, ".data.rel.ro"+suffix, 06) + } /* data only written by relocations */ - sect = addsection(segro, ".data.rel.ro", 06) + sect = addrelrosection("") sect.Vaddr = 0 Linklookup(ctxt, "runtime.types", 0).Sect = sect @@ -1554,11 +1593,10 @@ func (ctxt *Link) dodata() { } sect.Length = uint64(datsize) - sect.Vaddr - } /* typelink */ - sect = addsection(segro, relroPrefix+".typelink", relroPerms) + sect = addrelrosection(".typelink") sect.Align = dataMaxAlign[obj.STYPELINK] datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) @@ -1575,7 +1613,7 @@ func (ctxt *Link) dodata() { sect.Length = uint64(datsize) - sect.Vaddr /* itablink */ - sect = addsection(segro, relroPrefix+".itablink", relroPerms) + sect = addrelrosection(".itablink") sect.Align = dataMaxAlign[obj.SITABLINK] datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) @@ -1592,7 +1630,7 @@ func (ctxt *Link) dodata() { sect.Length = uint64(datsize) - sect.Vaddr /* gosymtab */ - sect = addsection(segro, relroPrefix+".gosymtab", relroPerms) + sect = addrelrosection(".gosymtab") sect.Align = dataMaxAlign[obj.SSYMTAB] datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) @@ -1609,7 +1647,7 @@ func (ctxt *Link) dodata() { sect.Length = uint64(datsize) - sect.Vaddr /* gopclntab */ - sect = addsection(segro, relroPrefix+".gopclntab", relroPerms) + sect = addrelrosection(".gopclntab") sect.Align = dataMaxAlign[obj.SPCLNTAB] datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) @@ -1625,33 +1663,6 @@ func (ctxt *Link) dodata() { checkdatsize(ctxt, datsize, obj.SRODATA) sect.Length = uint64(datsize) - sect.Vaddr - /* read-only ELF, Mach-O sections */ - for _, s := range data[obj.SELFROSECT] { - sect = addsection(segro, s.Name, 04) - sect.Align = symalign(s) - datsize = Rnd(datsize, int64(sect.Align)) - sect.Vaddr = uint64(datsize) - s.Sect = sect - s.Type = obj.SRODATA - s.Value = int64(uint64(datsize) - sect.Vaddr) - datsize += s.Size - sect.Length = uint64(datsize) - sect.Vaddr - } - checkdatsize(ctxt, datsize, obj.SELFROSECT) - - for _, s := range data[obj.SMACHOPLT] { - sect = addsection(segro, s.Name, 04) - sect.Align = symalign(s) - datsize = Rnd(datsize, int64(sect.Align)) - sect.Vaddr = uint64(datsize) - s.Sect = sect - s.Type = obj.SRODATA - s.Value = int64(uint64(datsize) - sect.Vaddr) - datsize += s.Size - sect.Length = uint64(datsize) - sect.Vaddr - } - checkdatsize(ctxt, datsize, obj.SMACHOPLT) - // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. if datsize != int64(uint32(datsize)) { ctxt.Diag("read-only data segment too large") @@ -1711,6 +1722,10 @@ func (ctxt *Link) dodata() { sect.Extnum = int16(n) n++ } + for sect := Segrelrodata.Sect; sect != nil; sect = sect.Next { + sect.Extnum = int16(n) + n++ + } for sect := Segdata.Sect; sect != nil; sect = sect.Next { sect.Extnum = int16(n) n++ @@ -1897,6 +1912,17 @@ func (ctxt *Link) address() { if Segrodata.Sect != nil { // align to page boundary so as not to mix // rodata and executable text. + // + // Note: gold or GNU ld will reduce the size of the executable + // file by arranging for the relro segment to end at a page + // boundary, and overlap the end of the text segment with the + // start of the relro segment in the file. The PT_LOAD segments + // will be such that the last page of the text segment will be + // mapped twice, once r-x and once starting out rw- and, after + // relocation processing, changed to r--. + // + // Ideally the last page of the text segment would not be + // writable even for this short period. va = uint64(Rnd(int64(va), int64(*FlagRound))) Segrodata.Rwx = 04 @@ -1912,6 +1938,24 @@ func (ctxt *Link) address() { Segrodata.Length = va - Segrodata.Vaddr Segrodata.Filelen = Segrodata.Length } + if Segrelrodata.Sect != nil { + // align to page boundary so as not to mix + // rodata, rel-ro data, and executable text. + va = uint64(Rnd(int64(va), int64(*FlagRound))) + + Segrelrodata.Rwx = 06 + Segrelrodata.Vaddr = va + Segrelrodata.Fileoff = va - Segrodata.Vaddr + Segrodata.Fileoff + Segrelrodata.Filelen = 0 + for s := Segrelrodata.Sect; s != nil; s = s.Next { + va = uint64(Rnd(int64(va), int64(s.Align))) + s.Vaddr = va + va += s.Length + } + + Segrelrodata.Length = va - Segrelrodata.Vaddr + Segrelrodata.Filelen = Segrelrodata.Length + } va = uint64(Rnd(int64(va), int64(*FlagRound))) Segdata.Rwx = 06 @@ -1979,24 +2023,15 @@ func (ctxt *Link) address() { Segdwarf.Filelen = va - Segdwarf.Vaddr - text := Segtext.Sect - var rodata *Section - if Segrodata.Sect != nil { - rodata = Segrodata.Sect - } else { - rodata = text.Next - } - var relrodata *Section - typelink := rodata.Next - if UseRelro() { - // There is another section (.data.rel.ro) when building a shared - // object on elf systems. - relrodata = typelink - typelink = typelink.Next - } - itablink := typelink.Next - symtab := itablink.Next - pclntab := symtab.Next + var ( + text = Segtext.Sect + rodata = Linklookup(ctxt, "runtime.rodata", 0).Sect + typelink = Linklookup(ctxt, "runtime.typelink", 0).Sect + itablink = Linklookup(ctxt, "runtime.itablink", 0).Sect + symtab = Linklookup(ctxt, "runtime.symtab", 0).Sect + pclntab = Linklookup(ctxt, "runtime.pclntab", 0).Sect + types = Linklookup(ctxt, "runtime.types", 0).Sect + ) for _, s := range datap { ctxt.Cursym = s @@ -2025,11 +2060,6 @@ func (ctxt *Link) address() { s.Value = int64(sectSym.Sect.Vaddr + 16) } - types := relrodata - if types == nil { - types = rodata - } - ctxt.xdefine("runtime.text", obj.STEXT, int64(text.Vaddr)) ctxt.xdefine("runtime.etext", obj.STEXT, int64(text.Vaddr+text.Length)) if Headtype == obj.Hwindows || Headtype == obj.Hwindowsgui { diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index 3275c10d78..6d09d173b8 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -1783,6 +1783,9 @@ func Elfemitreloc(ctxt *Link) { for sect := Segrodata.Sect; sect != nil; sect = sect.Next { elfrelocsect(ctxt, sect, datap) } + for sect := Segrelrodata.Sect; sect != nil; sect = sect.Next { + elfrelocsect(ctxt, sect, datap) + } for sect := Segdata.Sect; sect != nil; sect = sect.Next { elfrelocsect(ctxt, sect, datap) } @@ -2114,6 +2117,9 @@ func Asmbelfsetup(ctxt *Link) { for sect := Segrodata.Sect; sect != nil; sect = sect.Next { elfshalloc(ctxt, sect) } + for sect := Segrelrodata.Sect; sect != nil; sect = sect.Next { + elfshalloc(ctxt, sect) + } for sect := Segdata.Sect; sect != nil; sect = sect.Next { elfshalloc(ctxt, sect) } @@ -2283,6 +2289,9 @@ func Asmbelf(ctxt *Link, symo int64) { if Segrodata.Sect != nil { elfphload(ctxt, &Segrodata) } + if Segrelrodata.Sect != nil { + elfphload(ctxt, &Segrelrodata) + } elfphload(ctxt, &Segdata) /* Dynamic linking sections */ @@ -2482,6 +2491,9 @@ elfobj: for sect := Segrodata.Sect; sect != nil; sect = sect.Next { elfshbits(ctxt, sect) } + for sect := Segrelrodata.Sect; sect != nil; sect = sect.Next { + elfshbits(ctxt, sect) + } for sect := Segdata.Sect; sect != nil; sect = sect.Next { elfshbits(ctxt, sect) } @@ -2496,6 +2508,9 @@ elfobj: for sect := Segrodata.Sect; sect != nil; sect = sect.Next { elfshreloc(ctxt, sect) } + for sect := Segrelrodata.Sect; sect != nil; sect = sect.Next { + elfshreloc(ctxt, sect) + } for sect := Segdata.Sect; sect != nil; sect = sect.Next { elfshreloc(ctxt, sect) } diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 4f4473cc12..c7e2c2433f 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -191,10 +191,11 @@ var ( ) var ( - Segtext Segment - Segrodata Segment - Segdata Segment - Segdwarf Segment + Segtext Segment + Segrodata Segment + Segrelrodata Segment + Segdata Segment + Segdwarf Segment ) /* whence for ldpkg */ diff --git a/src/cmd/link/internal/mips64/asm.go b/src/cmd/link/internal/mips64/asm.go index 1c7751dc16..f0c5926a19 100644 --- a/src/cmd/link/internal/mips64/asm.go +++ b/src/cmd/link/internal/mips64/asm.go @@ -203,10 +203,16 @@ func asmb(ctxt *ld.Link) { if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f rodatblk\n", obj.Cputime()) } - ld.Cseek(int64(ld.Segrodata.Fileoff)) ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) } + if ld.Segrelrodata.Filelen > 0 { + if ctxt.Debugvlog != 0 { + ctxt.Logf("%5.2f rodatblk\n", obj.Cputime()) + } + ld.Cseek(int64(ld.Segrelrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + } if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f datblk\n", obj.Cputime()) diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index 9cbd34b981..3bbe9ec8ba 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -824,10 +824,16 @@ func asmb(ctxt *ld.Link) { if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f rodatblk\n", obj.Cputime()) } - ld.Cseek(int64(ld.Segrodata.Fileoff)) ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) } + if ld.Segrelrodata.Filelen > 0 { + if ctxt.Debugvlog != 0 { + ctxt.Logf("%5.2f relrodatblk\n", obj.Cputime()) + } + ld.Cseek(int64(ld.Segrelrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + } if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f datblk\n", obj.Cputime()) diff --git a/src/cmd/link/internal/s390x/asm.go b/src/cmd/link/internal/s390x/asm.go index 47e042d4fa..0c44ccd384 100644 --- a/src/cmd/link/internal/s390x/asm.go +++ b/src/cmd/link/internal/s390x/asm.go @@ -519,10 +519,16 @@ func asmb(ctxt *ld.Link) { if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f rodatblk\n", obj.Cputime()) } - ld.Cseek(int64(ld.Segrodata.Fileoff)) ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) } + if ld.Segrelrodata.Filelen > 0 { + if ctxt.Debugvlog != 0 { + ctxt.Logf("%5.2f rodatblk\n", obj.Cputime()) + } + ld.Cseek(int64(ld.Segrelrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + } if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f datblk\n", obj.Cputime()) diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go index c80d9629e1..972fc80bf0 100644 --- a/src/cmd/link/internal/x86/asm.go +++ b/src/cmd/link/internal/x86/asm.go @@ -649,6 +649,13 @@ func asmb(ctxt *ld.Link) { ld.Cseek(int64(ld.Segrodata.Fileoff)) ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) } + if ld.Segrelrodata.Filelen > 0 { + if ctxt.Debugvlog != 0 { + ctxt.Logf("%5.2f relrodatblk\n", obj.Cputime()) + } + ld.Cseek(int64(ld.Segrelrodata.Fileoff)) + ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen)) + } if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f datblk\n", obj.Cputime()) -- 2.48.1