Skip to content

Commit 7b23386

Browse files
committed
delocate: use address helpers for known symbols on aarch64
Replace adr with address helper functions for known global symbols on aarch64 to remove the ±1MiB PC-relative reach limitation. The FIPS module .text section now exceeds 1MiB (~1.62MB), causing adr instructions to fail when referencing symbols far away in the module. The fix introduces lightweight helper functions that are emitted outside the FIPS module boundary (after BORINGSSL_bcm_text_end). These helpers use adrp+add which the linker resolves and which support ±4GiB range. Inside the module, call sites use bl (±128MiB range) to reach the helpers, with save/restore of x0 and x30. This keeps the .text bytes within the hashed FIPS region free of relocations, preserving the integrity check mechanism. Local .L* symbols continue to use adr since they are always close to their reference site. Benchmarks show no measurable performance impact (all within ±0.36% noise) since the change only affects address loads of global symbols during setup/initialization, not hot inner loops.
1 parent 1c9754e commit 7b23386

3 files changed

Lines changed: 142 additions & 13 deletions

File tree

util/fipstools/delocate/delocate.go

Lines changed: 74 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,12 @@ type delocation struct {
104104
// tocLoaders is a set of symbol names for which TOC helper functions
105105
// are required. (ppc64le only.)
106106
tocLoaders map[string]struct{}
107-
// gotExternalsNeeded is a set of symbol names for which we need
107+
// localAddrHelpers is a set of known symbol names (already converted
108+
// to local target names) for which address helper functions are needed
109+
// on aarch64. These helpers use adrp+add (which the linker resolves)
110+
// and live outside the FIPS module boundary, avoiding the ±1MiB reach
111+
// limitation of adr within the module.
112+
localAddrHelpers map[string]struct{} // gotExternalsNeeded is a set of symbol names for which we need
108113
// “delta” symbols: symbols that contain the offset from their location
109114
// to the memory in question.
110115
gotExternalsNeeded map[string]struct{}
@@ -440,6 +445,12 @@ func instructionArgs(node *node32) (argNodes []*node32) {
440445

441446
// Aarch64 support
442447

448+
// localAddrHelperName returns the name of a synthesised function that loads
449+
// the address of a known symbol using adrp+add (outside the FIPS module).
450+
func localAddrHelperName(symbol string) string {
451+
return ".Lboringssl_loadaddr_" + symbol
452+
}
453+
443454
// gotHelperName returns the name of a synthesised function that returns an
444455
// address from the GOT.
445456
func gotHelperName(symbol string) string {
@@ -449,33 +460,69 @@ func gotHelperName(symbol string) string {
449460
// loadAarch64Address emits instructions to put the address of |symbol|
450461
// (optionally adjusted by |offsetStr|) into |targetReg|.
451462
func (d *delocation) loadAarch64Address(statement *node32, targetReg string, symbol string, offsetStr string) (*node32, error) {
452-
// There are two paths here: either the symbol is known to be local in which
453-
// case adr is used to get the address (within 1MiB), or a GOT reference is
454-
// really needed in which case the code needs to jump to a helper function.
463+
// There are three paths here:
464+
//
465+
// 1. Local (.L*) symbols use adr directly since they are always close
466+
// to their reference site (within ±1MiB).
455467
//
456-
// A helper function is needed because using code appears to be the only way
457-
// to load a GOT value. On other platforms we have ".quad foo@GOT" outside of
458-
// the module, but on Aarch64 that results in a "COPY" relocation and linker
459-
// comments suggest it's a weird hack. So, for each GOT symbol needed, we emit
460-
// a function outside of the module that returns the address from the GOT in
461-
// x0.
468+
// 2. Known global symbols (defined within the FIPS module) use a helper
469+
// function that lives outside the module boundary. The helper uses
470+
// adrp+add (which the linker resolves) to load the full address.
471+
// This avoids the ±1MiB reach limitation of adr for large modules.
472+
//
473+
// 3. External/GOT symbols use a helper function that loads and
474+
// dereferences the GOT entry.
462475

463476
d.writeCommentedNode(statement)
464477

465478
_, isKnown := d.symbols[symbol]
466479
isLocal := strings.HasPrefix(symbol, ".L")
467-
if isKnown || isLocal || isSynthesized(symbol, aarch64) {
480+
if isLocal || isSynthesized(symbol, aarch64) {
468481
if isLocal {
469482
symbol = d.mapLocalSymbol(symbol)
470-
} else if isKnown {
471-
symbol = localTargetName(symbol)
472483
}
473484

474485
d.output.WriteString("\tadr " + targetReg + ", " + symbol + offsetStr + "\n")
475486

476487
return statement, nil
477488
}
478489

490+
if isKnown {
491+
// Known symbols within the module use address helper functions
492+
// that live outside the hashed FIPS region. This avoids the ±1MiB
493+
// adr reach limit while keeping the module's .text free of
494+
// relocations (required for the FIPS integrity check).
495+
localSymbol := localTargetName(symbol)
496+
497+
d.localAddrHelpers[localSymbol] = struct{}{}
498+
helperFunc := localAddrHelperName(localSymbol)
499+
500+
// Use the same save/restore pattern as GOT helpers.
501+
d.output.WriteString("\tsub sp, sp, 128\n")
502+
d.output.WriteString("\tstp x0, x30, [sp, #-16]!\n")
503+
d.output.WriteString("\tbl " + helperFunc + "\n")
504+
505+
if targetReg == "x0" {
506+
d.output.WriteString("\tldp xzr, x30, [sp], #16\n")
507+
} else if targetReg == "x30" {
508+
d.output.WriteString("\tmov " + targetReg + ", x0\n")
509+
d.output.WriteString("\tldp x0, xzr, [sp], #16\n")
510+
} else {
511+
d.output.WriteString("\tmov " + targetReg + ", x0\n")
512+
d.output.WriteString("\tldp x0, x30, [sp], #16\n")
513+
}
514+
515+
d.output.WriteString("\tadd sp, sp, 128\n")
516+
517+
// If there's an offset (e.g., symbol+4096), add it after loading
518+
// the base address.
519+
if len(offsetStr) != 0 {
520+
d.output.WriteString("\tadd " + targetReg + ", " + targetReg + ", " + offsetStr + "\n")
521+
}
522+
523+
return statement, nil
524+
}
525+
479526
if len(offsetStr) != 0 {
480527
panic("non-zero offset for helper-based reference")
481528
}
@@ -2291,6 +2338,7 @@ func transform(w stringWriter, includes []string, inputs []inputFile, startEndDe
22912338
redirectors: make(map[string]string),
22922339
bssAccessorsNeeded: make(map[string]string),
22932340
tocLoaders: make(map[string]struct{}),
2341+
localAddrHelpers: make(map[string]struct{}),
22942342
gotExternalsNeeded: make(map[string]struct{}),
22952343
gotOffsetsNeeded: make(map[string]struct{}),
22962344
gotOffOffsetsNeeded: make(map[string]struct{}),
@@ -2448,6 +2496,19 @@ func transform(w stringWriter, includes []string, inputs []inputFile, startEndDe
24482496
})
24492497
}
24502498

2499+
// Emit address helpers for known symbols within the module.
2500+
// These use adrp+add which the linker resolves, avoiding the
2501+
// ±1MiB adr reach limit for large FIPS modules.
2502+
localAddrNames := sortedSet(d.localAddrHelpers)
2503+
for _, symbol := range localAddrNames {
2504+
sym := symbol // capture for closure
2505+
writeAarch64Function(w, localAddrHelperName(sym), func(w stringWriter) {
2506+
w.WriteString("\tadrp x0, " + sym + "\n")
2507+
w.WriteString("\tadd x0, x0, :lo12:" + sym + "\n")
2508+
w.WriteString("\tret\n")
2509+
})
2510+
}
2511+
24512512
writeAarch64Function(w, ".LOPENSSL_armcap_P_addr", func(w stringWriter) {
24522513
w.WriteString("\tadrp x0, OPENSSL_armcap_P\n")
24532514
w.WriteString("\tadd x0, x0, :lo12:OPENSSL_armcap_P\n")

util/fipstools/delocate/testdata/aarch64-Basic/in.s

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,22 @@ foo:
5151

5252
bl bss_symbol_bss_get
5353

54+
// Known symbol address load (global function defined in module)
55+
adrp x0, foo
56+
add x1, x0, :lo12:foo
57+
58+
// Known symbol address load with no-op add
59+
adrp x0, foo
60+
add x0, x0, :lo12:foo
61+
62+
// Known symbol load
63+
adrp x10, foo
64+
ldr x0, [x10, :lo12:foo]
65+
66+
// Known symbol address load with offset
67+
adrp x6, foo+4096
68+
add x6, x6, :lo12:foo+4096
69+
5470
// Regression test for a two-digit index.
5571
ld1 { v1.b }[10], [x9]
5672

util/fipstools/delocate/testdata/aarch64-Basic/out.s

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,47 @@ foo:
142142

143143
bl bss_symbol_bss_get
144144

145+
// Known symbol address load (global function defined in module)
146+
// WAS adrp x0, foo
147+
sub sp, sp, 128
148+
stp x0, x30, [sp, #-16]!
149+
bl .Lboringssl_loadaddr_.Lfoo_local_target
150+
ldp xzr, x30, [sp], #16
151+
add sp, sp, 128
152+
// WAS add x1, x0, :lo12:foo
153+
add x1, x0, #0
154+
155+
// Known symbol address load with no-op add
156+
// WAS adrp x0, foo
157+
sub sp, sp, 128
158+
stp x0, x30, [sp, #-16]!
159+
bl .Lboringssl_loadaddr_.Lfoo_local_target
160+
ldp xzr, x30, [sp], #16
161+
add sp, sp, 128
162+
// WAS add x0, x0, :lo12:foo
163+
164+
// Known symbol load
165+
// WAS adrp x10, foo
166+
sub sp, sp, 128
167+
stp x0, x30, [sp, #-16]!
168+
bl .Lboringssl_loadaddr_.Lfoo_local_target
169+
mov x10, x0
170+
ldp x0, x30, [sp], #16
171+
add sp, sp, 128
172+
// WAS ldr x0, [x10, :lo12:foo]
173+
ldr x0, [x10]
174+
175+
// Known symbol address load with offset
176+
// WAS adrp x6, foo+4096
177+
sub sp, sp, 128
178+
stp x0, x30, [sp, #-16]!
179+
bl .Lboringssl_loadaddr_.Lfoo_local_target
180+
mov x6, x0
181+
ldp x0, x30, [sp], #16
182+
add sp, sp, 128
183+
add x6, x6, +4096
184+
// WAS add x6, x6, :lo12:foo+4096
185+
145186
// Regression test for a two-digit index.
146187
ld1 { v1.b }[10], [x9]
147188

@@ -314,6 +355,17 @@ bss_symbol_bss_get:
314355
.cfi_endproc
315356
.size .Lboringssl_loadgot_stderr, .-.Lboringssl_loadgot_stderr
316357
.p2align 2
358+
.hidden .Lboringssl_loadaddr_.Lfoo_local_target
359+
.type .Lboringssl_loadaddr_.Lfoo_local_target, @function
360+
.Lboringssl_loadaddr_.Lfoo_local_target:
361+
.cfi_startproc
362+
hint #34 // bti c
363+
adrp x0, .Lfoo_local_target
364+
add x0, x0, :lo12:.Lfoo_local_target
365+
ret
366+
.cfi_endproc
367+
.size .Lboringssl_loadaddr_.Lfoo_local_target, .-.Lboringssl_loadaddr_.Lfoo_local_target
368+
.p2align 2
317369
.hidden .LOPENSSL_armcap_P_addr
318370
.type .LOPENSSL_armcap_P_addr, @function
319371
.LOPENSSL_armcap_P_addr:

0 commit comments

Comments
 (0)