diff --git a/asm/arm64_assemble.go b/asm/arm64_assemble.go index 294f3b9..a338f9f 100644 --- a/asm/arm64_assemble.go +++ b/asm/arm64_assemble.go @@ -192,9 +192,9 @@ func encodeARM64Instr(instr *ast.Instr, pc int, offsets map[string]int, fi arm64 } return a64wordLE(uint32(immFromOperand(ops[0]))), nil case "B": - return encodeARM64Branch(mnem, ops, pc, offsets, false, resolve) + return encodeARM64Branch(mnem, ops, pc, offsets, false, relocs, resolve) case "BL", "CALL": - return encodeARM64Branch(mnem, ops, pc, offsets, true, resolve) + return encodeARM64Branch(mnem, ops, pc, offsets, true, relocs, resolve) case "MOV", "MOVD", "MOVW", "MOVWU", "MOVH", "MOVHU", "MOVB", "MOVBU", "FMOVS", "FMOVD": return encodeARM64Mov(instr, mnem, fi, relocs) @@ -300,7 +300,7 @@ func encodeARM64Instr(instr *ast.Instr, pc int, offsets map[string]int, fi arm64 // ---- branch encoding ---- // encodeARM64Branch encodes an unconditional branch (B/BL) to a label. -func encodeARM64Branch(mnem string, ops []*ast.Operand, pc int, offsets map[string]int, link bool, resolve func(string) string) ([]byte, error) { +func encodeARM64Branch(mnem string, ops []*ast.Operand, pc int, offsets map[string]int, link bool, relocs *[]Reloc, resolve func(string) string) ([]byte, error) { if len(ops) != 1 { return nil, fmt.Errorf("%s expects 1 operand, got %d", mnem, len(ops)) } @@ -308,6 +308,15 @@ func encodeARM64Branch(mnem string, ops []*ast.Operand, pc int, offsets map[stri // External symbol reference: BL sym(SB). if link && op.Addr.Sym != nil && op.Addr.Sym.Pseudo == "SB" { + if relocs != nil { + *relocs = append(*relocs, Reloc{ + Off: 0, + After: 4, + Name: op.Addr.Sym.Name, + Addend: op.Addr.Sym.Offset, + Kind: RelArm64Branch, + }) + } // Emit BL with zero offset; the linker fills in the target. return a64wordLE(a64Branch(1, 0)), nil } @@ -1324,5 +1333,6 @@ func AssembleFileARM64(f *ast.File) (*Image, error) { }) } + markExternals(img, dataSyms) return img, nil } diff --git a/asm/elfarm64.go b/asm/elfarm64.go index 5ddc4f8..9d3bb4e 100644 --- a/asm/elfarm64.go +++ b/asm/elfarm64.go @@ -16,6 +16,7 @@ const ( // AArch64 relocation types (the ELF psABI). rArm64PrelPgHi21 = 275 // R_AARCH64_ADR_PREL_PG_HI21 (ADRP page) rArm64AddAbsLo12NC = 277 // R_AARCH64_ADD_ABS_LO12_NC (ADD/STR/LDR page offset) + rArm64Call26 = 283 // R_AARCH64_CALL26 (BL instruction) ) // ELFAARCH64Object returns the image as an ELF64 relocatable object file for @@ -94,10 +95,14 @@ func (img *Image) ELFAARCH64Object() ([]byte, error) { if !ok { return nil, fmt.Errorf("relocation references unknown symbol %q", r.Name) } - typ := uint32(rArm64PrelPgHi21) - if r.Kind == RelArm64Addr && r.Off%4 == 4 { - // The second instruction in an ADRP pair uses ADD_ABS_LO12_NC. + var typ uint32 + switch { + case r.Kind == RelArm64Branch: + typ = rArm64Call26 + case r.Kind == RelArm64Addr && r.Off%4 == 4: typ = rArm64AddAbsLo12NC + default: + typ = rArm64PrelPgHi21 } relas = append(relas, elfRela{ off: uint64(fn.Offset + r.Off), diff --git a/asm/goobjarm64.go b/asm/goobjarm64.go index bf6de16..2d6e361 100644 --- a/asm/goobjarm64.go +++ b/asm/goobjarm64.go @@ -24,14 +24,17 @@ func (img *Image) GOObjectAARCH64(pkgPath, srcPath string) ([]byte, error) { return nil, err } return img.emitGOObject(pkgPath, srcPath, pre, 4, func(r Reloc) (uint16, uint8) { + if r.Kind == RelArm64Branch { + return relocArm64Branch, 4 + } return relocArm64Addr, 4 }) } -// arm64 relocation types (cmd/internal/objabi). R_ADDRARM64 resolves an -// ADRP+ADD/LDR/STR pair to a symbol's address. +// arm64 relocation types (cmd/internal/objabi). const ( - relocArm64Addr = 9 // R_ADDRARM64 + relocArm64Addr = 3 // R_ADDRARM64 — ADRP+ADD/LDR/STR pair + relocArm64Branch = 9 // R_CALLARM64 — BL instruction ) // toolchainObjectPreambleAARCH64 returns the "go object ...\n!\n" header diff --git a/asm/link.go b/asm/link.go index 5977c5f..a9f51ad 100644 --- a/asm/link.go +++ b/asm/link.go @@ -97,6 +97,7 @@ const ( RelLoong64AddrHi // R_LOONG64_ADDR_HI (pcalau12i) RelLoong64AddrLo // R_LOONG64_ADDR_LO (addi.d/ld/st) RelArm64Addr // R_ADDRARM64 (ADRP + ADD/LDR/STR pair) + RelArm64Branch // R_CALLARM64 (BL instruction) ) type Reloc struct { @@ -302,6 +303,7 @@ func AssembleFileRISCV(f *ast.File) (*Image, error) { }) } + markExternals(img, dataSyms) return img, nil } @@ -373,9 +375,38 @@ func AssembleFileLOONG64(f *ast.File) (*Image, error) { }) } + markExternals(img, dataSyms) return img, nil } +// markExternals identifies relocations that reference symbols not defined in +// the file (neither a GLOBL/DATA symbol nor a TEXT function) and records them +// as external. The non-amd64 architectures emit relocations for every SB +// reference; this post-processing step distinguishes file-local from external. +func markExternals(img *Image, dataSyms []dataSym) { + known := make(map[string]bool, len(dataSyms)+len(img.Funcs)) + for _, d := range dataSyms { + known[d.name] = true + } + for _, fn := range img.Funcs { + known[fn.Name] = true + } + externals := map[string]bool{} + for i := range img.Funcs { + for j := range img.Funcs[i].Relocs { + r := &img.Funcs[i].Relocs[j] + if !known[r.Name] { + r.External = true + externals[r.Name] = true + } + } + } + for name := range externals { + img.Externals = append(img.Externals, name) + } + sort.Strings(img.Externals) +} + // dataSym is one GLOBL symbol and its DATA initialiser. type dataSym struct { name string