// Copyright (c) 2026 Petr BalvĂ­n (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 // the shared one in goobj.go, the toolchain preamble, the go120ld header // with its block offsets, the string table, the symbol definitions and the // reloc/aux/data index arrays, with the loong64 preamble, the MinLC of 4 // 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 } return img.emitGOObject(pkgPath, srcPath, pre, 4, func(r Reloc) (uint16, uint8) { // A pcalau12i+addi.d pair: the high part carries // R_LOONG64_ADDR_HI, the low part R_LOONG64_ADDR_LO. if r.Kind == RelLoong64AddrLo { return relocLoong64AddrLo, 4 } return relocLoong64AddrHi, 4 }) } // 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 ) // 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 }