[bolt] Bolt is creating instruction patches even for skipped functions.
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
I have a application where I skip several functions in reloc mode. The bolted code is also bigger than original code and hence I see it emitted in a new place. However I see that there are instruction patches created even for the code that are in skip list. If the code is large enough, the relocations that are applied with respect to new section for all such patches, will start overflowing. Here is an example:
`$ cat skip-func-scan-refs.s`
```asm
.text
.globl _start
.type _start, %function
_start:
mov w0, #5
bl skip_me
bl hot_func
mov w8, #93
svc #0
.size _start, .-_start
## 2MB of NOPs to push BOLT's new sections >1MB from original .text.
.type bloat, %function
bloat:
.rept 524288
nop
.endr
ret
.size bloat, .-bloat
## Data embedded in .text, referenced by skip_me via ADR.
.p2align 2
my_data:
.word 0x10, 0x20, 0x30, 0x40
.word 0x50, 0x60, 0x70, 0x80
.word 0x90, 0xa0, 0xb0, 0xc0
.word 0xd0, 0xe0, 0xf0, 0xff
## Function skipped via --skip-funcs=skip_me.
## Contains ADR to my_data. Without the fix, scanExternalRefs creates a
## __BP_N patch for this ADR that fails with ADRLiteral21 out of range.
.globl skip_me
.type skip_me, %function
skip_me:
cmp w0, #15
b.hi .Lbad
adr x8, my_data
ldr w0, [x8, w0, uxtw #2]
ret
.Lbad:
mov w0, #-1
ret
.size skip_me, .-skip_me
## Hot function that BOLT processes normally.
.globl hot_func
.type hot_func, %function
hot_func:
mul w0, w0, w0
add w0, w0, #1
ret
.size hot_func, .-hot_func
```
```console
$ llvm-mc -filetype=obj -triple aarch64-linux skip-func-scan-refs.s -o skip.o
$ ld.lld -pie --emit-relocs --nostdlib -o skip.exe skip.o
$ llvm-bolt -o skip.bolt skip.exe --skip-funcs=skip_me
BOLT-INFO: shared object or position-independent executable detected
BOLT-INFO: Target architecture: aarch64
BOLT-INFO: BOLT version: e5460bf266669571df592cd3be39476b2eb13755
BOLT-INFO: first alloc address is 0x0
BOLT-INFO: creating new program header table at address 0x400000, offset 0x400000
BOLT-INFO: enabling relocation mode
BOLT-INFO: number of removed linker-inserted veneers: 0
BOLT-INFO: 0 out of 5 functions in the binary (0.0%) have non-empty execution profile
BOLT-INFO: Starting stub-insertion pass
BOLT-INFO: Inserted 0 stubs in the hot area and 0 stubs in the cold area. Shared 0 times, iterated 1 times.
BOLT-WARNING: Running parallel work of 0 estimated cost, will switch to trivial scheduling.
BOLT-INFO: rewritten pac-ret DWARF info in 0 out of 5 functions (0.00%).
BOLT-ERROR: JITLink failed: In graph in-memory object file, section .local.text.__BP_0: relocation target 0x600018 (:0x600000 + 0x18) is out of range of ADRLiteral21 fixup at address 0x600000 ($x, 0x210270 + 0x0)
```
Since we are skipping these functions, we really do not need to readjust their non-branch instructions with new (but actually old here) location.
I see the bug here : https://github.com/llvm/llvm-project/blob/main/bolt/lib/Rewrite/RewriteInstance.cpp#L3694
```c++
if (!shouldDisassemble(Function)) {
NamedRegionTimer T("scan", "scan functions", "buildfuncs",
"Scan Binary Functions", opts::TimeBuild);
Function.scanExternalRefs(); <====== No checks for skipped functions . If we do check for `!Function.isIgnored()` we avoid this patch
Function.setSimple(false); <=====
continue;
}
```
This `scanEnternalRefs` is creating the `__BP_N` instruction patches and keeping them in a far away section `.local.text.__BP_N` . ADR above is using this section address, not the original address where it will be written back causing the overflow.
For functions that fail `shouldDisassemble()`, they may be complex enough for CFG analysis and require patches. The above condition preserves such behavior.
I will send a PR, please review comment and let me know if I am wrong.
Contributor guide
Research direction
Start in bolt/lib/Rewrite/RewriteInstance.cpp around line 3694 and read the shouldDisassemble path that calls Function.scanExternalRefs(). Reproduce the failure with the supplied AArch64 assembly, llvm-mc, ld.lld, and llvm-bolt commands using --skip-funcs=skip_me. Done means skipped functions no longer create the failing ADRLiteral21 patch while the existing handling for functions requiring CFG analysis remains intact.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers, tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100