mirror of
https://github.com/golang/go.git
synced 2025-05-30 19:52:53 +00:00
Currently in libfuzzer mode, we put our counters in section __sancov_cntrs. When linking with C/C++ code that also has fuzzer counters, apparently the C linker combines our counters and their counters and registers them together. But in the Go runtime we also have code to register our counters. So the Go counters ended up registered twice, causing problems. Since we already have code to register our counters, put them in a Go-specific section so it won't be combined with the C counters. Fixes #57449. Change-Id: If3d41735124e7e301572d4b7aecf7d057ac134c0 Reviewed-on: https://go-review.googlesource.com/c/go/+/459055 Reviewed-by: Nicolas Hillegeer <aktau@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Cherry Mui <cherryyz@google.com>
2396 lines
62 KiB
Go
2396 lines
62 KiB
Go
// 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/notsha256"
|
|
"cmd/internal/objabi"
|
|
"cmd/internal/sys"
|
|
"cmd/link/internal/loader"
|
|
"cmd/link/internal/sym"
|
|
"debug/elf"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"internal/buildcfg"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"sort"
|
|
"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.
|
|
*/
|
|
|
|
/*
|
|
* Note header. The ".note" section contains an array of notes. Each
|
|
* begins with this header, aligned to a word boundary. Immediately
|
|
* following the note header is n_namesz bytes of name, padded to the
|
|
* next word boundary. Then comes n_descsz bytes of descriptor, again
|
|
* padded to a word boundary. The values of n_namesz and n_descsz do
|
|
* not include the padding.
|
|
*/
|
|
type elfNote struct {
|
|
nNamesz uint32
|
|
nDescsz uint32
|
|
nType uint32
|
|
}
|
|
|
|
/* For accessing the fields of r_info. */
|
|
|
|
/* For constructing r_info from field values. */
|
|
|
|
/*
|
|
* Relocation types.
|
|
*/
|
|
const (
|
|
ARM_MAGIC_TRAMP_NUMBER = 0x5c000003
|
|
)
|
|
|
|
/*
|
|
* Symbol table entries.
|
|
*/
|
|
|
|
/* For accessing the fields of st_info. */
|
|
|
|
/* For constructing st_info from field values. */
|
|
|
|
/* For accessing the fields of st_other. */
|
|
|
|
/*
|
|
* ELF header.
|
|
*/
|
|
type ElfEhdr elf.Header64
|
|
|
|
/*
|
|
* Section header.
|
|
*/
|
|
type ElfShdr struct {
|
|
elf.Section64
|
|
shnum elf.SectionIndex
|
|
}
|
|
|
|
/*
|
|
* Program header.
|
|
*/
|
|
type ElfPhdr elf.ProgHeader
|
|
|
|
/* For accessing the fields of r_info. */
|
|
|
|
/* For constructing r_info from field values. */
|
|
|
|
/*
|
|
* Symbol table entries.
|
|
*/
|
|
|
|
/* For accessing the fields of st_info. */
|
|
|
|
/* For constructing st_info from field values. */
|
|
|
|
/* For accessing the fields of st_other. */
|
|
|
|
/*
|
|
* Go linker interface
|
|
*/
|
|
const (
|
|
ELF64HDRSIZE = 64
|
|
ELF64PHDRSIZE = 56
|
|
ELF64SHDRSIZE = 64
|
|
ELF64RELSIZE = 16
|
|
ELF64RELASIZE = 24
|
|
ELF64SYMSIZE = 24
|
|
ELF32HDRSIZE = 52
|
|
ELF32PHDRSIZE = 32
|
|
ELF32SHDRSIZE = 40
|
|
ELF32SYMSIZE = 16
|
|
ELF32RELSIZE = 8
|
|
)
|
|
|
|
/*
|
|
* The interface uses the 64-bit structures always,
|
|
* to avoid code duplication. The writers know how to
|
|
* marshal a 32-bit representation from the 64-bit structure.
|
|
*/
|
|
|
|
var Elfstrdat []byte
|
|
|
|
/*
|
|
* Total amount of space to reserve at the start of the file
|
|
* for Header, PHeaders, SHeaders, and interp.
|
|
* May waste some.
|
|
* On FreeBSD, cannot be larger than a page.
|
|
*/
|
|
const (
|
|
ELFRESERVE = 4096
|
|
)
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
const (
|
|
NSECT = 400
|
|
)
|
|
|
|
var (
|
|
Nelfsym = 1
|
|
|
|
elf64 bool
|
|
// Either ".rel" or ".rela" depending on which type of relocation the
|
|
// target platform uses.
|
|
elfRelType string
|
|
|
|
ehdr ElfEhdr
|
|
phdr [NSECT]*ElfPhdr
|
|
shdr [NSECT]*ElfShdr
|
|
|
|
interp string
|
|
)
|
|
|
|
type Elfstring struct {
|
|
s string
|
|
off int
|
|
}
|
|
|
|
var elfstr [100]Elfstring
|
|
|
|
var nelfstr int
|
|
|
|
var buildinfo []byte
|
|
|
|
/*
|
|
Initialize the global variable that describes the ELF header. It will be updated as
|
|
we write section and prog 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 {
|
|
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 = 0x3 /* LoongArch lp64d */
|
|
}
|
|
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 */
|
|
}
|
|
}
|
|
|
|
// Make 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))
|
|
}
|
|
|
|
func elf64shdr(out *OutBuf, e *ElfShdr) {
|
|
out.Write32(e.Name)
|
|
out.Write32(uint32(e.Type))
|
|
out.Write64(uint64(e.Flags))
|
|
out.Write64(e.Addr)
|
|
out.Write64(e.Off)
|
|
out.Write64(e.Size)
|
|
out.Write32(e.Link)
|
|
out.Write32(e.Info)
|
|
out.Write64(e.Addralign)
|
|
out.Write64(e.Entsize)
|
|
}
|
|
|
|
func elf32shdr(out *OutBuf, e *ElfShdr) {
|
|
out.Write32(e.Name)
|
|
out.Write32(uint32(e.Type))
|
|
out.Write32(uint32(e.Flags))
|
|
out.Write32(uint32(e.Addr))
|
|
out.Write32(uint32(e.Off))
|
|
out.Write32(uint32(e.Size))
|
|
out.Write32(e.Link)
|
|
out.Write32(e.Info)
|
|
out.Write32(uint32(e.Addralign))
|
|
out.Write32(uint32(e.Entsize))
|
|
}
|
|
|
|
func elfwriteshdrs(out *OutBuf) uint32 {
|
|
if elf64 {
|
|
for i := 0; i < int(ehdr.Shnum); i++ {
|
|
elf64shdr(out, shdr[i])
|
|
}
|
|
return uint32(ehdr.Shnum) * ELF64SHDRSIZE
|
|
}
|
|
|
|
for i := 0; i < int(ehdr.Shnum); i++ {
|
|
elf32shdr(out, shdr[i])
|
|
}
|
|
return uint32(ehdr.Shnum) * ELF32SHDRSIZE
|
|
}
|
|
|
|
func elfsetstring(ctxt *Link, s loader.Sym, str string, off int) {
|
|
if nelfstr >= len(elfstr) {
|
|
ctxt.Errorf(s, "too many elf strings")
|
|
errorexit()
|
|
}
|
|
|
|
elfstr[nelfstr].s = str
|
|
elfstr[nelfstr].off = off
|
|
nelfstr++
|
|
}
|
|
|
|
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)
|
|
if ehdr.Phnum >= NSECT {
|
|
Errorf(nil, "too many phdrs")
|
|
} else {
|
|
phdr[ehdr.Phnum] = e
|
|
ehdr.Phnum++
|
|
}
|
|
if elf64 {
|
|
ehdr.Shoff += ELF64PHDRSIZE
|
|
} else {
|
|
ehdr.Shoff += ELF32PHDRSIZE
|
|
}
|
|
return e
|
|
}
|
|
|
|
func newElfShdr(name int64) *ElfShdr {
|
|
e := new(ElfShdr)
|
|
e.Name = uint32(name)
|
|
e.shnum = elf.SectionIndex(ehdr.Shnum)
|
|
if ehdr.Shnum >= NSECT {
|
|
Errorf(nil, "too many shdrs")
|
|
} else {
|
|
shdr[ehdr.Shnum] = e
|
|
ehdr.Shnum++
|
|
}
|
|
|
|
return e
|
|
}
|
|
|
|
func getElfEhdr() *ElfEhdr {
|
|
return &ehdr
|
|
}
|
|
|
|
func elf64writehdr(out *OutBuf) uint32 {
|
|
out.Write(ehdr.Ident[:])
|
|
out.Write16(uint16(ehdr.Type))
|
|
out.Write16(uint16(ehdr.Machine))
|
|
out.Write32(uint32(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(uint16(ehdr.Type))
|
|
out.Write16(uint16(ehdr.Machine))
|
|
out.Write32(uint32(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)
|
|
}
|
|
|
|
/* Taken directly from the definition document for ELF64. */
|
|
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
|
|
}
|
|
|
|
// 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(val string) {
|
|
if !strings.HasPrefix(val, "0x") {
|
|
Exitf("-B argument must start with 0x: %s", val)
|
|
}
|
|
|
|
ov := val
|
|
val = val[2:]
|
|
|
|
const maxLen = 32
|
|
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)
|
|
if ctxt.BuildMode == BuildModePIE {
|
|
// https://github.com/bminor/glibc/blob/895ef79e04a953cac1493863bcae29ad85657ee1/elf/elf.h#L986
|
|
const DTFLAGS_1_PIE = 0x08000000
|
|
Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS_1, uint64(DTFLAGS_1_PIE))
|
|
}
|
|
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.Vaddr = seg.Vaddr
|
|
ph.Paddr = seg.Vaddr
|
|
ph.Memsz = seg.Length
|
|
ph.Off = seg.Fileoff
|
|
ph.Filesz = seg.Filelen
|
|
ph.Align = uint64(*FlagRound)
|
|
}
|
|
|
|
func elfshname(name string) *ElfShdr {
|
|
for i := 0; i < nelfstr; i++ {
|
|
if name != elfstr[i].s {
|
|
continue
|
|
}
|
|
off := elfstr[i].off
|
|
for i = 0; i < int(ehdr.Shnum); i++ {
|
|
sh := shdr[i]
|
|
if sh.Name == uint32(off) {
|
|
return sh
|
|
}
|
|
}
|
|
return newElfShdr(int64(off))
|
|
}
|
|
Exitf("cannot find elf name %s", name)
|
|
return nil
|
|
}
|
|
|
|
// Create an ElfShdr for the section with name.
|
|
// Create a duplicate if one already exists with that name.
|
|
func elfshnamedup(name string) *ElfShdr {
|
|
for i := 0; i < nelfstr; i++ {
|
|
if name == elfstr[i].s {
|
|
off := elfstr[i].off
|
|
return newElfShdr(int64(off))
|
|
}
|
|
}
|
|
|
|
Errorf(nil, "cannot find elf name %s", name)
|
|
errorexit()
|
|
return nil
|
|
}
|
|
|
|
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(nil, "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.
|
|
if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
|
|
return nil
|
|
}
|
|
if sect.Name == ".shstrtab" || sect.Name == ".tbss" {
|
|
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 != 0 && sh.Info != uint32(sect.Elfsect.(*ElfShdr).shnum) {
|
|
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 = uint32(elfshname(".symtab").shnum)
|
|
sh.Info = uint32(sect.Elfsect.(*ElfShdr).shnum)
|
|
sh.Off = sect.Reloff
|
|
sh.Size = sect.Rellen
|
|
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 Elfreloc1
|
|
// 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.Elfreloc1(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.ElfrelocSize)
|
|
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() != loader.Sym(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
|
|
|
|
/* predefine strings we need for section headers */
|
|
shstrtab := ldr.CreateSymForUpdate(".shstrtab", 0)
|
|
|
|
shstrtab.SetType(sym.SELFROSECT)
|
|
|
|
shstrtab.Addstring("")
|
|
shstrtab.Addstring(".text")
|
|
shstrtab.Addstring(".noptrdata")
|
|
shstrtab.Addstring(".data")
|
|
shstrtab.Addstring(".bss")
|
|
shstrtab.Addstring(".noptrbss")
|
|
shstrtab.Addstring(".go.fuzzcntrs")
|
|
shstrtab.Addstring(".go.buildinfo")
|
|
if ctxt.IsMIPS() {
|
|
shstrtab.Addstring(".MIPS.abiflags")
|
|
shstrtab.Addstring(".gnu.attributes")
|
|
}
|
|
|
|
// generate .tbss section for dynamic internal linker or external
|
|
// linking, so that various binutils could correctly calculate
|
|
// PT_TLS size. See https://golang.org/issue/5200.
|
|
if !*FlagD || ctxt.IsExternal() {
|
|
shstrtab.Addstring(".tbss")
|
|
}
|
|
if ctxt.IsNetbsd() {
|
|
shstrtab.Addstring(".note.netbsd.ident")
|
|
if *flagRace {
|
|
shstrtab.Addstring(".note.netbsd.pax")
|
|
}
|
|
}
|
|
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")
|
|
}
|
|
if *flagBuildid != "" {
|
|
shstrtab.Addstring(".note.go.buildid")
|
|
}
|
|
shstrtab.Addstring(".elfdata")
|
|
shstrtab.Addstring(".rodata")
|
|
// See the comment about data.rel.ro.FOO section names in data.go.
|
|
relro_prefix := ""
|
|
if ctxt.UseRelro() {
|
|
shstrtab.Addstring(".data.rel.ro")
|
|
relro_prefix = ".data.rel.ro"
|
|
}
|
|
shstrtab.Addstring(relro_prefix + ".typelink")
|
|
shstrtab.Addstring(relro_prefix + ".itablink")
|
|
shstrtab.Addstring(relro_prefix + ".gosymtab")
|
|
shstrtab.Addstring(relro_prefix + ".gopclntab")
|
|
|
|
if ctxt.IsExternal() {
|
|
*FlagD = true
|
|
|
|
shstrtab.Addstring(elfRelType + ".text")
|
|
shstrtab.Addstring(elfRelType + ".rodata")
|
|
shstrtab.Addstring(elfRelType + relro_prefix + ".typelink")
|
|
shstrtab.Addstring(elfRelType + relro_prefix + ".itablink")
|
|
shstrtab.Addstring(elfRelType + relro_prefix + ".gosymtab")
|
|
shstrtab.Addstring(elfRelType + relro_prefix + ".gopclntab")
|
|
shstrtab.Addstring(elfRelType + ".noptrdata")
|
|
shstrtab.Addstring(elfRelType + ".data")
|
|
if ctxt.UseRelro() {
|
|
shstrtab.Addstring(elfRelType + ".data.rel.ro")
|
|
}
|
|
shstrtab.Addstring(elfRelType + ".go.buildinfo")
|
|
if ctxt.IsMIPS() {
|
|
shstrtab.Addstring(elfRelType + ".MIPS.abiflags")
|
|
shstrtab.Addstring(elfRelType + ".gnu.attributes")
|
|
}
|
|
|
|
// add a .note.GNU-stack section to mark the stack as non-executable
|
|
shstrtab.Addstring(".note.GNU-stack")
|
|
|
|
if ctxt.IsShared() {
|
|
shstrtab.Addstring(".note.go.abihash")
|
|
shstrtab.Addstring(".note.go.pkg-list")
|
|
shstrtab.Addstring(".note.go.deps")
|
|
}
|
|
}
|
|
|
|
hasinitarr := ctxt.linkShared
|
|
|
|
/* shared library initializer */
|
|
switch ctxt.BuildMode {
|
|
case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePlugin:
|
|
hasinitarr = true
|
|
}
|
|
|
|
if hasinitarr {
|
|
shstrtab.Addstring(".init_array")
|
|
shstrtab.Addstring(elfRelType + ".init_array")
|
|
}
|
|
|
|
if !*FlagS {
|
|
shstrtab.Addstring(".symtab")
|
|
shstrtab.Addstring(".strtab")
|
|
dwarfaddshstrings(ctxt, shstrtab)
|
|
}
|
|
|
|
shstrtab.Addstring(".shstrtab")
|
|
|
|
if !*FlagD { /* -d suppresses dynamic loader format */
|
|
shstrtab.Addstring(".interp")
|
|
shstrtab.Addstring(".hash")
|
|
shstrtab.Addstring(".got")
|
|
if ctxt.IsPPC64() {
|
|
shstrtab.Addstring(".glink")
|
|
}
|
|
shstrtab.Addstring(".got.plt")
|
|
shstrtab.Addstring(".dynamic")
|
|
shstrtab.Addstring(".dynsym")
|
|
shstrtab.Addstring(".dynstr")
|
|
shstrtab.Addstring(elfRelType)
|
|
shstrtab.Addstring(elfRelType + ".plt")
|
|
|
|
shstrtab.Addstring(".plt")
|
|
shstrtab.Addstring(".gnu.version")
|
|
shstrtab.Addstring(".gnu.version_r")
|
|
|
|
/* 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)
|
|
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)
|
|
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)
|
|
dynamic.SetType(sym.SELFSECT) // writable
|
|
|
|
if ctxt.IsS390X() {
|
|
// S390X uses .got instead of .got.plt
|
|
gotplt = got
|
|
}
|
|
thearch.Elfsetupplt(ctxt, 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(notsha256.Size)
|
|
|
|
sort.Sort(byPkg(ctxt.Library))
|
|
h := notsha256.New()
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Do not write DT_NULL. elfdynhash will finish it.
|
|
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
|
|
if !*FlagS {
|
|
symo = int64(Segdwarf.Fileoff + Segdwarf.Filelen)
|
|
symo = Rnd(symo, int64(ctxt.Arch.PtrSize))
|
|
ctxt.Out.SeekSet(symo)
|
|
asmElfSym(ctxt)
|
|
ctxt.Out.Write(Elfstrdat)
|
|
if ctxt.IsExternal() {
|
|
elfEmitReloc(ctxt)
|
|
}
|
|
}
|
|
ctxt.Out.SeekSet(0)
|
|
|
|
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.Androiddynld
|
|
if interpreter == "" {
|
|
Exitf("ELF interpreter not set")
|
|
}
|
|
} else {
|
|
interpreter = thearch.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.LinuxdynldMusl; musl != "" {
|
|
if _, err := os.Stat(musl); err == nil {
|
|
interpreter = musl
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
case objabi.Hfreebsd:
|
|
interpreter = thearch.Freebsddynld
|
|
|
|
case objabi.Hnetbsd:
|
|
interpreter = thearch.Netbsddynld
|
|
|
|
case objabi.Hopenbsd:
|
|
interpreter = thearch.Openbsddynld
|
|
|
|
case objabi.Hdragonfly:
|
|
interpreter = thearch.Dragonflydynld
|
|
|
|
case objabi.Hsolaris:
|
|
interpreter = thearch.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 = uint32(elfshname(".dynstr").shnum)
|
|
|
|
// 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 = uint32(elfshname(".dynsym").shnum)
|
|
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 = uint32(elfshname(".dynstr").shnum)
|
|
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 = uint32(elfshname(".dynsym").shnum)
|
|
sh.Info = uint32(elfshname(".plt").shnum)
|
|
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 = uint32(elfshname(".dynsym").shnum)
|
|
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 = uint32(elfshname(".dynsym").shnum)
|
|
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 = uint32(elfshname(".dynsym").shnum)
|
|
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 = uint32(elfshname(".dynsym").shnum)
|
|
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 = uint32(elfshname(".dynstr").shnum)
|
|
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 {
|
|
ph := newElfPhdr()
|
|
ph.Type = elf.PT_GNU_STACK
|
|
ph.Flags = elf.PF_W + elf.PF_R
|
|
ph.Align = uint64(ctxt.Arch.RegSize)
|
|
|
|
ph = newElfPhdr()
|
|
ph.Type = elf.PT_PAX_FLAGS
|
|
ph.Flags = 0x2a00 // mprotect, randexec, emutramp disabled
|
|
ph.Align = uint64(ctxt.Arch.RegSize)
|
|
} else if ctxt.HeadType == objabi.Hsolaris {
|
|
ph := newElfPhdr()
|
|
ph.Type = elf.PT_SUNWSTACK
|
|
ph.Flags = elf.PF_W + elf.PF_R
|
|
} else if 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)
|
|
}
|
|
|
|
elfobj:
|
|
sh := elfshname(".shstrtab")
|
|
sh.Type = uint32(elf.SHT_STRTAB)
|
|
sh.Addralign = 1
|
|
shsym(sh, ldr, ldr.Lookup(".shstrtab", 0))
|
|
eh.Shstrndx = uint16(sh.shnum)
|
|
|
|
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")
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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 = uint32(elfshname(".strtab").shnum)
|
|
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
|
|
}
|
|
|
|
/* 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
|
|
}
|
|
|
|
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(nil, "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(nil, "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 == sym.STEXT {
|
|
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 == sym.STEXT {
|
|
t = elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC)
|
|
} else if target.Arch.Family == sys.ARM && cgoeDynamic && st == sym.STEXT {
|
|
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)
|
|
}
|
|
}
|
|
}
|