cmd/link: load symbols from .syso in external link mode

Fix linking with a package having a .syso file in external link mode,
that would otherwise cause an error before executing the external
linker because it can't find symbols that are exported in the said
.syso file.

Fixes #33139

Change-Id: Id3ee737fba1c6f1e37910593dfedf9c84486d398
Reviewed-on: https://go-review.googlesource.com/c/go/+/186417
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/src/cmd/go/testdata/script/link_syso_issue33139.txt b/src/cmd/go/testdata/script/link_syso_issue33139.txt
new file mode 100644
index 0000000..53587e6
--- /dev/null
+++ b/src/cmd/go/testdata/script/link_syso_issue33139.txt
@@ -0,0 +1,29 @@
+# Test that we can use the external linker with a host syso file that is
+# embedded in a package, that is referenced by a Go assembly stub.
+# See issue 33139.
+[!gc] stop
+cc -c -o syso/objTestImpl.syso syso/src/objTestImpl.c
+go build -ldflags='-linkmode=external' ./cmd/main.go
+
+-- syso/objTest.s --
+#include "textflag.h"
+
+TEXT ·ObjTest(SB), NOSPLIT, $0
+	JMP objTestImpl(SB)
+
+-- syso/pkg.go --
+package syso
+
+func ObjTest()
+
+-- syso/src/objTestImpl.c --
+void objTestImpl() { /* Empty */ }
+
+-- cmd/main.go --
+package main
+
+import "syso"
+
+func main() {
+	syso.ObjTest()
+}
diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go
index 43d387c..41efd93 100644
--- a/src/cmd/link/internal/arm/asm.go
+++ b/src/cmd/link/internal/arm/asm.go
@@ -613,7 +613,7 @@
 				rs = rs.Outer
 			}
 
-			if rs.Type != sym.SHOSTOBJ && rs.Type != sym.SDYNIMPORT && rs.Sect == nil {
+			if rs.Type != sym.SHOSTOBJ && rs.Type != sym.SDYNIMPORT && rs.Type != sym.SUNDEFEXT && rs.Sect == nil {
 				ld.Errorf(s, "missing section for %s", rs.Name)
 			}
 			r.Xsym = rs
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index 2266d30..ea67483 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -96,7 +96,7 @@
 		if !r.Type.IsDirectJump() {
 			continue
 		}
-		if Symaddr(r.Sym) == 0 && r.Sym.Type != sym.SDYNIMPORT {
+		if Symaddr(r.Sym) == 0 && (r.Sym.Type != sym.SDYNIMPORT && r.Sym.Type != sym.SUNDEFEXT) {
 			if r.Sym.File != s.File {
 				if !isRuntimeDepPkg(s.File) || !isRuntimeDepPkg(r.Sym.File) {
 					ctxt.ErrorUnresolved(s, r)
@@ -418,6 +418,17 @@
 			}
 			fallthrough
 		case objabi.R_CALL, objabi.R_PCREL:
+			if ctxt.LinkMode == LinkExternal && r.Sym != nil && r.Sym.Type == sym.SUNDEFEXT {
+				// pass through to the external linker.
+				r.Done = false
+				r.Xadd = 0
+				if ctxt.IsELF {
+					r.Xadd -= int64(r.Siz)
+				}
+				r.Xsym = r.Sym
+				o = 0
+				break
+			}
 			if ctxt.LinkMode == LinkExternal && r.Sym != nil && r.Sym.Type != sym.SCONST && (r.Sym.Sect != s.Sect || r.Type == objabi.R_GOTPCREL) {
 				r.Done = false
 
diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go
index 80d7ac3..37adeb7 100644
--- a/src/cmd/link/internal/ld/go.go
+++ b/src/cmd/link/internal/ld/go.go
@@ -334,6 +334,24 @@
 }
 
 func (ctxt *Link) addexport() {
+	// Track undefined external symbols during external link.
+	if ctxt.LinkMode == LinkExternal {
+		for _, s := range ctxt.Syms.Allsym {
+			if !s.Attr.Reachable() || s.Attr.Special() || s.Attr.SubSymbol() {
+				continue
+			}
+			if s.Type != sym.STEXT {
+				continue
+			}
+			for i := range s.R {
+				r := &s.R[i]
+				if r.Sym != nil && r.Sym.Type == sym.Sxxx {
+					r.Sym.Type = sym.SUNDEFEXT
+				}
+			}
+		}
+	}
+
 	// TODO(aix)
 	if ctxt.HeadType == objabi.Hdarwin || ctxt.HeadType == objabi.Haix {
 		return
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 182e5b0..98c5e6c 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -2348,7 +2348,7 @@
 			}
 			put(ctxt, s, s.Name, BSSSym, Symaddr(s), s.Gotype)
 
-		case sym.SHOSTOBJ:
+		case sym.SHOSTOBJ, sym.SUNDEFEXT:
 			if ctxt.HeadType == objabi.Hwindows || ctxt.IsELF {
 				put(ctxt, s, s.Name, UndefinedSym, s.Value, nil)
 			}
diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
index 7453f37..e9e4876 100644
--- a/src/cmd/link/internal/ld/macho.go
+++ b/src/cmd/link/internal/ld/macho.go
@@ -809,7 +809,7 @@
 			}
 		}
 
-		if s.Type == sym.SDYNIMPORT || s.Type == sym.SHOSTOBJ {
+		if s.Type == sym.SDYNIMPORT || s.Type == sym.SHOSTOBJ || s.Type == sym.SUNDEFEXT {
 			if s.Attr.Reachable() {
 				addsym(ctxt, s, "", DataSym, 0, nil)
 			}
@@ -886,7 +886,7 @@
 		// replace "·" as ".", because DTrace cannot handle it.
 		Addstring(symstr, strings.Replace(s.Extname(), "·", ".", -1))
 
-		if s.Type == sym.SDYNIMPORT || s.Type == sym.SHOSTOBJ {
+		if s.Type == sym.SDYNIMPORT || s.Type == sym.SHOSTOBJ || s.Type == sym.SUNDEFEXT {
 			symtab.AddUint8(0x01)                             // type N_EXT, external symbol
 			symtab.AddUint8(0)                                // no section
 			symtab.AddUint16(ctxt.Arch, 0)                    // desc
diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go
index 12363626..6d4674d 100644
--- a/src/cmd/link/internal/ld/pe.go
+++ b/src/cmd/link/internal/ld/pe.go
@@ -685,7 +685,7 @@
 		// Only windows/386 requires underscore prefix on external symbols.
 		if ctxt.Arch.Family == sys.I386 &&
 			ctxt.LinkMode == LinkExternal &&
-			(s.Type == sym.SHOSTOBJ || s.Attr.CgoExport()) {
+			(s.Type == sym.SHOSTOBJ || s.Type == sym.SUNDEFEXT || s.Attr.CgoExport()) {
 			s.Name = "_" + s.Name
 		}
 
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index d686a8a..4925eda 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -110,7 +110,7 @@
 	}
 
 	var elfshnum int
-	if xo.Type == sym.SDYNIMPORT || xo.Type == sym.SHOSTOBJ {
+	if xo.Type == sym.SDYNIMPORT || xo.Type == sym.SHOSTOBJ || xo.Type == sym.SUNDEFEXT {
 		elfshnum = SHN_UNDEF
 	} else {
 		if xo.Sect == nil {
diff --git a/src/cmd/link/internal/sym/symkind.go b/src/cmd/link/internal/sym/symkind.go
index 4e44d3f..5309e07 100644
--- a/src/cmd/link/internal/sym/symkind.go
+++ b/src/cmd/link/internal/sym/symkind.go
@@ -104,6 +104,7 @@
 	SCONST
 	SDYNIMPORT
 	SHOSTOBJ
+	SUNDEFEXT // Undefined symbol for resolution by external linker
 
 	// Sections for debugging information
 	SDWARFSECT
diff --git a/src/cmd/link/internal/sym/symkind_string.go b/src/cmd/link/internal/sym/symkind_string.go
index 2732ec7..e48d90c 100644
--- a/src/cmd/link/internal/sym/symkind_string.go
+++ b/src/cmd/link/internal/sym/symkind_string.go
@@ -1,4 +1,4 @@
-// Code generated by "stringer -type=SymKind"; DO NOT EDIT.
+// Code generated by "stringer -type=SymKind symkind.go"; DO NOT EDIT.
 
 package sym
 
@@ -54,17 +54,18 @@
 	_ = x[SCONST-43]
 	_ = x[SDYNIMPORT-44]
 	_ = x[SHOSTOBJ-45]
-	_ = x[SDWARFSECT-46]
-	_ = x[SDWARFINFO-47]
-	_ = x[SDWARFRANGE-48]
-	_ = x[SDWARFLOC-49]
-	_ = x[SDWARFLINES-50]
-	_ = x[SABIALIAS-51]
+	_ = x[SUNDEFEXT-46]
+	_ = x[SDWARFSECT-47]
+	_ = x[SDWARFINFO-48]
+	_ = x[SDWARFRANGE-49]
+	_ = x[SDWARFLOC-50]
+	_ = x[SDWARFLINES-51]
+	_ = x[SABIALIAS-52]
 }
 
-const _SymKind_name = "SxxxSTEXTSELFRXSECTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSMACHOPLTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSFirstWritableSBUILDINFOSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASXCOFFTOCSBSSSNOPTRBSSSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILEPATHSCONSTSDYNIMPORTSHOSTOBJSDWARFSECTSDWARFINFOSDWARFRANGESDWARFLOCSDWARFLINESSABIALIAS"
+const _SymKind_name = "SxxxSTEXTSELFRXSECTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSMACHOPLTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSFirstWritableSBUILDINFOSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASXCOFFTOCSBSSSNOPTRBSSSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILEPATHSCONSTSDYNIMPORTSHOSTOBJSUNDEFEXTSDWARFSECTSDWARFINFOSDWARFRANGESDWARFLOCSDWARFLINESSABIALIAS"
 
-var _SymKind_index = [...]uint16{0, 4, 9, 19, 24, 31, 40, 47, 54, 61, 69, 79, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 220, 230, 238, 244, 253, 261, 268, 278, 286, 291, 300, 304, 313, 320, 325, 337, 349, 366, 383, 392, 398, 408, 416, 426, 436, 447, 456, 467, 476}
+var _SymKind_index = [...]uint16{0, 4, 9, 19, 24, 31, 40, 47, 54, 61, 69, 79, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 220, 230, 238, 244, 253, 261, 268, 278, 286, 291, 300, 304, 313, 320, 325, 337, 349, 366, 383, 392, 398, 408, 416, 425, 435, 445, 456, 465, 476, 485}
 
 func (i SymKind) String() string {
 	if i >= SymKind(len(_SymKind_index)-1) {