// Copyright 2018 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 (
"bytes"
"cmd/internal/objabi"
"cmd/link/internal/sym"
"encoding/binary"
"io/ioutil"
"math/bits"
"path/filepath"
"sort"
"strings"
)
// This file handles all algorithms related to XCOFF files generation.
// Most of them are adaptations of the ones in cmd/link/internal/pe.go
// as PE and XCOFF are based on COFF files.
// XCOFF files generated are 64 bits.
const (
// Total amount of space to reserve at the start of the file
// for File Header, Auxiliary Header, and Section Headers.
// May waste some.
XCOFFHDRRESERVE = FILHSZ_64 + AOUTHSZ_EXEC64 + SCNHSZ_64*23
XCOFFSECTALIGN int64 = 32 // base on dump -o
// XCOFF binaries should normally have all its sections position-independent.
// However, this is not yet possible for .text because of some R_ADDR relocations
// inside RODATA symbols.
// .data and .bss are position-independent so their address start inside a unreachable
// segment during execution to force segfault if something is wrong.
XCOFFTEXTBASE = 0x100000000 // Start of text address
XCOFFDATABASE = 0x200000000 // Start of data address
)
// File Header
type XcoffFileHdr64 struct {
Fmagic uint16 // Target machine
Fnscns uint16 // Number of sections
Ftimedat int32 // Time and date of file creation
Fsymptr uint64 // Byte offset to symbol table start
Fopthdr uint16 // Number of bytes in optional header
Fflags uint16 // Flags
Fnsyms int32 // Number of entries in symbol table
}
const (
U64_TOCMAGIC = 0767 // AIX 64-bit XCOFF
)
// Flags that describe the type of the object file.
const (
F_RELFLG = 0x0001
F_EXEC = 0x0002
F_LNNO = 0x0004
F_FDPR_PROF = 0x0010
F_FDPR_OPTI = 0x0020
F_DSA = 0x0040
F_VARPG = 0x0100
F_DYNLOAD = 0x1000
F_SHROBJ = 0x2000
F_LOADONLY = 0x4000
)
// Auxiliary Header
type XcoffAoutHdr64 struct {
Omagic int16 // Flags - Ignored If Vstamp Is 1
Ovstamp int16 // Version
Odebugger uint32 // Reserved For Debugger
Otextstart uint64 // Virtual Address Of Text
Odatastart uint64 // Virtual Address Of Data
Otoc uint64 // Toc Address
Osnentry int16 // Section Number For Entry Point
Osntext int16 // Section Number For Text
Osndata int16 // Section Number For Data
Osntoc int16 // Section Number For Toc
Osnloader int16 // Section Number For Loader
Osnbss int16 // Section Number For Bss
Oalgntext int16 // Max Text Alignment
Oalgndata int16 // Max Data Alignment
Omodtype [2]byte // Module Type Field
Ocpuflag uint8 // Bit Flags - Cputypes Of Objects
Ocputype uint8 // Reserved for CPU type
Otextpsize uint8 // Requested text page size
Odatapsize uint8 // Requested data page size
Ostackpsize uint8 // Requested stack page size
Oflags uint8 // Flags And TLS Alignment
Otsize uint64 // Text Size In Bytes
Odsize uint64 // Data Size In Bytes
Obsize uint64 // Bss Size In Bytes
Oentry uint64 // Entry Point Address
Omaxstack uint64 // Max Stack Size Allowed
Omaxdata uint64 // Max Data Size Allowed
Osntdata int16 // Section Number For Tdata Section
Osntbss int16 // Section Number For Tbss Section
Ox64flags uint16 // Additional Flags For 64-Bit Objects
Oresv3a int16 // Reserved
Oresv3 [2]int32 // Reserved
}
// Section Header
type XcoffScnHdr64 struct {
Sname [8]byte // Section Name
Spaddr uint64 // Physical Address
Svaddr uint64 // Virtual Address
Ssize uint64 // Section Size
Sscnptr uint64 // File Offset To Raw Data
Srelptr uint64 // File Offset To Relocation
Slnnoptr uint64 // File Offset To Line Numbers
Snreloc uint32 // Number Of Relocation Entries
Snlnno uint32 // Number Of Line Number Entries
Sflags uint32 // flags
}
// Flags defining the section type.
const (
STYP_DWARF = 0x0010
STYP_TEXT = 0x0020
STYP_DATA = 0x0040
STYP_BSS = 0x0080
STYP_EXCEPT = 0x0100
STYP_INFO = 0x0200
STYP_TDATA = 0x0400
STYP_TBSS = 0x0800
STYP_LOADER = 0x1000
STYP_DEBUG = 0x2000
STYP_TYPCHK = 0x4000
STYP_OVRFLO = 0x8000
)
const (
SSUBTYP_DWINFO = 0x10000 // DWARF info section
SSUBTYP_DWLINE = 0x20000 // DWARF line-number section
SSUBTYP_DWPBNMS = 0x30000 // DWARF public names section
SSUBTYP_DWPBTYP = 0x40000 // DWARF public types section
SSUBTYP_DWARNGE = 0x50000 // DWARF aranges section
SSUBTYP_DWABREV = 0x60000 // DWARF abbreviation section
SSUBTYP_DWSTR = 0x70000 // DWARF strings section
SSUBTYP_DWRNGES = 0x80000 // DWARF ranges section
SSUBTYP_DWLOC = 0x90000 // DWARF location lists section
SSUBTYP_DWFRAME = 0xA0000 // DWARF frames section
SSUBTYP_DWMAC = 0xB0000 // DWARF macros section
)
// Headers size
const (
FILHSZ_32 = 20
FILHSZ_64 = 24
AOUTHSZ_EXEC32 = 72
AOUTHSZ_EXEC64 = 120
SCNHSZ_32 = 40
SCNHSZ_64 = 72
LDHDRSZ_32 = 32
LDHDRSZ_64 = 56
LDSYMSZ_64 = 24
RELSZ_64 = 14
)
// Type representing all XCOFF symbols.
type xcoffSym interface {
}
// Symbol Table Entry
type XcoffSymEnt64 struct {
Nvalue uint64 // Symbol value
Noffset uint32 // Offset of the name in string table or .debug section
Nscnum int16 // Section number of symbol
Ntype uint16 // Basic and derived type specification
Nsclass uint8 // Storage class of symbol
Nnumaux int8 // Number of auxiliary entries
}
const SYMESZ = 18
const (
// Nscnum
N_DEBUG = -2
N_ABS = -1
N_UNDEF = 0
//Ntype
SYM_V_INTERNAL = 0x1000
SYM_V_HIDDEN = 0x2000
SYM_V_PROTECTED = 0x3000
SYM_V_EXPORTED = 0x4000
SYM_TYPE_FUNC = 0x0020 // is function
)
// Storage Class.
const (
C_NULL = 0 // Symbol table entry marked for deletion
C_EXT = 2 // External symbol
C_STAT = 3 // Static symbol
C_BLOCK = 100 // Beginning or end of inner block
C_FCN = 101 // Beginning or end of function
C_FILE = 103 // Source file name and compiler information
C_HIDEXT = 107 // Unnamed external symbol
C_BINCL = 108 // Beginning of include file
C_EINCL = 109 // End of include file
C_WEAKEXT = 111 // Weak external symbol
C_DWARF = 112 // DWARF symbol
C_GSYM = 128 // Global variable
C_LSYM = 129 // Automatic variable allocated on stack
C_PSYM = 130 // Argument to subroutine allocated on stack
C_RSYM = 131 // Register variable
C_RPSYM = 132 // Argument to function or procedure stored in register
C_STSYM = 133 // Statically allocated symbol
C_BCOMM = 135 // Beginning of common block
C_ECOML = 136 // Local member of common block
C_ECOMM = 137 // End of common block
C_DECL = 140 // Declaration of object
C_ENTRY = 141 // Alternate entry
C_FUN = 142 // Function or procedure
C_BSTAT = 143 // Beginning of static block
C_ESTAT = 144 // End of static block
C_GTLS = 145 // Global thread-local variable
C_STTLS = 146 // Static thread-local variable
)
// File Auxiliary Entry
type XcoffAuxFile64 struct {
Xzeroes uint32 // The name is always in the string table
Xoffset uint32 // Offset in the string table
X_pad1 [6]byte
Xftype uint8 // Source file string type
X_pad2 [2]byte
Xauxtype uint8 // Type of auxiliary entry
}
// Function Auxiliary Entry
type XcoffAuxFcn64 struct {
Xlnnoptr uint64 // File pointer to line number
Xfsize uint32 // Size of function in bytes
Xendndx uint32 // Symbol table index of next entry
Xpad uint8 // Unused
Xauxtype uint8 // Type of auxiliary entry
}
// csect Auxiliary Entry.
type XcoffAuxCSect64 struct {
Xscnlenlo uint32 // Lower 4 bytes of length or symbol table index
Xparmhash uint32 // Offset of parameter type-check string
Xsnhash uint16 // .typchk section number
Xsmtyp uint8 // Symbol alignment and type
Xsmclas uint8 // Storage-mapping class
Xscnlenhi uint32 // Upper 4 bytes of length or symbol table index
Xpad uint8 // Unused
Xauxtype uint8 // Type of auxiliary entry
}
// DWARF Auxiliary Entry
type XcoffAuxDWARF64 struct {
Xscnlen uint64 // Length of this symbol section
X_pad [9]byte
Xauxtype uint8 // Type of auxiliary entry
}
// Auxiliary type
const (
_AUX_EXCEPT = 255
_AUX_FCN = 254
_AUX_SYM = 253
_AUX_FILE = 252
_AUX_CSECT = 251
_AUX_SECT = 250
)
// Xftype field
const (
XFT_FN = 0 // Source File Name
XFT_CT = 1 // Compile Time Stamp
XFT_CV = 2 // Compiler Version Number
XFT_CD = 128 // Compiler Defined Information/
)
// Symbol type field.
const (
XTY_ER = 0 // External reference
XTY_SD = 1 // Section definition
XTY_LD = 2 // Label definition
XTY_CM = 3 // Common csect definition
XTY_WK = 0x8 // Weak symbol
XTY_EXP = 0x10 // Exported symbol
XTY_ENT = 0x20 // Entry point symbol
XTY_IMP = 0x40 // Imported symbol
)
// Storage-mapping class.
const (
XMC_PR = 0 // Program code
XMC_RO = 1 // Read-only constant
XMC_DB = 2 // Debug dictionary table
XMC_TC = 3 // TOC entry
XMC_UA = 4 // Unclassified
XMC_RW = 5 // Read/Write data
XMC_GL = 6 // Global linkage
XMC_XO = 7 // Extended operation
XMC_SV = 8 // 32-bit supervisor call descriptor
XMC_BS = 9 // BSS class
XMC_DS = 10 // Function descriptor
XMC_UC = 11 // Unnamed FORTRAN common
XMC_TC0 = 15 // TOC anchor
XMC_TD = 16 // Scalar data entry in the TOC
XMC_SV64 = 17 // 64-bit supervisor call descriptor
XMC_SV3264 = 18 // Supervisor call descriptor for both 32-bit and 64-bit
XMC_TL = 20 // Read/Write thread-local data
XMC_UL = 21 // Read/Write thread-local data (.tbss)
XMC_TE = 22 // TOC entry
)
// Loader Header
type XcoffLdHdr64 struct {
Lversion int32 // Loader section version number
Lnsyms int32 // Number of symbol table entries
Lnreloc int32 // Number of relocation table entries
Listlen uint32 // Length of import file ID string table
Lnimpid int32 // Number of import file IDs
Lstlen uint32 // Length of string table
Limpoff uint64 // Offset to start of import file IDs
Lstoff uint64 // Offset to start of string table
Lsymoff uint64 // Offset to start of symbol table
Lrldoff uint64 // Offset to start of relocation entries
}
// Loader Symbol
type XcoffLdSym64 struct {
Lvalue uint64 // Address field
Loffset uint32 // Byte offset into string table of symbol name
Lscnum int16 // Section number containing symbol
Lsmtype int8 // Symbol type, export, import flags
Lsmclas int8 // Symbol storage class
Lifile int32 // Import file ID; ordinal of import file IDs
Lparm uint32 // Parameter type-check field
}
type xcoffLoaderSymbol struct {
sym *sym.Symbol
smtype int8
smclas int8
}
type XcoffLdImportFile64 struct {
Limpidpath string
Limpidbase string
Limpidmem string
}
type XcoffLdRel64 struct {
Lvaddr uint64 // Address Field
Lrtype uint16 // Relocation Size and Type
Lrsecnm int16 // Section Number being relocated
Lsymndx int32 // Loader-Section symbol table index
}
// xcoffLoaderReloc holds information about a relocation made by the loader.
type xcoffLoaderReloc struct {
sym *sym.Symbol
rel *sym.Reloc
rtype uint16
symndx int32
}
const (
XCOFF_R_POS = 0x00 // A(sym) Positive Relocation
XCOFF_R_NEG = 0x01 // -A(sym) Negative Relocation
XCOFF_R_REL = 0x02 // A(sym-*) Relative to self
XCOFF_R_TOC = 0x03 // A(sym-TOC) Relative to TOC
XCOFF_R_TRL = 0x12 // A(sym-TOC) TOC Relative indirect load.
XCOFF_R_TRLA = 0x13 // A(sym-TOC) TOC Rel load address. modifiable inst
XCOFF_R_GL = 0x05 // A(external TOC of sym) Global Linkage
XCOFF_R_TCL = 0x06 // A(local TOC of sym) Local object TOC address
XCOFF_R_RL = 0x0C // A(sym) Pos indirect load. modifiable instruction
XCOFF_R_RLA = 0x0D // A(sym) Pos Load Address. modifiable instruction
XCOFF_R_REF = 0x0F // AL0(sym) Non relocating ref. No garbage collect
XCOFF_R_BA = 0x08 // A(sym) Branch absolute. Cannot modify instruction
XCOFF_R_RBA = 0x18 // A(sym) Branch absolute. modifiable instruction
XCOFF_R_BR = 0x0A // A(sym-*) Branch rel to self. non modifiable
XCOFF_R_RBR = 0x1A // A(sym-*) Branch rel to self. modifiable instr
XCOFF_R_TLS = 0x20 // General-dynamic reference to TLS symbol
XCOFF_R_TLS_IE = 0x21 // Initial-exec reference to TLS symbol
XCOFF_R_TLS_LD = 0x22 // Local-dynamic reference to TLS symbol
XCOFF_R_TLS_LE = 0x23 // Local-exec reference to TLS symbol
XCOFF_R_TLSM = 0x24 // Module reference to TLS symbol
XCOFF_R_TLSML = 0x25 // Module reference to local (own) module
XCOFF_R_TOCU = 0x30 // Relative to TOC - high order bits
XCOFF_R_TOCL = 0x31 // Relative to TOC - low order bits
)
type XcoffLdStr64 struct {
size uint16
name string
}
// xcoffFile is used to build XCOFF file.
type xcoffFile struct {
xfhdr XcoffFileHdr64
xahdr XcoffAoutHdr64
sections []*XcoffScnHdr64
sectText *XcoffScnHdr64
sectData *XcoffScnHdr64
sectBss *XcoffScnHdr64
stringTable xcoffStringTable
sectNameToScnum map[string]int16
loaderSize uint64
symtabOffset int64 // offset to the start of symbol table
symbolCount uint32 // number of symbol table records written
symtabSym []xcoffSym // XCOFF symbols for the symbol table
dynLibraries map[string]int // Dynamic libraries in .loader section. The integer represents its import file number (- 1)
loaderSymbols []*xcoffLoaderSymbol // symbols inside .loader symbol table
loaderReloc []*xcoffLoaderReloc // Reloc that must be made inside loader
}
// Var used by XCOFF Generation algorithms
var (
xfile xcoffFile
)
// xcoffStringTable is a XCOFF string table.
type xcoffStringTable struct {
strings []string
stringsLen int
}
// size returns size of string table t.
func (t *xcoffStringTable) size() int {
// string table starts with 4-byte length at the beginning
return t.stringsLen + 4
}
// add adds string str to string table t.
func (t *xcoffStringTable) add(str string) int {
off := t.size()
t.strings = append(t.strings, str)
t.stringsLen += len(str) + 1 // each string will have 0 appended to it
return off
}
// write writes string table t into the output file.
func (t *xcoffStringTable) write(out *OutBuf) {
out.Write32(uint32(t.size()))
for _, s := range t.strings {
out.WriteString(s)
out.Write8(0)
}
}
// write writes XCOFF section sect into the output file.
func (sect *XcoffScnHdr64) write(ctxt *Link) {
binary.Write(ctxt.Out, binary.BigEndian, sect)
ctxt.Out.Write32(0) // Add 4 empty bytes at the end to match alignment
}
// addSection adds section to the XCOFF file f.
func (f *xcoffFile) addSection(name string, addr uint64, size uint64, fileoff uint64, flags uint32) *XcoffScnHdr64 {
sect := &XcoffScnHdr64{
Spaddr: addr,
Svaddr: addr,
Ssize: size,
Sscnptr: fileoff,
Sflags: flags,
}
copy(sect.Sname[:], name) // copy string to [8]byte
f.sections = append(f.sections, sect)
f.sectNameToScnum[name] = int16(len(f.sections))
return sect
}
// addDwarfSection adds a dwarf section to the XCOFF file f.
// This function is similar to addSection, but Dwarf section names
// must be modified to conventional names and they are various subtypes.
func (f *xcoffFile) addDwarfSection(s *sym.Section) *XcoffScnHdr64 {
newName, subtype := xcoffGetDwarfSubtype(s.Name)
return f.addSection(newName, 0, s.Length, s.Seg.Fileoff+s.Vaddr-s.Seg.Vaddr, STYP_DWARF|subtype)
}
// xcoffGetDwarfSubtype returns the XCOFF name of the DWARF section str
// and its subtype constant.
func xcoffGetDwarfSubtype(str string) (string, uint32) {
switch str {
default:
Exitf("unknown DWARF section name for XCOFF: %s", str)
case ".debug_abbrev":
return ".dwabrev", SSUBTYP_DWABREV
case ".debug_info":
return ".dwinfo", SSUBTYP_DWINFO
case ".debug_frame":
return ".dwframe", SSUBTYP_DWFRAME
case ".debug_line":
return ".dwline", SSUBTYP_DWLINE
case ".debug_loc":
return ".dwloc", SSUBTYP_DWLOC
case ".debug_pubnames":
return ".dwpbnms", SSUBTYP_DWPBNMS
case ".debug_pubtypes":
return ".dwpbtyp", SSUBTYP_DWPBTYP
case ".debug_ranges":
return ".dwrnges", SSUBTYP_DWRNGES
}
// never used
return "", 0
}
// getXCOFFscnum returns the XCOFF section number of a Go section.
func (f *xcoffFile) getXCOFFscnum(sect *sym.Section) int16 {
switch sect.Seg {
case &Segtext:
return f.sectNameToScnum[".text"]
case &Segdata:
if sect.Name == ".noptrbss" || sect.Name == ".bss" {
return f.sectNameToScnum[".bss"]
}
if sect.Name == ".tbss" {
return f.sectNameToScnum[".tbss"]
}
return f.sectNameToScnum[".data"]
case &Segdwarf:
name, _ := xcoffGetDwarfSubtype(sect.Name)
return f.sectNameToScnum[name]
case &Segrelrodata:
return f.sectNameToScnum[".data"]
}
Errorf(nil, "getXCOFFscnum not implemented for section %s", sect.Name)
return -1
}
// Xcoffinit initialised some internal value and setups
// already known header information
func Xcoffinit(ctxt *Link) {
xfile.dynLibraries = make(map[string]int)
HEADR = int32(Rnd(XCOFFHDRRESERVE, XCOFFSECTALIGN))
if *FlagTextAddr != -1 {
Errorf(nil, "-T not available on AIX")
}
*FlagTextAddr = XCOFFTEXTBASE + int64(HEADR)
if *FlagRound != -1 {
Errorf(nil, "-R not available on AIX")
}
*FlagRound = int(XCOFFSECTALIGN)
}
// SYMBOL TABLE
// type records C_FILE information needed for genasmsym in XCOFF.
type xcoffSymSrcFile struct {
name string
file *XcoffSymEnt64 // Symbol of this C_FILE
csectAux *XcoffAuxCSect64 // Symbol for the current .csect
csectSymNb uint64 // Symbol number for the current .csect
csectSize int64
}
var (
currDwscnoff = make(map[string]uint64) // Needed to create C_DWARF symbols
currSymSrcFile xcoffSymSrcFile
outerSymSize = make(map[string]int64)
)
// xcoffUpdateOuterSize stores the size of outer symbols in order to have it
// in the symbol table.
func xcoffUpdateOuterSize(ctxt *Link, size int64, stype sym.SymKind) {
if size == 0 {
return
}
switch stype {
default:
Errorf(nil, "unknown XCOFF outer symbol for type %s", stype.String())
case sym.SRODATA, sym.SRODATARELRO, sym.SFUNCTAB, sym.SSTRING:
// Nothing to do
case sym.STYPERELRO:
if ctxt.UseRelro() && (ctxt.BuildMode == BuildModeCArchive || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE) {
outerSymSize["typerel.*"] = size
return
}
fallthrough
case sym.STYPE:
if !ctxt.DynlinkingGo() {
// runtime.types size must be removed.
outerSymSize["type.*"] = size - ctxt.Syms.ROLookup("runtime.types", 0).Size
}
case sym.SGOSTRING:
outerSymSize["go.string.*"] = size
case sym.SGOFUNC:
if !ctxt.DynlinkingGo() {
outerSymSize["go.func.*"] = size
}
case sym.SGOFUNCRELRO:
outerSymSize["go.funcrel.*"] = size
case sym.SGCBITS:
outerSymSize["runtime.gcbits.*"] = size
case sym.SITABLINK:
outerSymSize["runtime.itablink"] = size
}
}
// addSymbol writes a symbol or an auxiliary symbol entry on ctxt.out.
func (f *xcoffFile) addSymbol(sym xcoffSym) {
f.symtabSym = append(f.symtabSym, sym)
f.symbolCount++
}
// xcoffAlign returns the log base 2 of the symbol's alignment.
func xcoffAlign(x *sym.Symbol, t SymbolType) uint8 {
align := x.Align
if align == 0 {
if t == TextSym {
align = int32(Funcalign)
} else {
align = symalign(x)
}
}
return logBase2(int(align))
}
// logBase2 returns the log in base 2 of a.
func logBase2(a int) uint8 {
return uint8(bits.Len(uint(a)) - 1)
}
// Write symbols needed when a new file appared :
// - a C_FILE with one auxiliary entry for its name
// - C_DWARF symbols to provide debug information
// - a C_HIDEXT which will be a csect containing all of its functions
// It needs several parameters to create .csect symbols such as its entry point and its section number.
//
// Currently, a new file is in fact a new package. It seems to be OK, but it might change
// in the future.
func (f *xcoffFile) writeSymbolNewFile(ctxt *Link, name string, firstEntry uint64, extnum int16) {
/* C_FILE */
s := &XcoffSymEnt64{
Noffset: uint32(f.stringTable.add(".file")),
Nsclass: C_FILE,
Nscnum: N_DEBUG,
Ntype: 0, // Go isn't inside predefined language.
Nnumaux: 1,
}
f.addSymbol(s)
currSymSrcFile.file = s
// Auxiliary entry for file name.
auxf := &XcoffAuxFile64{
Xoffset: uint32(f.stringTable.add(name)),
Xftype: XFT_FN,
Xauxtype: _AUX_FILE,
}
f.addSymbol(auxf)
/* Dwarf */
for _, sect := range Segdwarf.Sections {
var dwsize uint64
if ctxt.LinkMode == LinkInternal {
// Find the size of this corresponding package DWARF compilation unit.
// This size is set during DWARF generation (see dwarf.go).
dwsize = getDwsectCUSize(sect.Name, name)
// .debug_abbrev is commun to all packages and not found with the previous function
if sect.Name == ".debug_abbrev" {
s := ctxt.Syms.ROLookup(sect.Name, 0)
dwsize = uint64(s.Size)
}
} else {
// There is only one .FILE with external linking.
dwsize = sect.Length
}
// get XCOFF name
name, _ := xcoffGetDwarfSubtype(sect.Name)
s := &XcoffSymEnt64{
Nvalue: currDwscnoff[sect.Name],
Noffset: uint32(f.stringTable.add(name)),
Nsclass: C_DWARF,
Nscnum: f.getXCOFFscnum(sect),
Nnumaux: 1,
}
if currSymSrcFile.csectAux == nil {
// Dwarf relocations need the symbol number of .dw* symbols.
// It doesn't need to know it for each package, one is enough.
// currSymSrcFile.csectAux == nil means first package.
dws := ctxt.Syms.Lookup(sect.Name, 0)
dws.Dynid = int32(f.symbolCount)
if sect.Name == ".debug_frame" && ctxt.LinkMode != LinkExternal {
// CIE size must be added to the first package.
dwsize += 48
}
}
f.addSymbol(s)
// update the DWARF section offset in this file
if sect.Name != ".debug_abbrev" {
currDwscnoff[sect.Name] += dwsize
}
// Auxiliary dwarf section
auxd := &XcoffAuxDWARF64{
Xscnlen: dwsize,
Xauxtype: _AUX_SECT,
}
f.addSymbol(auxd)
}
/* .csect */
// Check if extnum is in text.
// This is temporary and only here to check if this algorithm is correct.
if extnum != 1 {
Exitf("XCOFF symtab: A new file was detected with its first symbol not in .text")
}
currSymSrcFile.csectSymNb = uint64(f.symbolCount)
// No offset because no name
s = &XcoffSymEnt64{
Nvalue: firstEntry,
Nscnum: extnum,
Nsclass: C_HIDEXT,
Ntype: 0, // check visibility ?
Nnumaux: 1,
}
f.addSymbol(s)
aux := &XcoffAuxCSect64{
Xsmclas: XMC_PR,
Xsmtyp: XTY_SD | logBase2(Funcalign)<<3,
Xauxtype: _AUX_CSECT,
}
f.addSymbol(aux)
currSymSrcFile.csectAux = aux
currSymSrcFile.csectSize = 0
}
// Update values for the previous package.
// - Svalue of the C_FILE symbol: if it is the last one, this Svalue must be -1
// - Xsclen of the csect symbol.
func (f *xcoffFile) updatePreviousFile(ctxt *Link, last bool) {
// first file
if currSymSrcFile.file == nil {
return
}
// Update C_FILE
cfile := currSymSrcFile.file
if last {
cfile.Nvalue = 0xFFFFFFFFFFFFFFFF
} else {
cfile.Nvalue = uint64(f.symbolCount)
}
// update csect scnlen in this auxiliary entry
aux := currSymSrcFile.csectAux
aux.Xscnlenlo = uint32(currSymSrcFile.csectSize & 0xFFFFFFFF)
aux.Xscnlenhi = uint32(currSymSrcFile.csectSize >> 32)
}
// Write symbol representing a .text function.
// The symbol table is split with C_FILE corresponding to each package
// and not to each source file as it should be.
func (f *xcoffFile) writeSymbolFunc(ctxt *Link, x *sym.Symbol) []xcoffSym {
// New XCOFF symbols which will be written.
syms := []xcoffSym{}
// Check if a new file is detected.
if strings.Contains(x.Name, "-tramp") || strings.HasPrefix(x.Name, "runtime.text.") {
// Trampoline don't have a FILE so there are considered
// in the current file.
// Same goes for runtime.text.X symbols.
} else if x.File == "" { // Undefined global symbol
// If this happens, the algorithme must be redone.
if currSymSrcFile.name != "" {
Exitf("undefined global symbol found inside another file")
}
} else {
// Current file has changed. New C_FILE, C_DWARF, etc must be generated.
if currSymSrcFile.name != x.File {
if ctxt.LinkMode == LinkInternal {
// update previous file values
xfile.updatePreviousFile(ctxt, false)
currSymSrcFile.name = x.File
f.writeSymbolNewFile(ctxt, x.File, uint64(x.Value), xfile.getXCOFFscnum(x.Sect))
} else {
// With external linking, ld will crash if there is several
// .FILE and DWARF debugging enable, somewhere during
// the relocation phase.
// Therefore, all packages are merged under a fake .FILE
// "go_functions".
// TODO(aix); remove once ld has been fixed or the triggering
// relocation has been found and fixed.
if currSymSrcFile.name == "" {
currSymSrcFile.name = x.File
f.writeSymbolNewFile(ctxt, "go_functions", uint64(x.Value), xfile.getXCOFFscnum(x.Sect))
}
}
}
}
s := &XcoffSymEnt64{
Nsclass: C_EXT,
Noffset: uint32(xfile.stringTable.add(x.Extname())),
Nvalue: uint64(x.Value),
Nscnum: f.getXCOFFscnum(x.Sect),
Ntype: SYM_TYPE_FUNC,
Nnumaux: 2,
}
if x.Version != 0 || x.Attr.VisibilityHidden() || x.Attr.Local() {
s.Nsclass = C_HIDEXT
}
x.Dynid = int32(xfile.symbolCount)
syms = append(syms, s)
// Update current csect size
currSymSrcFile.csectSize += x.Size
// create auxiliary entries
a2 := &XcoffAuxFcn64{
Xfsize: uint32(x.Size),
Xlnnoptr: 0, // TODO
Xendndx: xfile.symbolCount + 3, // this symbol + 2 aux entries
Xauxtype: _AUX_FCN,
}
syms = append(syms, a2)
a4 := &XcoffAuxCSect64{
Xscnlenlo: uint32(currSymSrcFile.csectSymNb & 0xFFFFFFFF),
Xscnlenhi: uint32(currSymSrcFile.csectSymNb >> 32),
Xsmclas: XMC_PR, // Program Code
Xsmtyp: XTY_LD, // label definition (based on C)
Xauxtype: _AUX_CSECT,
}
a4.Xsmtyp |= uint8(xcoffAlign(x, TextSym) << 3)
syms = append(syms, a4)
return syms
}
// put function used by genasmsym to write symbol table
func putaixsym(ctxt *Link, x *sym.Symbol, str string, t SymbolType, addr int64, go_ *sym.Symbol) {
// All XCOFF symbols generated by this GO symbols
// Can be a symbol entry or a auxiliary entry
syms := []xcoffSym{}
switch t {
default:
return
case TextSym:
if x.FuncInfo != nil || strings.Contains(x.Name, "-tramp") || strings.HasPrefix(x.Name, "runtime.text.") {
// Function within a file
syms = xfile.writeSymbolFunc(ctxt, x)
} else {
// Only runtime.text and runtime.etext come through this way
if x.Name != "runtime.text" && x.Name != "runtime.etext" && x.Name != "go.buildid" {
Exitf("putaixsym: unknown text symbol %s", x.Name)
}
s := &XcoffSymEnt64{
Nsclass: C_HIDEXT,
Noffset: uint32(xfile.stringTable.add(str)),
Nvalue: uint64(x.Value),
Nscnum: xfile.getXCOFFscnum(x.Sect),
Ntype: SYM_TYPE_FUNC,
Nnumaux: 1,
}
x.Dynid = int32(xfile.symbolCount)
syms = append(syms, s)
size := uint64(x.Size)
a4 := &XcoffAuxCSect64{
Xauxtype: _AUX_CSECT,
Xscnlenlo: uint32(size & 0xFFFFFFFF),
Xscnlenhi: uint32(size >> 32),
Xsmclas: XMC_PR,
Xsmtyp: XTY_SD,
}
a4.Xsmtyp |= uint8(xcoffAlign(x, TextSym) << 3)
syms = append(syms, a4)
}
case DataSym, BSSSym:
s := &XcoffSymEnt64{
Nsclass: C_EXT,
Noffset: uint32(xfile.stringTable.add(str)),
Nvalue: uint64(x.Value),
Nscnum: xfile.getXCOFFscnum(x.Sect),
Nnumaux: 1,
}
if x.Version != 0 || x.Attr.VisibilityHidden() || x.Attr.Local() {
// There is more symbols in the case of a global data
// which are related to the assembly generated
// to access such symbols.
// But as Golang as its own way to check if a symbol is
// global or local (the capital letter), we don't need to
// implement them yet.
s.Nsclass = C_HIDEXT
}
x.Dynid = int32(xfile.symbolCount)
syms = append(syms, s)
// Create auxiliary entry
// Normally, size should be the size of csect containing all
// the data and bss symbols of one file/package.
// However, it's easier to just have a csect for each symbol.
// It might change
size := uint64(x.Size)
a4 := &XcoffAuxCSect64{
Xauxtype: _AUX_CSECT,
Xscnlenlo: uint32(size & 0xFFFFFFFF),
Xscnlenhi: uint32(size >> 32),
}
if x.Type >= sym.STYPE && x.Type <= sym.SPCLNTAB {
if ctxt.LinkMode == LinkExternal && strings.HasPrefix(x.Sect.Name, ".data.rel.ro") {
// During external linking, read-only datas with relocation
// must be in .data.
a4.Xsmclas = XMC_RW
} else {
// Read only data
a4.Xsmclas = XMC_RO
}
} else if x.Type == sym.SDATA && strings.HasPrefix(x.Name, "TOC.") && ctxt.LinkMode == LinkExternal {
a4.Xsmclas = XMC_TC
} else if x.Name == "TOC" {
a4.Xsmclas = XMC_TC0
} else {
a4.Xsmclas = XMC_RW
}
if t == DataSym {
a4.Xsmtyp |= XTY_SD
} else {
a4.Xsmtyp |= XTY_CM
}
a4.Xsmtyp |= uint8(xcoffAlign(x, t) << 3)
syms = append(syms, a4)
case UndefinedSym:
if x.Type != sym.SDYNIMPORT && x.Type != sym.SHOSTOBJ {
return
}
s := &XcoffSymEnt64{
Nsclass: C_EXT,
Noffset: uint32(xfile.stringTable.add(str)),
Nnumaux: 1,
}
x.Dynid = int32(xfile.symbolCount)
syms = append(syms, s)
a4 := &XcoffAuxCSect64{
Xauxtype: _AUX_CSECT,
Xsmclas: XMC_DS,
Xsmtyp: XTY_ER | XTY_IMP,
}
if x.Name == "__n_pthreads" {
// Currently, all imported symbols made by cgo_import_dynamic are
// syscall functions, except __n_pthreads which is a variable.
// TODO(aix): Find a way to detect variables imported by cgo.
a4.Xsmclas = XMC_RW
}
syms = append(syms, a4)
case TLSSym:
s := &XcoffSymEnt64{
Nsclass: C_EXT,
Noffset: uint32(xfile.stringTable.add(str)),
Nscnum: xfile.getXCOFFscnum(x.Sect),
Nvalue: uint64(x.Value),
Nnumaux: 1,
}
x.Dynid = int32(xfile.symbolCount)
syms = append(syms, s)
size := uint64(x.Size)
a4 := &XcoffAuxCSect64{
Xauxtype: _AUX_CSECT,
Xsmclas: XMC_UL,
Xsmtyp: XTY_CM,
Xscnlenlo: uint32(size & 0xFFFFFFFF),
Xscnlenhi: uint32(size >> 32),
}
syms = append(syms, a4)
}
for _, s := range syms {
xfile.addSymbol(s)
}
}
// Generate XCOFF Symbol table.
// It will be written in out file in Asmbxcoff, because it must be
// at the very end, especially after relocation sections which needs symbols' index.
func (f *xcoffFile) asmaixsym(ctxt *Link) {
// Get correct size for symbols wrapping others symbols like go.string.*
// sym.Size can be used directly as the symbols have already been written.
for name, size := range outerSymSize {
sym := ctxt.Syms.ROLookup(name, 0)
if sym == nil {
Errorf(nil, "unknown outer symbol with name %s", name)
} else {
sym.Size = size
}
}
genasmsym(ctxt, putaixsym)
xfile.updatePreviousFile(ctxt, true)
}
func (f *xcoffFile) genDynSym(ctxt *Link) {
var dynsyms []*sym.Symbol
for _, s := range ctxt.Syms.Allsym {
if s.Type != sym.SHOSTOBJ && s.Type != sym.SDYNIMPORT {
continue
}
dynsyms = append(dynsyms, s)
}
for _, s := range dynsyms {
f.adddynimpsym(ctxt, s)
if _, ok := f.dynLibraries[s.Dynimplib()]; !ok {
f.dynLibraries[s.Dynimplib()] = len(f.dynLibraries)
}
}
}
// (*xcoffFile)adddynimpsym adds the dynamic symbol "s" to a XCOFF file.
// A new symbol named s.Extname() is created to be the actual dynamic symbol
// in the .loader section and in the symbol table as an External Reference.
// The symbol "s" is transformed to SXCOFFTOC to end up in .data section.
// However, there is no writing protection on those symbols and
// it might need to be added.
// TODO(aix): Handles dynamic symbols without library.
func (f *xcoffFile) adddynimpsym(ctxt *Link, s *sym.Symbol) {
// Check that library name is given.
// Pattern is already checked when compiling.
if ctxt.LinkMode == LinkInternal && s.Dynimplib() == "" {
Errorf(s, "imported symbol must have a given library")
}
s.Type = sym.SXCOFFTOC
// Create new dynamic symbol
extsym := ctxt.Syms.Lookup(s.Extname(), 0)
extsym.Type = sym.SDYNIMPORT
extsym.Attr |= sym.AttrReachable
extsym.SetDynimplib(s.Dynimplib())
extsym.SetExtname(s.Extname())
extsym.SetDynimpvers(s.Dynimpvers())
// Add loader symbol
lds := &xcoffLoaderSymbol{
sym: extsym,
smtype: XTY_IMP,
smclas: XMC_DS,
}
if s.Name == "__n_pthreads" {
// Currently, all imported symbols made by cgo_import_dynamic are
// syscall functions, except __n_pthreads which is a variable.
// TODO(aix): Find a way to detect variables imported by cgo.
lds.smclas = XMC_RW
}
f.loaderSymbols = append(f.loaderSymbols, lds)
// Relocation to retrieve the external address
s.AddBytes(make([]byte, 8))
s.SetAddr(ctxt.Arch, 0, extsym)
}
// Xcoffadddynrel adds a dynamic relocation in a XCOFF file.
// This relocation will be made by the loader.
func Xcoffadddynrel(ctxt *Link, s *sym.Symbol, r *sym.Reloc) bool {
if ctxt.LinkMode == LinkExternal {
return true
}
if s.Type <= sym.SPCLNTAB {
Errorf(s, "cannot have a relocation to %s in a text section symbol", r.Sym.Name)
return false
}
ldr := &xcoffLoaderReloc{
sym: s,
rel: r,
}
switch r.Type {
default:
Errorf(s, "unexpected .loader relocation to symbol: %s (type: %s)", r.Sym.Name, r.Type.String())
return false
case objabi.R_ADDR:
if s.Type == sym.SXCOFFTOC && r.Sym.Type == sym.SDYNIMPORT {
// Imported symbol relocation
for i, dynsym := range xfile.loaderSymbols {
if dynsym.sym.Name == r.Sym.Name {
ldr.symndx = int32(i + 3) // +3 because of 3 section symbols
break
}
}
} else if s.Type == sym.SDATA {
switch r.Sym.Sect.Seg {
default:
Errorf(s, "unknown segment for .loader relocation with symbol %s", r.Sym.Name)
case &Segtext:
case &Segrodata:
ldr.symndx = 0 // .text
case &Segdata:
if r.Sym.Type == sym.SBSS || r.Sym.Type == sym.SNOPTRBSS {
ldr.symndx = 2 // .bss
} else {
ldr.symndx = 1 // .data
}
}
} else {
Errorf(s, "unexpected type for .loader relocation R_ADDR for symbol %s: %s to %s", r.Sym.Name, s.Type, r.Sym.Type)
return false
}
ldr.rtype = 0x3F<<8 + XCOFF_R_POS
}
xfile.loaderReloc = append(xfile.loaderReloc, ldr)
return true
}
func (ctxt *Link) doxcoff() {
if *FlagD {
// All XCOFF files have dynamic symbols because of the syscalls.
Exitf("-d is not available on AIX")
}
// TOC
toc := ctxt.Syms.Lookup("TOC", 0)
toc.Type = sym.SXCOFFTOC
toc.Attr |= sym.AttrReachable
toc.Attr |= sym.AttrVisibilityHidden
// XCOFF does not allow relocations of data symbol address to a text symbol.
// Such case occurs when a RODATA symbol retrieves a data symbol address.
// When it happens, this RODATA symbol is moved to .data section.
// runtime.algarray is a readonly symbol but stored inside .data section.
// If it stays in .data, all type symbols will be moved to .data which
// cannot be done.
algarray := ctxt.Syms.Lookup("runtime.algarray", 0)
algarray.Type = sym.SRODATA
for {
again := false
for _, s := range ctxt.Syms.Allsym {
if s.Type != sym.SRODATA {
continue
}
for ri := range s.R {
r := &s.R[ri]
if r.Type != objabi.R_ADDR {
continue
}
if r.Sym.Type != sym.Sxxx && r.Sym.Type != sym.STEXT && r.Sym.Type != sym.SRODATA {
s.Type = sym.SDATA
again = true
break
}
}
}
if !again {
break
}
}
// Add entry point to .loader symbols.
ep := ctxt.Syms.ROLookup(*flagEntrySymbol, 0)
if !ep.Attr.Reachable() {
Exitf("wrong entry point")
}
xfile.loaderSymbols = append(xfile.loaderSymbols, &xcoffLoaderSymbol{
sym: ep,
smtype: XTY_ENT | XTY_SD,
smclas: XMC_DS,
})
xfile.genDynSym(ctxt)
for _, s := range ctxt.Syms.Allsym {
if strings.HasPrefix(s.Name, "TOC.") {
s.Type = sym.SXCOFFTOC
}
}
if ctxt.LinkMode == LinkExternal {
// Change rt0_go name to match name in runtime/cgo:main().
rt0 := ctxt.Syms.ROLookup("runtime.rt0_go", 0)
ctxt.Syms.Rename(rt0.Name, "runtime_rt0_go", 0, ctxt.Reachparent)
for _, s := range ctxt.Syms.Allsym {
if !s.Attr.CgoExport() {
continue
}
name := s.Extname()
if s.Type == sym.STEXT {
// On AIX, a exported function must have two symbols:
// - a .text symbol which must start with a ".".
// - a .data symbol which is a function descriptor.
ctxt.Syms.Rename(s.Name, "."+name, 0, ctxt.Reachparent)
desc := ctxt.Syms.Lookup(name, 0)
desc.Type = sym.SNOPTRDATA
desc.AddAddr(ctxt.Arch, s)
desc.AddAddr(ctxt.Arch, toc)
desc.AddUint64(ctxt.Arch, 0)
}
}
}
}
// Loader section
// Currently, this section is created from scratch when assembling the XCOFF file
// according to information retrieved in xfile object.
// Create loader section and returns its size
func Loaderblk(ctxt *Link, off uint64) {
xfile.writeLdrScn(ctxt, off)
}
func (f *xcoffFile) writeLdrScn(ctxt *Link, globalOff uint64) {
var symtab []*XcoffLdSym64
var strtab []*XcoffLdStr64
var importtab []*XcoffLdImportFile64
var reloctab []*XcoffLdRel64
var dynimpreloc []*XcoffLdRel64
// As the string table is updated in any loader subsection,
// its length must be computed at the same time.
stlen := uint32(0)
// Loader Header
hdr := &XcoffLdHdr64{
Lversion: 2,
Lsymoff: LDHDRSZ_64,
}
/* Symbol table */
for _, s := range f.loaderSymbols {
lds := &XcoffLdSym64{
Loffset: uint32(stlen + 2),
Lsmtype: s.smtype,
Lsmclas: s.smclas,
}
switch s.smtype {
default:
Errorf(s.sym, "unexpected loader symbol type: 0x%x", s.smtype)
case XTY_ENT | XTY_SD:
lds.Lvalue = uint64(s.sym.Value)
lds.Lscnum = f.getXCOFFscnum(s.sym.Sect)
case XTY_IMP:
lds.Lifile = int32(f.dynLibraries[s.sym.Dynimplib()] + 1)
}
ldstr := &XcoffLdStr64{
size: uint16(len(s.sym.Name) + 1), // + null terminator
name: s.sym.Name,
}
stlen += uint32(2 + ldstr.size) // 2 = sizeof ldstr.size
symtab = append(symtab, lds)
strtab = append(strtab, ldstr)
}
hdr.Lnsyms = int32(len(symtab))
hdr.Lrldoff = hdr.Lsymoff + uint64(24*hdr.Lnsyms) // 24 = sizeof one symbol
off := hdr.Lrldoff // current offset is the same of reloc offset
/* Reloc */
ep := ctxt.Syms.ROLookup(*flagEntrySymbol, 0)
ldr := &XcoffLdRel64{
Lvaddr: uint64(ep.Value),
Lrtype: 0x3F00,
Lrsecnm: f.getXCOFFscnum(ep.Sect),
Lsymndx: 0,
}
off += 16
reloctab = append(reloctab, ldr)
off += uint64(16 * len(f.loaderReloc))
for _, r := range f.loaderReloc {
ldr = &XcoffLdRel64{
Lvaddr: uint64(r.sym.Value + int64(r.rel.Off)),
Lrtype: r.rtype,
Lsymndx: r.symndx,
}
if r.sym.Sect != nil {
ldr.Lrsecnm = f.getXCOFFscnum(r.sym.Sect)
}
reloctab = append(reloctab, ldr)
}
off += uint64(16 * len(dynimpreloc))
reloctab = append(reloctab, dynimpreloc...)
hdr.Lnreloc = int32(len(reloctab))
hdr.Limpoff = off
/* Import */
// Default import: /usr/lib:/lib
ldimpf := &XcoffLdImportFile64{
Limpidpath: "/usr/lib:/lib",
}
off += uint64(len(ldimpf.Limpidpath) + len(ldimpf.Limpidbase) + len(ldimpf.Limpidmem) + 3) // + null delimiter
importtab = append(importtab, ldimpf)
// The map created by adddynimpsym associates the name to a number
// This number represents the librairie index (- 1) in this import files section
// Therefore, they must be sorted before being put inside the section
libsOrdered := make([]string, len(f.dynLibraries))
for key, val := range f.dynLibraries {
if libsOrdered[val] != "" {
continue
}
libsOrdered[val] = key
}
for _, lib := range libsOrdered {
// lib string is defined as base.a/mem.o or path/base.a/mem.o
n := strings.Split(lib, "/")
path := ""
base := n[len(n)-2]
mem := n[len(n)-1]
if len(n) > 2 {
path = lib[:len(lib)-len(base)-len(mem)-2]
}
ldimpf = &XcoffLdImportFile64{
Limpidpath: path,
Limpidbase: base,
Limpidmem: mem,
}
off += uint64(len(ldimpf.Limpidpath) + len(ldimpf.Limpidbase) + len(ldimpf.Limpidmem) + 3) // + null delimiter
importtab = append(importtab, ldimpf)
}
hdr.Lnimpid = int32(len(importtab))
hdr.Listlen = uint32(off - hdr.Limpoff)
hdr.Lstoff = off
hdr.Lstlen = stlen
/* Writing */
ctxt.Out.SeekSet(int64(globalOff))
binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, hdr)
for _, s := range symtab {
binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, s)
}
for _, r := range reloctab {
binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, r)
}
for _, f := range importtab {
ctxt.Out.WriteString(f.Limpidpath)
ctxt.Out.Write8(0)
ctxt.Out.WriteString(f.Limpidbase)
ctxt.Out.Write8(0)
ctxt.Out.WriteString(f.Limpidmem)
ctxt.Out.Write8(0)
}
for _, s := range strtab {
ctxt.Out.Write16(s.size)
ctxt.Out.WriteString(s.name)
ctxt.Out.Write8(0) // null terminator
}
f.loaderSize = off + uint64(stlen)
ctxt.Out.Flush()
/* again for printing */
if !*flagA {
return
}
ctxt.Logf("\n.loader section")
// write in buf
var buf bytes.Buffer
binary.Write(&buf, ctxt.Arch.ByteOrder, hdr)
for _, s := range symtab {
binary.Write(&buf, ctxt.Arch.ByteOrder, s)
}
for _, f := range importtab {
buf.WriteString(f.Limpidpath)
buf.WriteByte(0)
buf.WriteString(f.Limpidbase)
buf.WriteByte(0)
buf.WriteString(f.Limpidmem)
buf.WriteByte(0)
}
for _, s := range strtab {
binary.Write(&buf, ctxt.Arch.ByteOrder, s.size)
buf.WriteString(s.name)
buf.WriteByte(0) // null terminator
}
// Log buffer
ctxt.Logf("\n\t%.8x|", globalOff)
for i, b := range buf.Bytes() {
if i > 0 && i%16 == 0 {
ctxt.Logf("\n\t%.8x|", uint64(globalOff)+uint64(i))
}
ctxt.Logf(" %.2x", b)
}
ctxt.Logf("\n")
}
// XCOFF assembling and writing file
func (f *xcoffFile) writeFileHeader(ctxt *Link) {
// File header
f.xfhdr.Fmagic = U64_TOCMAGIC
f.xfhdr.Fnscns = uint16(len(f.sections))
f.xfhdr.Ftimedat = 0
if !*FlagS {
f.xfhdr.Fsymptr = uint64(f.symtabOffset)
f.xfhdr.Fnsyms = int32(f.symbolCount)
}
if ctxt.BuildMode == BuildModeExe && ctxt.LinkMode == LinkInternal {
f.xfhdr.Fopthdr = AOUTHSZ_EXEC64
f.xfhdr.Fflags = F_EXEC
// auxiliary header
f.xahdr.Ovstamp = 1 // based on dump -o
f.xahdr.Omagic = 0x10b
copy(f.xahdr.Omodtype[:], "1L")
entry := ctxt.Syms.ROLookup(*flagEntrySymbol, 0)
f.xahdr.Oentry = uint64(entry.Value)
f.xahdr.Osnentry = f.getXCOFFscnum(entry.Sect)
toc := ctxt.Syms.ROLookup("TOC", 0)
f.xahdr.Otoc = uint64(toc.Value)
f.xahdr.Osntoc = f.getXCOFFscnum(toc.Sect)
f.xahdr.Oalgntext = int16(logBase2(int(Funcalign)))
f.xahdr.Oalgndata = 0x5
binary.Write(ctxt.Out, binary.BigEndian, &f.xfhdr)
binary.Write(ctxt.Out, binary.BigEndian, &f.xahdr)
} else {
f.xfhdr.Fopthdr = 0
binary.Write(ctxt.Out, binary.BigEndian, &f.xfhdr)
}
}
func xcoffwrite(ctxt *Link) {
ctxt.Out.SeekSet(0)
xfile.writeFileHeader(ctxt)
for _, sect := range xfile.sections {
sect.write(ctxt)
}
}
// Generate XCOFF assembly file
func Asmbxcoff(ctxt *Link, fileoff int64) {
xfile.sectNameToScnum = make(map[string]int16)
// Add sections
s := xfile.addSection(".text", Segtext.Vaddr, Segtext.Length, Segtext.Fileoff, STYP_TEXT)
xfile.xahdr.Otextstart = s.Svaddr
xfile.xahdr.Osntext = xfile.sectNameToScnum[".text"]
xfile.xahdr.Otsize = s.Ssize
xfile.sectText = s
segdataVaddr := Segdata.Vaddr
segdataFilelen := Segdata.Filelen
segdataFileoff := Segdata.Fileoff
segbssFilelen := Segdata.Length - Segdata.Filelen
if len(Segrelrodata.Sections) > 0 {
// Merge relro segment to data segment as
// relro data are inside data segment on AIX.
segdataVaddr = Segrelrodata.Vaddr
segdataFileoff = Segrelrodata.Fileoff
segdataFilelen = Segdata.Vaddr + Segdata.Filelen - Segrelrodata.Vaddr
}
s = xfile.addSection(".data", segdataVaddr, segdataFilelen, segdataFileoff, STYP_DATA)
xfile.xahdr.Odatastart = s.Svaddr
xfile.xahdr.Osndata = xfile.sectNameToScnum[".data"]
xfile.xahdr.Odsize = s.Ssize
xfile.sectData = s
s = xfile.addSection(".bss", segdataVaddr+segdataFilelen, segbssFilelen, 0, STYP_BSS)
xfile.xahdr.Osnbss = xfile.sectNameToScnum[".bss"]
xfile.xahdr.Obsize = s.Ssize
xfile.sectBss = s
if ctxt.LinkMode == LinkExternal {
var tbss *sym.Section
for _, s := range Segdata.Sections {
if s.Name == ".tbss" {
tbss = s
break
}
}
s = xfile.addSection(".tbss", tbss.Vaddr, tbss.Length, 0, STYP_TBSS)
}
// add dwarf sections
for _, sect := range Segdwarf.Sections {
xfile.addDwarfSection(sect)
}
// add and write remaining sections
if ctxt.LinkMode == LinkInternal {
// Loader section
if ctxt.BuildMode == BuildModeExe {
Loaderblk(ctxt, uint64(fileoff))
s = xfile.addSection(".loader", 0, xfile.loaderSize, uint64(fileoff), STYP_LOADER)
xfile.xahdr.Osnloader = xfile.sectNameToScnum[".loader"]
// Update fileoff for symbol table
fileoff += int64(xfile.loaderSize)
}
}
// Create Symbol table
xfile.asmaixsym(ctxt)
if ctxt.LinkMode == LinkExternal {
xfile.emitRelocations(ctxt, fileoff)
}
// Write Symbol table
xfile.symtabOffset = ctxt.Out.Offset()
for _, s := range xfile.symtabSym {
binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, s)
}
// write string table
xfile.stringTable.write(ctxt.Out)
ctxt.Out.Flush()
// write headers
xcoffwrite(ctxt)
}
// byOffset is used to sort relocations by offset
type byOffset []sym.Reloc
func (x byOffset) Len() int { return len(x) }
func (x byOffset) Swap(i, j int) {
x[i], x[j] = x[j], x[i]
}
func (x byOffset) Less(i, j int) bool {
return x[i].Off < x[j].Off
}
// emitRelocations emits relocation entries for go.o in external linking.
func (f *xcoffFile) emitRelocations(ctxt *Link, fileoff int64) {
ctxt.Out.SeekSet(fileoff)
for ctxt.Out.Offset()&7 != 0 {
ctxt.Out.Write8(0)
}
// relocsect relocates symbols from first in section sect, and returns
// the total number of relocations emitted.
relocsect := func(sect *sym.Section, syms []*sym.Symbol, base uint64) uint32 {
// ctxt.Logf("%s 0x%x\n", sect.Name, sect.Vaddr)
// If main section has no bits, nothing to relocate.
if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
return 0
}
sect.Reloff = uint64(ctxt.Out.Offset())
for i, s := range syms {
if !s.Attr.Reachable() {
continue
}
if uint64(s.Value) >= sect.Vaddr {
syms = syms[i:]
break
}
}
eaddr := int64(sect.Vaddr + sect.Length)
for _, s := range syms {
if !s.Attr.Reachable() {
continue
}
if s.Value >= int64(eaddr) {
break
}
// Relocation must be ordered by address, so s.R is ordered by Off.
sort.Sort(byOffset(s.R))
for ri := range s.R {
r := &s.R[ri]
if r.Done {
continue
}
if r.Xsym == nil {
Errorf(s, "missing xsym in relocation")
continue
}
if r.Xsym.Dynid < 0 {
Errorf(s, "reloc %s to non-coff symbol %s (outer=%s) %d %d", r.Type.String(), r.Sym.Name, r.Xsym.Name, r.Sym.Type, r.Xsym.Dynid)
}
if !thearch.Xcoffreloc1(ctxt.Arch, ctxt.Out, s, r, int64(uint64(s.Value+int64(r.Off))-base)) {
Errorf(s, "unsupported obj reloc %d(%s)/%d to %s", r.Type, r.Type.String(), r.Siz, r.Sym.Name)
}
}
}
sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff
return uint32(sect.Rellen) / RELSZ_64
}
sects := []struct {
xcoffSect *XcoffScnHdr64
segs []*sym.Segment
}{
{f.sectText, []*sym.Segment{&Segtext}},
{f.sectData, []*sym.Segment{&Segrelrodata, &Segdata}},
}
for _, s := range sects {
s.xcoffSect.Srelptr = uint64(ctxt.Out.Offset())
n := uint32(0)
for _, seg := range s.segs {
for _, sect := range seg.Sections {
if sect.Name == ".text" {
n += relocsect(sect, ctxt.Textp, 0)
} else {
n += relocsect(sect, datap, 0)
}
}
}
s.xcoffSect.Snreloc += n
}
dwarfLoop:
for _, sect := range Segdwarf.Sections {
for _, xcoffSect := range f.sections {
_, subtyp := xcoffGetDwarfSubtype(sect.Name)
if xcoffSect.Sflags&0xF0000 == subtyp {
xcoffSect.Srelptr = uint64(ctxt.Out.Offset())
xcoffSect.Snreloc = relocsect(sect, dwarfp, sect.Vaddr)
continue dwarfLoop
}
}
Errorf(nil, "emitRelocations: could not find %q section", sect.Name)
}
}
// xcoffCreateExportFile creates a file with exported symbols for
// -Wl,-bE option.
// ld won't export symbols unless they are listed in an export file.
func xcoffCreateExportFile(ctxt *Link) (fname string) {
fname = filepath.Join(*flagTmpdir, "export_file.exp")
var buf bytes.Buffer
for _, s := range ctxt.Syms.Allsym {
if !s.Attr.CgoExport() {
continue
}
if !strings.HasPrefix(s.String(), "_cgoexp_") {
continue
}
// Retrieve the name of the initial symbol
// exported by cgo.
// The corresponding Go symbol is:
// _cgoexp_hashcode_symname.
name := strings.SplitN(s.Extname(), "_", 4)[3]
buf.Write([]byte(name + "\n"))
}
err := ioutil.WriteFile(fname, buf.Bytes(), 0666)
if err != nil {
Errorf(nil, "WriteFile %s failed: %v", fname, err)
}
return fname
}
|