fix(asm): emit NOPTR data as its own symbol kind
Assisted-by: GLM 5.3
This commit is contained in:
+14
-65
@@ -178,26 +178,10 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("baseline build: %v\n%s", err, buildLog)
|
t.Fatalf("baseline build: %v\n%s", err, buildLog)
|
||||||
}
|
}
|
||||||
var work, linkLine, asmObj string
|
st := parseBuildLog(t, buildLog, "main_arm64.s")
|
||||||
for line := range strings.SplitSeq(string(buildLog), "\n") {
|
defer os.RemoveAll(st.work)
|
||||||
switch {
|
|
||||||
case strings.HasPrefix(line, "WORK="):
|
|
||||||
work = strings.TrimPrefix(line, "WORK=")
|
|
||||||
case strings.Contains(line, "/asm ") && strings.Contains(line, "main_arm64.s") && !strings.Contains(line, "-gensymabis"):
|
|
||||||
asmObj = fieldAfter(line, "-o")
|
|
||||||
case strings.Contains(line, "/link ") && strings.Contains(line, "-importcfg"):
|
|
||||||
linkLine = line
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if work == "" || asmObj == "" {
|
|
||||||
t.Skipf("could not parse build log (work=%q asmObj=%q)", work, asmObj)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(work)
|
|
||||||
|
|
||||||
// Expand $WORK in the object path.
|
// Assemble the same source with gasm and substitute the object.
|
||||||
asmObj = strings.ReplaceAll(asmObj, "$WORK", work)
|
|
||||||
|
|
||||||
// Read the toolchain-produced object and assemble the same source with gasm.
|
|
||||||
src, err := os.ReadFile(filepath.Join(dir, "main_arm64.s"))
|
src, err := os.ReadFile(filepath.Join(dir, "main_arm64.s"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -210,30 +194,16 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("AssembleFileARM64: %v", err)
|
t.Fatalf("AssembleFileARM64: %v", err)
|
||||||
}
|
}
|
||||||
gasmObj, err := img.GOObjectAARCH64("a64link", "main_arm64.s")
|
// The package path is "main", the prefix the Go code's references carry.
|
||||||
|
gasmObj, err := img.GOObjectAARCH64("main", "main_arm64.s")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("GOObjectAARCH64: %v", err)
|
t.Fatalf("GOObjectAARCH64: %v", err)
|
||||||
}
|
}
|
||||||
|
substituteAndRelink(t, goBin, dir, st, filepath.Join(dir, "prog2"),
|
||||||
// Replace the toolchain-produced object with gasm's.
|
gasmObj, "GOARCH=arm64")
|
||||||
if err := os.WriteFile(asmObj, gasmObj, 0o644); err != nil {
|
|
||||||
t.Fatalf("write gasm object: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-link.
|
|
||||||
if linkLine == "" {
|
|
||||||
t.Skip("could not find link command in build log")
|
|
||||||
}
|
|
||||||
// Expand $WORK in the link command.
|
|
||||||
linkLine = strings.ReplaceAll(linkLine, "$WORK", work)
|
|
||||||
linkCmd := exec.Command("bash", "-c", "cd "+dir+" && "+linkLine)
|
|
||||||
linkCmd.Env = append(os.Environ(), "GOARCH=arm64")
|
|
||||||
if out, err := linkCmd.CombinedOutput(); err != nil {
|
|
||||||
t.Fatalf("re-link with gasm object: %v\n%s", err, out)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify the binary exists and contains the symbol.
|
// Verify the binary exists and contains the symbol.
|
||||||
binPath := filepath.Join(dir, "prog")
|
binPath := filepath.Join(dir, "prog2")
|
||||||
if _, err := os.Stat(binPath); err != nil {
|
if _, err := os.Stat(binPath); err != nil {
|
||||||
t.Fatalf("binary not found: %v", err)
|
t.Fatalf("binary not found: %v", err)
|
||||||
}
|
}
|
||||||
@@ -296,23 +266,8 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("baseline build: %v\n%s", err, buildLog)
|
t.Fatalf("baseline build: %v\n%s", err, buildLog)
|
||||||
}
|
}
|
||||||
var work, linkLine, asmObj string
|
st := parseBuildLog(t, buildLog, "main_arm64.s")
|
||||||
for line := range strings.SplitSeq(string(buildLog), "\n") {
|
defer os.RemoveAll(st.work)
|
||||||
switch {
|
|
||||||
case strings.HasPrefix(line, "WORK="):
|
|
||||||
work = strings.TrimPrefix(line, "WORK=")
|
|
||||||
case strings.Contains(line, "/asm ") && strings.Contains(line, "main_arm64.s") && !strings.Contains(line, "-gensymabis"):
|
|
||||||
asmObj = fieldAfter(line, "-o")
|
|
||||||
case strings.Contains(line, "/link ") && strings.Contains(line, "-importcfg"):
|
|
||||||
linkLine = line
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if work == "" || asmObj == "" || linkLine == "" {
|
|
||||||
t.Skipf("could not parse build log (work=%q asmObj=%q link=%q)", work, asmObj, linkLine)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(work)
|
|
||||||
asmObj = strings.ReplaceAll(asmObj, "$WORK", work)
|
|
||||||
linkLine = strings.ReplaceAll(linkLine, "$WORK", work)
|
|
||||||
|
|
||||||
src, err := os.ReadFile(filepath.Join(dir, "main_arm64.s"))
|
src, err := os.ReadFile(filepath.Join(dir, "main_arm64.s"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -326,19 +281,13 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("AssembleFileARM64: %v", err)
|
t.Fatalf("AssembleFileARM64: %v", err)
|
||||||
}
|
}
|
||||||
gasmObj, err := img.GOObjectAARCH64("a64dlink", "main_arm64.s")
|
gasmObj, err := img.GOObjectAARCH64("main", "main_arm64.s")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("GOObjectAARCH64: %v", err)
|
t.Fatalf("GOObjectAARCH64: %v", err)
|
||||||
}
|
}
|
||||||
if err := os.WriteFile(asmObj, gasmObj, 0o644); err != nil {
|
substituteAndRelink(t, goBin, dir, st, filepath.Join(dir, "prog2"),
|
||||||
t.Fatalf("write gasm object: %v", err)
|
gasmObj, "GOARCH=arm64")
|
||||||
}
|
binData, err := os.ReadFile(filepath.Join(dir, "prog2"))
|
||||||
linkCmd := exec.Command("bash", "-c", "cd "+dir+" && "+linkLine)
|
|
||||||
linkCmd.Env = append(os.Environ(), "GOARCH=arm64")
|
|
||||||
if out, err := linkCmd.CombinedOutput(); err != nil {
|
|
||||||
t.Fatalf("re-link with gasm object: %v\n%s", err, out)
|
|
||||||
}
|
|
||||||
binData, err := os.ReadFile(filepath.Join(dir, "prog"))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4003,6 +4003,7 @@ func AssembleFileARM64(f *ast.File) (*Image, error) {
|
|||||||
Size: d.size,
|
Size: d.size,
|
||||||
Static: d.static,
|
Static: d.static,
|
||||||
Rodata: d.rodata,
|
Rodata: d.rodata,
|
||||||
|
Noptr: d.noptr,
|
||||||
Dupok: d.dupok,
|
Dupok: d.dupok,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ const (
|
|||||||
const (
|
const (
|
||||||
kindSTEXT = 1
|
kindSTEXT = 1
|
||||||
kindSRODATA = 3
|
kindSRODATA = 3
|
||||||
|
kindSNOPTRDATA = 5
|
||||||
kindSDATA = 7
|
kindSDATA = 7
|
||||||
kindSDWARFFCN = 14
|
kindSDWARFFCN = 14
|
||||||
kindSDWARFLINES = 20
|
kindSDWARFLINES = 20
|
||||||
@@ -305,9 +306,16 @@ func (img *Image) emitGOObject(pkgPath, srcPath string, pre []byte, minLC int, r
|
|||||||
if !d.Static {
|
if !d.Static {
|
||||||
name = pkgPath + "." + name
|
name = pkgPath + "." + name
|
||||||
}
|
}
|
||||||
|
// RODATA implies no pointers, so it wins over NOPTR: the kind is
|
||||||
|
// SRODATA either way, exactly as the toolchain chooses it. Plain
|
||||||
|
// NOPTR data is SNOPTRDATA, which the linker keeps out of the GC's
|
||||||
|
// type scan; a plain SDATA symbol would demand Go type information
|
||||||
|
// no assembly file can supply, and the link would fail.
|
||||||
typ := uint8(kindSDATA)
|
typ := uint8(kindSDATA)
|
||||||
if d.Rodata {
|
if d.Rodata {
|
||||||
typ = kindSRODATA
|
typ = kindSRODATA
|
||||||
|
} else if d.Noptr {
|
||||||
|
typ = kindSNOPTRDATA
|
||||||
}
|
}
|
||||||
flag := uint8(0)
|
flag := uint8(0)
|
||||||
if d.Dupok {
|
if d.Dupok {
|
||||||
|
|||||||
@@ -514,6 +514,137 @@ func fieldAfter(line, flag string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buildLogSteps is what a substitution test needs from a `go build -x -work`
|
||||||
|
// log: the work directory, the assembler's object, the package archive and
|
||||||
|
// the link command line.
|
||||||
|
type buildLogSteps struct {
|
||||||
|
work string
|
||||||
|
asmObj string // $WORK expanded
|
||||||
|
pkgArch string // $WORK expanded
|
||||||
|
linkLine string // still carries $WORK placeholders
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseBuildLog extracts the build steps from a `go build -x -work` log.
|
||||||
|
// asmFile names the assembly file whose object the test substitutes. A
|
||||||
|
// missing step is a failure, not a skip: the toolchain changed shape and the
|
||||||
|
// substitution would silently test nothing.
|
||||||
|
func parseBuildLog(t *testing.T, log []byte, asmFile string) buildLogSteps {
|
||||||
|
t.Helper()
|
||||||
|
var st buildLogSteps
|
||||||
|
for line := range strings.SplitSeq(string(log), "\n") {
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(line, "WORK="):
|
||||||
|
st.work = strings.TrimPrefix(line, "WORK=")
|
||||||
|
case strings.Contains(line, "/asm ") && strings.Contains(line, asmFile) && !strings.Contains(line, "-gensymabis"):
|
||||||
|
st.asmObj = fieldAfter(line, "-o")
|
||||||
|
case strings.Contains(line, "pack r") && strings.Contains(line, "_pkg_.a"):
|
||||||
|
rest := strings.TrimSpace(strings.SplitN(line, "pack r", 2)[1])
|
||||||
|
st.pkgArch = strings.Fields(strings.SplitN(rest, "#", 2)[0])[0]
|
||||||
|
case strings.Contains(line, "/link ") && strings.Contains(line, "-importcfg"):
|
||||||
|
st.linkLine = line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if st.work == "" || st.asmObj == "" || st.pkgArch == "" || st.linkLine == "" {
|
||||||
|
t.Fatalf("could not locate the build steps (work=%q asmObj=%q pkgArch=%q link=%q):\n%s",
|
||||||
|
st.work, st.asmObj, st.pkgArch, st.linkLine, log)
|
||||||
|
}
|
||||||
|
st.asmObj = strings.ReplaceAll(st.asmObj, "$WORK", st.work)
|
||||||
|
st.pkgArch = strings.ReplaceAll(st.pkgArch, "$WORK", st.work)
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
|
||||||
|
// substituteAndRelink swaps the gasm object into the package archive the
|
||||||
|
// baseline build produced and re-runs the captured link line against the
|
||||||
|
// rebuilt archive, writing the binary to outBin (the -x log's link step
|
||||||
|
// always targets the action graph's internal a.out, which the helper
|
||||||
|
// redirects; the copy to the -o target is a separate build action the helper
|
||||||
|
// does not need). The archive handed to the linker is proven to carry the
|
||||||
|
// gasm object byte for byte, so a build-layout change that skipped the
|
||||||
|
// substitution fails here instead of passing vacuously.
|
||||||
|
func substituteAndRelink(t *testing.T, goBin, dir string, st buildLogSteps, outBin string, gasmObj []byte, extraEnv ...string) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
// Extract the archive, overwrite the assembler's member with the gasm
|
||||||
|
// object and repack (go tool pack has no replace-in-place).
|
||||||
|
membersDir := filepath.Join(dir, "members")
|
||||||
|
if err := os.MkdirAll(membersDir, 0o755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
extract := exec.Command(goBin, "tool", "pack", "x", st.pkgArch)
|
||||||
|
extract.Dir = membersDir
|
||||||
|
if out, err := extract.CombinedOutput(); err != nil {
|
||||||
|
t.Fatalf("pack x: %v\n%s", err, out)
|
||||||
|
}
|
||||||
|
member := filepath.Join(membersDir, filepath.Base(st.asmObj))
|
||||||
|
if _, err := os.Stat(member); err != nil {
|
||||||
|
t.Fatalf("the assembler's archive member was not extracted: %v", err)
|
||||||
|
}
|
||||||
|
if err := os.Chmod(member, 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(member, gasmObj, 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
listCmd := exec.Command(goBin, "tool", "pack", "t", st.pkgArch)
|
||||||
|
listOut, err := listCmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("pack t: %v\n%s", err, listOut)
|
||||||
|
}
|
||||||
|
newArch := filepath.Join(dir, "pkg.a")
|
||||||
|
args := []string{"tool", "pack", "c", newArch}
|
||||||
|
seen := map[string]bool{}
|
||||||
|
for m := range strings.FieldsSeq(string(listOut)) {
|
||||||
|
if seen[m] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[m] = true
|
||||||
|
if err := os.Chmod(filepath.Join(membersDir, m), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
args = append(args, m)
|
||||||
|
}
|
||||||
|
pack := exec.Command(goBin, args...)
|
||||||
|
pack.Dir = membersDir
|
||||||
|
if out, err := pack.CombinedOutput(); err != nil {
|
||||||
|
t.Fatalf("pack c: %v\n%s", err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prove the substitution: the archive the linker is about to consume
|
||||||
|
// holds the gasm object, byte for byte.
|
||||||
|
checkDir := filepath.Join(dir, "check")
|
||||||
|
if err := os.MkdirAll(checkDir, 0o755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
check := exec.Command(goBin, "tool", "pack", "x", newArch)
|
||||||
|
check.Dir = checkDir
|
||||||
|
if out, err := check.CombinedOutput(); err != nil {
|
||||||
|
t.Fatalf("pack x (verification): %v\n%s", err, out)
|
||||||
|
}
|
||||||
|
got, err := os.ReadFile(filepath.Join(checkDir, filepath.Base(st.asmObj)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("read the substituted member back: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(got, gasmObj) {
|
||||||
|
t.Fatal("the repacked archive does not carry the gasm object")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-link. The line carries a GOROOT assignment and $WORK placeholders;
|
||||||
|
// GOEXPERIMENT must match the toolchain's own, because the linker
|
||||||
|
// compares the object header against its configuration.
|
||||||
|
goExp, _ := exec.Command(goBin, "env", "GOEXPERIMENT").Output()
|
||||||
|
linkLine := strings.ReplaceAll(st.linkLine, "$WORK", st.work)
|
||||||
|
linkLine = strings.ReplaceAll(linkLine, filepath.Join(st.work, "b001", "_pkg_.a"), newArch)
|
||||||
|
linkLine = strings.ReplaceAll(linkLine, filepath.Join(st.work, "b001", "exe", "a.out"), outBin)
|
||||||
|
env := append(os.Environ(), "GOEXPERIMENT="+strings.TrimSpace(string(goExp)))
|
||||||
|
env = append(env, extraEnv...)
|
||||||
|
link := exec.Command("sh", "-c", linkLine)
|
||||||
|
link.Dir = dir
|
||||||
|
link.Env = env
|
||||||
|
if out, err := link.CombinedOutput(); err != nil {
|
||||||
|
t.Fatalf("link with the gasm object: %v\n%s", err, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestGOObjectExternalPackageLink is the cross-package end-to-end check: a
|
// TestGOObjectExternalPackageLink is the cross-package end-to-end check: a
|
||||||
// GOOBJ whose code references a real external package symbol (runtime's
|
// GOOBJ whose code references a real external package symbol (runtime's
|
||||||
// morestack, a plain reference rather than the builtin noctxt form) must
|
// morestack, a plain reference rather than the builtin noctxt form) must
|
||||||
|
|||||||
+13
-3
@@ -134,7 +134,8 @@ type DataSymbol struct {
|
|||||||
Offset int // byte offset within Data
|
Offset int // byte offset within Data
|
||||||
Size int
|
Size int
|
||||||
Static bool // the <> marker: file-local, not exported
|
Static bool // the <> marker: file-local, not exported
|
||||||
Rodata bool // the RODATA flag: read-only data
|
Rodata bool // the RODATA flag: read-only data (implies no pointers)
|
||||||
|
Noptr bool // the NOPTR flag: data with no pointers, kept out of GC scanning
|
||||||
Dupok bool // the DUPOK flag: duplicate-OK
|
Dupok bool // the DUPOK flag: duplicate-OK
|
||||||
// Relocs carries the symbol-valued DATA initialisers ("DATA s+0(SB)/8,
|
// Relocs carries the symbol-valued DATA initialisers ("DATA s+0(SB)/8,
|
||||||
// $other(SB)"): fields of this symbol's data that hold another symbol's
|
// $other(SB)"): fields of this symbol's data that hold another symbol's
|
||||||
@@ -269,6 +270,7 @@ func AssembleFile(f *ast.File, opts ...AssembleOption) (*Image, error) {
|
|||||||
Size: len(d.buf),
|
Size: len(d.buf),
|
||||||
Static: d.static,
|
Static: d.static,
|
||||||
Rodata: d.rodata,
|
Rodata: d.rodata,
|
||||||
|
Noptr: d.noptr,
|
||||||
Dupok: d.dupok,
|
Dupok: d.dupok,
|
||||||
})
|
})
|
||||||
img.Data = append(img.Data, d.buf...)
|
img.Data = append(img.Data, d.buf...)
|
||||||
@@ -413,6 +415,7 @@ func AssembleFileRISCV(f *ast.File) (*Image, error) {
|
|||||||
Size: d.size,
|
Size: d.size,
|
||||||
Static: d.static,
|
Static: d.static,
|
||||||
Rodata: d.rodata,
|
Rodata: d.rodata,
|
||||||
|
Noptr: d.noptr,
|
||||||
Dupok: d.dupok,
|
Dupok: d.dupok,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -485,6 +488,7 @@ func AssembleFileLOONG64(f *ast.File) (*Image, error) {
|
|||||||
Size: d.size,
|
Size: d.size,
|
||||||
Static: d.static,
|
Static: d.static,
|
||||||
Rodata: d.rodata,
|
Rodata: d.rodata,
|
||||||
|
Noptr: d.noptr,
|
||||||
Dupok: d.dupok,
|
Dupok: d.dupok,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -546,6 +550,7 @@ type dataSym struct {
|
|||||||
size int
|
size int
|
||||||
static bool
|
static bool
|
||||||
rodata bool
|
rodata bool
|
||||||
|
noptr bool
|
||||||
dupok bool
|
dupok bool
|
||||||
// relocs are the symbol-valued DATA fields, in declaration order; Off
|
// relocs are the symbol-valued DATA fields, in declaration order; Off
|
||||||
// is relative to the symbol's data start.
|
// is relative to the symbol's data start.
|
||||||
@@ -587,12 +592,14 @@ func collectData(f *ast.File) ([]dataSym, error) {
|
|||||||
switch f {
|
switch f {
|
||||||
case "RODATA":
|
case "RODATA":
|
||||||
ds.rodata = true
|
ds.rodata = true
|
||||||
|
case "NOPTR":
|
||||||
|
ds.noptr = true
|
||||||
case "DUPOK":
|
case "DUPOK":
|
||||||
ds.dupok = true
|
ds.dupok = true
|
||||||
default:
|
default:
|
||||||
// Legacy numeric flag constants (runtime/textflag.h):
|
// Legacy numeric flag constants (runtime/textflag.h):
|
||||||
// DUPOK is 2, RODATA is 8; combinations arrive as one
|
// DUPOK is 2, RODATA is 8, NOPTR is 16; combinations arrive
|
||||||
// number (e.g. 10 = RODATA|DUPOK).
|
// as one number (e.g. 10 = RODATA|DUPOK).
|
||||||
if n, err := strconv.Atoi(f); err == nil {
|
if n, err := strconv.Atoi(f); err == nil {
|
||||||
if n&2 != 0 {
|
if n&2 != 0 {
|
||||||
ds.dupok = true
|
ds.dupok = true
|
||||||
@@ -600,6 +607,9 @@ func collectData(f *ast.File) ([]dataSym, error) {
|
|||||||
if n&8 != 0 {
|
if n&8 != 0 {
|
||||||
ds.rodata = true
|
ds.rodata = true
|
||||||
}
|
}
|
||||||
|
if n&16 != 0 {
|
||||||
|
ds.noptr = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-26
@@ -361,23 +361,8 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("baseline build: %v\n%s", err, buildLog)
|
t.Fatalf("baseline build: %v\n%s", err, buildLog)
|
||||||
}
|
}
|
||||||
var work, linkLine, asmObj string
|
st := parseBuildLog(t, buildLog, "main_amd64.s")
|
||||||
for line := range strings.SplitSeq(string(buildLog), "\n") {
|
defer os.RemoveAll(st.work)
|
||||||
switch {
|
|
||||||
case strings.HasPrefix(line, "WORK="):
|
|
||||||
work = strings.TrimPrefix(line, "WORK=")
|
|
||||||
case strings.Contains(line, "/asm ") && strings.Contains(line, "main_amd64.s") && !strings.Contains(line, "-gensymabis"):
|
|
||||||
asmObj = fieldAfter(line, "-o")
|
|
||||||
case strings.Contains(line, "/link ") && strings.Contains(line, "-importcfg"):
|
|
||||||
linkLine = line
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if work == "" || asmObj == "" || linkLine == "" {
|
|
||||||
t.Skipf("could not parse build log (work=%q asmObj=%q link=%q)", work, asmObj, linkLine)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(work)
|
|
||||||
asmObj = strings.ReplaceAll(asmObj, "$WORK", work)
|
|
||||||
linkLine = strings.ReplaceAll(linkLine, "$WORK", work)
|
|
||||||
|
|
||||||
// Assemble the same source with gasm and substitute the object.
|
// Assemble the same source with gasm and substitute the object.
|
||||||
src, err := os.ReadFile(filepath.Join(dir, "main_amd64.s"))
|
src, err := os.ReadFile(filepath.Join(dir, "main_amd64.s"))
|
||||||
@@ -392,21 +377,18 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("AssembleFile: %v", err)
|
t.Fatalf("AssembleFile: %v", err)
|
||||||
}
|
}
|
||||||
gasmObj, err := img.GOObject("dlink", "main_amd64.s")
|
// The package path is "main": the linker resolves the Go code's
|
||||||
|
// references against main.<name>, so the object must define the symbols
|
||||||
|
// under that prefix whatever the module is called.
|
||||||
|
gasmObj, err := img.GOObject("main", "main_amd64.s")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("GOObject: %v", err)
|
t.Fatalf("GOObject: %v", err)
|
||||||
}
|
}
|
||||||
if err := os.WriteFile(asmObj, gasmObj, 0o644); err != nil {
|
substituteAndRelink(t, goBin, dir, st, filepath.Join(dir, "prog2"), gasmObj)
|
||||||
t.Fatalf("write gasm object: %v", err)
|
|
||||||
}
|
|
||||||
linkCmd := exec.Command("bash", "-c", "cd "+dir+" && "+linkLine)
|
|
||||||
if out, err := linkCmd.CombinedOutput(); err != nil {
|
|
||||||
t.Fatalf("re-link with gasm object: %v\n%s", err, out)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The linked program must run and find the right function behind the
|
// The linked program must run and find the right function behind the
|
||||||
// data word.
|
// data word.
|
||||||
out, err := exec.Command(filepath.Join(dir, "prog")).CombinedOutput()
|
out, err := exec.Command(filepath.Join(dir, "prog2")).CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("linked program failed: %v\n%s", err, out)
|
t.Fatalf("linked program failed: %v\n%s", err, out)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user