From c79b2009ef0b82e9c50ced139b2fd752018da0dd Mon Sep 17 00:00:00 2001 From: Dmitri Goutnik Date: Wed, 15 Jun 2022 14:50:19 -0500 Subject: [PATCH] cmd/link: define ELF .note section on FreeBSD Write .note signature section when targeting FreeBSD, similar to NetBSD and OpenBSD. This allows binaries to declare the ABI version they were compiled for and opt out of ASLR when compiled with -race. Fixes #48164 Change-Id: Ie54dd5c70697a3f42a75fd640540350fd8a4dc71 Reviewed-on: https://go-review.googlesource.com/c/go/+/412494 Reviewed-by: Meng Zhuo Reviewed-by: Cherry Mui Reviewed-by: Than McIntosh Reviewed-by: Yuval Pavel Zholkover --- src/cmd/link/elf_test.go | 4 +- src/cmd/link/internal/ld/elf.go | 84 ++++++++++++++++++++++++++++-- src/cmd/link/internal/ld/target.go | 5 ++ 3 files changed, 87 insertions(+), 6 deletions(-) diff --git a/src/cmd/link/elf_test.go b/src/cmd/link/elf_test.go index dd202a32dc..8f7af2598c 100644 --- a/src/cmd/link/elf_test.go +++ b/src/cmd/link/elf_test.go @@ -204,8 +204,8 @@ func TestMergeNoteSections(t *testing.T) { expected := 1 switch runtime.GOOS { - case "linux", "freebsd", "dragonfly": - case "openbsd", "netbsd": + case "linux", "dragonfly": + case "openbsd", "netbsd", "freebsd": // These OSes require independent segment expected = 2 default: diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index dd5844bd10..41fc9ab76f 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -607,8 +607,13 @@ func elfWriteMipsAbiFlags(ctxt *Link) int { return int(sh.Size) } -func elfnote(sh *ElfShdr, startva uint64, resoff uint64, sz int) int { - n := 3*4 + uint64(sz) + resoff%4 +func elfnote(sh *ElfShdr, startva uint64, resoff uint64, sizes ...int) int { + n := resoff % 4 + // if section contains multiple notes (as is the case with FreeBSD signature), + // multiple note sizes can be specified + for _, sz := range sizes { + n += 3*4 + uint64(sz) + } sh.Type = uint32(elf.SHT_NOTE) sh.Flags = uint64(elf.SHF_ALLOC) @@ -714,6 +719,67 @@ func elfwriteopenbsdsig(out *OutBuf) int { return int(sh.Size) } +// FreeBSD Signature (as per sys/elf_common.h) +const ( + ELF_NOTE_FREEBSD_NAMESZ = 8 + ELF_NOTE_FREEBSD_DESCSZ = 4 + ELF_NOTE_FREEBSD_ABI_TAG = 1 + ELF_NOTE_FREEBSD_NOINIT_TAG = 2 + ELF_NOTE_FREEBSD_FEATURE_CTL_TAG = 4 + ELF_NOTE_FREEBSD_VERSION = 1203000 // 12.3-RELEASE + ELF_NOTE_FREEBSD_FCTL_ASLR_DISABLE = 0x1 +) + +const ELF_NOTE_FREEBSD_NAME = "FreeBSD\x00" + +func elffreebsdsig(sh *ElfShdr, startva uint64, resoff uint64) int { + n := ELF_NOTE_FREEBSD_NAMESZ + ELF_NOTE_FREEBSD_DESCSZ + // FreeBSD signature section contains 3 equally sized notes + return elfnote(sh, startva, resoff, n, n, n) +} + +// elfwritefreebsdsig writes FreeBSD .note section. +// +// See https://www.netbsd.org/docs/kernel/elf-notes.html for the description of +// a Note element format and +// https://github.com/freebsd/freebsd-src/blob/main/sys/sys/elf_common.h#L790 +// for the FreeBSD-specific values. +func elfwritefreebsdsig(out *OutBuf) int { + sh := elfshname(".note.tag") + if sh == nil { + return 0 + } + out.SeekSet(int64(sh.Off)) + + // NT_FREEBSD_ABI_TAG + out.Write32(ELF_NOTE_FREEBSD_NAMESZ) + out.Write32(ELF_NOTE_FREEBSD_DESCSZ) + out.Write32(ELF_NOTE_FREEBSD_ABI_TAG) + out.WriteString(ELF_NOTE_FREEBSD_NAME) + out.Write32(ELF_NOTE_FREEBSD_VERSION) + + // NT_FREEBSD_NOINIT_TAG + out.Write32(ELF_NOTE_FREEBSD_NAMESZ) + out.Write32(ELF_NOTE_FREEBSD_DESCSZ) + out.Write32(ELF_NOTE_FREEBSD_NOINIT_TAG) + out.WriteString(ELF_NOTE_FREEBSD_NAME) + out.Write32(0) + + // NT_FREEBSD_FEATURE_CTL + out.Write32(ELF_NOTE_FREEBSD_NAMESZ) + out.Write32(ELF_NOTE_FREEBSD_DESCSZ) + out.Write32(ELF_NOTE_FREEBSD_FEATURE_CTL_TAG) + out.WriteString(ELF_NOTE_FREEBSD_NAME) + if *flagRace { + // The race detector can't handle ASLR, turn the ASLR off when compiling with -race. + out.Write32(ELF_NOTE_FREEBSD_FCTL_ASLR_DISABLE) + } else { + out.Write32(0) + } + + return int(sh.Size) +} + func addbuildinfo(val string) { if !strings.HasPrefix(val, "0x") { Exitf("-B argument must start with 0x: %s", val) @@ -1327,6 +1393,9 @@ func (ctxt *Link) doelf() { if ctxt.IsOpenbsd() { shstrtab.Addstring(".note.openbsd.ident") } + if ctxt.IsFreebsd() { + shstrtab.Addstring(".note.tag") + } if len(buildinfo) > 0 { shstrtab.Addstring(".note.gnu.build-id") } @@ -1820,7 +1889,7 @@ func asmbElf(ctxt *Link) { phsh(ph, sh) } - if ctxt.HeadType == objabi.Hnetbsd || ctxt.HeadType == objabi.Hopenbsd { + if ctxt.HeadType == objabi.Hnetbsd || ctxt.HeadType == objabi.Hopenbsd || ctxt.HeadType == objabi.Hfreebsd { var sh *ElfShdr switch ctxt.HeadType { case objabi.Hnetbsd: @@ -1830,8 +1899,12 @@ func asmbElf(ctxt *Link) { case objabi.Hopenbsd: sh = elfshname(".note.openbsd.ident") resoff -= int64(elfopenbsdsig(sh, uint64(startva), uint64(resoff))) + + case objabi.Hfreebsd: + sh = elfshname(".note.tag") + resoff -= int64(elffreebsdsig(sh, uint64(startva), uint64(resoff))) } - // netbsd and openbsd require ident in an independent segment. + // NetBSD, OpenBSD and FreeBSD require ident in an independent segment. pnotei := newElfPhdr() pnotei.Type = elf.PT_NOTE pnotei.Flags = elf.PF_R @@ -2209,6 +2282,9 @@ elfobj: if ctxt.HeadType == objabi.Hopenbsd { a += int64(elfwriteopenbsdsig(ctxt.Out)) } + if ctxt.HeadType == objabi.Hfreebsd { + a += int64(elfwritefreebsdsig(ctxt.Out)) + } if len(buildinfo) > 0 { a += int64(elfwritebuildinfo(ctxt.Out)) } diff --git a/src/cmd/link/internal/ld/target.go b/src/cmd/link/internal/ld/target.go index cc8e4181b6..d0ce99f3e9 100644 --- a/src/cmd/link/internal/ld/target.go +++ b/src/cmd/link/internal/ld/target.go @@ -176,6 +176,11 @@ func (t *Target) IsOpenbsd() bool { return t.HeadType == objabi.Hopenbsd } +func (t *Target) IsFreebsd() bool { + t.mustSetHeadType() + return t.HeadType == objabi.Hfreebsd +} + func (t *Target) mustSetHeadType() { if t.HeadType == objabi.Hunknown { panic("HeadType is not set") -- 2.50.0