// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ld

import (
	"cmd/internal/hash"
	"cmd/internal/objabi"
	"cmd/internal/sys"
	"cmd/link/internal/loader"
	"cmd/link/internal/sym"
	"cmp"
	"debug/elf"
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"internal/buildcfg"
	"os"
	"path/filepath"
	"runtime"
	"slices"
	"strings"
)

/*
 * Derived from:
 * $FreeBSD: src/sys/sys/elf32.h,v 1.8.14.1 2005/12/30 22:13:58 marcel Exp $
 * $FreeBSD: src/sys/sys/elf64.h,v 1.10.14.1 2005/12/30 22:13:58 marcel Exp $
 * $FreeBSD: src/sys/sys/elf_common.h,v 1.15.8.1 2005/12/30 22:13:58 marcel Exp $
 * $FreeBSD: src/sys/alpha/include/elf.h,v 1.14 2003/09/25 01:10:22 peter Exp $
 * $FreeBSD: src/sys/amd64/include/elf.h,v 1.18 2004/08/03 08:21:48 dfr Exp $
 * $FreeBSD: src/sys/arm/include/elf.h,v 1.5.2.1 2006/06/30 21:42:52 cognet Exp $
 * $FreeBSD: src/sys/i386/include/elf.h,v 1.16 2004/08/02 19:12:17 dfr Exp $
 * $FreeBSD: src/sys/powerpc/include/elf.h,v 1.7 2004/11/02 09:47:01 ssouhlal Exp $
 * $FreeBSD: src/sys/sparc64/include/elf.h,v 1.12 2003/09/25 01:10:26 peter Exp $
 *
 * Copyright (c) 1996-1998 John D. Polstra.  All rights reserved.
 * Copyright (c) 2001 David E. O'Brien
 * Portions Copyright 2009 The Go Authors. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

// ELF definitions that are independent of architecture or word size.

// We use the 64-bit data structures on both 32- and 64-bit machines
// in order to write the code just once. The 64-bit data structure is
// written in the 32-bit format on the 32-bit machines.

// ElfEhdr is the ELF file header.
type ElfEhdr elf.Header64

// ElfShdr is an ELF section table entry.
type ElfShdr struct {
	elf.Section64

	// nameString is the section name as a string.
	// This is not to be confused with Name,
	// inherited from elf.Section64, which is an offset.
	nameString string

	// The section index, set by elfSortShdrs.
	// Don't read this directly, use elfShdrShnum.
	shnum elf.SectionIndex

	// Because we don't compute the final section number
	// until late in the link, when the link and info fields
	// hold section indexes, we store pointers, and fetch
	// the final section index when we write them out.
	link *ElfShdr
	info *ElfShdr

	// We compute the section offsets of reloc sections
	// after we create the ELF section header.
	// This field lets us fetch the section offset and size.
	relocSect *sym.Section
}

// ElfPhdr is the ELF program, or segment, header.
type ElfPhdr elf.ProgHeader

const (
	ELF64HDRSIZE  = 64
	ELF64PHDRSIZE = 56
	ELF64SHDRSIZE = 64
	ELF64RELSIZE  = 16
	ELF64RELASIZE = 24
	ELF64SYMSIZE  = 24
	ELF32HDRSIZE  = 52
	ELF32PHDRSIZE = 32
	ELF32SHDRSIZE = 40
	ELF32SYMSIZE  = 16
	ELF32RELSIZE  = 8
)

var elfstrdat []byte

// ELFRESERVE is the total amount of space to reserve at the
// start of the file for Header, PHeaders, SHeaders, and interp.
// May waste some space.
// On FreeBSD, cannot be larger than a page.
const ELFRESERVE = 4096

var (
	Nelfsym = 1

	elf64 bool
	// Either ".rel" or ".rela" depending on which type of relocation the
	// target platform uses.
	elfRelType string

	ehdr       ElfEhdr
	phdr       = make([]*ElfPhdr, 0, 8)
	shdr       = make([]*ElfShdr, 0, 64)
	shdrSorted bool

	interp string
)

// ELFArch includes target-specific hooks for ELF targets.
// This is initialized by the target-specific Init function
// called by the linker's main function in cmd/link/main.go.
type ELFArch struct {
	// TODO: Document these fields.

	Androiddynld   string
	Linuxdynld     string
	LinuxdynldMusl string
	Freebsddynld   string
	Netbsddynld    string
	Openbsddynld   string
	Dragonflydynld string
	Solarisdynld   string

	Reloc1    func(*Link, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int, int64) bool
	RelocSize uint32 // size of an ELF relocation record, must match Reloc1.
	SetupPLT  func(ctxt *Link, ldr *loader.Loader, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym)

	// DynamicReadOnly can be set to true to make the .dynamic
	// section read-only. By default it is writable.
	// This is used by MIPS targets.
	DynamicReadOnly bool
}

var buildinfo []byte

// Elfinit initializes the global ehdr variable that holds the ELF header.
// It will be updated as write section and program headers.
func Elfinit(ctxt *Link) {
	ctxt.IsELF = true

	if ctxt.Arch.InFamily(sys.AMD64, sys.ARM64, sys.Loong64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) {
		elfRelType = ".rela"
	} else {
		elfRelType = ".rel"
	}

	switch ctxt.Arch.Family {
	// 64-bit architectures
	case sys.PPC64, sys.S390X:
		if ctxt.Arch.ByteOrder == binary.BigEndian && ctxt.HeadType != objabi.Hopenbsd {
			ehdr.Flags = 1 // Version 1 ABI
		} else {
			ehdr.Flags = 2 // Version 2 ABI
		}
		fallthrough
	case sys.AMD64, sys.ARM64, sys.Loong64, sys.MIPS64, sys.RISCV64:
		if ctxt.Arch.Family == sys.MIPS64 {
			ehdr.Flags = 0x20000004 // MIPS 3 CPIC
		}
		if ctxt.Arch.Family == sys.Loong64 {
			ehdr.Flags = 0x43 // DOUBLE_FLOAT, OBJABI_V1
		}
		if ctxt.Arch.Family == sys.RISCV64 {
			ehdr.Flags = 0x4 // RISCV Float ABI Double
		}
		elf64 = true

		ehdr.Phoff = ELF64HDRSIZE      // Must be ELF64HDRSIZE: first PHdr must follow ELF header
		ehdr.Shoff = ELF64HDRSIZE      // Will move as we add PHeaders
		ehdr.Ehsize = ELF64HDRSIZE     // Must be ELF64HDRSIZE
		ehdr.Phentsize = ELF64PHDRSIZE // Must be ELF64PHDRSIZE
		ehdr.Shentsize = ELF64SHDRSIZE // Must be ELF64SHDRSIZE

	// 32-bit architectures
	case sys.ARM, sys.MIPS:
		if ctxt.Arch.Family == sys.ARM {
			// we use EABI on linux/arm, freebsd/arm, netbsd/arm.
			if ctxt.HeadType == objabi.Hlinux || ctxt.HeadType == objabi.Hfreebsd || ctxt.HeadType == objabi.Hnetbsd {
				// We set a value here that makes no indication of which
				// float ABI the object uses, because this is information
				// used by the dynamic linker to compare executables and
				// shared libraries -- so it only matters for cgo calls, and
				// the information properly comes from the object files
				// produced by the host C compiler. parseArmAttributes in
				// ldelf.go reads that information and updates this field as
				// appropriate.
				ehdr.Flags = 0x5000002 // has entry point, Version5 EABI
			}
		} else if ctxt.Arch.Family == sys.MIPS {
			ehdr.Flags = 0x50001004 // MIPS 32 CPIC O32
		}
		fallthrough
	default:
		ehdr.Phoff = ELF32HDRSIZE
		// Must be ELF32HDRSIZE: first PHdr must follow ELF header
		ehdr.Shoff = ELF32HDRSIZE      // Will move as we add PHeaders
		ehdr.Ehsize = ELF32HDRSIZE     // Must be ELF32HDRSIZE
		ehdr.Phentsize = ELF32PHDRSIZE // Must be ELF32PHDRSIZE
		ehdr.Shentsize = ELF32SHDRSIZE // Must be ELF32SHDRSIZE
	}
}

// fixElfPhdr makes sure PT_LOAD is aligned properly and that there is no gap.
// Correct ELF loaders will do this implicitly,
// but buggy ELF loaders like the one in some
// versions of QEMU and UPX won't.
func fixElfPhdr(e *ElfPhdr) {
	frag := int(e.Vaddr & (e.Align - 1))

	e.Off -= uint64(frag)
	e.Vaddr -= uint64(frag)
	e.Paddr -= uint64(frag)
	e.Filesz += uint64(frag)
	e.Memsz += uint64(frag)
}

func elf64phdr(out *OutBuf, e *ElfPhdr) {
	if e.Type == elf.PT_LOAD {
		fixElfPhdr(e)
	}

	out.Write32(uint32(e.Type))
	out.Write32(uint32(e.Flags))
	out.Write64(e.Off)
	out.Write64(e.Vaddr)
	out.Write64(e.Paddr)
	out.Write64(e.Filesz)
	out.Write64(e.Memsz)
	out.Write64(e.Align)
}

func elf32phdr(out *OutBuf, e *ElfPhdr) {
	if e.Type == elf.PT_LOAD {
		fixElfPhdr(e)
	}

	out.Write32(uint32(e.Type))
	out.Write32(uint32(e.Off))
	out.Write32(uint32(e.Vaddr))
	out.Write32(uint32(e.Paddr))
	out.Write32(uint32(e.Filesz))
	out.Write32(uint32(e.Memsz))
	out.Write32(uint32(e.Flags))
	out.Write32(uint32(e.Align))
}

// elfShdrShnum returns the section index of an ElfShdr.
func elfShdrShnum(e *ElfShdr) elf.SectionIndex {
	if e.shnum == -1 {
		Errorf("internal error: retrieved section index before it is set")
		errorexit()
	}
	return e.shnum
}

// elfShdrOff returns the section offset for an ElfShdr.
func elfShdrOff(e *ElfShdr) uint64 {
	if e.relocSect != nil {
		if e.Off != 0 {
			Errorf("internal error: ElfShdr relocSect == %p Off == %d", e.relocSect, e.Off)
			errorexit()
		}
		return e.relocSect.Reloff
	}
	return e.Off
}

// elfShdrSize returns the section size for an ElfShdr.
func elfShdrSize(e *ElfShdr) uint64 {
	if e.relocSect != nil {
		if e.Size != 0 {
			Errorf("internal error: ElfShdr relocSect == %p Size == %d", e.relocSect, e.Size)
			errorexit()
		}
		return e.relocSect.Rellen
	}
	return e.Size
}

// elfShdrLink returns the link value for an ElfShdr.
func elfShdrLink(e *ElfShdr) uint32 {
	if e.link != nil {
		if e.Link != 0 {
			Errorf("internal error: ElfShdr link == %p Link == %d", e.link, e.Link)
			errorexit()
		}
		return uint32(elfShdrShnum(e.link))
	}
	return e.Link
}

// elfShdrInfo returns the info value for an ElfShdr.
func elfShdrInfo(e *ElfShdr) uint32 {
	if e.info != nil {
		if e.Info != 0 {
			Errorf("internal error: ElfShdr info == %p Info == %d", e.info, e.Info)
			errorexit()
		}
		return uint32(elfShdrShnum(e.info))
	}
	return e.Info
}

func elf64shdr(out *OutBuf, e *ElfShdr) {
	out.Write32(e.Name)
	out.Write32(e.Type)
	out.Write64(e.Flags)
	out.Write64(e.Addr)
	out.Write64(elfShdrOff(e))
	out.Write64(elfShdrSize(e))
	out.Write32(elfShdrLink(e))
	out.Write32(elfShdrInfo(e))
	out.Write64(e.Addralign)
	out.Write64(e.Entsize)
}

func elf32shdr(out *OutBuf, e *ElfShdr) {
	out.Write32(e.Name)
	out.Write32(e.Type)
	out.Write32(uint32(e.Flags))
	out.Write32(uint32(e.Addr))
	out.Write32(uint32(elfShdrOff(e)))
	out.Write32(uint32(elfShdrSize(e)))
	out.Write32(elfShdrLink(e))
	out.Write32(elfShdrInfo(e))
	out.Write32(uint32(e.Addralign))
	out.Write32(uint32(e.Entsize))
}

func elfwriteshdrs(out *OutBuf) uint32 {
	if elf64 {
		for _, sh := range shdr {
			elf64shdr(out, sh)
		}
		return uint32(len(shdr)) * ELF64SHDRSIZE
	}

	for _, sh := range shdr {
		elf32shdr(out, sh)
	}
	return uint32(len(shdr)) * ELF32SHDRSIZE
}

// elfSortShdrs sorts the section headers so that allocated sections
// are first, in address order. This isn't required for correctness,
// but it makes the ELF file easier for humans to read.
// We only do this for an executable, not an object file.
func elfSortShdrs(ctxt *Link) {
	if ctxt.LinkMode != LinkExternal {
		// Use [1:] to leave the empty section header zero in place.
		slices.SortStableFunc(shdr[1:], func(a, b *ElfShdr) int {
			isAllocated := func(h *ElfShdr) bool {
				return elf.SectionFlag(h.Flags)&elf.SHF_ALLOC != 0
			}
			if isAllocated(a) {
				if isAllocated(b) {
					if r := cmp.Compare(a.Addr, b.Addr); r != 0 {
						return r
					}
					// With same address, sort smallest
					// section first.
					return cmp.Compare(a.Size, b.Size)
				}
				// Allocated before unallocated.
				return -1
			}
			if isAllocated(b) {
				// Allocated before unallocated.
				return 1
			}
			return 0
		})
	}
	for i, h := range shdr {
		h.shnum = elf.SectionIndex(i)
	}
	shdrSorted = true
}

// elfWriteShstrtab writes out the ELF section string table.
// It also sets the Name field of the section headers.
// It returns the length of the string table.
func elfWriteShstrtab(ctxt *Link) uint32 {
	// Map from section name to shstrtab offset.
	m := make(map[string]uint32, len(shdr))

	m[""] = 0
	ctxt.Out.WriteByte(0)
	off := uint32(1)

	writeString := func(s string) {
		m[s] = off
		ctxt.Out.WriteString(s)
		ctxt.Out.WriteByte(0)
		off += uint32(len(s)) + 1
	}

	// As a minor optimization, do the relocation sections first,
	// as they may let us reuse the suffix.
	// That is, the offset for ".text" can point into ".rel.text".
	// We don't do a full suffix search as the relocation sections
	// are likely to be the only match.
	for _, sh := range shdr {
		if suffix, ok := strings.CutPrefix(sh.nameString, elfRelType); ok {
			if _, found := m[suffix]; !found {
				m[suffix] = off + uint32(len(elfRelType))
			}
			writeString(sh.nameString)
		}
	}

	for _, sh := range shdr {
		if shOff, ok := m[sh.nameString]; ok {
			sh.Name = shOff
		} else {
			sh.Name = off
			writeString(sh.nameString)
		}
	}

	return off
}

func elfwritephdrs(out *OutBuf) uint32 {
	if elf64 {
		for i := 0; i < int(ehdr.Phnum); i++ {
			elf64phdr(out, phdr[i])
		}
		return uint32(ehdr.Phnum) * ELF64PHDRSIZE
	}

	for i := 0; i < int(ehdr.Phnum); i++ {
		elf32phdr(out, phdr[i])
	}
	return uint32(ehdr.Phnum) * ELF32PHDRSIZE
}

func newElfPhdr() *ElfPhdr {
	e := new(ElfPhdr)
	phdr = append(phdr, e)
	ehdr.Phnum++
	if elf64 {
		ehdr.Shoff += ELF64PHDRSIZE
	} else {
		ehdr.Shoff += ELF32PHDRSIZE
	}
	return e
}

func newElfShdr(name string) *ElfShdr {
	if shdrSorted {
		Errorf("internal error: creating a section header after they were sorted")
		errorexit()
	}

	e := &ElfShdr{
		nameString: name,
		shnum:      -1, // make invalid for now, set by elfSortShdrs
	}
	shdr = append(shdr, e)
	return e
}

func getElfEhdr() *ElfEhdr {
	return &ehdr
}

func elf64writehdr(out *OutBuf) uint32 {
	out.Write(ehdr.Ident[:])
	out.Write16(ehdr.Type)
	out.Write16(ehdr.Machine)
	out.Write32(ehdr.Version)
	out.Write64(ehdr.Entry)
	out.Write64(ehdr.Phoff)
	out.Write64(ehdr.Shoff)
	out.Write32(ehdr.Flags)
	out.Write16(ehdr.Ehsize)
	out.Write16(ehdr.Phentsize)
	out.Write16(ehdr.Phnum)
	out.Write16(ehdr.Shentsize)
	out.Write16(ehdr.Shnum)
	out.Write16(ehdr.Shstrndx)
	return ELF64HDRSIZE
}

func elf32writehdr(out *OutBuf) uint32 {
	out.Write(ehdr.Ident[:])
	out.Write16(ehdr.Type)
	out.Write16(ehdr.Machine)
	out.Write32(ehdr.Version)
	out.Write32(uint32(ehdr.Entry))
	out.Write32(uint32(ehdr.Phoff))
	out.Write32(uint32(ehdr.Shoff))
	out.Write32(ehdr.Flags)
	out.Write16(ehdr.Ehsize)
	out.Write16(ehdr.Phentsize)
	out.Write16(ehdr.Phnum)
	out.Write16(ehdr.Shentsize)
	out.Write16(ehdr.Shnum)
	out.Write16(ehdr.Shstrndx)
	return ELF32HDRSIZE
}

func elfwritehdr(out *OutBuf) uint32 {
	if elf64 {
		return elf64writehdr(out)
	}
	return elf32writehdr(out)
}

// elfhash is the dynamic symbol hash function .
// This is taken directly from the definition document for ELF,
// except that instead of finishing with "h &^= g" we just write
// "h &= 0xfffffff", which results in the same value.
func elfhash(name string) uint32 {
	var h uint32
	for i := 0; i < len(name); i++ {
		h = (h << 4) + uint32(name[i])
		if g := h & 0xf0000000; g != 0 {
			h ^= g >> 24
		}
		h &= 0x0fffffff
	}
	return h
}

func elfWriteDynEntSym(ctxt *Link, s *loader.SymbolBuilder, tag elf.DynTag, t loader.Sym) {
	Elfwritedynentsymplus(ctxt, s, tag, t, 0)
}

func Elfwritedynent(arch *sys.Arch, s *loader.SymbolBuilder, tag elf.DynTag, val uint64) {
	if elf64 {
		s.AddUint64(arch, uint64(tag))
		s.AddUint64(arch, val)
	} else {
		s.AddUint32(arch, uint32(tag))
		s.AddUint32(arch, uint32(val))
	}
}

func Elfwritedynentsymplus(ctxt *Link, s *loader.SymbolBuilder, tag elf.DynTag, t loader.Sym, add int64) {
	if elf64 {
		s.AddUint64(ctxt.Arch, uint64(tag))
	} else {
		s.AddUint32(ctxt.Arch, uint32(tag))
	}
	s.AddAddrPlus(ctxt.Arch, t, add)
}

func elfwritedynentsymsize(ctxt *Link, s *loader.SymbolBuilder, tag elf.DynTag, t loader.Sym) {
	if elf64 {
		s.AddUint64(ctxt.Arch, uint64(tag))
	} else {
		s.AddUint32(ctxt.Arch, uint32(tag))
	}
	s.AddSize(ctxt.Arch, t)
}

func elfinterp(sh *ElfShdr, startva uint64, resoff uint64, p string) int {
	interp = p
	n := len(interp) + 1
	sh.Addr = startva + resoff - uint64(n)
	sh.Off = resoff - uint64(n)
	sh.Size = uint64(n)

	return n
}

func elfwriteinterp(out *OutBuf) int {
	sh := elfshname(".interp")
	out.SeekSet(int64(sh.Off))
	out.WriteString(interp)
	out.Write8(0)
	return int(sh.Size)
}

// member of .gnu.attributes of MIPS for fpAbi
const (
	// No floating point is present in the module (default)
	MIPS_FPABI_NONE = 0
	// FP code in the module uses the FP32 ABI for a 32-bit ABI
	MIPS_FPABI_ANY = 1
	// FP code in the module only uses single precision ABI
	MIPS_FPABI_SINGLE = 2
	// FP code in the module uses soft-float ABI
	MIPS_FPABI_SOFT = 3
	// FP code in the module assumes an FPU with FR=1 and has 12
	// callee-saved doubles. Historic, no longer supported.
	MIPS_FPABI_HIST = 4
	// FP code in the module uses the FPXX  ABI
	MIPS_FPABI_FPXX = 5
	// FP code in the module uses the FP64  ABI
	MIPS_FPABI_FP64 = 6
	// FP code in the module uses the FP64A ABI
	MIPS_FPABI_FP64A = 7
)

func elfMipsAbiFlags(sh *ElfShdr, startva uint64, resoff uint64) int {
	n := 24
	sh.Addr = startva + resoff - uint64(n)
	sh.Off = resoff - uint64(n)
	sh.Size = uint64(n)
	sh.Type = uint32(elf.SHT_MIPS_ABIFLAGS)
	sh.Flags = uint64(elf.SHF_ALLOC)

	return n
}

// elfWriteMipsAbiFlags writes the .MIPS.abiflags section.
//
// Layout is given by this C definition:
//
//	typedef struct
//	{
//	  /* Version of flags structure.  */
//	  uint16_t version;
//	  /* The level of the ISA: 1-5, 32, 64.  */
//	  uint8_t isa_level;
//	  /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise.  */
//	  uint8_t isa_rev;
//	  /* The size of general purpose registers.  */
//	  uint8_t gpr_size;
//	  /* The size of co-processor 1 registers.  */
//	  uint8_t cpr1_size;
//	  /* The size of co-processor 2 registers.  */
//	  uint8_t cpr2_size;
//	  /* The floating-point ABI.  */
//	  uint8_t fp_abi;
//	  /* Processor-specific extension.  */
//	  uint32_t isa_ext;
//	  /* Mask of ASEs used.  */
//	  uint32_t ases;
//	  /* Mask of general flags.  */
//	  uint32_t flags1;
//	  uint32_t flags2;
//	} Elf_Internal_ABIFlags_v0;
func elfWriteMipsAbiFlags(ctxt *Link) int {
	sh := elfshname(".MIPS.abiflags")
	ctxt.Out.SeekSet(int64(sh.Off))
	ctxt.Out.Write16(0) // version
	ctxt.Out.Write8(32) // isaLevel
	ctxt.Out.Write8(1)  // isaRev
	ctxt.Out.Write8(1)  // gprSize
	ctxt.Out.Write8(1)  // cpr1Size
	ctxt.Out.Write8(0)  // cpr2Size
	if buildcfg.GOMIPS == "softfloat" {
		ctxt.Out.Write8(MIPS_FPABI_SOFT) // fpAbi
	} else {
		// Go cannot make sure non odd-number-fpr is used (ie, in load a double from memory).
		// So, we mark the object is MIPS I style paired float/double register scheme,
		// aka MIPS_FPABI_ANY. If we mark the object as FPXX, the kernel may use FR=1 mode,
		// then we meet some problem.
		// Note: MIPS_FPABI_ANY is bad naming: in fact it is MIPS I style FPR usage.
		//       It is not for 'ANY'.
		// TODO: switch to FPXX after be sure that no odd-number-fpr is used.
		ctxt.Out.Write8(MIPS_FPABI_ANY) // fpAbi
	}
	ctxt.Out.Write32(0) // isaExt
	ctxt.Out.Write32(0) // ases
	ctxt.Out.Write32(0) // flags1
	ctxt.Out.Write32(0) // flags2
	return int(sh.Size)
}

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)
	sh.Addralign = 4
	sh.Addr = startva + resoff - n
	sh.Off = resoff - n
	sh.Size = n - resoff%4

	return int(n)
}

func elfwritenotehdr(out *OutBuf, str string, namesz uint32, descsz uint32, tag uint32) *ElfShdr {
	sh := elfshname(str)

	// Write Elf_Note header.
	out.SeekSet(int64(sh.Off))

	out.Write32(namesz)
	out.Write32(descsz)
	out.Write32(tag)

	return sh
}

// NetBSD Signature (as per sys/exec_elf.h)
const (
	ELF_NOTE_NETBSD_NAMESZ  = 7
	ELF_NOTE_NETBSD_DESCSZ  = 4
	ELF_NOTE_NETBSD_TAG     = 1
	ELF_NOTE_NETBSD_VERSION = 700000000 // NetBSD 7.0
)

var ELF_NOTE_NETBSD_NAME = []byte("NetBSD\x00")

func elfnetbsdsig(sh *ElfShdr, startva uint64, resoff uint64) int {
	n := int(Rnd(ELF_NOTE_NETBSD_NAMESZ, 4) + Rnd(ELF_NOTE_NETBSD_DESCSZ, 4))
	return elfnote(sh, startva, resoff, n)
}

func elfwritenetbsdsig(out *OutBuf) int {
	// Write Elf_Note header.
	sh := elfwritenotehdr(out, ".note.netbsd.ident", ELF_NOTE_NETBSD_NAMESZ, ELF_NOTE_NETBSD_DESCSZ, ELF_NOTE_NETBSD_TAG)

	if sh == nil {
		return 0
	}

	// Followed by NetBSD string and version.
	out.Write(ELF_NOTE_NETBSD_NAME)
	out.Write8(0)
	out.Write32(ELF_NOTE_NETBSD_VERSION)

	return int(sh.Size)
}

// The race detector can't handle ASLR (address space layout randomization).
// ASLR is on by default for NetBSD, so we turn the ASLR off explicitly
// using a magic elf Note when building race binaries.

func elfnetbsdpax(sh *ElfShdr, startva uint64, resoff uint64) int {
	n := int(Rnd(4, 4) + Rnd(4, 4))
	return elfnote(sh, startva, resoff, n)
}

func elfwritenetbsdpax(out *OutBuf) int {
	sh := elfwritenotehdr(out, ".note.netbsd.pax", 4 /* length of PaX\x00 */, 4 /* length of flags */, 0x03 /* PaX type */)
	if sh == nil {
		return 0
	}
	out.Write([]byte("PaX\x00"))
	out.Write32(0x20) // 0x20 = Force disable ASLR
	return int(sh.Size)
}

// OpenBSD Signature
const (
	ELF_NOTE_OPENBSD_NAMESZ  = 8
	ELF_NOTE_OPENBSD_DESCSZ  = 4
	ELF_NOTE_OPENBSD_TAG     = 1
	ELF_NOTE_OPENBSD_VERSION = 0
)

var ELF_NOTE_OPENBSD_NAME = []byte("OpenBSD\x00")

func elfopenbsdsig(sh *ElfShdr, startva uint64, resoff uint64) int {
	n := ELF_NOTE_OPENBSD_NAMESZ + ELF_NOTE_OPENBSD_DESCSZ
	return elfnote(sh, startva, resoff, n)
}

func elfwriteopenbsdsig(out *OutBuf) int {
	// Write Elf_Note header.
	sh := elfwritenotehdr(out, ".note.openbsd.ident", ELF_NOTE_OPENBSD_NAMESZ, ELF_NOTE_OPENBSD_DESCSZ, ELF_NOTE_OPENBSD_TAG)

	if sh == nil {
		return 0
	}

	// Followed by OpenBSD string and version.
	out.Write(ELF_NOTE_OPENBSD_NAME)

	out.Write32(ELF_NOTE_OPENBSD_VERSION)

	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(ctxt *Link) {
	val := *flagHostBuildid
	if val == "" || val == "none" {
		return
	}
	if val == "gobuildid" {
		buildID := *flagBuildid
		if buildID == "" {
			Exitf("-B gobuildid requires a Go build ID supplied via -buildid")
		}

		if ctxt.IsDarwin() {
			buildinfo = uuidFromGoBuildId(buildID)
			return
		}

		hashedBuildID := hash.Sum32([]byte(buildID))
		buildinfo = hashedBuildID[:20]

		return
	}

	if !strings.HasPrefix(val, "0x") {
		Exitf("-B argument must start with 0x: %s", val)
	}
	ov := val
	val = val[2:]

	maxLen := 32
	if ctxt.IsDarwin() {
		maxLen = 16
	}
	if hex.DecodedLen(len(val)) > maxLen {
		Exitf("-B option too long (max %d digits): %s", maxLen, ov)
	}

	b, err := hex.DecodeString(val)
	if err != nil {
		if err == hex.ErrLength {
			Exitf("-B argument must have even number of digits: %s", ov)
		}
		if inv, ok := err.(hex.InvalidByteError); ok {
			Exitf("-B argument contains invalid hex digit %c: %s", byte(inv), ov)
		}
		Exitf("-B argument contains invalid hex: %s", ov)
	}

	buildinfo = b
}

// Build info note
const (
	ELF_NOTE_BUILDINFO_NAMESZ = 4
	ELF_NOTE_BUILDINFO_TAG    = 3
)

var ELF_NOTE_BUILDINFO_NAME = []byte("GNU\x00")

func elfbuildinfo(sh *ElfShdr, startva uint64, resoff uint64) int {
	n := int(ELF_NOTE_BUILDINFO_NAMESZ + Rnd(int64(len(buildinfo)), 4))
	return elfnote(sh, startva, resoff, n)
}

func elfgobuildid(sh *ElfShdr, startva uint64, resoff uint64) int {
	n := len(ELF_NOTE_GO_NAME) + int(Rnd(int64(len(*flagBuildid)), 4))
	return elfnote(sh, startva, resoff, n)
}

func elfwritebuildinfo(out *OutBuf) int {
	sh := elfwritenotehdr(out, ".note.gnu.build-id", ELF_NOTE_BUILDINFO_NAMESZ, uint32(len(buildinfo)), ELF_NOTE_BUILDINFO_TAG)
	if sh == nil {
		return 0
	}

	out.Write(ELF_NOTE_BUILDINFO_NAME)
	out.Write(buildinfo)
	var zero = make([]byte, 4)
	out.Write(zero[:int(Rnd(int64(len(buildinfo)), 4)-int64(len(buildinfo)))])

	return int(sh.Size)
}

func elfwritegobuildid(out *OutBuf) int {
	sh := elfwritenotehdr(out, ".note.go.buildid", uint32(len(ELF_NOTE_GO_NAME)), uint32(len(*flagBuildid)), ELF_NOTE_GOBUILDID_TAG)
	if sh == nil {
		return 0
	}

	out.Write(ELF_NOTE_GO_NAME)
	out.Write([]byte(*flagBuildid))
	var zero = make([]byte, 4)
	out.Write(zero[:int(Rnd(int64(len(*flagBuildid)), 4)-int64(len(*flagBuildid)))])

	return int(sh.Size)
}

// Go specific notes
const (
	ELF_NOTE_GOPKGLIST_TAG = 1
	ELF_NOTE_GOABIHASH_TAG = 2
	ELF_NOTE_GODEPS_TAG    = 3
	ELF_NOTE_GOBUILDID_TAG = 4
)

var ELF_NOTE_GO_NAME = []byte("Go\x00\x00")

var elfverneed int

type Elfaux struct {
	next *Elfaux
	num  int
	vers string
}

type Elflib struct {
	next *Elflib
	aux  *Elfaux
	file string
}

func addelflib(list **Elflib, file string, vers string) *Elfaux {
	var lib *Elflib

	for lib = *list; lib != nil; lib = lib.next {
		if lib.file == file {
			goto havelib
		}
	}
	lib = new(Elflib)
	lib.next = *list
	lib.file = file
	*list = lib

havelib:
	for aux := lib.aux; aux != nil; aux = aux.next {
		if aux.vers == vers {
			return aux
		}
	}
	aux := new(Elfaux)
	aux.next = lib.aux
	aux.vers = vers
	lib.aux = aux

	return aux
}

func elfdynhash(ctxt *Link) {
	if !ctxt.IsELF {
		return
	}

	nsym := Nelfsym
	ldr := ctxt.loader
	s := ldr.CreateSymForUpdate(".hash", 0)
	s.SetType(sym.SELFROSECT)

	i := nsym
	nbucket := 1
	for i > 0 {
		nbucket++
		i >>= 1
	}

	var needlib *Elflib
	need := make([]*Elfaux, nsym)
	chain := make([]uint32, nsym)
	buckets := make([]uint32, nbucket)

	for _, sy := range ldr.DynidSyms() {

		dynid := ldr.SymDynid(sy)
		if ldr.SymDynimpvers(sy) != "" {
			need[dynid] = addelflib(&needlib, ldr.SymDynimplib(sy), ldr.SymDynimpvers(sy))
		}

		name := ldr.SymExtname(sy)
		hc := elfhash(name)

		b := hc % uint32(nbucket)
		chain[dynid] = buckets[b]
		buckets[b] = uint32(dynid)
	}

	// s390x (ELF64) hash table entries are 8 bytes
	if ctxt.Arch.Family == sys.S390X {
		s.AddUint64(ctxt.Arch, uint64(nbucket))
		s.AddUint64(ctxt.Arch, uint64(nsym))
		for i := 0; i < nbucket; i++ {
			s.AddUint64(ctxt.Arch, uint64(buckets[i]))
		}
		for i := 0; i < nsym; i++ {
			s.AddUint64(ctxt.Arch, uint64(chain[i]))
		}
	} else {
		s.AddUint32(ctxt.Arch, uint32(nbucket))
		s.AddUint32(ctxt.Arch, uint32(nsym))
		for i := 0; i < nbucket; i++ {
			s.AddUint32(ctxt.Arch, buckets[i])
		}
		for i := 0; i < nsym; i++ {
			s.AddUint32(ctxt.Arch, chain[i])
		}
	}

	dynstr := ldr.CreateSymForUpdate(".dynstr", 0)

	// version symbols
	gnuVersionR := ldr.CreateSymForUpdate(".gnu.version_r", 0)
	s = gnuVersionR
	i = 2
	nfile := 0
	for l := needlib; l != nil; l = l.next {
		nfile++

		// header
		s.AddUint16(ctxt.Arch, 1) // table version
		j := 0
		for x := l.aux; x != nil; x = x.next {
			j++
		}
		s.AddUint16(ctxt.Arch, uint16(j))                        // aux count
		s.AddUint32(ctxt.Arch, uint32(dynstr.Addstring(l.file))) // file string offset
		s.AddUint32(ctxt.Arch, 16)                               // offset from header to first aux
		if l.next != nil {
			s.AddUint32(ctxt.Arch, 16+uint32(j)*16) // offset from this header to next
		} else {
			s.AddUint32(ctxt.Arch, 0)
		}

		for x := l.aux; x != nil; x = x.next {
			x.num = i
			i++

			// aux struct
			s.AddUint32(ctxt.Arch, elfhash(x.vers))                  // hash
			s.AddUint16(ctxt.Arch, 0)                                // flags
			s.AddUint16(ctxt.Arch, uint16(x.num))                    // other - index we refer to this by
			s.AddUint32(ctxt.Arch, uint32(dynstr.Addstring(x.vers))) // version string offset
			if x.next != nil {
				s.AddUint32(ctxt.Arch, 16) // offset from this aux to next
			} else {
				s.AddUint32(ctxt.Arch, 0)
			}
		}
	}

	// version references
	gnuVersion := ldr.CreateSymForUpdate(".gnu.version", 0)
	s = gnuVersion

	for i := 0; i < nsym; i++ {
		if i == 0 {
			s.AddUint16(ctxt.Arch, 0) // first entry - no symbol
		} else if need[i] == nil {
			s.AddUint16(ctxt.Arch, 1) // global
		} else {
			s.AddUint16(ctxt.Arch, uint16(need[i].num))
		}
	}

	s = ldr.CreateSymForUpdate(".dynamic", 0)

	var dtFlags1 elf.DynFlag1
	if *flagBindNow {
		dtFlags1 |= elf.DF_1_NOW
		Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS, uint64(elf.DF_BIND_NOW))
	}
	if ctxt.BuildMode == BuildModePIE {
		dtFlags1 |= elf.DF_1_PIE
	}
	Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS_1, uint64(dtFlags1))

	elfverneed = nfile
	if elfverneed != 0 {
		elfWriteDynEntSym(ctxt, s, elf.DT_VERNEED, gnuVersionR.Sym())
		Elfwritedynent(ctxt.Arch, s, elf.DT_VERNEEDNUM, uint64(nfile))
		elfWriteDynEntSym(ctxt, s, elf.DT_VERSYM, gnuVersion.Sym())
	}

	sy := ldr.CreateSymForUpdate(elfRelType+".plt", 0)
	if sy.Size() > 0 {
		if elfRelType == ".rela" {
			Elfwritedynent(ctxt.Arch, s, elf.DT_PLTREL, uint64(elf.DT_RELA))
		} else {
			Elfwritedynent(ctxt.Arch, s, elf.DT_PLTREL, uint64(elf.DT_REL))
		}
		elfwritedynentsymsize(ctxt, s, elf.DT_PLTRELSZ, sy.Sym())
		elfWriteDynEntSym(ctxt, s, elf.DT_JMPREL, sy.Sym())
	}

	Elfwritedynent(ctxt.Arch, s, elf.DT_NULL, 0)
}

func elfphload(seg *sym.Segment) *ElfPhdr {
	ph := newElfPhdr()
	ph.Type = elf.PT_LOAD
	if seg.Rwx&4 != 0 {
		ph.Flags |= elf.PF_R
	}
	if seg.Rwx&2 != 0 {
		ph.Flags |= elf.PF_W
	}
	if seg.Rwx&1 != 0 {
		ph.Flags |= elf.PF_X
	}
	ph.Vaddr = seg.Vaddr
	ph.Paddr = seg.Vaddr
	ph.Memsz = seg.Length
	ph.Off = seg.Fileoff
	ph.Filesz = seg.Filelen
	ph.Align = uint64(*FlagRound)

	return ph
}

func elfphrelro(seg *sym.Segment) {
	ph := newElfPhdr()
	ph.Type = elf.PT_GNU_RELRO
	ph.Flags = elf.PF_R
	ph.Vaddr = seg.Vaddr
	ph.Paddr = seg.Vaddr
	ph.Memsz = seg.Length
	ph.Off = seg.Fileoff
	ph.Filesz = seg.Filelen
	ph.Align = uint64(*FlagRound)
}

// elfshname finds or creates a section given its name.
func elfshname(name string) *ElfShdr {
	for _, sh := range shdr {
		if sh.nameString == name {
			return sh
		}
	}
	return newElfShdr(name)
}

// elfshnamedup creates a new section with a given name.
// If there is an existing section with this name, it creates a duplicate.
func elfshnamedup(name string) *ElfShdr {
	return newElfShdr(name)
}

func elfshalloc(sect *sym.Section) *ElfShdr {
	sh := elfshname(sect.Name)
	sect.Elfsect = sh
	return sh
}

func elfshbits(linkmode LinkMode, sect *sym.Section) *ElfShdr {
	var sh *ElfShdr

	if sect.Name == ".text" {
		if sect.Elfsect == nil {
			sect.Elfsect = elfshnamedup(sect.Name)
		}
		sh = sect.Elfsect.(*ElfShdr)
	} else {
		sh = elfshalloc(sect)
	}

	// If this section has already been set up as a note, we assume type_ and
	// flags are already correct, but the other fields still need filling in.
	if sh.Type == uint32(elf.SHT_NOTE) {
		if linkmode != LinkExternal {
			// TODO(mwhudson): the approach here will work OK when
			// linking internally for notes that we want to be included
			// in a loadable segment (e.g. the abihash note) but not for
			// notes that we do not want to be mapped (e.g. the package
			// list note). The real fix is probably to define new values
			// for Symbol.Type corresponding to mapped and unmapped notes
			// and handle them in dodata().
			Errorf("sh.Type == SHT_NOTE in elfshbits when linking internally")
		}
		sh.Addralign = uint64(sect.Align)
		sh.Size = sect.Length
		sh.Off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr
		return sh
	}
	if sh.Type > 0 {
		return sh
	}

	if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen {
		switch sect.Name {
		case ".init_array":
			sh.Type = uint32(elf.SHT_INIT_ARRAY)
		default:
			sh.Type = uint32(elf.SHT_PROGBITS)
		}
	} else {
		sh.Type = uint32(elf.SHT_NOBITS)
	}
	sh.Flags = uint64(elf.SHF_ALLOC)
	if sect.Rwx&1 != 0 {
		sh.Flags |= uint64(elf.SHF_EXECINSTR)
	}
	if sect.Rwx&2 != 0 {
		sh.Flags |= uint64(elf.SHF_WRITE)
	}
	if sect.Name == ".tbss" {
		sh.Flags |= uint64(elf.SHF_TLS)
		sh.Type = uint32(elf.SHT_NOBITS)
	}
	if linkmode != LinkExternal {
		sh.Addr = sect.Vaddr
	}

	if strings.HasPrefix(sect.Name, ".debug") || strings.HasPrefix(sect.Name, ".zdebug") {
		sh.Flags = 0
		sh.Addr = 0
		if sect.Compressed {
			sh.Flags |= uint64(elf.SHF_COMPRESSED)
		}
	}

	sh.Addralign = uint64(sect.Align)
	sh.Size = sect.Length
	if sect.Name != ".tbss" {
		sh.Off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr
	}

	return sh
}

func elfshreloc(arch *sys.Arch, sect *sym.Section) *ElfShdr {
	// If main section is SHT_NOBITS, nothing to relocate.
	// Also nothing to relocate in .shstrtab or notes or .gopclntab.
	if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
		return nil
	}
	switch sect.Name {
	case ".shstrtab", ".tbss", ".gopclntab":
		return nil
	}
	if sect.Elfsect.(*ElfShdr).Type == uint32(elf.SHT_NOTE) {
		return nil
	}

	typ := elf.SHT_REL
	if elfRelType == ".rela" {
		typ = elf.SHT_RELA
	}

	sh := elfshname(elfRelType + sect.Name)
	// There could be multiple text sections but each needs
	// its own .rela.text.

	if sect.Name == ".text" {
		if sh.info != nil && sh.info != sect.Elfsect.(*ElfShdr) {
			sh = elfshnamedup(elfRelType + sect.Name)
		}
	}

	sh.Type = uint32(typ)
	sh.Entsize = uint64(arch.RegSize) * 2
	if typ == elf.SHT_RELA {
		sh.Entsize += uint64(arch.RegSize)
	}
	sh.link = elfshname(".symtab")
	sh.info = sect.Elfsect.(*ElfShdr)
	sh.relocSect = sect
	sh.Addralign = uint64(arch.RegSize)
	return sh
}

func elfrelocsect(ctxt *Link, out *OutBuf, sect *sym.Section, syms []loader.Sym) {
	// If main section is SHT_NOBITS, nothing to relocate.
	// Also nothing to relocate in .shstrtab.
	if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
		return
	}
	if sect.Name == ".shstrtab" {
		return
	}

	ldr := ctxt.loader
	for i, s := range syms {
		if !ldr.AttrReachable(s) {
			panic("should never happen")
		}
		if uint64(ldr.SymValue(s)) >= sect.Vaddr {
			syms = syms[i:]
			break
		}
	}

	eaddr := sect.Vaddr + sect.Length
	for _, s := range syms {
		if !ldr.AttrReachable(s) {
			continue
		}
		if ldr.SymValue(s) >= int64(eaddr) {
			break
		}

		// Compute external relocations on the go, and pass to
		// ELF.Reloc1 to stream out.
		relocs := ldr.Relocs(s)
		for ri := 0; ri < relocs.Count(); ri++ {
			r := relocs.At(ri)
			rr, ok := extreloc(ctxt, ldr, s, r)
			if !ok {
				continue
			}
			if rr.Xsym == 0 {
				ldr.Errorf(s, "missing xsym in relocation")
				continue
			}
			esr := ElfSymForReloc(ctxt, rr.Xsym)
			if esr == 0 {
				ldr.Errorf(s, "reloc %d (%s) to non-elf symbol %s (outer=%s) %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymName(r.Sym()), ldr.SymName(rr.Xsym), ldr.SymType(r.Sym()), ldr.SymType(r.Sym()).String())
			}
			if !ldr.AttrReachable(rr.Xsym) {
				ldr.Errorf(s, "unreachable reloc %d (%s) target %v", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymName(rr.Xsym))
			}
			if !thearch.ELF.Reloc1(ctxt, out, ldr, s, rr, ri, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-sect.Vaddr)) {
				ldr.Errorf(s, "unsupported obj reloc %d (%s)/%d to %s", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), r.Siz(), ldr.SymName(r.Sym()))
			}
		}
	}

	// sanity check
	if uint64(out.Offset()) != sect.Reloff+sect.Rellen {
		panic(fmt.Sprintf("elfrelocsect: size mismatch %d != %d + %d", out.Offset(), sect.Reloff, sect.Rellen))
	}
}

func elfEmitReloc(ctxt *Link) {
	for ctxt.Out.Offset()&7 != 0 {
		ctxt.Out.Write8(0)
	}

	sizeExtRelocs(ctxt, thearch.ELF.RelocSize)
	relocSect, wg := relocSectFn(ctxt, elfrelocsect)

	for _, sect := range Segtext.Sections {
		if sect.Name == ".text" {
			relocSect(ctxt, sect, ctxt.Textp)
		} else {
			relocSect(ctxt, sect, ctxt.datap)
		}
	}

	for _, sect := range Segrodata.Sections {
		relocSect(ctxt, sect, ctxt.datap)
	}
	for _, sect := range Segrelrodata.Sections {
		relocSect(ctxt, sect, ctxt.datap)
	}
	for _, sect := range Segdata.Sections {
		relocSect(ctxt, sect, ctxt.datap)
	}
	for i := 0; i < len(Segdwarf.Sections); i++ {
		sect := Segdwarf.Sections[i]
		si := dwarfp[i]
		if si.secSym() != sect.Sym ||
			ctxt.loader.SymSect(si.secSym()) != sect {
			panic("inconsistency between dwarfp and Segdwarf")
		}
		relocSect(ctxt, sect, si.syms)
	}
	wg.Wait()
}

func addgonote(ctxt *Link, sectionName string, tag uint32, desc []byte) {
	ldr := ctxt.loader
	s := ldr.CreateSymForUpdate(sectionName, 0)
	s.SetType(sym.SELFROSECT)
	// namesz
	s.AddUint32(ctxt.Arch, uint32(len(ELF_NOTE_GO_NAME)))
	// descsz
	s.AddUint32(ctxt.Arch, uint32(len(desc)))
	// tag
	s.AddUint32(ctxt.Arch, tag)
	// name + padding
	s.AddBytes(ELF_NOTE_GO_NAME)
	for len(s.Data())%4 != 0 {
		s.AddUint8(0)
	}
	// desc + padding
	s.AddBytes(desc)
	for len(s.Data())%4 != 0 {
		s.AddUint8(0)
	}
	s.SetSize(int64(len(s.Data())))
	s.SetAlign(4)
}

func (ctxt *Link) doelf() {
	ldr := ctxt.loader

	if ctxt.IsExternal() {
		*FlagD = true
	}

	if !*FlagD { // -d suppresses dynamic loader format
		// dynamic symbol table - first entry all zeros
		dynsym := ldr.CreateSymForUpdate(".dynsym", 0)

		dynsym.SetType(sym.SELFROSECT)
		if elf64 {
			dynsym.SetSize(dynsym.Size() + ELF64SYMSIZE)
		} else {
			dynsym.SetSize(dynsym.Size() + ELF32SYMSIZE)
		}

		// dynamic string table
		dynstr := ldr.CreateSymForUpdate(".dynstr", 0)

		dynstr.SetType(sym.SELFROSECT)
		if dynstr.Size() == 0 {
			dynstr.Addstring("")
		}

		// relocation table
		s := ldr.CreateSymForUpdate(elfRelType, 0)
		s.SetType(sym.SELFROSECT)

		// global offset table
		got := ldr.CreateSymForUpdate(".got", 0)
		if ctxt.UseRelro() {
			got.SetType(sym.SELFRELROSECT)
		} else {
			got.SetType(sym.SELFGOT) // writable
		}

		// ppc64 glink resolver
		if ctxt.IsPPC64() {
			s := ldr.CreateSymForUpdate(".glink", 0)
			s.SetType(sym.SELFRXSECT)
		}

		// hash
		hash := ldr.CreateSymForUpdate(".hash", 0)
		hash.SetType(sym.SELFROSECT)

		gotplt := ldr.CreateSymForUpdate(".got.plt", 0)
		if ctxt.UseRelro() && *flagBindNow {
			gotplt.SetType(sym.SELFRELROSECT)
		} else {
			gotplt.SetType(sym.SELFSECT) // writable
		}

		plt := ldr.CreateSymForUpdate(".plt", 0)
		if ctxt.IsPPC64() {
			// In the ppc64 ABI, .plt is a data section
			// written by the dynamic linker.
			plt.SetType(sym.SELFSECT)
		} else {
			plt.SetType(sym.SELFRXSECT)
		}

		s = ldr.CreateSymForUpdate(elfRelType+".plt", 0)
		s.SetType(sym.SELFROSECT)

		s = ldr.CreateSymForUpdate(".gnu.version", 0)
		s.SetType(sym.SELFROSECT)

		s = ldr.CreateSymForUpdate(".gnu.version_r", 0)
		s.SetType(sym.SELFROSECT)

		// define dynamic elf table
		dynamic := ldr.CreateSymForUpdate(".dynamic", 0)
		switch {
		case thearch.ELF.DynamicReadOnly:
			dynamic.SetType(sym.SELFROSECT)
		case ctxt.UseRelro():
			dynamic.SetType(sym.SELFRELROSECT)
		default:
			dynamic.SetType(sym.SELFSECT)
		}

		if ctxt.IsS390X() {
			// S390X uses .got instead of .got.plt
			gotplt = got
		}
		thearch.ELF.SetupPLT(ctxt, ctxt.loader, plt, gotplt, dynamic.Sym())

		// .dynamic table
		elfWriteDynEntSym(ctxt, dynamic, elf.DT_HASH, hash.Sym())

		elfWriteDynEntSym(ctxt, dynamic, elf.DT_SYMTAB, dynsym.Sym())
		if elf64 {
			Elfwritedynent(ctxt.Arch, dynamic, elf.DT_SYMENT, ELF64SYMSIZE)
		} else {
			Elfwritedynent(ctxt.Arch, dynamic, elf.DT_SYMENT, ELF32SYMSIZE)
		}
		elfWriteDynEntSym(ctxt, dynamic, elf.DT_STRTAB, dynstr.Sym())
		elfwritedynentsymsize(ctxt, dynamic, elf.DT_STRSZ, dynstr.Sym())
		if elfRelType == ".rela" {
			rela := ldr.LookupOrCreateSym(".rela", 0)
			elfWriteDynEntSym(ctxt, dynamic, elf.DT_RELA, rela)
			elfwritedynentsymsize(ctxt, dynamic, elf.DT_RELASZ, rela)
			Elfwritedynent(ctxt.Arch, dynamic, elf.DT_RELAENT, ELF64RELASIZE)
		} else {
			rel := ldr.LookupOrCreateSym(".rel", 0)
			elfWriteDynEntSym(ctxt, dynamic, elf.DT_REL, rel)
			elfwritedynentsymsize(ctxt, dynamic, elf.DT_RELSZ, rel)
			Elfwritedynent(ctxt.Arch, dynamic, elf.DT_RELENT, ELF32RELSIZE)
		}

		if rpath.val != "" {
			Elfwritedynent(ctxt.Arch, dynamic, elf.DT_RUNPATH, uint64(dynstr.Addstring(rpath.val)))
		}

		if ctxt.IsPPC64() {
			elfWriteDynEntSym(ctxt, dynamic, elf.DT_PLTGOT, plt.Sym())
		} else {
			elfWriteDynEntSym(ctxt, dynamic, elf.DT_PLTGOT, gotplt.Sym())
		}

		if ctxt.IsPPC64() {
			Elfwritedynent(ctxt.Arch, dynamic, elf.DT_PPC64_OPT, 0)
		}

		// Solaris dynamic linker can't handle an empty .rela.plt if
		// DT_JMPREL is emitted so we have to defer generation of elf.DT_PLTREL,
		// DT_PLTRELSZ, and elf.DT_JMPREL dynamic entries until after we know the
		// size of .rel(a).plt section.

		Elfwritedynent(ctxt.Arch, dynamic, elf.DT_DEBUG, 0)
	}

	if ctxt.IsShared() {
		// The go.link.abihashbytes symbol will be pointed at the appropriate
		// part of the .note.go.abihash section in data.go:func address().
		s := ldr.LookupOrCreateSym("go:link.abihashbytes", 0)
		sb := ldr.MakeSymbolUpdater(s)
		ldr.SetAttrLocal(s, true)
		sb.SetType(sym.SRODATA)
		ldr.SetAttrSpecial(s, true)
		sb.SetReachable(true)
		sb.SetSize(hash.Size32)
		slices.SortFunc(ctxt.Library, func(a, b *sym.Library) int {
			return strings.Compare(a.Pkg, b.Pkg)
		})
		h := hash.New32()
		for _, l := range ctxt.Library {
			h.Write(l.Fingerprint[:])
		}
		addgonote(ctxt, ".note.go.abihash", ELF_NOTE_GOABIHASH_TAG, h.Sum([]byte{}))
		addgonote(ctxt, ".note.go.pkg-list", ELF_NOTE_GOPKGLIST_TAG, pkglistfornote)
		var deplist []string
		for _, shlib := range ctxt.Shlibs {
			deplist = append(deplist, filepath.Base(shlib.Path))
		}
		addgonote(ctxt, ".note.go.deps", ELF_NOTE_GODEPS_TAG, []byte(strings.Join(deplist, "\n")))
	}

	if ctxt.LinkMode == LinkExternal && *flagBuildid != "" {
		addgonote(ctxt, ".note.go.buildid", ELF_NOTE_GOBUILDID_TAG, []byte(*flagBuildid))
	}

	//type mipsGnuAttributes struct {
	//	version uint8   // 'A'
	//	length  uint32  // 15 including itself
	//	gnu     [4]byte // "gnu\0"
	//	tag     uint8   // 1:file, 2: section, 3: symbol, 1 here
	//	taglen  uint32  // tag length, including tag, 7 here
	//	tagfp   uint8   // 4
	//	fpAbi  uint8    // see .MIPS.abiflags
	//}
	if ctxt.IsMIPS() {
		gnuattributes := ldr.CreateSymForUpdate(".gnu.attributes", 0)
		gnuattributes.SetType(sym.SELFROSECT)
		gnuattributes.SetReachable(true)
		gnuattributes.AddUint8('A')               // version 'A'
		gnuattributes.AddUint32(ctxt.Arch, 15)    // length 15 including itself
		gnuattributes.AddBytes([]byte("gnu\x00")) // "gnu\0"
		gnuattributes.AddUint8(1)                 // 1:file, 2: section, 3: symbol, 1 here
		gnuattributes.AddUint32(ctxt.Arch, 7)     // tag length, including tag, 7 here
		gnuattributes.AddUint8(4)                 // 4 for FP, 8 for MSA
		if buildcfg.GOMIPS == "softfloat" {
			gnuattributes.AddUint8(MIPS_FPABI_SOFT)
		} else {
			// Note: MIPS_FPABI_ANY is bad naming: in fact it is MIPS I style FPR usage.
			//       It is not for 'ANY'.
			// TODO: switch to FPXX after be sure that no odd-number-fpr is used.
			gnuattributes.AddUint8(MIPS_FPABI_ANY)
		}
	}
}

// shsym fills in fields of sh where s contains the contents of the section.
func shsym(sh *ElfShdr, ldr *loader.Loader, s loader.Sym) {
	if s == 0 {
		panic("bad symbol in shsym2")
	}
	addr := ldr.SymValue(s)
	if sh.Flags&uint64(elf.SHF_ALLOC) != 0 {
		sh.Addr = uint64(addr)
	}
	sh.Off = uint64(datoff(ldr, s, addr))
	sh.Size = uint64(ldr.SymSize(s))
}

func phsh(ph *ElfPhdr, sh *ElfShdr) {
	ph.Vaddr = sh.Addr
	ph.Paddr = ph.Vaddr
	ph.Off = sh.Off
	ph.Filesz = sh.Size
	ph.Memsz = sh.Size
	ph.Align = sh.Addralign
}

func Asmbelfsetup() {
	// This null SHdr must appear before all others.
	elfshname("")

	for _, sect := range Segtext.Sections {
		// There could be multiple .text sections. Instead check the Elfsect
		// field to determine if already has an ElfShdr and if not, create one.
		if sect.Name == ".text" {
			if sect.Elfsect == nil {
				sect.Elfsect = elfshnamedup(sect.Name)
			}
		} else {
			elfshalloc(sect)
		}
	}
	for _, sect := range Segrodata.Sections {
		elfshalloc(sect)
	}
	for _, sect := range Segrelrodata.Sections {
		elfshalloc(sect)
	}
	for _, sect := range Segdata.Sections {
		elfshalloc(sect)
	}
	for _, sect := range Segdwarf.Sections {
		elfshalloc(sect)
	}
}

func asmbElf(ctxt *Link) {
	var symo int64
	symo = int64(Segdwarf.Fileoff + Segdwarf.Filelen)
	symo = Rnd(symo, int64(ctxt.Arch.PtrSize))

	ldr := ctxt.loader
	eh := getElfEhdr()
	switch ctxt.Arch.Family {
	default:
		Exitf("unknown architecture in asmbelf: %v", ctxt.Arch.Family)
	case sys.MIPS, sys.MIPS64:
		eh.Machine = uint16(elf.EM_MIPS)
	case sys.Loong64:
		eh.Machine = uint16(elf.EM_LOONGARCH)
	case sys.ARM:
		eh.Machine = uint16(elf.EM_ARM)
	case sys.AMD64:
		eh.Machine = uint16(elf.EM_X86_64)
	case sys.ARM64:
		eh.Machine = uint16(elf.EM_AARCH64)
	case sys.I386:
		eh.Machine = uint16(elf.EM_386)
	case sys.PPC64:
		eh.Machine = uint16(elf.EM_PPC64)
	case sys.RISCV64:
		eh.Machine = uint16(elf.EM_RISCV)
	case sys.S390X:
		eh.Machine = uint16(elf.EM_S390)
	}

	elfreserve := int64(ELFRESERVE)

	numtext := int64(0)
	for _, sect := range Segtext.Sections {
		if sect.Name == ".text" {
			numtext++
		}
	}

	// If there are multiple text sections, extra space is needed
	// in the elfreserve for the additional .text and .rela.text
	// section headers.  It can handle 4 extra now. Headers are
	// 64 bytes.

	if numtext > 4 {
		elfreserve += elfreserve + numtext*64*2
	}

	startva := *FlagTextAddr - int64(HEADR)
	resoff := elfreserve

	var pph *ElfPhdr
	var pnote *ElfPhdr
	getpnote := func() *ElfPhdr {
		if pnote == nil {
			pnote = newElfPhdr()
			pnote.Type = elf.PT_NOTE
			pnote.Flags = elf.PF_R
		}
		return pnote
	}
	if *flagRace && ctxt.IsNetbsd() {
		sh := elfshname(".note.netbsd.pax")
		resoff -= int64(elfnetbsdpax(sh, uint64(startva), uint64(resoff)))
		phsh(getpnote(), sh)
	}
	if ctxt.LinkMode == LinkExternal {
		// skip program headers
		eh.Phoff = 0

		eh.Phentsize = 0

		if ctxt.BuildMode == BuildModeShared {
			sh := elfshname(".note.go.pkg-list")
			sh.Type = uint32(elf.SHT_NOTE)
			sh = elfshname(".note.go.abihash")
			sh.Type = uint32(elf.SHT_NOTE)
			sh.Flags = uint64(elf.SHF_ALLOC)
			sh = elfshname(".note.go.deps")
			sh.Type = uint32(elf.SHT_NOTE)
		}

		if *flagBuildid != "" {
			sh := elfshname(".note.go.buildid")
			sh.Type = uint32(elf.SHT_NOTE)
			sh.Flags = uint64(elf.SHF_ALLOC)
		}

		goto elfobj
	}

	// program header info
	pph = newElfPhdr()

	pph.Type = elf.PT_PHDR
	pph.Flags = elf.PF_R
	pph.Off = uint64(eh.Ehsize)
	pph.Vaddr = uint64(*FlagTextAddr) - uint64(HEADR) + pph.Off
	pph.Paddr = uint64(*FlagTextAddr) - uint64(HEADR) + pph.Off
	pph.Align = uint64(*FlagRound)

	// PHDR must be in a loaded segment. Adjust the text
	// segment boundaries downwards to include it.
	{
		o := int64(Segtext.Vaddr - pph.Vaddr)
		Segtext.Vaddr -= uint64(o)
		Segtext.Length += uint64(o)
		o = int64(Segtext.Fileoff - pph.Off)
		Segtext.Fileoff -= uint64(o)
		Segtext.Filelen += uint64(o)
	}

	if !*FlagD { // -d suppresses dynamic loader format
		// interpreter
		sh := elfshname(".interp")

		sh.Type = uint32(elf.SHT_PROGBITS)
		sh.Flags = uint64(elf.SHF_ALLOC)
		sh.Addralign = 1

		if interpreter == "" && buildcfg.GOOS == runtime.GOOS && buildcfg.GOARCH == runtime.GOARCH && buildcfg.GO_LDSO != "" {
			interpreter = buildcfg.GO_LDSO
		}

		if interpreter == "" {
			switch ctxt.HeadType {
			case objabi.Hlinux:
				if buildcfg.GOOS == "android" {
					interpreter = thearch.ELF.Androiddynld
					if interpreter == "" {
						Exitf("ELF interpreter not set")
					}
				} else {
					interpreter = thearch.ELF.Linuxdynld
					// If interpreter does not exist, try musl instead.
					// This lets the same cmd/link binary work on
					// both glibc-based and musl-based systems.
					if _, err := os.Stat(interpreter); err != nil {
						if musl := thearch.ELF.LinuxdynldMusl; musl != "" {
							if _, err := os.Stat(musl); err == nil {
								interpreter = musl
							}
						}
					}
				}

			case objabi.Hfreebsd:
				interpreter = thearch.ELF.Freebsddynld

			case objabi.Hnetbsd:
				interpreter = thearch.ELF.Netbsddynld

			case objabi.Hopenbsd:
				interpreter = thearch.ELF.Openbsddynld

			case objabi.Hdragonfly:
				interpreter = thearch.ELF.Dragonflydynld

			case objabi.Hsolaris:
				interpreter = thearch.ELF.Solarisdynld
			}
		}

		resoff -= int64(elfinterp(sh, uint64(startva), uint64(resoff), interpreter))

		ph := newElfPhdr()
		ph.Type = elf.PT_INTERP
		ph.Flags = elf.PF_R
		phsh(ph, sh)
	}

	if ctxt.HeadType == objabi.Hnetbsd || ctxt.HeadType == objabi.Hopenbsd || ctxt.HeadType == objabi.Hfreebsd {
		var sh *ElfShdr
		switch ctxt.HeadType {
		case objabi.Hnetbsd:
			sh = elfshname(".note.netbsd.ident")
			resoff -= int64(elfnetbsdsig(sh, uint64(startva), uint64(resoff)))

		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, OpenBSD and FreeBSD require ident in an independent segment.
		pnotei := newElfPhdr()
		pnotei.Type = elf.PT_NOTE
		pnotei.Flags = elf.PF_R
		phsh(pnotei, sh)
	}

	if len(buildinfo) > 0 {
		sh := elfshname(".note.gnu.build-id")
		resoff -= int64(elfbuildinfo(sh, uint64(startva), uint64(resoff)))
		phsh(getpnote(), sh)
	}

	if *flagBuildid != "" {
		sh := elfshname(".note.go.buildid")
		resoff -= int64(elfgobuildid(sh, uint64(startva), uint64(resoff)))
		phsh(getpnote(), sh)
	}

	// Additions to the reserved area must be above this line.

	elfphload(&Segtext)
	if len(Segrodata.Sections) > 0 {
		elfphload(&Segrodata)
	}
	if len(Segrelrodata.Sections) > 0 {
		elfphload(&Segrelrodata)
		elfphrelro(&Segrelrodata)
	}
	elfphload(&Segdata)

	// Dynamic linking sections
	if !*FlagD {
		sh := elfshname(".dynsym")
		sh.Type = uint32(elf.SHT_DYNSYM)
		sh.Flags = uint64(elf.SHF_ALLOC)
		if elf64 {
			sh.Entsize = ELF64SYMSIZE
		} else {
			sh.Entsize = ELF32SYMSIZE
		}
		sh.Addralign = uint64(ctxt.Arch.RegSize)
		sh.link = elfshname(".dynstr")

		// sh.Info is the index of first non-local symbol (number of local symbols)
		s := ldr.Lookup(".dynsym", 0)
		i := uint32(0)
		for sub := s; sub != 0; sub = ldr.SubSym(sub) {
			i++
			if !ldr.AttrLocal(sub) {
				break
			}
		}
		sh.Info = i
		shsym(sh, ldr, s)

		sh = elfshname(".dynstr")
		sh.Type = uint32(elf.SHT_STRTAB)
		sh.Flags = uint64(elf.SHF_ALLOC)
		sh.Addralign = 1
		shsym(sh, ldr, ldr.Lookup(".dynstr", 0))

		if elfverneed != 0 {
			sh := elfshname(".gnu.version")
			sh.Type = uint32(elf.SHT_GNU_VERSYM)
			sh.Flags = uint64(elf.SHF_ALLOC)
			sh.Addralign = 2
			sh.link = elfshname(".dynsym")
			sh.Entsize = 2
			shsym(sh, ldr, ldr.Lookup(".gnu.version", 0))

			sh = elfshname(".gnu.version_r")
			sh.Type = uint32(elf.SHT_GNU_VERNEED)
			sh.Flags = uint64(elf.SHF_ALLOC)
			sh.Addralign = uint64(ctxt.Arch.RegSize)
			sh.Info = uint32(elfverneed)
			sh.link = elfshname(".dynstr")
			shsym(sh, ldr, ldr.Lookup(".gnu.version_r", 0))
		}

		if elfRelType == ".rela" {
			sh := elfshname(".rela.plt")
			sh.Type = uint32(elf.SHT_RELA)
			sh.Flags = uint64(elf.SHF_ALLOC)
			sh.Entsize = ELF64RELASIZE
			sh.Addralign = uint64(ctxt.Arch.RegSize)
			sh.link = elfshname(".dynsym")
			sh.info = elfshname(".plt")
			shsym(sh, ldr, ldr.Lookup(".rela.plt", 0))

			sh = elfshname(".rela")
			sh.Type = uint32(elf.SHT_RELA)
			sh.Flags = uint64(elf.SHF_ALLOC)
			sh.Entsize = ELF64RELASIZE
			sh.Addralign = 8
			sh.link = elfshname(".dynsym")
			shsym(sh, ldr, ldr.Lookup(".rela", 0))
		} else {
			sh := elfshname(".rel.plt")
			sh.Type = uint32(elf.SHT_REL)
			sh.Flags = uint64(elf.SHF_ALLOC)
			sh.Entsize = ELF32RELSIZE
			sh.Addralign = 4
			sh.link = elfshname(".dynsym")
			shsym(sh, ldr, ldr.Lookup(".rel.plt", 0))

			sh = elfshname(".rel")
			sh.Type = uint32(elf.SHT_REL)
			sh.Flags = uint64(elf.SHF_ALLOC)
			sh.Entsize = ELF32RELSIZE
			sh.Addralign = 4
			sh.link = elfshname(".dynsym")
			shsym(sh, ldr, ldr.Lookup(".rel", 0))
		}

		if elf.Machine(eh.Machine) == elf.EM_PPC64 {
			sh := elfshname(".glink")
			sh.Type = uint32(elf.SHT_PROGBITS)
			sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_EXECINSTR)
			sh.Addralign = 4
			shsym(sh, ldr, ldr.Lookup(".glink", 0))
		}

		sh = elfshname(".plt")
		sh.Type = uint32(elf.SHT_PROGBITS)
		sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_EXECINSTR)
		if elf.Machine(eh.Machine) == elf.EM_X86_64 {
			sh.Entsize = 16
		} else if elf.Machine(eh.Machine) == elf.EM_S390 {
			sh.Entsize = 32
		} else if elf.Machine(eh.Machine) == elf.EM_PPC64 {
			// On ppc64, this is just a table of addresses
			// filled by the dynamic linker
			sh.Type = uint32(elf.SHT_NOBITS)

			sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE)
			sh.Entsize = 8
		} else {
			sh.Entsize = 4
		}
		sh.Addralign = sh.Entsize
		shsym(sh, ldr, ldr.Lookup(".plt", 0))

		// On ppc64, .got comes from the input files, so don't
		// create it here, and .got.plt is not used.
		if elf.Machine(eh.Machine) != elf.EM_PPC64 {
			sh := elfshname(".got")
			sh.Type = uint32(elf.SHT_PROGBITS)
			sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE)
			sh.Entsize = uint64(ctxt.Arch.RegSize)
			sh.Addralign = uint64(ctxt.Arch.RegSize)
			shsym(sh, ldr, ldr.Lookup(".got", 0))

			sh = elfshname(".got.plt")
			sh.Type = uint32(elf.SHT_PROGBITS)
			sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE)
			sh.Entsize = uint64(ctxt.Arch.RegSize)
			sh.Addralign = uint64(ctxt.Arch.RegSize)
			shsym(sh, ldr, ldr.Lookup(".got.plt", 0))
		}

		sh = elfshname(".hash")
		sh.Type = uint32(elf.SHT_HASH)
		sh.Flags = uint64(elf.SHF_ALLOC)
		sh.Entsize = 4
		sh.Addralign = uint64(ctxt.Arch.RegSize)
		sh.link = elfshname(".dynsym")
		shsym(sh, ldr, ldr.Lookup(".hash", 0))

		// sh and elf.PT_DYNAMIC for .dynamic section
		sh = elfshname(".dynamic")

		sh.Type = uint32(elf.SHT_DYNAMIC)
		sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE)
		sh.Entsize = 2 * uint64(ctxt.Arch.RegSize)
		sh.Addralign = uint64(ctxt.Arch.RegSize)
		sh.link = elfshname(".dynstr")
		shsym(sh, ldr, ldr.Lookup(".dynamic", 0))
		ph := newElfPhdr()
		ph.Type = elf.PT_DYNAMIC
		ph.Flags = elf.PF_R + elf.PF_W
		phsh(ph, sh)

		// Thread-local storage segment (really just size).
		tlssize := uint64(0)
		for _, sect := range Segdata.Sections {
			if sect.Name == ".tbss" {
				tlssize = sect.Length
			}
		}
		if tlssize != 0 {
			ph := newElfPhdr()
			ph.Type = elf.PT_TLS
			ph.Flags = elf.PF_R
			ph.Memsz = tlssize
			ph.Align = uint64(ctxt.Arch.RegSize)
		}
	}

	if ctxt.HeadType == objabi.Hlinux || ctxt.HeadType == objabi.Hfreebsd {
		ph := newElfPhdr()
		ph.Type = elf.PT_GNU_STACK
		ph.Flags = elf.PF_W + elf.PF_R
		ph.Align = uint64(ctxt.Arch.RegSize)
	} else if ctxt.HeadType == objabi.Hopenbsd {
		ph := newElfPhdr()
		ph.Type = elf.PT_OPENBSD_NOBTCFI
		ph.Flags = elf.PF_X
	} else if ctxt.HeadType == objabi.Hsolaris {
		ph := newElfPhdr()
		ph.Type = elf.PT_SUNWSTACK
		ph.Flags = elf.PF_W + elf.PF_R
	}

elfobj:
	if ctxt.IsMIPS() {
		sh := elfshname(".MIPS.abiflags")
		sh.Type = uint32(elf.SHT_MIPS_ABIFLAGS)
		sh.Flags = uint64(elf.SHF_ALLOC)
		sh.Addralign = 8
		resoff -= int64(elfMipsAbiFlags(sh, uint64(startva), uint64(resoff)))

		ph := newElfPhdr()
		ph.Type = elf.PT_MIPS_ABIFLAGS
		ph.Flags = elf.PF_R
		phsh(ph, sh)

		sh = elfshname(".gnu.attributes")
		sh.Type = uint32(elf.SHT_GNU_ATTRIBUTES)
		sh.Addralign = 1
		ldr := ctxt.loader
		shsym(sh, ldr, ldr.Lookup(".gnu.attributes", 0))
	}

	// put these sections early in the list
	if !*FlagS {
		elfshname(".symtab")
		elfshname(".strtab")
	}
	elfshname(".shstrtab")

	for _, sect := range Segtext.Sections {
		elfshbits(ctxt.LinkMode, sect)
	}
	for _, sect := range Segrodata.Sections {
		elfshbits(ctxt.LinkMode, sect)
	}
	for _, sect := range Segrelrodata.Sections {
		elfshbits(ctxt.LinkMode, sect)
	}
	for _, sect := range Segdata.Sections {
		elfshbits(ctxt.LinkMode, sect)
	}
	for _, sect := range Segdwarf.Sections {
		elfshbits(ctxt.LinkMode, sect)
	}

	if ctxt.LinkMode == LinkExternal {
		for _, sect := range Segtext.Sections {
			elfshreloc(ctxt.Arch, sect)
		}
		for _, sect := range Segrodata.Sections {
			elfshreloc(ctxt.Arch, sect)
		}
		for _, sect := range Segrelrodata.Sections {
			elfshreloc(ctxt.Arch, sect)
		}
		for _, sect := range Segdata.Sections {
			elfshreloc(ctxt.Arch, sect)
		}
		for _, si := range dwarfp {
			sect := ldr.SymSect(si.secSym())
			elfshreloc(ctxt.Arch, sect)
		}
		// add a .note.GNU-stack section to mark the stack as non-executable
		sh := elfshname(".note.GNU-stack")

		sh.Type = uint32(elf.SHT_PROGBITS)
		sh.Addralign = 1
		sh.Flags = 0
	}

	elfSortShdrs(ctxt)

	sh := elfshname(".shstrtab")
	eh.Shstrndx = uint16(elfShdrShnum(sh))

	var shstrtabLen uint32
	ctxt.Out.SeekSet(symo)
	if *FlagS {
		shstrtabLen = elfWriteShstrtab(ctxt)
	} else {
		asmElfSym(ctxt)
		ctxt.Out.Write(elfstrdat)
		shstrtabLen = elfWriteShstrtab(ctxt)
		if ctxt.IsExternal() {
			elfEmitReloc(ctxt)
		}
	}
	ctxt.Out.SeekSet(0)

	var shstroff uint64
	if !*FlagS {
		sh := elfshname(".symtab")
		sh.Type = uint32(elf.SHT_SYMTAB)
		sh.Off = uint64(symo)
		sh.Size = uint64(symSize)
		sh.Addralign = uint64(ctxt.Arch.RegSize)
		sh.Entsize = 8 + 2*uint64(ctxt.Arch.RegSize)
		sh.link = elfshname(".strtab")
		sh.Info = uint32(elfglobalsymndx)

		sh = elfshname(".strtab")
		sh.Type = uint32(elf.SHT_STRTAB)
		sh.Off = uint64(symo) + uint64(symSize)
		sh.Size = uint64(len(elfstrdat))
		sh.Addralign = 1
		shstroff = sh.Off + sh.Size
	} else {
		shstroff = uint64(symo)
	}

	sh = elfshname(".shstrtab")
	sh.Type = uint32(elf.SHT_STRTAB)
	sh.Off = shstroff
	sh.Size = uint64(shstrtabLen)
	sh.Addralign = 1

	// Main header
	copy(eh.Ident[:], elf.ELFMAG)

	var osabi elf.OSABI
	switch ctxt.HeadType {
	case objabi.Hfreebsd:
		osabi = elf.ELFOSABI_FREEBSD
	case objabi.Hnetbsd:
		osabi = elf.ELFOSABI_NETBSD
	case objabi.Hopenbsd:
		osabi = elf.ELFOSABI_OPENBSD
	case objabi.Hdragonfly:
		osabi = elf.ELFOSABI_NONE
	}
	eh.Ident[elf.EI_OSABI] = byte(osabi)

	if elf64 {
		eh.Ident[elf.EI_CLASS] = byte(elf.ELFCLASS64)
	} else {
		eh.Ident[elf.EI_CLASS] = byte(elf.ELFCLASS32)
	}
	if ctxt.Arch.ByteOrder == binary.BigEndian {
		eh.Ident[elf.EI_DATA] = byte(elf.ELFDATA2MSB)
	} else {
		eh.Ident[elf.EI_DATA] = byte(elf.ELFDATA2LSB)
	}
	eh.Ident[elf.EI_VERSION] = byte(elf.EV_CURRENT)

	if ctxt.LinkMode == LinkExternal {
		eh.Type = uint16(elf.ET_REL)
	} else if ctxt.BuildMode == BuildModePIE {
		eh.Type = uint16(elf.ET_DYN)
	} else {
		eh.Type = uint16(elf.ET_EXEC)
	}

	if ctxt.LinkMode != LinkExternal {
		eh.Entry = uint64(Entryvalue(ctxt))
	}

	eh.Version = uint32(elf.EV_CURRENT)

	if pph != nil {
		pph.Filesz = uint64(eh.Phnum) * uint64(eh.Phentsize)
		pph.Memsz = pph.Filesz
	}

	if len(shdr) >= 0xffff {
		Errorf("too many ELF sections")
	}
	eh.Shnum = uint16(len(shdr))

	ctxt.Out.SeekSet(0)
	a := int64(0)
	a += int64(elfwritehdr(ctxt.Out))
	a += int64(elfwritephdrs(ctxt.Out))
	a += int64(elfwriteshdrs(ctxt.Out))
	if !*FlagD {
		a += int64(elfwriteinterp(ctxt.Out))
	}
	if ctxt.IsMIPS() {
		a += int64(elfWriteMipsAbiFlags(ctxt))
	}

	if ctxt.LinkMode != LinkExternal {
		if ctxt.HeadType == objabi.Hnetbsd {
			a += int64(elfwritenetbsdsig(ctxt.Out))
		}
		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))
		}
		if *flagBuildid != "" {
			a += int64(elfwritegobuildid(ctxt.Out))
		}
	}
	if *flagRace && ctxt.IsNetbsd() {
		a += int64(elfwritenetbsdpax(ctxt.Out))
	}

	if a > elfreserve {
		Errorf("ELFRESERVE too small: %d > %d with %d text sections", a, elfreserve, numtext)
	}

	// Verify the amount of space allocated for the elf header is sufficient.  The file offsets are
	// already computed in layout, so we could spill into another section.
	if a > int64(HEADR) {
		Errorf("HEADR too small: %d > %d with %d text sections", a, HEADR, numtext)
	}
}

func elfadddynsym(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.Sym) {
	ldr.SetSymDynid(s, int32(Nelfsym))
	Nelfsym++
	d := ldr.MakeSymbolUpdater(syms.DynSym)
	name := ldr.SymExtname(s)
	dstru := ldr.MakeSymbolUpdater(syms.DynStr)
	st := ldr.SymType(s)
	cgoeStatic := ldr.AttrCgoExportStatic(s)
	cgoeDynamic := ldr.AttrCgoExportDynamic(s)
	cgoexp := (cgoeStatic || cgoeDynamic)

	d.AddUint32(target.Arch, uint32(dstru.Addstring(name)))

	if elf64 {
		// type
		var t uint8

		if cgoexp && st.IsText() {
			t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC)
		} else {
			t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_OBJECT)
		}
		d.AddUint8(t)

		// reserved
		d.AddUint8(0)

		// section where symbol is defined
		if st == sym.SDYNIMPORT {
			d.AddUint16(target.Arch, uint16(elf.SHN_UNDEF))
		} else {
			d.AddUint16(target.Arch, 1)
		}

		// value
		if st == sym.SDYNIMPORT {
			d.AddUint64(target.Arch, 0)
		} else {
			d.AddAddrPlus(target.Arch, s, 0)
		}

		// size of object
		d.AddUint64(target.Arch, uint64(len(ldr.Data(s))))

		dil := ldr.SymDynimplib(s)

		if !cgoeDynamic && dil != "" && !seenlib[dil] {
			du := ldr.MakeSymbolUpdater(syms.Dynamic)
			Elfwritedynent(target.Arch, du, elf.DT_NEEDED, uint64(dstru.Addstring(dil)))
			seenlib[dil] = true
		}
	} else {

		// value
		if st == sym.SDYNIMPORT {
			d.AddUint32(target.Arch, 0)
		} else {
			d.AddAddrPlus(target.Arch, s, 0)
		}

		// size of object
		d.AddUint32(target.Arch, uint32(len(ldr.Data(s))))

		// type
		var t uint8

		// TODO(mwhudson): presumably the behavior should actually be the same on both arm and 386.
		if target.Arch.Family == sys.I386 && cgoexp && st.IsText() {
			t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC)
		} else if target.Arch.Family == sys.ARM && cgoeDynamic && st.IsText() {
			t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC)
		} else {
			t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_OBJECT)
		}
		d.AddUint8(t)
		d.AddUint8(0)

		// shndx
		if st == sym.SDYNIMPORT {
			d.AddUint16(target.Arch, uint16(elf.SHN_UNDEF))
		} else {
			d.AddUint16(target.Arch, 1)
		}
	}
}
