2026-08-13 11:24:44 +02:00
|
|
|
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
|
|
|
|
|
|
package asm
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"sync"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// GOObjectLOONG64 emits a GOOBJ object file for LoongArch. The layout is
|
2026-09-14 18:22:18 +02:00
|
|
|
// the shared one in goobj.go, the toolchain preamble, the go120ld header
|
2026-08-13 11:24:44 +02:00
|
|
|
// with its block offsets, the string table, the symbol definitions and the
|
2026-09-14 18:22:18 +02:00
|
|
|
// reloc/aux/data index arrays, with the loong64 preamble, the MinLC of 4
|
2026-08-13 11:24:44 +02:00
|
|
|
// for the pc-value deltas, and R_LOONG64_ADDR_HI/LO relocation types for
|
|
|
|
|
// the pcalau12i+addi.d address pairs.
|
|
|
|
|
func (img *Image) GOObjectLOONG64(pkgPath, srcPath string) ([]byte, error) {
|
|
|
|
|
pre, err := toolchainObjectPreambleLOONG64()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2026-08-13 12:07:29 +02:00
|
|
|
return img.emitGOObject(pkgPath, srcPath, pre, 4, func(r Reloc) (uint16, uint8) {
|
2026-08-13 11:24:44 +02:00
|
|
|
// A pcalau12i+addi.d pair: the high part carries
|
2026-09-14 21:21:58 +02:00
|
|
|
// R_LOONG64_ADDR_HI, the low part R_LOONG64_ADDR_LO; the guard's
|
|
|
|
|
// morestack call carries R_CALLLOONG64.
|
|
|
|
|
switch {
|
|
|
|
|
case r.Kind == RelLoong64AddrLo:
|
2026-08-13 12:07:29 +02:00
|
|
|
return relocLoong64AddrLo, 4
|
2026-09-14 21:21:58 +02:00
|
|
|
case r.Kind == RelLoong64Branch:
|
|
|
|
|
return relocCallLoong64, 4
|
|
|
|
|
default:
|
|
|
|
|
return relocLoong64AddrHi, 4
|
2026-08-13 11:24:44 +02:00
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Loong64 relocation types (cmd/internal/objabi). R_LOONG64_ADDR_HI
|
|
|
|
|
// resolves the high 20 bits of a PC-relative address into pcalau12i;
|
|
|
|
|
// R_LOONG64_ADDR_LO the low 12 bits into addi.d/ld/st.
|
|
|
|
|
const (
|
|
|
|
|
relocLoong64AddrHi = 77 // R_LOONG64_ADDR_HI
|
|
|
|
|
relocLoong64AddrLo = 78 // R_LOONG64_ADDR_LO
|
2026-09-14 21:21:58 +02:00
|
|
|
relocCallLoong64 = 84 // R_CALLLOONG64
|
2026-08-13 11:24:44 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// toolchainObjectPreambleLOONG64 returns the "go object ...\n!\n" header
|
|
|
|
|
// the installed go tool asm writes for loong64, captured by assembling a
|
|
|
|
|
// one-instruction probe (see toolchainObjectPreamble).
|
|
|
|
|
var (
|
|
|
|
|
preambleLOONG64Once sync.Once
|
|
|
|
|
preambleLOONG64 []byte
|
|
|
|
|
preambleLOONG64Err error
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func toolchainObjectPreambleLOONG64() ([]byte, error) {
|
|
|
|
|
preambleLOONG64Once.Do(func() {
|
|
|
|
|
goBin, err := exec.LookPath("go")
|
|
|
|
|
if err != nil {
|
|
|
|
|
preambleLOONG64Err = fmt.Errorf("GOOBJ emission needs the Go toolchain: %w", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
dir, err := os.MkdirTemp("", "gasm-preamble-loong64")
|
|
|
|
|
if err != nil {
|
|
|
|
|
preambleLOONG64Err = err
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
defer os.RemoveAll(dir)
|
|
|
|
|
src := filepath.Join(dir, "probe_loong64.s")
|
|
|
|
|
if err := os.WriteFile(src, []byte("TEXT \u00b7x(SB), $0-0\n\tRET\n"), 0o644); err != nil {
|
|
|
|
|
preambleLOONG64Err = err
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
obj := filepath.Join(dir, "probe.o")
|
|
|
|
|
cmd := exec.Command(goBin, "tool", "asm", "-p", "probe", "-o", obj, src)
|
|
|
|
|
cmd.Env = append(os.Environ(), "GOARCH=loong64")
|
|
|
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
|
|
|
preambleLOONG64Err = fmt.Errorf("probing the assembler for the object header: %v\n%s", err, out)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
data, err := os.ReadFile(obj)
|
|
|
|
|
if err != nil {
|
|
|
|
|
preambleLOONG64Err = err
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
i := bytes.Index(data, []byte("\n!\n"))
|
|
|
|
|
if i < 0 || !bytes.HasPrefix(data[i+3:], []byte(goobjMagic)) {
|
|
|
|
|
preambleLOONG64Err = fmt.Errorf("unrecognised assembler object layout")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
preambleLOONG64 = data[:i+3]
|
|
|
|
|
})
|
|
|
|
|
return preambleLOONG64, preambleLOONG64Err
|
|
|
|
|
}
|