From 2deda8792a1f0139eba0bee527f3141659491444 Mon Sep 17 00:00:00 2001
From: Clément Chigot <clement.chigot@atos.net>
Date: Wed, 20 Feb 2019 15:48:22 +0100
Subject: [PATCH] cmd/link: improve XCOFF symbol table

This commit improves symbol table for XCOFF format.
It adds symbol alignment, TLS symbols and move the whole symbol table at
the end of the FILE. As relocations in the future external linking will
need symbols' index, we cannot write the symbol table when it's
generated.

Change-Id: I5dcae85b95e538b65f1a128faf56d4e2aa15baf1
Reviewed-on: https://go-review.googlesource.com/c/163998
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
---

diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index 0bd7d82..e72ad40 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -994,6 +994,7 @@
 	for int64(align) > s.Size && align > min {
 		align >>= 1
 	}
+	s.Align = align
 	return align
 }
 
diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go
index 1561ce8..e565a35 100644
--- a/src/cmd/link/internal/ld/xcoff.go
+++ b/src/cmd/link/internal/ld/xcoff.go
@@ -9,6 +9,7 @@
 	"cmd/internal/objabi"
 	"cmd/link/internal/sym"
 	"encoding/binary"
+	"math/bits"
 	"strings"
 )
 
@@ -155,6 +156,10 @@
 	LDSYMSZ_64     = 24
 )
 
+// Type representing all XCOFF symbols.
+type xcoffSym interface {
+}
+
 // Symbol Table Entry
 type XcoffSymEnt64 struct {
 	Nvalue  uint64 // Symbol value
@@ -214,9 +219,12 @@
 
 // File Auxiliary Entry
 type XcoffAuxFile64 struct {
-	Xfname   [8]byte // Name or offset inside string table
-	Xftype   uint8   // Source file string type
-	Xauxtype uint8   // Type of auxiliary entry
+	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
@@ -240,6 +248,13 @@
 	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
@@ -365,6 +380,7 @@
 	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
@@ -504,8 +520,9 @@
 // type records C_FILE information needed for genasmsym in XCOFF.
 type xcoffSymSrcFile struct {
 	name       string
-	fileSymNb  uint32 // Symbol number of this C_FILE
-	csectSymNb uint64 // Symbol number for the current .csect
+	file       *XcoffSymEnt64   // Symbol of this C_FILE
+	csectAux   *XcoffAuxCSect64 // Symbol for the current .csect
+	csectSymNb uint64           // Symbol number for the current .csect
 	csectSize  int64
 }
 
@@ -514,12 +531,30 @@
 	currSymSrcFile xcoffSymSrcFile
 )
 
-// writeSymbol writes a symbol or an auxiliary symbol entry on ctxt.out.
-func (f *xcoffFile) writeSymbol(out *OutBuf, byteOrder binary.ByteOrder, sym interface{}) {
-	binary.Write(out, byteOrder, sym)
+// addSymbol writes a symbol or an auxiliary symbol entry on ctxt.out.
+func (f *xcoffFile) addSymbol(sym xcoffSym) {
+	f.symtabSym = append(f.symtabSym, sym)
 	f.symbolCount++
 }
 
+// xcoffAlign returns the log base 2 of the symbol's alignment.
+func xcoffAlign(x *sym.Symbol, t SymbolType) uint8 {
+	align := x.Align
+	if align == 0 {
+		if t == TextSym {
+			align = int32(Funcalign)
+		} else {
+			align = symalign(x)
+		}
+	}
+	return logBase2(int(align))
+}
+
+// logBase2 returns the log in base 2 of a.
+func logBase2(a int) uint8 {
+	return uint8(bits.Len(uint(a)) - 1)
+}
+
 // Write symbols needed when a new file appared :
 // - a C_FILE with one auxiliary entry for its name
 // - C_DWARF symbols to provide debug information
@@ -537,17 +572,16 @@
 		Ntype:   0, // Go isn't inside predefined language.
 		Nnumaux: 1,
 	}
-	f.writeSymbol(ctxt.Out, ctxt.Arch.ByteOrder, s)
+	f.addSymbol(s)
+	currSymSrcFile.file = s
 
 	// Auxiliary entry for file name.
-	ctxt.Out.Write32(0)
-	ctxt.Out.Write32(uint32(f.stringTable.add(name)))
-	ctxt.Out.Write32(0) // 6 bytes empty
-	ctxt.Out.Write16(0)
-	ctxt.Out.Write8(XFT_FN)
-	ctxt.Out.Write16(0) // 2 bytes empty
-	ctxt.Out.Write8(_AUX_FILE)
-	f.symbolCount++
+	auxf := &XcoffAuxFile64{
+		Xoffset:  uint32(f.stringTable.add(name)),
+		Xftype:   XFT_FN,
+		Xauxtype: _AUX_FILE,
+	}
+	f.addSymbol(auxf)
 
 	/* Dwarf */
 	for _, sect := range Segdwarf.Sections {
@@ -569,7 +603,7 @@
 			Nscnum:  f.getXCOFFscnum(sect),
 			Nnumaux: 1,
 		}
-		f.writeSymbol(ctxt.Out, ctxt.Arch.ByteOrder, s)
+		f.addSymbol(s)
 
 		// update the DWARF section offset in this file
 		if sect.Name != ".debug_abbrev" {
@@ -577,11 +611,12 @@
 		}
 
 		// Auxiliary dwarf section
-		ctxt.Out.Write64(dwsize) // section length
-		ctxt.Out.Write64(0)      // nreloc
-		ctxt.Out.Write8(0)       // pad
-		ctxt.Out.Write8(_AUX_SECT)
-		f.symbolCount++
+		auxd := &XcoffAuxDWARF64{
+			Xscnlen:  dwsize,
+			Xauxtype: _AUX_SECT,
+		}
+
+		f.addSymbol(auxd)
 	}
 
 	/* .csect */
@@ -592,7 +627,6 @@
 	}
 
 	currSymSrcFile.csectSymNb = uint64(f.symbolCount)
-	currSymSrcFile.csectSize = 0
 
 	// No offset because no name
 	s = &XcoffSymEnt64{
@@ -602,15 +636,17 @@
 		Ntype:   0, // check visibility ?
 		Nnumaux: 1,
 	}
-	f.writeSymbol(ctxt.Out, ctxt.Arch.ByteOrder, s)
+	f.addSymbol(s)
 
 	aux := &XcoffAuxCSect64{
 		Xsmclas:  XMC_PR,
-		Xsmtyp:   XTY_SD | 5<<3, // align = 5
+		Xsmtyp:   XTY_SD | logBase2(Funcalign)<<3,
 		Xauxtype: _AUX_CSECT,
 	}
-	f.writeSymbol(ctxt.Out, ctxt.Arch.ByteOrder, aux)
+	f.addSymbol(aux)
 
+	currSymSrcFile.csectAux = aux
+	currSymSrcFile.csectSize = 0
 }
 
 // Update values for the previous package.
@@ -618,39 +654,30 @@
 //  - Xsclen of the csect symbol.
 func (f *xcoffFile) updatePreviousFile(ctxt *Link, last bool) {
 	// first file
-	if currSymSrcFile.fileSymNb == 0 {
+	if currSymSrcFile.file == nil {
 		return
 	}
 
-	prevOff := f.symtabOffset + int64(currSymSrcFile.fileSymNb*SYMESZ)
-	currOff := ctxt.Out.Offset()
-
 	// Update C_FILE
-	ctxt.Out.SeekSet(prevOff)
+	cfile := currSymSrcFile.file
 	if last {
-		ctxt.Out.Write64(0xFFFFFFFFFFFFFFFF)
+		cfile.Nvalue = 0xFFFFFFFFFFFFFFFF
 	} else {
-		ctxt.Out.Write64(uint64(f.symbolCount))
+		cfile.Nvalue = uint64(f.symbolCount)
 	}
 
 	// update csect scnlen in this auxiliary entry
-	prevOff = f.symtabOffset + int64((currSymSrcFile.csectSymNb+1)*SYMESZ)
-	ctxt.Out.SeekSet(prevOff)
-	ctxt.Out.Write32(uint32(currSymSrcFile.csectSize & 0xFFFFFFFF))
-	prevOff += 12
-	ctxt.Out.SeekSet(prevOff)
-	ctxt.Out.Write32(uint32(currSymSrcFile.csectSize >> 32))
-
-	ctxt.Out.SeekSet(currOff)
-
+	aux := currSymSrcFile.csectAux
+	aux.Xscnlenlo = uint32(currSymSrcFile.csectSize & 0xFFFFFFFF)
+	aux.Xscnlenhi = uint32(currSymSrcFile.csectSize >> 32)
 }
 
 // Write symbol representing a .text function.
 // The symbol table is split with C_FILE corresponding to each package
 // and not to each source file as it should be.
-func (f *xcoffFile) writeSymbolFunc(ctxt *Link, x *sym.Symbol) []interface{} {
+func (f *xcoffFile) writeSymbolFunc(ctxt *Link, x *sym.Symbol) []xcoffSym {
 	// New XCOFF symbols which will be written.
-	syms := []interface{}{}
+	syms := []xcoffSym{}
 
 	// Check if a new file is detected.
 	if x.File == "" { // Undefined global symbol
@@ -664,7 +691,6 @@
 			// update previous file values
 			xfile.updatePreviousFile(ctxt, false)
 			currSymSrcFile.name = x.File
-			currSymSrcFile.fileSymNb = f.symbolCount
 			f.writeSymbolNewFile(ctxt, x.File, uint64(x.Value), xfile.getXCOFFscnum(x.Sect))
 		}
 	}
@@ -703,6 +729,8 @@
 		Xsmtyp:    XTY_LD, // label definition (based on C)
 		Xauxtype:  _AUX_CSECT,
 	}
+	a4.Xsmtyp |= uint8(xcoffAlign(x, TextSym) << 3)
+
 	syms = append(syms, a4)
 	return syms
 }
@@ -712,7 +740,7 @@
 
 	// All XCOFF symbols generated by this GO symbols
 	// Can be a symbol entry or a auxiliary entry
-	syms := []interface{}{}
+	syms := []xcoffSym{}
 
 	switch t {
 	default:
@@ -745,6 +773,7 @@
 				Xsmclas:   XMC_PR,
 				Xsmtyp:    XTY_SD,
 			}
+			a4.Xsmtyp |= uint8(xcoffAlign(x, TextSym) << 3)
 			syms = append(syms, a4)
 
 		}
@@ -785,6 +814,8 @@
 		// Read only data
 		if x.Type >= sym.STYPE && x.Type <= sym.SPCLNTAB {
 			a4.Xsmclas = XMC_RO
+		} else if x.Name == "TOC" {
+			a4.Xsmclas = XMC_TC0
 		} else {
 			a4.Xsmclas = XMC_RW
 		}
@@ -794,6 +825,8 @@
 			a4.Xsmtyp |= XTY_CM
 		}
 
+		a4.Xsmtyp |= uint8(xcoffAlign(x, t) << 3)
+
 		syms = append(syms, a4)
 
 	case UndefinedSym:
@@ -821,23 +854,42 @@
 		}
 
 		syms = append(syms, a4)
+
+	case TLSSym:
+		s := &XcoffSymEnt64{
+			Nsclass: C_EXT,
+			Noffset: uint32(xfile.stringTable.add(str)),
+			Nscnum:  xfile.getXCOFFscnum(x.Sect),
+			Nvalue:  uint64(x.Value),
+			Nnumaux: 1,
+		}
+
+		x.Dynid = int32(xfile.symbolCount)
+		syms = append(syms, s)
+
+		size := uint64(x.Size)
+		a4 := &XcoffAuxCSect64{
+			Xauxtype:  _AUX_CSECT,
+			Xsmclas:   XMC_UL,
+			Xsmtyp:    XTY_CM,
+			Xscnlenlo: uint32(size & 0xFFFFFFFF),
+			Xscnlenhi: uint32(size >> 32),
+		}
+
+		syms = append(syms, a4)
 	}
 
 	for _, s := range syms {
-		xfile.writeSymbol(ctxt.Out, ctxt.Arch.ByteOrder, s)
+		xfile.addSymbol(s)
 	}
 }
 
-// Generate XCOFF Symbol table and XCOFF String table
+// 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) {
-	// write symbol table
 	genasmsym(ctxt, putaixsym)
-
-	// update last file Svalue
 	xfile.updatePreviousFile(ctxt, true)
-
-	// write string table
-	xfile.stringTable.write(ctxt.Out)
 }
 
 func (f *xcoffFile) genDynSym(ctxt *Link) {
@@ -1251,8 +1303,7 @@
 		f.xahdr.Otoc = uint64(toc.Value)
 		f.xahdr.Osntoc = f.getXCOFFscnum(toc.Sect)
 
-		// Based on dump -o
-		f.xahdr.Oalgntext = 0x5
+		f.xahdr.Oalgntext = int16(logBase2(int(Funcalign)))
 		f.xahdr.Oalgndata = 0x5
 
 		binary.Write(ctxt.Out, binary.BigEndian, &f.xfhdr)
@@ -1310,11 +1361,16 @@
 		// TODO: Relocation
 	}
 
-	// Write symbol table
-	symo := Rnd(ctxt.Out.Offset(), int64(*FlagRound))
-	xfile.symtabOffset = symo
-	ctxt.Out.SeekSet(int64(symo))
+	// Write symtab
 	xfile.asmaixsym(ctxt)
+	xfile.symtabOffset = ctxt.Out.Offset()
+	for _, s := range xfile.symtabSym {
+		binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, s)
+	}
+	// write string table
+	xfile.stringTable.write(ctxt.Out)
+
+	ctxt.Out.Flush()
 
 	// write headers
 	xcoffwrite(ctxt)
