llvm / llvm/llvm-project

[llvm-objcopy] llvm-objcopy --change-section-lma shifts unaddressed segments (e.g. PT_RISCV_ATTRIBUTES), causing spurious underflow

Open
#218,858 2 comments 0 reactions 0 assignees View on GitHub
tools:llvm-objcopy/strip
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

`--change-section-lma` shifts every segment with `MemSize > 0`, without checking whether the segment was ever assigned a real address. A segment that legitimately has `VAddr == PAddr == 0` (never placed in memory by the linker) but a nonzero size — e.g. a `PT_RISCV_ATTRIBUTES` segment — gets shifted anyway, and a large-enough negative shift underflows and aborts the entire operation, even though other real segments in the same file would have shifted correctly.

### Repro
```
$ cat repro.yaml
!ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Sections:
- Name: .text
Type: SHT_PROGBITS
Size: 0x10
- Name: .riscv.attributes
Type: SHT_PROGBITS
Size: 0x38
ProgramHeaders:
- Type: PT_LOAD
VAddr: 0x1000
FirstSec: .text
LastSec: .text
- Type: 0x70000003 # PT_RISCV_ATTRIBUTES
Flags: [ PF_R ]
VAddr: 0x0
PAddr: 0x0
Align: 0x1
FirstSec: .riscv.attributes
LastSec: .riscv.attributes

$ yaml2obj repro.yaml -o repro.elf
$ llvm-objcopy --change-section-lma *-0x30 repro.elf shifted.elf
error: address 0x0 cannot be decreased by 0x30. The result would underflow
```

I discovered the problem while trying to build a real-world RISC-V ELF (a Zephyr RTOS build for a RISC-V VPR coprocessor core) containing a genuine PT_RISCV_ATTRIBUTES segment using an LLVM toolchain built from the current main (24.0.0). The problem also reproduces with LLVM 19.1.7 that is bundled with Zephyr-SDK-1.0.1. and on current main.

If I configure my build to use the GNU objcopy (also bundled with the same Zephyr SDK) the problem doesn't arise and I get a working executable.

I used Claude to analyze the problem and have created a PR (#218414) with a proposed fix + a new test. The fix is very small and solves the Zephyr problem and passes the llvm-objcopy tests, so it seems that nothing obvious is broken. As I am not versed in the llvm-objcopy nor ELFs there can obviously be something that I don't understand. There seems to have been a previous issue (#124680) that made changes to `--change-section-lma`

### Expected
A segment with no real address `(VAddr == 0 && PAddr == 0)` shouldn't participate in the shift, regardless of its size.

Contributor guide

Open the contributing guide

Research direction

Start with llvm-objcopy's --change-section-lma handling and reproduce the failure using repro.yaml, yaml2obj, and the command shown. Confirm that the unaddressed PT_RISCV_ATTRIBUTES segment does not participate in the shift, while real segments still shift and the operation completes without an underflow error.

Written by the indexing model from the issue text.

Assessment

Domain
compilers, tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.