diff --git a/src/cmd/internal/ld/decodesym.go b/src/cmd/internal/ld/decodesym.go
index 4bad549fd4946ebb1525629f1850e1b5765f0ef4..19d2502c7a39c141d71a23372ee1e56157a86b18 100644
--- a/src/cmd/internal/ld/decodesym.go
+++ b/src/cmd/internal/ld/decodesym.go
@@ -67,10 +67,20 @@ func decodetype_size(s *LSym) int64 {
 
 // Type.commonType.gc
 func decodetype_gcprog(s *LSym) *LSym {
+	if s.Type == obj.SDYNIMPORT {
+		// The gcprog for "type.$name" is calle "type..gcprog.$name".
+		x := "type..gcprog." + s.Name[5:]
+		return Linklookup(Ctxt, x, 0)
+	}
 	return decode_reloc_sym(s, 1*int32(Thearch.Ptrsize)+8+2*int32(Thearch.Ptrsize))
 }
 
 func decodetype_gcmask(s *LSym) []byte {
+	if s.Type == obj.SDYNIMPORT {
+		// ldshlibsyms makes special efforts to read the value
+		// of gcmask for types defined in that shared library.
+		return s.gcmask
+	}
 	mask := decode_reloc_sym(s, 1*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize))
 	return mask.P
 }
diff --git a/src/cmd/internal/ld/lib.go b/src/cmd/internal/ld/lib.go
index 3d466e5e184d8146740c0fbe3382501ffd16b53b..3f7b04fbbb2501599a5880fafde9c29853a499db 100644
--- a/src/cmd/internal/ld/lib.go
+++ b/src/cmd/internal/ld/lib.go
@@ -1136,11 +1136,23 @@ func ldshlibsyms(shlib string) {
 		return
 	}
 	defer f.Close()
-	syms, err := f.DynamicSymbols()
+	syms, err := f.Symbols()
 	if err != nil {
 		Diag("cannot read symbols from shared library: %s", libpath)
 		return
 	}
+	// If a package has a global variable of a type defined in another shared
+	// library, we need to know the gcmask used by the type, if any.  To support
+	// this, we read all the runtime.gcbits.* symbols, keep a map of address to
+	// gcmask, and after we're read all the symbols, read the addresses of the
+	// gcmasks symbols out of the type data to look up the gcmask for each type.
+	// This depends on the fact that the runtime.gcbits.* symbols are local (so
+	// the address is actually present in the type data and we don't have to
+	// search all relocations to find the ones which correspond to gcmasks) and
+	// also that the shared library we are linking against has not had the symbol
+	// table removed.
+	gcmasks := make(map[uint64][]byte)
+	types := []*LSym{}
 	for _, s := range syms {
 		if elf.ST_TYPE(s.Info) == elf.STT_NOTYPE || elf.ST_TYPE(s.Info) == elf.STT_SECTION {
 			continue
@@ -1151,6 +1163,20 @@ func ldshlibsyms(shlib string) {
 		if strings.HasPrefix(s.Name, "_") {
 			continue
 		}
+		if strings.HasPrefix(s.Name, "runtime.gcbits.0x") {
+			data := make([]byte, s.Size)
+			sect := f.Sections[s.Section]
+			if sect.Type == elf.SHT_PROGBITS {
+				n, err := sect.ReadAt(data, int64(s.Value-sect.Offset))
+				if uint64(n) != s.Size {
+					Diag("Error reading contents of %s: %v", s.Name, err)
+				}
+			}
+			gcmasks[s.Value] = data
+		}
+		if elf.ST_BIND(s.Info) != elf.STB_GLOBAL {
+			continue
+		}
 		lsym := Linklookup(Ctxt, s.Name, 0)
 		if lsym.Type != 0 && lsym.Dupok == 0 {
 			Diag(
@@ -1159,6 +1185,35 @@ func ldshlibsyms(shlib string) {
 		}
 		lsym.Type = obj.SDYNIMPORT
 		lsym.File = libpath
+		if strings.HasPrefix(lsym.Name, "type.") {
+			data := make([]byte, s.Size)
+			sect := f.Sections[s.Section]
+			if sect.Type == elf.SHT_PROGBITS {
+				n, err := sect.ReadAt(data, int64(s.Value-sect.Offset))
+				if uint64(n) != s.Size {
+					Diag("Error reading contents of %s: %v", s.Name, err)
+				}
+				lsym.P = data
+			}
+			if !strings.HasPrefix(lsym.Name, "type..") {
+				types = append(types, lsym)
+			}
+		}
+	}
+
+	for _, t := range types {
+		if decodetype_noptr(t) != 0 || decodetype_usegcprog(t) != 0 {
+			continue
+		}
+		// The expression on the next line is a copy of the expression from
+		// decodetype_gcmask in decodesym.go, which in turn depends on details of
+		// how the type data is laid out, as seen in gc/reflect.go:dcommontype.
+		addr := decode_inuxi(t.P[1*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize):], Thearch.Ptrsize)
+		tgcmask, ok := gcmasks[addr]
+		if !ok {
+			Diag("bits not found for %s at %d", t.Name, addr)
+		}
+		t.gcmask = tgcmask
 	}
 
 	// We might have overwritten some functions above (this tends to happen for the
diff --git a/src/cmd/internal/ld/link.go b/src/cmd/internal/ld/link.go
index fd480733f7dfaa88353f3a6a1fbb3e967e0dcb4d..a92ab59499e81a2cc0426d70065281a169312445 100644
--- a/src/cmd/internal/ld/link.go
+++ b/src/cmd/internal/ld/link.go
@@ -75,6 +75,7 @@ type LSym struct {
 	P           []byte
 	R           []Reloc
 	Local       bool
+	gcmask      []byte
 }
 
 type Reloc struct {
diff --git a/src/cmd/internal/ld/objfile.go b/src/cmd/internal/ld/objfile.go
index c424cdca8c2533b1908e56c5ad9e3d3a17bf6d71..1e45d72fd8b009c84819e9f6a96b061425147b44 100644
--- a/src/cmd/internal/ld/objfile.go
+++ b/src/cmd/internal/ld/objfile.go
@@ -340,6 +340,8 @@ func rdsym(ctxt *Link, f *Biobuf, pkg string) *LSym {
 			s.Reachable = false
 		}
 	}
-
+	if v == 0 && strings.HasPrefix(s.Name, "runtime.gcbits.0x") {
+		s.Local = true
+	}
 	return s
 }