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
 
        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 {
        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 {
        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))
+}
 
                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")
 
                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)
 
                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:"
 
                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
                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 {
 
        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 {