feat(asm): emit RISC-V GOOBJ with the shared emitter

Assisted-by: DeepSeek V4 Pro
This commit is contained in:
2026-08-13 12:07:29 +02:00
parent 31ee8e7941
commit 9b5c878f9e
9 changed files with 358 additions and 383 deletions
+20 -10
View File
@@ -143,14 +143,15 @@ func (img *Image) GOObject(pkgPath, srcPath string) ([]byte, error) {
return nil, err
}
// amd64: MinLC 1, R_PCREL for the code relocations.
return img.emitGOObject(pkgPath, srcPath, pre, 1, func(Reloc) uint16 { return relocPCRel })
return img.emitGOObject(pkgPath, srcPath, pre, 1, func(Reloc) (uint16, uint8) { return relocPCRel, 4 })
}
// emitGOObject assembles the GOOBJ payload for any architecture. pre is
// the toolchain's object preamble; minLC is the architecture's minimum
// instruction length, the unit of the pc-value table deltas; relocType
// maps a code relocation to its objabi relocation type.
func (img *Image) emitGOObject(pkgPath, srcPath string, pre []byte, minLC int, relocType func(Reloc) uint16) ([]byte, error) {
// instruction length, the unit of the pc-value table deltas; relocField
// maps a code relocation to its objabi relocation type and the width of
// the instruction field the linker writes.
func (img *Image) emitGOObject(pkgPath, srcPath string, pre []byte, minLC int, relocField func(Reloc) (uint16, uint8)) ([]byte, error) {
if pkgPath == "" {
return nil, fmt.Errorf("GOOBJ emission requires a package path (-p)")
}
@@ -197,8 +198,16 @@ func (img *Image) emitGOObject(pkgPath, srcPath string, pre []byte, minLC int, r
fnNpIdx[i] = len(nps)
code := append([]byte(nil), img.Code[fn.Offset:fn.Offset+fn.Size]...)
for _, r := range fn.Relocs {
// The linker writes the resolved displacement into the field;
// leave it zero, as cmd/asm's object does.
// Only the amd64 encoder resolves file-local static symbols
// into a disp32 field at assemble time; GOOBJ must leave that
// field zero for the linker to fill. The RISC-V and LoongArch
// encoders emit zero immediates with a relocation instead, and
// their relocations cover whole AUIPC/pcalau12i pairs, so
// zeroing r.Off would erase the opcode/register bits the linker
// preserves when it patches only the immediate.
if r.Kind != RelPCRel32 {
continue
}
if r.Off >= 0 && r.Off+4 <= len(code) {
code[r.Off], code[r.Off+1], code[r.Off+2], code[r.Off+3] = 0, 0, 0, 0
}
@@ -290,6 +299,7 @@ func (img *Image) emitGOObject(pkgPath, srcPath string, pre []byte, minLC int, r
for i, fn := range img.Funcs {
si := len(defs) + fnNpIdx[i]
for _, r := range fn.Relocs {
typ, size := relocField(r)
if r.External {
// Split package-qualified name: "runtime·morestack" → runtime, morestack.
pkg, name := splitQualified(r.Name)
@@ -306,8 +316,8 @@ func (img *Image) emitGOObject(pkgPath, srcPath string, pre []byte, minLC int, r
}
var rec [23]byte
binary.LittleEndian.PutUint32(rec[0:], uint32(int32(r.Off)))
rec[4] = 4 // field width
binary.LittleEndian.PutUint16(rec[5:], relocType(r))
rec[4] = size // field width
binary.LittleEndian.PutUint16(rec[5:], typ)
binary.LittleEndian.PutUint64(rec[7:], uint64(r.Addend))
binary.LittleEndian.PutUint32(rec[15:], uint32(pIdx))
binary.LittleEndian.PutUint32(rec[19:], uint32(sIdx))
@@ -320,8 +330,8 @@ func (img *Image) emitGOObject(pkgPath, srcPath string, pre []byte, minLC int, r
}
var rec [23]byte
binary.LittleEndian.PutUint32(rec[0:], uint32(int32(r.Off)))
rec[4] = 4 // field width
binary.LittleEndian.PutUint16(rec[5:], relocType(r))
rec[4] = size // field width
binary.LittleEndian.PutUint16(rec[5:], typ)
binary.LittleEndian.PutUint64(rec[7:], uint64(r.Addend))
binary.LittleEndian.PutUint32(rec[15:], pkgIdxSelf)
binary.LittleEndian.PutUint32(rec[19:], uint32(di))