diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
commit | 43a123c1ae6613b3efeed291fa552ecd909d3acf (patch) | |
tree | fd92518b7024bc74031f78a1cf9e454b65e73665 /src/cmd/link/internal/ld/xcoff.go | |
parent | Initial commit. (diff) | |
download | golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.tar.xz golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.zip |
Adding upstream version 1.20.14.upstream/1.20.14upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/link/internal/ld/xcoff.go')
-rw-r--r-- | src/cmd/link/internal/ld/xcoff.go | 1815 |
1 files changed, 1815 insertions, 0 deletions
diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go new file mode 100644 index 0000000..1265c45 --- /dev/null +++ b/src/cmd/link/internal/ld/xcoff.go @@ -0,0 +1,1815 @@ +// 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" + "encoding/binary" + "fmt" + "math/bits" + "os" + "path/filepath" + "sort" + "strings" + "sync" + + "cmd/internal/objabi" + "cmd/link/internal/loader" + "cmd/link/internal/sym" +) + +// 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 + + // base on dump -o, then rounded from 32B to 64B to + // match worst case elf text section alignment on ppc64. + XCOFFSECTALIGN int64 = 64 + + // 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 loader.Sym + 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 loader.Sym + roff int32 + 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 + sync.Mutex // currently protect loaderReloc +} + +// 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 + csectVAStart int64 + csectVAEnd 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 + } + // TODO: use CarrierSymByType + + ldr := ctxt.loader + 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) { + // runtime.types size must be removed, as it's a real symbol. + tsize := ldr.SymSize(ldr.Lookup("runtime.types", 0)) + outerSymSize["typerel.*"] = size - tsize + return + } + fallthrough + case sym.STYPE: + if !ctxt.DynlinkingGo() { + // runtime.types size must be removed, as it's a real symbol. + tsize := ldr.SymSize(ldr.Lookup("runtime.types", 0)) + outerSymSize["type:*"] = size - tsize + } + 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.SPCLNTAB: + outerSymSize["runtime.pclntab"] = 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(ldr *loader.Loader, x loader.Sym, t SymbolType) uint8 { + align := ldr.SymAlign(x) + if align == 0 { + if t == TextSym { + align = int32(Funcalign) + } else { + align = symalign(ldr, 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 appeared: +// - 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) { + ldr := ctxt.loader + /* 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 common to all packages and not found with the previous function + if sect.Name == ".debug_abbrev" { + dwsize = uint64(ldr.SymSize(loader.Sym(sect.Sym))) + + } + } 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. + ldr.SetSymDynid(loader.Sym(sect.Sym), 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.csectVAStart = int64(firstEntry) + currSymSrcFile.csectVAEnd = int64(firstEntry) +} + +// 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 + csectSize := currSymSrcFile.csectVAEnd - currSymSrcFile.csectVAStart + aux.Xscnlenlo = uint32(csectSize & 0xFFFFFFFF) + aux.Xscnlenhi = uint32(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 loader.Sym) []xcoffSym { + // New XCOFF symbols which will be written. + syms := []xcoffSym{} + + // Check if a new file is detected. + ldr := ctxt.loader + name := ldr.SymName(x) + if strings.Contains(name, "-tramp") || strings.HasPrefix(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 ldr.SymPkg(x) == "" { // Undefined global symbol + // If this happens, the algorithm 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 != ldr.SymPkg(x) { + if ctxt.LinkMode == LinkInternal { + // update previous file values + xfile.updatePreviousFile(ctxt, false) + currSymSrcFile.name = ldr.SymPkg(x) + f.writeSymbolNewFile(ctxt, ldr.SymPkg(x), uint64(ldr.SymValue(x)), xfile.getXCOFFscnum(ldr.SymSect(x))) + } 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 = ldr.SymPkg(x) + f.writeSymbolNewFile(ctxt, "go_functions", uint64(ldr.SymValue(x)), xfile.getXCOFFscnum(ldr.SymSect(x))) + } + } + + } + } + + name = ldr.SymExtname(x) + name = mangleABIName(ctxt, ldr, x, name) + + s := &XcoffSymEnt64{ + Nsclass: C_EXT, + Noffset: uint32(xfile.stringTable.add(name)), + Nvalue: uint64(ldr.SymValue(x)), + Nscnum: f.getXCOFFscnum(ldr.SymSect(x)), + Ntype: SYM_TYPE_FUNC, + Nnumaux: 2, + } + + if ldr.IsFileLocal(x) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) { + s.Nsclass = C_HIDEXT + } + + ldr.SetSymDynid(x, int32(xfile.symbolCount)) + syms = append(syms, s) + + // Keep track of the section size by tracking the VA range. Individual + // alignment differences may introduce a few extra bytes of padding + // which are not fully accounted for by ldr.SymSize(x). + sv := ldr.SymValue(x) + ldr.SymSize(x) + if currSymSrcFile.csectVAEnd < sv { + currSymSrcFile.csectVAEnd = sv + } + + // create auxiliary entries + a2 := &XcoffAuxFcn64{ + Xfsize: uint32(ldr.SymSize(x)), + 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(ldr, x, TextSym) << 3) + + syms = append(syms, a4) + return syms +} + +// put function used by genasmsym to write symbol table. +func putaixsym(ctxt *Link, x loader.Sym, t SymbolType) { + // All XCOFF symbols generated by this GO symbols + // Can be a symbol entry or a auxiliary entry + syms := []xcoffSym{} + + ldr := ctxt.loader + name := ldr.SymName(x) + if t == UndefinedSym { + name = ldr.SymExtname(x) + } + + switch t { + default: + return + + case TextSym: + if ldr.SymPkg(x) != "" || strings.Contains(name, "-tramp") || strings.HasPrefix(name, "runtime.text.") { + // Function within a file + syms = xfile.writeSymbolFunc(ctxt, x) + } else { + // Only runtime.text and runtime.etext come through this way + if name != "runtime.text" && name != "runtime.etext" && name != "go:buildid" { + Exitf("putaixsym: unknown text symbol %s", name) + } + s := &XcoffSymEnt64{ + Nsclass: C_HIDEXT, + Noffset: uint32(xfile.stringTable.add(name)), + Nvalue: uint64(ldr.SymValue(x)), + Nscnum: xfile.getXCOFFscnum(ldr.SymSect(x)), + Ntype: SYM_TYPE_FUNC, + Nnumaux: 1, + } + ldr.SetSymDynid(x, int32(xfile.symbolCount)) + syms = append(syms, s) + + size := uint64(ldr.SymSize(x)) + a4 := &XcoffAuxCSect64{ + Xauxtype: _AUX_CSECT, + Xscnlenlo: uint32(size & 0xFFFFFFFF), + Xscnlenhi: uint32(size >> 32), + Xsmclas: XMC_PR, + Xsmtyp: XTY_SD, + } + a4.Xsmtyp |= uint8(xcoffAlign(ldr, x, TextSym) << 3) + syms = append(syms, a4) + } + + case DataSym, BSSSym: + s := &XcoffSymEnt64{ + Nsclass: C_EXT, + Noffset: uint32(xfile.stringTable.add(name)), + Nvalue: uint64(ldr.SymValue(x)), + Nscnum: xfile.getXCOFFscnum(ldr.SymSect(x)), + Nnumaux: 1, + } + + if ldr.IsFileLocal(x) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) { + // 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 + } + + ldr.SetSymDynid(x, 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(ldr.SymSize(x)) + a4 := &XcoffAuxCSect64{ + Xauxtype: _AUX_CSECT, + Xscnlenlo: uint32(size & 0xFFFFFFFF), + Xscnlenhi: uint32(size >> 32), + } + + if ty := ldr.SymType(x); ty >= sym.STYPE && ty <= sym.SPCLNTAB { + if ctxt.IsExternal() && strings.HasPrefix(ldr.SymSect(x).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 /*ty == sym.SDATA &&*/ strings.HasPrefix(ldr.SymName(x), "TOC.") && ctxt.IsExternal() { + a4.Xsmclas = XMC_TC + } else if ldr.SymName(x) == "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(ldr, x, t) << 3) + + syms = append(syms, a4) + + case UndefinedSym: + if ty := ldr.SymType(x); ty != sym.SDYNIMPORT && ty != sym.SHOSTOBJ && ty != sym.SUNDEFEXT { + return + } + s := &XcoffSymEnt64{ + Nsclass: C_EXT, + Noffset: uint32(xfile.stringTable.add(name)), + Nnumaux: 1, + } + ldr.SetSymDynid(x, int32(xfile.symbolCount)) + syms = append(syms, s) + + a4 := &XcoffAuxCSect64{ + Xauxtype: _AUX_CSECT, + Xsmclas: XMC_DS, + Xsmtyp: XTY_ER | XTY_IMP, + } + + if ldr.SymName(x) == "__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(name)), + Nscnum: xfile.getXCOFFscnum(ldr.SymSect(x)), + Nvalue: uint64(ldr.SymValue(x)), + Nnumaux: 1, + } + + ldr.SetSymDynid(x, int32(xfile.symbolCount)) + syms = append(syms, s) + + size := uint64(ldr.SymSize(x)) + 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) { + ldr := ctxt.loader + // 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 := ldr.Lookup(name, 0) + if sym == 0 { + Errorf(nil, "unknown outer symbol with name %s", name) + } else { + s := ldr.MakeSymbolUpdater(sym) + s.SetSize(size) + } + } + + // These symbols won't show up in the first loop below because we + // skip sym.STEXT symbols. Normal sym.STEXT symbols are emitted by walking textp. + s := ldr.Lookup("runtime.text", 0) + if ldr.SymType(s) == sym.STEXT { + // We've already included this symbol in ctxt.Textp on AIX with external linker. + // See data.go:/textaddress + if !ctxt.IsExternal() { + putaixsym(ctxt, s, TextSym) + } + } + + n := 1 + // Generate base addresses for all text sections if there are multiple + for _, sect := range Segtext.Sections[1:] { + if sect.Name != ".text" || ctxt.IsExternal() { + // On AIX, runtime.text.X are symbols already in the symtab. + break + } + s = ldr.Lookup(fmt.Sprintf("runtime.text.%d", n), 0) + if s == 0 { + break + } + if ldr.SymType(s) == sym.STEXT { + putaixsym(ctxt, s, TextSym) + } + n++ + } + + s = ldr.Lookup("runtime.etext", 0) + if ldr.SymType(s) == sym.STEXT { + // We've already included this symbol in ctxt.Textp + // on AIX with external linker. + // See data.go:/textaddress + if !ctxt.IsExternal() { + putaixsym(ctxt, s, TextSym) + } + } + + shouldBeInSymbolTable := func(s loader.Sym, name string) bool { + if name == ".go.buildinfo" { + // On AIX, .go.buildinfo must be in the symbol table as + // it has relocations. + return true + } + if ldr.AttrNotInSymbolTable(s) { + return false + } + if (name == "" || name[0] == '.') && !ldr.IsFileLocal(s) && name != ".TOC." { + return false + } + return true + } + + for s, nsym := loader.Sym(1), loader.Sym(ldr.NSym()); s < nsym; s++ { + if !shouldBeInSymbolTable(s, ldr.SymName(s)) { + continue + } + st := ldr.SymType(s) + switch { + case st == sym.STLSBSS: + if ctxt.IsExternal() { + putaixsym(ctxt, s, TLSSym) + } + + case st == sym.SBSS, st == sym.SNOPTRBSS, st == sym.SLIBFUZZER_8BIT_COUNTER, st == sym.SCOVERAGE_COUNTER: + if ldr.AttrReachable(s) { + data := ldr.Data(s) + if len(data) > 0 { + ldr.Errorf(s, "should not be bss (size=%d type=%v special=%v)", len(data), ldr.SymType(s), ldr.AttrSpecial(s)) + } + putaixsym(ctxt, s, BSSSym) + } + + case st >= sym.SELFRXSECT && st < sym.SXREF: // data sections handled in dodata + if ldr.AttrReachable(s) { + putaixsym(ctxt, s, DataSym) + } + + case st == sym.SUNDEFEXT: + putaixsym(ctxt, s, UndefinedSym) + + case st == sym.SDYNIMPORT: + if ldr.AttrReachable(s) { + putaixsym(ctxt, s, UndefinedSym) + } + } + } + + for _, s := range ctxt.Textp { + putaixsym(ctxt, s, TextSym) + } + + if ctxt.Debugvlog != 0 || *flagN { + ctxt.Logf("symsize = %d\n", uint32(symSize)) + } + xfile.updatePreviousFile(ctxt, true) +} + +func (f *xcoffFile) genDynSym(ctxt *Link) { + ldr := ctxt.loader + var dynsyms []loader.Sym + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) { + continue + } + if t := ldr.SymType(s); t != sym.SHOSTOBJ && t != sym.SDYNIMPORT { + continue + } + dynsyms = append(dynsyms, s) + } + + for _, s := range dynsyms { + f.adddynimpsym(ctxt, s) + + if _, ok := f.dynLibraries[ldr.SymDynimplib(s)]; !ok { + f.dynLibraries[ldr.SymDynimplib(s)] = 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 loader.Sym) { + // Check that library name is given. + // Pattern is already checked when compiling. + ldr := ctxt.loader + if ctxt.IsInternal() && ldr.SymDynimplib(s) == "" { + ctxt.Errorf(s, "imported symbol must have a given library") + } + + sb := ldr.MakeSymbolUpdater(s) + sb.SetReachable(true) + sb.SetType(sym.SXCOFFTOC) + + // Create new dynamic symbol + extsym := ldr.CreateSymForUpdate(ldr.SymExtname(s), 0) + extsym.SetType(sym.SDYNIMPORT) + extsym.SetDynimplib(ldr.SymDynimplib(s)) + extsym.SetExtname(ldr.SymExtname(s)) + extsym.SetDynimpvers(ldr.SymDynimpvers(s)) + + // Add loader symbol + lds := &xcoffLoaderSymbol{ + sym: extsym.Sym(), + smtype: XTY_IMP, + smclas: XMC_DS, + } + if ldr.SymName(s) == "__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 + sb.AddBytes(make([]byte, 8)) + r, _ := sb.AddRel(objabi.R_ADDR) + r.SetSym(extsym.Sym()) + r.SetSiz(uint8(ctxt.Arch.PtrSize)) + // TODO: maybe this could be + // sb.SetSize(0) + // sb.SetData(nil) + // sb.AddAddr(ctxt.Arch, extsym.Sym()) + // If the size is not 0 to begin with, I don't think the added 8 bytes + // of zeros are necessary. +} + +// Xcoffadddynrel adds a dynamic relocation in a XCOFF file. +// This relocation will be made by the loader. +func Xcoffadddynrel(target *Target, ldr *loader.Loader, syms *ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool { + if target.IsExternal() { + return true + } + if ldr.SymType(s) <= sym.SPCLNTAB { + ldr.Errorf(s, "cannot have a relocation to %s in a text section symbol", ldr.SymName(r.Sym())) + return false + } + + xldr := &xcoffLoaderReloc{ + sym: s, + roff: r.Off(), + } + targ := r.Sym() + var targType sym.SymKind + if targ != 0 { + targType = ldr.SymType(targ) + } + + switch r.Type() { + default: + ldr.Errorf(s, "unexpected .loader relocation to symbol: %s (type: %s)", ldr.SymName(targ), r.Type().String()) + return false + case objabi.R_ADDR: + if ldr.SymType(s) == sym.SXCOFFTOC && targType == sym.SDYNIMPORT { + // Imported symbol relocation + for i, dynsym := range xfile.loaderSymbols { + if ldr.SymName(dynsym.sym) == ldr.SymName(targ) { + xldr.symndx = int32(i + 3) // +3 because of 3 section symbols + break + } + } + } else if t := ldr.SymType(s); t == sym.SDATA || t == sym.SNOPTRDATA || t == sym.SBUILDINFO || t == sym.SXCOFFTOC { + switch ldr.SymSect(targ).Seg { + default: + ldr.Errorf(s, "unknown segment for .loader relocation with symbol %s", ldr.SymName(targ)) + case &Segtext: + case &Segrodata: + xldr.symndx = 0 // .text + case &Segdata: + if targType == sym.SBSS || targType == sym.SNOPTRBSS { + xldr.symndx = 2 // .bss + } else { + xldr.symndx = 1 // .data + } + } + + } else { + ldr.Errorf(s, "unexpected type for .loader relocation R_ADDR for symbol %s: %s to %s", ldr.SymName(targ), ldr.SymType(s), ldr.SymType(targ)) + return false + } + + xldr.rtype = 0x3F<<8 + XCOFF_R_POS + } + + xfile.Lock() + xfile.loaderReloc = append(xfile.loaderReloc, xldr) + xfile.Unlock() + return true +} + +func (ctxt *Link) doxcoff() { + ldr := ctxt.loader + + // TOC + toc := ldr.CreateSymForUpdate("TOC", 0) + toc.SetType(sym.SXCOFFTOC) + toc.SetVisibilityHidden(true) + + // Add entry point to .loader symbols. + ep := ldr.Lookup(*flagEntrySymbol, 0) + if ep == 0 || !ldr.AttrReachable(ep) { + 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 := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if strings.HasPrefix(ldr.SymName(s), "TOC.") { + sb := ldr.MakeSymbolUpdater(s) + sb.SetType(sym.SXCOFFTOC) + } + } + + if ctxt.IsExternal() { + // Change rt0_go name to match name in runtime/cgo:main(). + rt0 := ldr.Lookup("runtime.rt0_go", 0) + ldr.SetSymExtname(rt0, "runtime_rt0_go") + + nsym := loader.Sym(ldr.NSym()) + for s := loader.Sym(1); s < nsym; s++ { + if !ldr.AttrCgoExport(s) { + continue + } + if ldr.IsFileLocal(s) { + panic("cgo_export on static symbol") + } + + if ldr.SymType(s) == 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. + name := ldr.SymExtname(s) + ldr.SetSymExtname(s, "."+name) + + desc := ldr.MakeSymbolUpdater(ldr.CreateExtSym(name, 0)) + desc.SetReachable(true) + desc.SetType(sym.SNOPTRDATA) + desc.AddAddr(ctxt.Arch, s) + desc.AddAddr(ctxt.Arch, toc.Sym()) + 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, + } + + ldr := ctxt.loader + /* Symbol table */ + for _, s := range f.loaderSymbols { + lds := &XcoffLdSym64{ + Loffset: uint32(stlen + 2), + Lsmtype: s.smtype, + Lsmclas: s.smclas, + } + sym := s.sym + switch s.smtype { + default: + ldr.Errorf(sym, "unexpected loader symbol type: 0x%x", s.smtype) + case XTY_ENT | XTY_SD: + lds.Lvalue = uint64(ldr.SymValue(sym)) + lds.Lscnum = f.getXCOFFscnum(ldr.SymSect(sym)) + case XTY_IMP: + lds.Lifile = int32(f.dynLibraries[ldr.SymDynimplib(sym)] + 1) + } + ldstr := &XcoffLdStr64{ + size: uint16(len(ldr.SymName(sym)) + 1), // + null terminator + name: ldr.SymName(sym), + } + 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 */ + // Ensure deterministic order + sort.Slice(f.loaderReloc, func(i, j int) bool { + r1, r2 := f.loaderReloc[i], f.loaderReloc[j] + if r1.sym != r2.sym { + return r1.sym < r2.sym + } + if r1.roff != r2.roff { + return r1.roff < r2.roff + } + if r1.rtype != r2.rtype { + return r1.rtype < r2.rtype + } + return r1.symndx < r2.symndx + }) + + ep := ldr.Lookup(*flagEntrySymbol, 0) + xldr := &XcoffLdRel64{ + Lvaddr: uint64(ldr.SymValue(ep)), + Lrtype: 0x3F00, + Lrsecnm: f.getXCOFFscnum(ldr.SymSect(ep)), + Lsymndx: 0, + } + off += 16 + reloctab = append(reloctab, xldr) + + off += uint64(16 * len(f.loaderReloc)) + for _, r := range f.loaderReloc { + symp := r.sym + if symp == 0 { + panic("unexpected 0 sym value") + } + xldr = &XcoffLdRel64{ + Lvaddr: uint64(ldr.SymValue(symp) + int64(r.roff)), + Lrtype: r.rtype, + Lsymndx: r.symndx, + } + + if ldr.SymSect(symp) != nil { + xldr.Lrsecnm = f.getXCOFFscnum(ldr.SymSect(symp)) + } + + reloctab = append(reloctab, xldr) + } + + 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) +} + +// 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 { + ldr := ctxt.loader + 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 := ldr.Lookup(*flagEntrySymbol, 0) + f.xahdr.Oentry = uint64(ldr.SymValue(entry)) + f.xahdr.Osnentry = f.getXCOFFscnum(ldr.SymSect(entry)) + toc := ldr.Lookup("TOC", 0) + f.xahdr.Otoc = uint64(ldr.SymValue(toc)) + f.xahdr.Osntoc = f.getXCOFFscnum(ldr.SymSect(toc)) + + f.xahdr.Oalgntext = int16(logBase2(int(XCOFFSECTALIGN))) + 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) { + ctxt.Out.SeekSet(0) + fileoff := int64(Segdwarf.Fileoff + Segdwarf.Filelen) + fileoff = int64(Rnd(int64(fileoff), int64(*FlagRound))) + + 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) + + // write headers + xcoffwrite(ctxt) +} + +// 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) + } + + ldr := ctxt.loader + // relocsect relocates symbols from first in section sect, and returns + // the total number of relocations emitted. + relocsect := func(sect *sym.Section, syms []loader.Sym, 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 !ldr.AttrReachable(s) { + continue + } + if uint64(ldr.SymValue(s)) >= sect.Vaddr { + syms = syms[i:] + break + } + } + eaddr := int64(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 Xcoffreloc1 to stream out. + // Relocation must be ordered by address, so create a list of sorted indices. + relocs := ldr.Relocs(s) + sorted := make([]int, relocs.Count()) + for i := 0; i < relocs.Count(); i++ { + sorted[i] = i + } + sort.Slice(sorted, func(i, j int) bool { + return relocs.At(sorted[i]).Off() < relocs.At(sorted[j]).Off() + }) + + for _, ri := range sorted { + 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 + } + if ldr.SymDynid(rr.Xsym) < 0 { + ldr.Errorf(s, "reloc %s to non-coff symbol %s (outer=%s) %d %d", r.Type(), ldr.SymName(r.Sym()), ldr.SymName(rr.Xsym), ldr.SymType(r.Sym()), ldr.SymDynid(rr.Xsym)) + } + if !thearch.Xcoffreloc1(ctxt.Arch, ctxt.Out, ldr, s, rr, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-base)) { + ldr.Errorf(s, "unsupported obj reloc %d(%s)/%d to %s", r.Type(), r.Type(), r.Siz(), ldr.SymName(r.Sym())) + } + } + } + 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, ctxt.datap, 0) + } + } + } + s.xcoffSect.Snreloc += n + } + +dwarfLoop: + for i := 0; i < len(Segdwarf.Sections); i++ { + sect := Segdwarf.Sections[i] + si := dwarfp[i] + if si.secSym() != loader.Sym(sect.Sym) || + ldr.SymSect(si.secSym()) != sect { + panic("inconsistency between dwarfp and Segdwarf") + } + for _, xcoffSect := range f.sections { + _, subtyp := xcoffGetDwarfSubtype(sect.Name) + if xcoffSect.Sflags&0xF0000 == subtyp { + xcoffSect.Srelptr = uint64(ctxt.Out.Offset()) + xcoffSect.Snreloc = relocsect(sect, si.syms, 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 + + ldr := ctxt.loader + for s, nsym := loader.Sym(1), loader.Sym(ldr.NSym()); s < nsym; s++ { + if !ldr.AttrCgoExport(s) { + continue + } + extname := ldr.SymExtname(s) + if !strings.HasPrefix(extname, "._cgoexp_") { + continue + } + if ldr.IsFileLocal(s) { + continue // Only export non-static symbols + } + + // Retrieve the name of the initial symbol + // exported by cgo. + // The corresponding Go symbol is: + // _cgoexp_hashcode_symname. + name := strings.SplitN(extname, "_", 4)[3] + + buf.Write([]byte(name + "\n")) + } + + err := os.WriteFile(fname, buf.Bytes(), 0666) + if err != nil { + Errorf(nil, "WriteFile %s failed: %v", fname, err) + } + + return fname +} |