]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.link] cmd/link: use pclntabState and eliminate globals
authorJeremy Faller <jeremy@golang.org>
Tue, 14 Jul 2020 17:12:58 +0000 (13:12 -0400)
committerJeremy Faller <jeremy@golang.org>
Fri, 31 Jul 2020 13:54:51 +0000 (13:54 +0000)
Non functional change.

As runtime.pclntab breaks up, it'll be easier if we can just pass around
the pclntab state. Also, eliminate the globals in pclntab.

Change-Id: I2a5849e8f5f422a336a881e53a261e3997d11c44
Reviewed-on: https://go-review.googlesource.com/c/go/+/242599
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
src/cmd/link/internal/ld/main.go
src/cmd/link/internal/ld/pcln.go
src/cmd/link/internal/ld/symtab.go

index c2532ac290cd6aa2bea5c41266643b6aea55439b..3702f28dd85604ec8672f2ff1d913517342fc017 100644 (file)
@@ -288,13 +288,14 @@ func Main(arch *sys.Arch, theArch Arch) {
        bench.Start("buildinfo")
        ctxt.buildinfo()
        bench.Start("pclntab")
-       container := ctxt.pclntab()
+       containers := ctxt.findContainerSyms()
+       pclnState := ctxt.pclntab(containers)
        bench.Start("findfunctab")
-       ctxt.findfunctab(container)
+       ctxt.findfunctab(pclnState, containers)
        bench.Start("dwarfGenerateDebugSyms")
        dwarfGenerateDebugSyms(ctxt)
        bench.Start("symtab")
-       symGroupType := ctxt.symtab()
+       symGroupType := ctxt.symtab(pclnState)
        bench.Start("dodata")
        ctxt.dodata(symGroupType)
        bench.Start("address")
index 5ea210c139fc1d66c18b1931672d4ef98de6f011..3759decbebb5cd8a34f0c51d6938a7508730be53 100644 (file)
@@ -19,28 +19,51 @@ import (
        "strings"
 )
 
-// pclnState holds state information used during pclntab generation.
-// Here 'ldr' is just a pointer to the context's loader, 'container'
-// is a bitmap holding whether a given symbol index is an outer or
-// container symbol, 'deferReturnSym' is the index for the symbol
-// "runtime.deferreturn", 'nameToOffset' is a helper function for
-// capturing function names, 'numberedFiles' records the file number
-// assigned to a given file symbol, 'filepaths' is a slice of
-// expanded paths (indexed by file number).
-type pclnState struct {
+// oldPclnState holds state information used during pclntab generation.  Here
+// 'ldr' is just a pointer to the context's loader, 'deferReturnSym' is the
+// index for the symbol "runtime.deferreturn", 'nameToOffset' is a helper
+// function for capturing function names, 'numberedFiles' records the file
+// number assigned to a given file symbol, 'filepaths' is a slice of expanded
+// paths (indexed by file number).
+//
+// NB: This is deprecated, and will be eliminated when pclntab_old is
+// eliminated.
+type oldPclnState struct {
        ldr            *loader.Loader
-       container      loader.Bitmap
        deferReturnSym loader.Sym
        nameToOffset   func(name string) int32
        numberedFiles  map[loader.Sym]int64
        filepaths      []string
 }
 
-func makepclnState(ctxt *Link) pclnState {
+// pclntab holds the state needed for pclntab generation.
+type pclntab struct {
+       // The first and last functions found.
+       firstFunc, lastFunc loader.Sym
+
+       // The offset to the filetab.
+       filetabOffset int32
+
+       // runtime.pclntab's symbols
+       carrier     loader.Sym
+       pclntab     loader.Sym
+       pcheader    loader.Sym
+       findfunctab loader.Sym
+
+       // The number of functions + number of TEXT sections - 1. This is such an
+       // unexpected value because platforms that have more than one TEXT section
+       // get a dummy function inserted between because the external linker can place
+       // functions in those areas. We mark those areas as not covered by the Go
+       // runtime.
+       //
+       // On most platforms this is the number of reachable functions.
+       nfunc int32
+}
+
+func makeOldPclnState(ctxt *Link) *oldPclnState {
        ldr := ctxt.loader
        drs := ldr.Lookup("runtime.deferreturn", sym.SymVerABIInternal)
-       return pclnState{
-               container:      loader.MakeBitmap(ldr.NSym()),
+       state := &oldPclnState{
                ldr:            ldr,
                deferReturnSym: drs,
                numberedFiles:  make(map[loader.Sym]int64),
@@ -49,9 +72,42 @@ func makepclnState(ctxt *Link) pclnState {
                // return a value slot in filepaths.
                filepaths: []string{""},
        }
+
+       return state
+}
+
+// makePclntab makes a pclnState object.
+func makePclntab(ctxt *Link, container loader.Bitmap) *pclntab {
+       ldr := ctxt.loader
+
+       state := &pclntab{}
+
+       // Gather some basic stats and info.
+       prevSect := ldr.SymSect(ctxt.Textp[0])
+       for _, s := range ctxt.Textp {
+               if !emitPcln(ctxt, s, container) {
+                       continue
+               }
+               state.nfunc++
+               if state.firstFunc == 0 {
+                       state.firstFunc = s
+               }
+               state.lastFunc = s
+               ss := ldr.SymSect(s)
+               if ss != prevSect {
+                       // With multiple text sections, the external linker may
+                       // insert functions between the sections, which are not
+                       // known by Go. This leaves holes in the PC range covered
+                       // by the func table. We need to generate an entry to mark
+                       // the hole.
+                       state.nfunc++
+                       prevSect = ss
+               }
+       }
+       return state
 }
 
-func (state *pclnState) ftabaddstring(ftab *loader.SymbolBuilder, s string) int32 {
+func ftabaddstring(ftab *loader.SymbolBuilder, s string) int32 {
        start := len(ftab.Data())
        ftab.Grow(int64(start + len(s) + 1)) // make room for s plus trailing NUL
        ftd := ftab.Data()
@@ -60,7 +116,7 @@ func (state *pclnState) ftabaddstring(ftab *loader.SymbolBuilder, s string) int3
 }
 
 // numberfile assigns a file number to the file if it hasn't been assigned already.
-func (state *pclnState) numberfile(file loader.Sym) int64 {
+func (state *oldPclnState) numberfile(file loader.Sym) int64 {
        if val, ok := state.numberedFiles[file]; ok {
                return val
        }
@@ -72,14 +128,14 @@ func (state *pclnState) numberfile(file loader.Sym) int64 {
        return val
 }
 
-func (state *pclnState) fileVal(file loader.Sym) int64 {
+func (state *oldPclnState) fileVal(file loader.Sym) int64 {
        if val, ok := state.numberedFiles[file]; ok {
                return val
        }
        panic("should have been numbered first")
 }
 
-func (state *pclnState) renumberfiles(ctxt *Link, fi loader.FuncInfo, d *sym.Pcdata) {
+func (state *oldPclnState) renumberfiles(ctxt *Link, fi loader.FuncInfo, d *sym.Pcdata) {
        // Give files numbers.
        nf := fi.NumFile()
        for i := uint32(0); i < nf; i++ {
@@ -146,7 +202,7 @@ func emitPcln(ctxt *Link, s loader.Sym, container loader.Bitmap) bool {
        return !container.Has(s)
 }
 
-func (state *pclnState) computeDeferReturn(target *Target, s loader.Sym) uint32 {
+func (state *oldPclnState) computeDeferReturn(target *Target, s loader.Sym) uint32 {
        deferreturn := uint32(0)
        lastWasmAddr := uint32(0)
 
@@ -194,7 +250,7 @@ func (state *pclnState) computeDeferReturn(target *Target, s loader.Sym) uint32
 
 // genInlTreeSym generates the InlTree sym for a function with the
 // specified FuncInfo.
-func (state *pclnState) genInlTreeSym(fi loader.FuncInfo, arch *sys.Arch) loader.Sym {
+func (state *oldPclnState) genInlTreeSym(fi loader.FuncInfo, arch *sys.Arch) loader.Sym {
        ldr := state.ldr
        its := ldr.CreateExtSym("", 0)
        inlTreeSym := ldr.MakeSymbolUpdater(its)
@@ -229,16 +285,16 @@ func (state *pclnState) genInlTreeSym(fi loader.FuncInfo, arch *sys.Arch) loader
 
 // generatePCHeader creates the runtime.pcheader symbol, setting it up as a
 // generator to fill in its data later.
-func generatePCHeader(ctxt *Link, carrier *loader.SymbolBuilder, pclntabSym loader.Sym) {
+func (state *pclntab) generatePCHeader(ctxt *Link) {
        ldr := ctxt.loader
        writeHeader := func(ctxt *Link, s loader.Sym) {
                ldr := ctxt.loader
                header := ctxt.loader.MakeSymbolUpdater(s)
 
                // Check symbol order.
-               diff := ldr.SymValue(pclntabSym) - ldr.SymValue(s)
+               diff := ldr.SymValue(state.pclntab) - ldr.SymValue(s)
                if diff <= 0 {
-                       panic(fmt.Sprintf("expected runtime.pcheader(%x) to be placed before runtime.pclntab(%x)", ldr.SymValue(s), ldr.SymValue(pclntabSym)))
+                       panic(fmt.Sprintf("expected runtime.pcheader(%x) to be placed before runtime.pclntab(%x)", ldr.SymValue(s), ldr.SymValue(state.pclntab)))
                }
 
                // Write header.
@@ -246,30 +302,21 @@ func generatePCHeader(ctxt *Link, carrier *loader.SymbolBuilder, pclntabSym load
                header.SetUint32(ctxt.Arch, 0, 0xfffffffa)
                header.SetUint8(ctxt.Arch, 6, uint8(ctxt.Arch.MinLC))
                header.SetUint8(ctxt.Arch, 7, uint8(ctxt.Arch.PtrSize))
-               off := header.SetUint(ctxt.Arch, 8, uint64(pclntabNfunc))
+               off := header.SetUint(ctxt.Arch, 8, uint64(state.nfunc))
                header.SetUintptr(ctxt.Arch, off, uintptr(diff))
        }
 
        size := int64(8 + 2*ctxt.Arch.PtrSize)
-       s := ctxt.createGeneratorSymbol("runtime.pcheader", 0, sym.SPCLNTAB, size, writeHeader)
-       ldr.SetAttrReachable(s, true)
-       ldr.SetCarrierSym(s, carrier.Sym())
+       state.pcheader = ctxt.createGeneratorSymbol("runtime.pcheader", 0, sym.SPCLNTAB, size, writeHeader)
+       ldr.SetAttrReachable(state.pcheader, true)
+       ldr.SetCarrierSym(state.pcheader, state.carrier)
 }
 
 // pclntab initializes the pclntab symbol with
 // runtime function and file name information.
 
-// These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab.
-var pclntabNfunc int32
-var pclntabFiletabOffset int32
-var pclntabFirstFunc loader.Sym
-var pclntabLastFunc loader.Sym
-
-// pclntab generates the pcln table for the link output. Return value
-// is a bitmap indexed by global symbol that marks 'container' text
-// symbols, e.g. the set of all symbols X such that Outer(S) = X for
-// some other text symbol S.
-func (ctxt *Link) pclntab() loader.Bitmap {
+// pclntab generates the pcln table for the link output.
+func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
        // Go 1.2's symtab layout is documented in golang.org/s/go12symtab, but the
        // layout and data has changed since that time.
        //
@@ -291,70 +338,39 @@ func (ctxt *Link) pclntab() loader.Bitmap {
        //        func structures, function names, pcdata tables.
        //        filetable
 
+       oldState := makeOldPclnState(ctxt)
+       state := makePclntab(ctxt, container)
+
        ldr := ctxt.loader
-       carrier := ldr.CreateSymForUpdate("runtime.pclntab", 0)
-       carrier.SetType(sym.SPCLNTAB)
-       carrier.SetReachable(true)
+       state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0)
+       ldr.MakeSymbolUpdater(state.carrier).SetType(sym.SPCLNTAB)
+       ldr.SetAttrReachable(state.carrier, true)
 
        // runtime.pclntab_old is just a placeholder,and will eventually be deleted.
        // It contains the pieces of runtime.pclntab that haven't moved to a more
        // ration form.
-       pclntabSym := ldr.LookupOrCreateSym("runtime.pclntab_old", 0)
-       generatePCHeader(ctxt, carrier, pclntabSym)
+       state.pclntab = ldr.LookupOrCreateSym("runtime.pclntab_old", 0)
+       state.generatePCHeader(ctxt)
 
        funcdataBytes := int64(0)
-       ldr.SetCarrierSym(pclntabSym, carrier.Sym())
-       ftab := ldr.MakeSymbolUpdater(pclntabSym)
+       ldr.SetCarrierSym(state.pclntab, state.carrier)
+       ftab := ldr.MakeSymbolUpdater(state.pclntab)
        ftab.SetType(sym.SPCLNTAB)
        ftab.SetReachable(true)
 
-       state := makepclnState(ctxt)
-
-       // Find container symbols and mark them as such.
-       for _, s := range ctxt.Textp {
-               outer := ldr.OuterSym(s)
-               if outer != 0 {
-                       state.container.Set(outer)
-               }
-       }
-
-       // Gather some basic stats and info.
-       var nfunc int32
-       prevSect := ldr.SymSect(ctxt.Textp[0])
-       for _, s := range ctxt.Textp {
-               if !emitPcln(ctxt, s, state.container) {
-                       continue
-               }
-               nfunc++
-               if pclntabFirstFunc == 0 {
-                       pclntabFirstFunc = s
-               }
-               ss := ldr.SymSect(s)
-               if ss != prevSect {
-                       // With multiple text sections, the external linker may
-                       // insert functions between the sections, which are not
-                       // known by Go. This leaves holes in the PC range covered
-                       // by the func table. We need to generate an entry to mark
-                       // the hole.
-                       nfunc++
-                       prevSect = ss
-               }
-       }
-
-       pclntabNfunc = nfunc
-       ftab.Grow(int64(nfunc)*2*int64(ctxt.Arch.PtrSize) + int64(ctxt.Arch.PtrSize) + 4)
+       ftab.Grow(int64(state.nfunc)*2*int64(ctxt.Arch.PtrSize) + int64(ctxt.Arch.PtrSize) + 4)
 
        szHint := len(ctxt.Textp) * 2
        funcnameoff := make(map[string]int32, szHint)
        nameToOffset := func(name string) int32 {
                nameoff, ok := funcnameoff[name]
                if !ok {
-                       nameoff = state.ftabaddstring(ftab, name)
+                       nameoff = ftabaddstring(ftab, name)
                        funcnameoff[name] = nameoff
                }
                return nameoff
        }
-       state.nameToOffset = nameToOffset
+       oldState.nameToOffset = nameToOffset
 
        pctaboff := make(map[string]uint32, szHint)
        writepctab := func(off int32, p []byte) int32 {
@@ -392,10 +408,10 @@ func (ctxt *Link) pclntab() loader.Bitmap {
        funcdata := []loader.Sym{}
        funcdataoff := []int64{}
 
-       nfunc = 0 // repurpose nfunc as a running index
+       var nfunc int32
        prevFunc := ctxt.Textp[0]
        for _, s := range ctxt.Textp {
-               if !emitPcln(ctxt, s, state.container) {
+               if !emitPcln(ctxt, s, container) {
                        continue
                }
 
@@ -489,20 +505,20 @@ func (ctxt *Link) pclntab() loader.Bitmap {
                off = int32(ftab.SetUint32(ctxt.Arch, int64(off), args))
 
                // deferreturn
-               deferreturn := state.computeDeferReturn(&ctxt.Target, s)
+               deferreturn := oldState.computeDeferReturn(&ctxt.Target, s)
                off = int32(ftab.SetUint32(ctxt.Arch, int64(off), deferreturn))
 
                if fi.Valid() {
                        pcsp = sym.Pcdata{P: fi.Pcsp()}
                        pcfile = sym.Pcdata{P: fi.Pcfile()}
                        pcline = sym.Pcdata{P: fi.Pcline()}
-                       state.renumberfiles(ctxt, fi, &pcfile)
+                       oldState.renumberfiles(ctxt, fi, &pcfile)
                        if false {
                                // Sanity check the new numbering
                                it := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
                                for it.Init(pcfile.P); !it.Done; it.Next() {
-                                       if it.Value < 1 || it.Value > int32(len(state.numberedFiles)) {
-                                               ctxt.Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.Value, len(state.numberedFiles))
+                                       if it.Value < 1 || it.Value > int32(len(oldState.numberedFiles)) {
+                                               ctxt.Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.Value, len(oldState.numberedFiles))
                                                errorexit()
                                        }
                                }
@@ -510,7 +526,7 @@ func (ctxt *Link) pclntab() loader.Bitmap {
                }
 
                if fi.Valid() && fi.NumInlTree() > 0 {
-                       its := state.genInlTreeSym(fi, ctxt.Arch)
+                       its := oldState.genInlTreeSym(fi, ctxt.Arch)
                        funcdata[objabi.FUNCDATA_InlTree] = its
                        pcdata[objabi.PCDATA_InlTreeIndex] = sym.Pcdata{P: fi.Pcinline()}
                }
@@ -567,36 +583,34 @@ func (ctxt *Link) pclntab() loader.Bitmap {
                nfunc++
        }
 
-       last := ctxt.Textp[len(ctxt.Textp)-1]
-       pclntabLastFunc = last
        // Final entry of table is just end pc.
-       setAddr(ftab, ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize), last, ldr.SymSize(last))
+       setAddr(ftab, ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize), state.lastFunc, ldr.SymSize(state.lastFunc))
 
        // Start file table.
        dSize := len(ftab.Data())
        start := int32(dSize)
        start += int32(-dSize) & (int32(ctxt.Arch.PtrSize) - 1)
-       pclntabFiletabOffset = start
+       state.filetabOffset = start
        ftab.SetUint32(ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint32(start))
 
-       nf := len(state.numberedFiles)
+       nf := len(oldState.numberedFiles)
        ftab.Grow(int64(start) + int64((nf+1)*4))
        ftab.SetUint32(ctxt.Arch, int64(start), uint32(nf+1))
        for i := nf; i > 0; i-- {
-               path := state.filepaths[i]
+               path := oldState.filepaths[i]
                val := int64(i)
-               ftab.SetUint32(ctxt.Arch, int64(start)+val*4, uint32(state.ftabaddstring(ftab, path)))
+               ftab.SetUint32(ctxt.Arch, int64(start)+val*4, uint32(ftabaddstring(ftab, path)))
        }
 
        ftab.SetSize(int64(len(ftab.Data())))
 
-       ctxt.NumFilesyms = len(state.numberedFiles)
+       ctxt.NumFilesyms = len(oldState.numberedFiles)
 
        if ctxt.Debugvlog != 0 {
                ctxt.Logf("pclntab=%d bytes, funcdata total %d bytes\n", ftab.Size(), funcdataBytes)
        }
 
-       return state.container
+       return state
 }
 
 func gorootFinal() string {
@@ -624,9 +638,7 @@ const (
 
 // findfunctab generates a lookup table to quickly find the containing
 // function for a pc. See src/runtime/symtab.go:findfunc for details.
-// 'container' is a bitmap indexed by global symbol holding whether
-// a given text symbols is a container (outer sym).
-func (ctxt *Link) findfunctab(container loader.Bitmap) {
+func (ctxt *Link) findfunctab(state *pclntab, container loader.Bitmap) {
        ldr := ctxt.loader
 
        // find min and max address
@@ -705,7 +717,22 @@ func (ctxt *Link) findfunctab(container loader.Bitmap) {
                }
        }
 
-       s := ctxt.createGeneratorSymbol("runtime.findfunctab", 0, sym.SRODATA, size, writeFindFuncTab)
-       ldr.SetAttrReachable(s, true)
-       ldr.SetAttrLocal(s, true)
+       state.findfunctab = ctxt.createGeneratorSymbol("runtime.findfunctab", 0, sym.SRODATA, size, writeFindFuncTab)
+       ldr.SetAttrReachable(state.findfunctab, true)
+       ldr.SetAttrLocal(state.findfunctab, true)
+}
+
+// findContainerSyms returns a bitmap, indexed by symbol number, where there's
+// a 1 for every container symbol.
+func (ctxt *Link) findContainerSyms() loader.Bitmap {
+       ldr := ctxt.loader
+       container := loader.MakeBitmap(ldr.NSym())
+       // Find container symbols and mark them as such.
+       for _, s := range ctxt.Textp {
+               outer := ldr.OuterSym(s)
+               if outer != 0 {
+                       container.Set(outer)
+               }
+       }
+       return container
 }
index 15fa162c601eba75838fb0f39c5f6c106101f559..bf8ead3d0cc7ec643b50c160a422a03a8348191c 100644 (file)
@@ -401,7 +401,7 @@ func textsectionmap(ctxt *Link) (loader.Sym, uint32) {
        return t.Sym(), uint32(n)
 }
 
-func (ctxt *Link) symtab() []sym.SymKind {
+func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
        ldr := ctxt.loader
 
        if !ctxt.IsAIX() {
@@ -608,25 +608,24 @@ func (ctxt *Link) symtab() []sym.SymKind {
        // This code uses several global variables that are set by pcln.go:pclntab.
        moduledata := ldr.MakeSymbolUpdater(ctxt.Moduledata)
        // The pcHeader
-       moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.pcheader", 0))
+       moduledata.AddAddr(ctxt.Arch, pcln.pcheader)
        // The pclntab slice
-       pclntab := ldr.Lookup("runtime.pclntab_old", 0)
-       moduledata.AddAddr(ctxt.Arch, pclntab)
-       moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pclntab)))
-       moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pclntab)))
+       moduledata.AddAddr(ctxt.Arch, pcln.pclntab)
+       moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pclntab)))
+       moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pclntab)))
        // The ftab slice
-       moduledata.AddAddr(ctxt.Arch, pclntab)
-       moduledata.AddUint(ctxt.Arch, uint64(pclntabNfunc+1))
-       moduledata.AddUint(ctxt.Arch, uint64(pclntabNfunc+1))
+       moduledata.AddAddr(ctxt.Arch, pcln.pclntab)
+       moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1))
+       moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1))
        // The filetab slice
-       moduledata.AddAddrPlus(ctxt.Arch, pclntab, int64(pclntabFiletabOffset))
+       moduledata.AddAddrPlus(ctxt.Arch, pcln.pclntab, int64(pcln.filetabOffset))
        moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1)
        moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1)
        // findfunctab
-       moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.findfunctab", 0))
+       moduledata.AddAddr(ctxt.Arch, pcln.findfunctab)
        // minpc, maxpc
-       moduledata.AddAddr(ctxt.Arch, pclntabFirstFunc)
-       moduledata.AddAddrPlus(ctxt.Arch, pclntabLastFunc, ldr.SymSize(pclntabLastFunc))
+       moduledata.AddAddr(ctxt.Arch, pcln.firstFunc)
+       moduledata.AddAddrPlus(ctxt.Arch, pcln.lastFunc, ldr.SymSize(pcln.lastFunc))
        // pointers to specific parts of the module
        moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.text", 0))
        moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.etext", 0))