taskident.c:62: relocation truncated to fit: R_MICROBLAZE_32_PCREL_LO
against symbol `_RTEMS_Name_to_id' defined in .text._RTEMS_Name_to_id
section in ./librtemscpu.a(rtemsnametoid.c.59.o)
Worked with claude a bit on this.
Came to the conclusion that I should be adding -Wl,--no-relax to my compile flags.
I had it look at how other processors are handling linkers and am attaching a possible AI generated ticket notes.
It might be worth adding the -Wl,--no-relax to the stock build?
MicroBlaze linker relaxation: R_MICROBLAZE_32_PCREL_LO truncation
Status: worked-around (not fixed). Follow-up tracked here.
Component: binutils / ld (BFD), MicroBlaze backend — bfd/elf32-microblaze.c.
Toolchain: microblaze-rtems7-*, binutils 2.36.1 (sourceware base commit 7af075d)
plus Xilinx meta-xilinx rel-v2021.1 patches 0001–0013.
Affects: any link of a large MicroBlaze executable (the RTEMS kernel + a heavy
test/app) when linker relaxation is enabled (the default).
1. Symptom
Linking certain test programs fails:
./librtemscpu.a(taskident.c.59.o): in function `rtems_task_ident':
taskident.c:62: relocation truncated to fit: R_MICROBLAZE_32_PCREL_LO
against symbol `_RTEMS_Name_to_id' defined in .text._RTEMS_Name_to_id
section in ./librtemscpu.a(rtemsnametoid.c.59.o)
collect2: error: ld returned 1 exit status
It is the MicroBlaze linker relaxation pass mis-shortening a function call.
Measured facts (BSP microblaze/kcu105, BUILD_TESTS=True, relax on)
- The compiler emits the correct 2-instruction PC-relative call
(imm+brlid, relocR_MICROBLAZE_64_PCREL, full ±2 GiB reach). - In the failing image the two functions land 32.6 KiB apart
(_RTEMS_Name_to_id−rtems_task_ident=0x8268= 33384 bytes),
just past the ±32 KiB reach of a singlebrlid(16-bit signed field). - Total
.textis only ~101 KiB; the gap is filled by ~hundreds of ordinary
kernel functions (scheduler, thread-queue, timecounter, fs-eval), no single
large symbol. - Scope: of 753 test exes, 24 link both symbols (the rest drop
rtems_task_identvia--gc-sections); of those, ~6 exceed 32 KiB and
truncate. So it is a handful of large images, not all builds. The library
build itself (objects only, no final link) never truncates.
2. Root cause (source-grounded)
File: bfd/elf32-microblaze.c, function microblaze_elf_relax_section.
Relaxation drops the redundant imm when it believes the target is within the
single-instruction range, and downgrades the reloc
R_MICROBLAZE_64_PCREL → R_MICROBLAZE_32_PCREL_LO (16-bit,
complain_overflow_signed, HOWTO ~line 103).
The “shall I drop the imm?” gate (2.36.1, ~line 1864):
if ((symval & 0xffff8000) == 0) /* accepts forward disp in [0, 0x7fff] */
{
/* We can delete this instruction. */
...
irel->r_info = ELF32_R_INFO (..., R_MICROBLAZE_32_PCREL_LO); /* ~line 1876 */
}
Three properties combine into the defect:
- The decision is committed irreversibly, early. Once the
immis deleted
and the reloc downgraded, every later pass skips it (the reloc-type filter at
~line 1785 only reconsidersR_MICROBLAZE_64_PCREL/_64/_TEXTREL_64).
Theimmis physically gone; it cannot be restored. - It does iterate, but never re-validates. The pass sets
*again = TRUE
when it changed anything (~line 2316), so the linker re-lays-out and re-runs —
but only to let other sections relax; an already-downgraded call is never
re-checked. - No safety margin.
symvalis the displacement against the layout at
decision time. Because code shrinks during relaxation, downstreamALIGN/
ALIGN_WITH_INPUTpadding can change, so a target’s final offset is not a
monotonic decrease of the decision-time estimate. A call estimated just under
the boundary can end up just over it (our case: +616 bytes past 32 KiB). The
finalrelocate_sectionthen emits “truncated to fit” — the safe failure
(it refuses to write a wrong branch rather than miscompile).
The in-tree comment confirms the author declined to handle this:
/* We only do this once per section. We may be able to delete some code
by running multiple passes, but it is not worth it. */
Not fixed upstream
Same defect verified in: sourceware 2.36.1 (our toolchain), sourceware 2.42,
Xilinx binutils-2_23-branch, and Xilinx master (a stale 2013 tree). 2.42 is
more aggressive (also relaxes backward calls) and no safer. Bumping binutils
does not fix this. Also review existing Xilinx patches 0004-LOCAL-Fix- relaxation-... and 0010-fixing-the-imm-bug for overlap — they do not cover
this case.
3. Current workaround (already applied)
configs/config_kcu105.ini:
BUILD_TESTS=FalseandBUILD_SAMPLES=False— the RTEMS tree builds libraries
only, so no executable link step exists to truncate.- (Alternative, when exes are wanted) add
-Wl,--no-relaxtoABI_FLAGS: keeps
everyimm, ~2% larger.text, sub-1% runtime, and more deterministic call
encodings (a WCET plus). Measured: sp01 +1.98%, ticker +1.87%, hello +2.19%.
The truncation risk still exists wherever the flight executable is finally
linked (outside this tree). If that image grows past the gap, that link will
fail the same way; fix is targeted -Wl,--no-relax on that one link.
4. Follow-up A — TEMP FIX: AVR-style safety margin (small, heuristic)
Goal: keep relaxation (recover the ~2%) while preventing the truncation, with
a minimal, well-precedented bfd change.
Precedent: bfd/elf32-avr.c:2655-2694. AVR relaxes call→rcall /
jmp→rjmp (same shape as the imm-drop) and guards the identical failure mode
(“shrinking the code size makes the gaps larger … use a heuristical safety
margin to avoid that during relax the distance gets again too large”).
Change: in microblaze_elf_relax_section, lower the acceptance ceiling by a
margin so a committed short call has slack to absorb alignment-induced growth.
/* elf32-microblaze.c — replace the gate (~line 1864, 2.36.1 forward-only form) */
#define MB_RELAX_SAFETY_MARGIN 0x400 /* bytes; absorbs ALIGN re-padding */
- if ((symval & 0xffff8000) == 0)
+ /* Keep the imm unless the forward displacement fits with margin to spare;
+ later relaxation passes can grow this gap via ALIGN padding and the
+ deletion is irreversible (reloc is downgraded to _32_PCREL_LO). See
+ the analogous safety margin in elf32-avr.c. */
+ if (symval <= (bfd_vma) (0x7fff - MB_RELAX_SAFETY_MARGIN))
(If working from the 2.42-style gate that also relaxes backward calls, apply the
margin symmetrically to both the == 0 and == 0xffff8000 bounds.)
Integration (RSB):
- Create
0014-microblaze-relax-imm-safety-margin.patchagainst the patched
2.36.1 tree (apply after 0001–0013; verify line context shifts). - Add to
src/rsb/rtems/config/tools/rtems-xilinx-binutils-2.36.cfg:
(Local patches can be hosted in-tree rather than via%patch add binutils -p1 file://.../0014-microblaze-relax-imm-safety-margin.patch %hash sha512 0014-microblaze-relax-imm-safety-margin.patch <sha>xilinx_github_url.) - Rebuild the toolchain (
build_tools.sh/ rsb), reinstall to/local/opt/rtems/7.
Verify:
config_kcu105.ini: setBUILD_TESTS=True, remove-Wl,--no-relax../waf -j30cleanly links all test/sample exes (esp. sp42, spfatal36).- Confirm relaxation still active and
.text~2% smaller than the--no-relax
build (proves imms are still being dropped where safe). - Spot-check sp42: the
rtems_task_ident→_RTEMS_Name_to_idcall retains its
imm(objdump -dr), no_32_PCREL_LOtruncation.
Effort: ~0.5–1 day. Risk: heuristic — a fixed margin is not provably
sufficient (worst-case alignment growth scales with the number of ALIGN
boundaries between caller and target). AVR itself calls it “heuristical.” Pick a
margin comfortably above observed growth; re-evaluate if images grow a lot.
Caveat for flight: this is a hand-maintained bfd patch you own and must
qualify; --no-relax remains lower-risk for the flight image.
5. Follow-up B — REAL FIX: RISC-V-style alignment relocations (large, correct)
Goal: make relaxation provably correct, eliminating the truncation class
entirely instead of papering over it.
Precedent: bfd/elfnn-riscv.c (+ gas/config/tc-riscv.c). RISC-V’s relax is
the modern reference: the assembler emits an explicit R_RISCV_ALIGN
relocation at every alignment directive, recording the required alignment and the
worst-case padding it reserved. The linker then knows exact alignment constraints
as it deletes bytes, so downstream addresses are computed correctly and a
committed relaxation can never go out of range. (AVR carries the analogous
R_AVR_ALIGN in addition to its margin.)
Scope (this is a real toolchain project, gas + bfd):
- Define
R_MICROBLAZE_ALIGN— new reloc type + HOWTO in
bfd/elf32-microblaze.cand the ELF reloc tables (include/elf/microblaze.h). - gas: emit
R_MICROBLAZE_ALIGNat.align/.p2alignin code sections,
reserving worst-case padding (gas/config/tc-microblaze.c), mirroring
riscv_handle_align/tc-riscv.c. - bfd relax: teach
microblaze_elf_relax_sectionto honor alignment relocs
when computing distances and when deleting bytes, so estimates are exact; then
the existing range check (no margin needed) becomes correct. Model the
delete-bytes + alignment handling on_bfd_riscv_relax_delete_bytes/
_bfd_riscv_relax_align. - Relocatable links / other tools must tolerate the new reloc
(objcopy,ld -r,readelf).
Verify: all of Follow-up A’s checks, plus with MB_RELAX_SAFETY_MARGIN
removed (margin no longer needed); add a torture test that places a call exactly
at the ±32 KiB boundary across several large .align directives and confirm no
truncation and correct runtime behavior.
Effort: weeks; spans gas + bfd; ideally upstreamed to sourceware (and/or
Xilinx) so it is not a perpetual local carry. Risk: high surface area, but it
is the only option that removes the failure mode rather than reducing its
probability.
6. Recommendation
- Now: keep the workaround (libraries-only here;
-Wl,--no-relaxon any
oversized final link, including the flight image). Zero toolchain risk. - Short term (optional): Follow-up A if the ~2% size/relaxation is wanted back
on test/dev builds. - Long term (only if MicroBlaze stays strategic): Follow-up B, upstreamed.
- For flight, prefer
--no-relaxover carrying a relaxation patch:
deterministic, qualifiable, and the ~2%/sub-1% cost is negligible.
7. References
bfd/elf32-microblaze.c—microblaze_elf_relax_section: gate ~line 1864,
reloc downgrade ~1876, reloc-type filter ~1785,*again~2316; HOWTO ~103.bfd/elf32-avr.c:2655-2694— safety-margin precedent.bfd/elfnn-riscv.c,gas/config/tc-riscv.c—R_RISCV_ALIGNprecedent.- RSB:
src/rsb/rtems/config/tools/rtems-xilinx-binutils-2.36.cfg(patch chain),
src/rsb/rtems/config/7/rtems-microblaze.bset(version selection). - Repro BSP/config:
configs/config_kcu105.ini, build dir
build/microblaze/kcu105.