feat(asm): encode the arm64 local-exec TLS load
Assisted-by: GLM 5.3 Flash
This commit is contained in:
1 parent
5f6be4584d
commit
c27ba30862
6 files changed
+114
-2
No files matched your search
+40
-2
@@ -30,8 +30,9 @@ import (
|
||||
// (the morestack check in the prologue and the call back into the runtime in
|
||||
// the epilogue) is not emitted, so the bytes match only for NOSPLIT functions
|
||||
// or zero-frame leaves, where the toolchain emits no guard either.
|
||||
func assembleARM64(t *ast.Text) ([]byte, map[string]int, []Reloc, []LineEntry, []SpadjStep, []Arm64Literal, error) {
|
||||
func assembleARM64(t *ast.Text, tls map[string]bool) ([]byte, map[string]int, []Reloc, []LineEntry, []SpadjStep, []Arm64Literal, error) {
|
||||
fi := arm64ComputeFrame(t)
|
||||
fi.tls = tls
|
||||
prologue := arm64Prologue(fi)
|
||||
guardLen := arm64GuardLen(fi)
|
||||
chain := arm64JumpChain(t)
|
||||
@@ -1989,6 +1990,24 @@ func encodeARM64Mov(instr *ast.Instr, mnem string, wb string, fi arm64FrameInfo,
|
||||
if rd < 0 {
|
||||
return nil, fmt.Errorf("%s sym(SB): invalid destination register", mnem)
|
||||
}
|
||||
// A TLSBSS symbol's load rides the local-exec model: one MOVZ word
|
||||
// with the R_ARM64_TLS_LE relocation (asm7.go case 69), the width
|
||||
// rows existing for MOVD alone.
|
||||
if fi.tls[src.Addr.Sym.Name] {
|
||||
if mnem != "MOVD" && mnem != "MOV" {
|
||||
return nil, fmt.Errorf("%s: illegal combination: the TLS load exists for MOVD only", mnem)
|
||||
}
|
||||
if relocs != nil {
|
||||
*relocs = append(*relocs, Reloc{
|
||||
Off: 0,
|
||||
After: 4,
|
||||
Name: src.Addr.Sym.Name,
|
||||
Addend: src.Addr.Sym.Offset,
|
||||
Kind: RelArm64TLSLE,
|
||||
})
|
||||
}
|
||||
return a64wordLE(a64MoveWide(1, 2, 0, 0, uint32(rd))), nil
|
||||
}
|
||||
return encodeARM64SBLoad(src.Addr.Sym, rd, mnem, relocs)
|
||||
}
|
||||
if dst.Addr.Sym != nil && dst.Addr.Sym.Pseudo == "SB" && isMemOperand(dst) {
|
||||
@@ -2116,6 +2135,9 @@ func arm64MovSize(mnem string, ops []*ast.Operand, fi arm64FrameInfo) int {
|
||||
if mnem == "FMOVQ" {
|
||||
return 12 // ADRP + ADD + LDR: the unaligned-access fallback
|
||||
}
|
||||
if fi.tls != nil && fi.tls[src.Addr.Sym.Name] {
|
||||
return 4 // the TLS-LE load is one MOVZ word
|
||||
}
|
||||
return 8 // ADRP + LDR
|
||||
case dst.Addr.Sym != nil && dst.Addr.Sym.Pseudo == "SB":
|
||||
if mnem == "FMOVQ" {
|
||||
@@ -5771,6 +5793,22 @@ func AssembleFileARM64(f *ast.File) (*Image, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The TLS symbols: a GLOBL marked TLSBSS makes the toolchain's aclass
|
||||
// key its load off objabi.STLSBSS (asm7.go C_TLSIE), so the loads of
|
||||
// these names encode as the local-exec MOVZ with the TLS_LE relocation.
|
||||
tlsSyms := map[string]bool{}
|
||||
for _, d := range f.Decls {
|
||||
g, ok := d.(*ast.Globl)
|
||||
if !ok || g.Name == nil {
|
||||
continue
|
||||
}
|
||||
for _, fl := range g.Flags {
|
||||
if fl == "TLSBSS" {
|
||||
tlsSyms[g.Name.Name] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
img := &Image{Symbols: map[string]int{}, SourcePath: f.Path}
|
||||
var pendingLits []Arm64Literal
|
||||
litSeen := map[string]bool{}
|
||||
@@ -5779,7 +5817,7 @@ func AssembleFileARM64(f *ast.File) (*Image, error) {
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
code, labels, relocs, lines, spadj, lits, err := assembleARM64(t)
|
||||
code, labels, relocs, lines, spadj, lits, err := assembleARM64(t, tlsSyms)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %w", t.Name.Name, err)
|
||||
}
|
||||
|
||||
@@ -69,6 +69,12 @@ type arm64FrameInfo struct {
|
||||
// autosize below StackSmall as NOSPLIT.
|
||||
needSplit bool
|
||||
splitClass int // 0: <=StackSmall, 1: <=StackBig, 2: >StackBig
|
||||
|
||||
// tls holds the names the file's own GLOBL declarations mark TLSBSS:
|
||||
// the toolchain's aclass keys the TLS-LE load off the symbol's type
|
||||
// (objabi.STLSBSS), which only the file's declarations reveal. A nil
|
||||
// map answers no, the plain static-symbol load.
|
||||
tls map[string]bool
|
||||
}
|
||||
|
||||
// arm64ComputeFrame derives the frame layout for a TEXT function.
|
||||
|
||||
@@ -124,3 +124,59 @@ func TestArm64GOObjRelocTypes(t *testing.T) {
|
||||
// The detailed layout is covered by the goobj tests; here we only pin
|
||||
// that emission succeeds with the new relocation kinds in play.
|
||||
}
|
||||
|
||||
// TestArm64TLSLoad pins the local-exec TLS load: a symbol the file's own
|
||||
// GLOBL marks TLSBSS loads as one MOVZ word carrying the R_ARM64_TLS_LE
|
||||
// relocation (asm7.go case 69), the shape `go tool asm` emits for the
|
||||
// runtime's tls_g accesses. A non-TLS GLOBL keeps the ADRP+LDR pair.
|
||||
func TestArm64TLSLoad(t *testing.T) {
|
||||
img := parseArm64File(t, "#include \"textflag.h\"\n\n"+
|
||||
"TEXT \u00b7f(SB), NOSPLIT, $0-0\n"+
|
||||
"\tMOVD tlsvar(SB), R0\n"+
|
||||
"\tMOVD plain(SB), R1\n"+
|
||||
"\tRET\n"+
|
||||
"GLOBL tlsvar(SB), TLSBSS, $8\n"+
|
||||
"GLOBL plain(SB), NOPTR, $8\n")
|
||||
fn := img.Funcs[0]
|
||||
if fn.Size != 4+8+4 {
|
||||
t.Fatalf("function size = %d, want 16", fn.Size)
|
||||
}
|
||||
if w := binary.LittleEndian.Uint32(img.Code[fn.Offset:]); w != 0xd2800000 {
|
||||
t.Errorf("TLS load word = %08x, want MOVZ 0 (d2800000)", w)
|
||||
}
|
||||
var tlsSeen, plainSeen bool
|
||||
for _, r := range fn.Relocs {
|
||||
if r.Name != "tlsvar" {
|
||||
continue
|
||||
}
|
||||
tlsSeen = true
|
||||
if r.Kind != RelArm64TLSLE {
|
||||
t.Errorf("tlsvar reloc kind = %v, want RelArm64TLSLE", r.Kind)
|
||||
}
|
||||
if r.Off != 0 {
|
||||
t.Errorf("tlsvar reloc off = %d, want 0", r.Off)
|
||||
}
|
||||
}
|
||||
for _, r := range fn.Relocs {
|
||||
if r.Name == "plain" && r.Kind == RelArm64LDST64 {
|
||||
plainSeen = true
|
||||
}
|
||||
}
|
||||
if !tlsSeen {
|
||||
t.Error("no tlsvar relocation recorded")
|
||||
}
|
||||
if !plainSeen {
|
||||
t.Error("the plain GLOBL load lost its ADRP+LDR relocation")
|
||||
}
|
||||
// The other widths have no TLS row: the toolchain refuses them.
|
||||
f, errs := parser.Parse("k_arm64.s", "TEXT \u00b7f(SB), NOSPLIT, $0-0\n"+
|
||||
"\tMOVW tlsvar(SB), R0\n"+
|
||||
"\tRET\n"+
|
||||
"GLOBL tlsvar(SB), TLSBSS, $8\n")
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("parse: %v", errs)
|
||||
}
|
||||
if _, err := AssembleFileARM64(f); err == nil {
|
||||
t.Error("MOVW of a TLS symbol assembled, want an illegal combination")
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,9 @@ const (
|
||||
rArm64AddAbsLo12NC = 277 // R_AARCH64_ADD_ABS_LO12_NC (ADD page offset)
|
||||
rArm64Call26 = 283 // R_AARCH64_CALL26 (BL instruction)
|
||||
rArm64Ldst64Lo12NC = 286 // R_AARCH64_LDST64_ABS_LO12_NC (64-bit LDR/STR page offset)
|
||||
// R_AARCH64_TLSLE_MOVW_TPREL_G0 (debug/elf 547): the local-exec TLS
|
||||
// load's MOVZ field, the module offset at bits [15:0].
|
||||
rArm64TLSLEMovwTprelG0 = 547
|
||||
// R_AARCH64_ABS32 (debug/elf 258): the absolute 32-bit address of a
|
||||
// symbol, the R_ADDR shape a 4-byte DATA field carries. ABS64 (257)
|
||||
// lives with the DWARF fixup constants as rAARCH64Abs64.
|
||||
@@ -131,6 +134,11 @@ func (img *Image) ELFAARCH64Object() ([]byte, error) {
|
||||
elfRela{off: uint64(fn.Offset + r.Off), typ: rArm64PrelPgHi21, sym: idx, addend: r.Addend},
|
||||
elfRela{off: uint64(fn.Offset + r.Off + 4), typ: rArm64Ldst64Lo12NC, sym: idx, addend: r.Addend},
|
||||
)
|
||||
case RelArm64TLSLE:
|
||||
// The local-exec MOVZ: one relocation over the imm16 field.
|
||||
relas = append(relas, elfRela{
|
||||
off: uint64(fn.Offset + r.Off), typ: rArm64TLSLEMovwTprelG0, sym: idx, addend: r.Addend,
|
||||
})
|
||||
default:
|
||||
return nil, fmt.Errorf("relocation kind %v unsupported in ELF emission", r.Kind)
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ func (img *Image) GOObjectAARCH64(pkgPath, srcPath string) ([]byte, error) {
|
||||
return relocArm64Branch, 4
|
||||
case RelArm64LDST64:
|
||||
return relocArm64LDST64, 8
|
||||
case RelArm64TLSLE:
|
||||
return relocArm64TLSLE, 4
|
||||
default:
|
||||
return relocArm64Addr, 8
|
||||
}
|
||||
@@ -60,6 +62,7 @@ func (img *Image) GOObjectAARCH64(pkgPath, srcPath string) ([]byte, error) {
|
||||
const (
|
||||
relocArm64Addr = 3 // R_ADDRARM64, ADRP+ADD pair
|
||||
relocArm64Branch = 9 // R_CALLARM64, BL instruction
|
||||
relocArm64TLSLE = 32 // R_ARM64_TLS_LE, MOVZ local-exec TLS load
|
||||
relocArm64LDST64 = 40 // R_ARM64_PCREL_LDST64, ADRP+LDR/STR pair
|
||||
)
|
||||
|
||||
|
||||
@@ -106,6 +106,7 @@ const (
|
||||
RelArm64LDST64 // R_ARM64_PCREL_LDST64 (ADRP + 64-bit LDR/STR pair)
|
||||
RelLoong64Branch // R_CALLLOONG64 (BL instruction)
|
||||
RelAddr // R_ADDR: the absolute address of a symbol held in a DATA field
|
||||
RelArm64TLSLE // R_ARM64_TLS_LE (MOVZ local-exec TLS load)
|
||||
)
|
||||
|
||||
type Reloc struct {
|
||||
|
||||
Reference in new issue
Block a user