// 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 }