Bug 31567 - gas/ld: Implicit addends for non-code sections
Summary: gas/ld: Implicit addends for non-code sections
Status: NEW
Alias: None
Product: binutils
Classification: Unclassified
Component: gas (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2024-03-28 07:00 UTC by Fangrui Song
Modified: 2024-03-28 07:00 UTC (History)
0 users

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Fangrui Song 2024-03-28 07:00:19 UTC
ELF defines two relocation formats, REL and RELA. REL uses implicit addends, saving space compared to RELA's explicit addend field.
However, REL is often inadequate for static code relocations because the instruction to be modified (commonly 4 bytes on RISC architectures) limits the available space for the addend.

GNU assembler generates RELA static relocations for most ports and generates REL for very few ones (AArch32, BPF, M32R, MIPS o32).
However, using RELA can be unnecessarily bulky for data and debug sections where the constraints on addend size don't apply.

I propose that we add an assembler option `-Wa,--implicit-addends-for-data` to allow non-code sections to use implicit addends to save space.

In a clang -O0 -g -gpubnames build, using REL for non-code sections (sections without the SHF_EXECINSTR flag) decreased relocation size by 27.1% and the .o file size by 6.4%.
Using CREL (`clang -Wa,--crel,--implicit-addends-for-data`) decreased the .o file size by 21.6%!

```
         |reloc size | .o size
---------+-----------+------------
RELA     | 550519056 | 2339938120  
REL data | 401209104 | 2190607000  -Wa,--implicit-addends-for-data  
CREL     |  44865612 | 1834284744  -Wa,--crel,--implicit-addends-for-data 
```

```
# https://github.com/MaskRay/llvm-project/tree/demo-crel
clang -fuse-ld=lld -Wa,--implicit-addends-for-data a.c -o a
```

The saving is less pronounced in a non-debug -O3 build (2.3% .o file size) due to relatively fewer data relocations.

```
         |reloc size | .o size
---------+-----------+-----------
RELA     | 28235664  | 136014800
REL data | 25081480  | 132847952
CREL     |  3812677  | 111591872  -Wa,--implicit-addends-for-data  
CREL     |  3482387  | 111261704  -Wa,--crel,--implicit-addends-for-data 
```

---

Technically, data sections can have static code relocations because assemblers don't reject `.data; call foo`.
Since we are adding an opt-in assembler option, we can rely on the user to do the right thing and only use data directives.

For a custom section, `sec->use_rela_p` is at a location matching the following stack trace.
```
perform_an_assembly_pass
  read_a_source_file
    obj_elf_section
      change_section
        subseg_force_new
          subseg_get
            bfd_make_section_anyway
              bfd_make_section_anyway_with_flags
                bfd_section_init
                  _bfd_elf_new_section_hook
```

_bfd_elf_new_section_hook sets `sec->use_rela_p = bed->default_use_rela_p;`

---

The GNU ld support for REL data relocations seems good, but .eh_frame needs to be fixed.

```
# clang is patched with my user branch mentioned by https://maskray.me/blog/2024-03-09-a-compact-relocation-format-for-elf#relleb-for-dynamic-relocations

% fclang -Wa,--implicit-addends-for-data a.c b.c -fuse-ld=bfd -o a
/usr/bin/ld.bfd: .eh_frame_hdr refers to overlapping FDEs
/usr/bin/ld.bfd: final link failed: bad value
clang: error: linker command failed with exit code 1 (use -v to see invocation)

% fclang -Wa,--implicit-addends-for-data a.c b.c -fuse-ld=bfd -o a -fno-asynchronous-unwind-tables
% ./a
hello
h
0x5633b227e020 0x5633b227e028

% fclang -Wa,--implicit-addends-for-data a.c b.c -fuse-ld=bfd -o a -fno-asynchronous-unwind-tables -static --target=aarch64-linux-gnu
% ./a
hello
h
0x490050 0x490058
```