]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: use types (and not GC programs) to build data/bss ptrmasks
authorkhr@golang.org <khr@golang.org>
Thu, 30 Nov 2023 06:03:22 +0000 (22:03 -0800)
committerKeith Randall <khr@golang.org>
Mon, 18 Nov 2024 20:36:04 +0000 (20:36 +0000)
The linker knows the types of the global variables. We can use those
types to build the GC programs that describe the data and bss pointer masks.

That way we don't use the GC programs of the constituent types.
This is part of an effort to remove GC programs from the runtime.

There's a major complication in that when we're linking against a
shared library (typically, libstd.so), the relocations we need to
break apart arrays and structs into constituent types are difficult to
find. Load that additional data when linking against shared libraries.

Change-Id: I8516b24a0604479895c7b8a8a358d3cd8d421530
Reviewed-on: https://go-review.googlesource.com/c/go/+/546216
Reviewed-by: Keith Randall <khr@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
src/cmd/link/internal/ld/data.go
src/cmd/link/internal/ld/decodesym.go
src/cmd/link/internal/ld/dwarf.go
src/cmd/link/internal/ld/lib.go
src/cmd/link/internal/ld/link.go

index a23e87d326d5c767d2abdc3f27c0a727f13c9fac..5f05399b5e72824786007c1165d237a9693c96cf 100644 (file)
@@ -1346,30 +1346,56 @@ func (p *GCProg) AddSym(s loader.Sym) {
                return
        }
 
-       ptrsize := int64(p.ctxt.Arch.PtrSize)
-       typData := ldr.Data(typ)
-       nptr := decodetypePtrdata(p.ctxt.Arch, typData) / ptrsize
-
        if debugGCProg {
-               fmt.Fprintf(os.Stderr, "gcprog sym: %s at %d (ptr=%d+%d)\n", ldr.SymName(s), ldr.SymValue(s), ldr.SymValue(s)/ptrsize, nptr)
+               fmt.Fprintf(os.Stderr, "gcprog sym: %s at %d (ptr=%d)\n", ldr.SymName(s), ldr.SymValue(s), ldr.SymValue(s)/int64(p.ctxt.Arch.PtrSize))
        }
 
        sval := ldr.SymValue(s)
-       if !decodetypeUsegcprog(p.ctxt.Arch, typData) {
+       p.AddType(sval, typ)
+}
+
+// Add to the gc program the ptr bits for the type typ at
+// byte offset off in the region being described.
+// The type must have a pointer in it.
+func (p *GCProg) AddType(off int64, typ loader.Sym) {
+       ldr := p.ctxt.loader
+       typData := ldr.Data(typ)
+       switch decodetypeKind(p.ctxt.Arch, typData) {
+       default:
+               if decodetypeUsegcprog(p.ctxt.Arch, typData) {
+                       p.ctxt.Errorf(p.sym.Sym(), "GC program for non-aggregate type")
+               }
                // Copy pointers from mask into program.
+               ptrsize := int64(p.ctxt.Arch.PtrSize)
+               ptrdata := decodetypePtrdata(p.ctxt.Arch, typData)
                mask := decodetypeGcmask(p.ctxt, typ)
-               for i := int64(0); i < nptr; i++ {
+               for i := int64(0); i < ptrdata/ptrsize; i++ {
                        if (mask[i/8]>>uint(i%8))&1 != 0 {
-                               p.w.Ptr(sval/ptrsize + i)
+                               p.w.Ptr(off/ptrsize + i)
+                       }
+               }
+       case abi.Array:
+               elem := decodetypeArrayElem(p.ctxt, p.ctxt.Arch, typ)
+               n := decodetypeArrayLen(ldr, p.ctxt.Arch, typ)
+               p.AddType(off, elem)
+               if n > 1 {
+                       // Issue repeat for subsequent n-1 instances.
+                       elemSize := decodetypeSize(p.ctxt.Arch, ldr.Data(elem))
+                       ptrsize := int64(p.ctxt.Arch.PtrSize)
+                       p.w.ZeroUntil((off + elemSize) / ptrsize)
+                       p.w.Repeat(elemSize/ptrsize, n-1)
+               }
+       case abi.Struct:
+               nField := decodetypeStructFieldCount(ldr, p.ctxt.Arch, typ)
+               for i := 0; i < nField; i++ {
+                       fTyp := decodetypeStructFieldType(p.ctxt, p.ctxt.Arch, typ, i)
+                       if decodetypePtrdata(p.ctxt.Arch, ldr.Data(fTyp)) == 0 {
+                               continue
                        }
+                       fOff := decodetypeStructFieldOffset(ldr, p.ctxt.Arch, typ, i)
+                       p.AddType(off+fOff, fTyp)
                }
-               return
        }
-
-       // Copy program.
-       prog := decodetypeGcprog(p.ctxt, typ)
-       p.w.ZeroUntil(sval / ptrsize)
-       p.w.Append(prog[4:], nptr)
 }
 
 // cutoff is the maximum data section size permitted by the linker
index 9bce4a7a1274511a3a31997505c95a5244d5bbd9..22d1fd02e254c28b3f85ae7a1e4133651ca124f6 100644 (file)
@@ -134,9 +134,8 @@ func decodetypeFuncOutType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym
        return decodetypeFuncInType(ldr, arch, symIdx, relocs, i+decodetypeFuncInCount(arch, ldr.Data(symIdx)))
 }
 
-func decodetypeArrayElem(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
-       relocs := ldr.Relocs(symIdx)
-       return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30
+func decodetypeArrayElem(ctxt *Link, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+       return decodeTargetSym(ctxt, arch, symIdx, int64(commonsize(arch))) // 0x1c / 0x30
 }
 
 func decodetypeArrayLen(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) int64 {
@@ -190,10 +189,10 @@ func decodetypeStructFieldName(ldr *loader.Loader, arch *sys.Arch, symIdx loader
        return decodetypeName(ldr, symIdx, &relocs, off)
 }
 
-func decodetypeStructFieldType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) loader.Sym {
+func decodetypeStructFieldType(ctxt *Link, arch *sys.Arch, symIdx loader.Sym, i int) loader.Sym {
+       ldr := ctxt.loader
        off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i)
-       relocs := ldr.Relocs(symIdx)
-       return decodeRelocSym(ldr, symIdx, &relocs, int32(off+arch.PtrSize))
+       return decodeTargetSym(ctxt, arch, symIdx, int64(off+arch.PtrSize))
 }
 
 func decodetypeStructFieldOffset(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int64 {
@@ -297,3 +296,36 @@ func decodeItabType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loade
        relocs := ldr.Relocs(symIdx)
        return decodeRelocSym(ldr, symIdx, &relocs, int32(abi.ITabTypeOff(arch.PtrSize)))
 }
+
+// decodeTargetSym finds the symbol pointed to by the pointer slot at offset off in s.
+func decodeTargetSym(ctxt *Link, arch *sys.Arch, s loader.Sym, off int64) loader.Sym {
+       ldr := ctxt.loader
+       if ldr.SymType(s) == sym.SDYNIMPORT {
+               // In this case, relocations are not associated with a
+               // particular symbol. Instead, they are all listed together
+               // in the containing shared library. Find the relocation
+               // in that shared library record.
+               name := ldr.SymName(s)
+               for _, sh := range ctxt.Shlibs {
+                       addr, ok := sh.symAddr[name]
+                       if !ok {
+                               continue
+                       }
+                       addr += uint64(off)
+                       target := sh.relocTarget[addr]
+                       if target == "" {
+                               Exitf("can't find relocation in %s at offset %d", name, off)
+                       }
+                       t := ldr.Lookup(target, 0)
+                       if t == 0 {
+                               Exitf("can't find target of relocation in %s at offset %d: %s", name, off, target)
+                       }
+                       return t
+               }
+       }
+
+       // For the normal case, just find the relocation within the symbol that
+       // lives at the requested offset.
+       relocs := ldr.Relocs(s)
+       return decodeRelocSym(ldr, s, &relocs, int32(off))
+}
index 7599b937ff02d7ab88d93e34a9b9dcbdae2a5b45..14c0b687d853a32fea3b95a5c5db347aa9b02ea5 100644 (file)
@@ -584,7 +584,7 @@ func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie {
                die = d.newdie(&dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name)
                typedefdie = d.dotypedef(&dwtypes, name, die)
                newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
-               s := decodetypeArrayElem(d.ldr, d.arch, gotype)
+               s := decodetypeArrayElem(d.linkctxt, d.arch, gotype)
                d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s))
                fld := d.newdie(die, dwarf.DW_ABRV_ARRAYRANGE, "range")
 
@@ -661,7 +661,7 @@ func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie {
                die = d.newdie(&dwtypes, dwarf.DW_ABRV_SLICETYPE, name)
                typedefdie = d.dotypedef(&dwtypes, name, die)
                newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
-               s := decodetypeArrayElem(d.ldr, d.arch, gotype)
+               s := decodetypeArrayElem(d.linkctxt, d.arch, gotype)
                elem := d.defgotype(s)
                d.newrefattr(die, dwarf.DW_AT_go_elem, elem)
 
@@ -676,7 +676,7 @@ func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie {
                nfields := decodetypeStructFieldCount(d.ldr, d.arch, gotype)
                for i := 0; i < nfields; i++ {
                        f := decodetypeStructFieldName(d.ldr, d.arch, gotype, i)
-                       s := decodetypeStructFieldType(d.ldr, d.arch, gotype, i)
+                       s := decodetypeStructFieldType(d.linkctxt, d.arch, gotype, i)
                        if f == "" {
                                sn := d.ldr.SymName(s)
                                f = sn[5:] // skip "type:"
index 46585479dab0cca442faee2a7c179a89c4834fd5..48bdf73b3ba2778f069587c7733985c958afb81a 100644 (file)
@@ -2622,6 +2622,7 @@ func ldshlibsyms(ctxt *Link, shlib string) {
                return
        }
 
+       symAddr := map[string]uint64{}
        for _, elfsym := range syms {
                if elf.ST_TYPE(elfsym.Info) == elf.STT_NOTYPE || elf.ST_TYPE(elfsym.Info) == elf.STT_SECTION {
                        continue
@@ -2673,8 +2674,82 @@ func ldshlibsyms(ctxt *Link, shlib string) {
                if symname != elfsym.Name {
                        l.SetSymExtname(s, elfsym.Name)
                }
+               symAddr[elfsym.Name] = elfsym.Value
        }
-       ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f})
+
+       // Load relocations.
+       // We only really need these for grokking the links between type descriptors
+       // when dynamic linking.
+       relocTarget := map[uint64]string{}
+       addends := false
+       sect := f.SectionByType(elf.SHT_REL)
+       if sect == nil {
+               sect = f.SectionByType(elf.SHT_RELA)
+               if sect == nil {
+                       log.Fatalf("can't find SHT_REL or SHT_RELA section of %s", shlib)
+               }
+               addends = true
+       }
+       // TODO: Multiple SHT_RELA/SHT_REL sections?
+       data, err := sect.Data()
+       if err != nil {
+               log.Fatalf("can't read relocation section of %s: %v", shlib, err)
+       }
+       bo := f.ByteOrder
+       for len(data) > 0 {
+               var off, idx uint64
+               var addend int64
+               switch f.Class {
+               case elf.ELFCLASS64:
+                       off = bo.Uint64(data)
+                       info := bo.Uint64(data[8:])
+                       data = data[16:]
+                       if addends {
+                               addend = int64(bo.Uint64(data))
+                               data = data[8:]
+                       }
+
+                       idx = info >> 32
+                       typ := info & 0xffff
+                       // buildmode=shared is only supported for amd64,arm64,loong64,s390x,ppc64le.
+                       // (List found by looking at the translation of R_ADDR by ../$ARCH/asm.go:elfreloc1)
+                       switch typ {
+                       case uint64(elf.R_X86_64_64):
+                       case uint64(elf.R_AARCH64_ABS64):
+                       case uint64(elf.R_LARCH_64):
+                       case uint64(elf.R_390_64):
+                       case uint64(elf.R_PPC64_ADDR64):
+                       default:
+                               continue
+                       }
+               case elf.ELFCLASS32:
+                       off = uint64(bo.Uint32(data))
+                       info := bo.Uint32(data[4:])
+                       data = data[8:]
+                       if addends {
+                               addend = int64(int32(bo.Uint32(data)))
+                               data = data[4:]
+                       }
+
+                       idx = uint64(info >> 8)
+                       typ := info & 0xff
+                       // buildmode=shared is only supported for 386,arm.
+                       switch typ {
+                       case uint32(elf.R_386_32):
+                       case uint32(elf.R_ARM_ABS32):
+                       default:
+                               continue
+                       }
+               default:
+                       log.Fatalf("unknown bit size %s", f.Class)
+               }
+               if addend != 0 {
+                       continue
+               }
+               relocTarget[off] = syms[idx-1].Name
+       }
+
+       ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f, symAddr: symAddr, relocTarget: relocTarget})
 }
 
 func addsection(ldr *loader.Loader, arch *sys.Arch, seg *sym.Segment, name string, rwx int) *sym.Section {
index 34221dfa8a764d30642307335d4f1df591562c07..df1fc7feabac302a8440833c878ee0c5bdac5fff 100644 (file)
@@ -44,8 +44,32 @@ type Shlib struct {
        Hash []byte
        Deps []string
        File *elf.File
+       // For every symbol defined in the shared library, record its address
+       // in the original shared library address space.
+       symAddr map[string]uint64
+       // For relocations in the shared library, map from the address
+       // (in the shared library address space) at which that
+       // relocation applies to the target symbol.  We only keep
+       // track of a single kind of relocation: a standard absolute
+       // address relocation with no addend. These were R_ADDR
+       // relocations when the shared library was built.
+       relocTarget map[uint64]string
 }
 
+// A relocation that applies to part of the shared library.
+type shlibReloc struct {
+       // Address (in the shared library address space) the relocation applies to.
+       addr uint64
+       // Target symbol name.
+       target string
+}
+
+type shlibRelocs []shlibReloc
+
+func (s shlibRelocs) Len() int           { return len(s) }
+func (s shlibRelocs) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
+func (s shlibRelocs) Less(i, j int) bool { return s[i].addr < s[j].addr }
+
 // Link holds the context for writing object code from a compiler
 // or for reading that input into the linker.
 type Link struct {