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")