Page MenuHomeFreeBSD

rtld: Support DT_RELR relative relocation format
ClosedPublic

Authored by fbsd-phab_maskray.me on Oct 16 2021, 6:43 PM.

Details

Summary

PIE and shared objects usually have many relative relocations. In
2017/2018, a compact relative relocation format RELR was proposed on
https://groups.google.com/g/generic-abi/c/bX460iggiKg/m/GxjM0L-PBAAJ
("Proposal for a new section type SHT_RELR") and is a pre-standard.
RELR usually takes 3% or smaller space than R_*_RELATIVE relocations.
The virtual memory size of a mostly statically linked PIE is typically 5~10% smaller.

ld.lld --pack-dyn-relocs=relr emits RELR relocations. DT_RELR has been
adopted by Android bionic, Linux kernel's arm64 port, Chrome OS (patched
glibc).

This patch adds DT_RELR support to rtld-elf.

Author: Fangrui Song <i@maskray.me>

Diff Detail

Repository
R10 FreeBSD src repository
Lint
Automatic diff as part of commit; lint not applicable.
Unit
Automatic diff as part of commit; unit tests not applicable.

Event Timeline

RELR is like a modern version of Firefox’s ELF hack https://glandium.org/blog/?p=1177#relocations


About "pre-standard": Cary Coutant took over generic-abi maintenanceship (https://groups.google.com/g/generic-abi/c/9OO5vhxb00Y "Ongoing Maintenance of the gABI") and plans to apply changes including RELR: https://sourceware.org/pipermail/libc-alpha/2021-October/131781.html

My testing on amd64:

# To build just libexec/rtld-elf
MAKEOBJDIRPREFIX=$PWD/obj/default make -j 12 buildenv
export CPUTYPE=  # zsh hack
make -C libexec/rtld-elf

cd /tmp/c
objdir=$HOME/freebsd/obj/default/usr/home/ray/freebsd/amd64.amd64
clang -Wl,--dynamic-linker=$objdir/libexec/rtld-elf/ld-elf.so.1.full -L/tmp/opt/lib -L$objdir/lib -Wl,-rpath=$objdir/lib/libc -fpic -shared b.c -o b.so -Wl,-Bsymbolic -Wl,--pack-dyn-relocs=relr
clang -Wl,--dynamic-linker=$objdir/libexec/rtld-elf/ld-elf.so.1.full -L/tmp/opt/lib -L$objdir/lib -Wl,-rpath=$objdir/lib/libc -fpie -pie a.c ./b.so -o a -Wl,--pack-dyn-relocs=relr
./a
// b.c
#include <stdio.h>

int vb;
int fb() {
  return vb++;
}
// a.c
#include <stdio.h>

int fb();
int main() {
  printf("%d\n", fb());
  printf("%d\n", fb());
  printf("%d\n", fb());
  printf("%d\n", fb());
}
libexec/rtld-elf/rtld.c
3166

Please use normal style(9) conventions for the new code. Most important, use tab for indent, not 4 spaces.

3172

You are checking the lowest bit there, right? I believe & 1 is much more common way to do this.

Initially I was quite confused by the code, but apparently they changed encoding against the initial proposal. Also they made all entries 64bit, right?

IMO the documentation in the tangled thread on some list/google groups is not enough argument to start supporting this stuff. The last activity occured in 2018.

3180

What does this 8 mean?

sys/sys/elf32.h
155 ↗(On Diff #96962)

If they changed relocation entry to be 64bit always, why this?

sys/sys/elf_common.h
622 ↗(On Diff #96962)

Is this value used somehow?

kib removed a subscriber: emaste.
fbsd-phab_maskray.me marked 5 inline comments as done.

address comments

libexec/rtld-elf/rtld.c
3166

OK. I'll use this neovim script for my FreeBSD code:

:so ~/Dev/freebsd/tools/tools/editing/freebsd.vim
:call FreeBSD_Style()
:set ts=8 sw=4 sts=0 noet
3172

Yes, the format changed. Ali Bahrami's comment (scroll to the bottom) is the format used by ld.lld, llvm-readelf -r, Android bionic, Linux kernel, and Chrome OS.

Someone may need to implement readelf -r for FreeBSD's readelf.

3180

CHAR_BIT = 8. I just switched to CHAR_BIT for clarity.

A bitmap entry describes 63 locations on a 64-bit machine. where skips 63 locations.

sys/sys/elf32.h
155 ↗(On Diff #96962)

They use typedef Elf32_Word Elf32_Relr; for ELFCLASS32. ld.lld --pack-dyn-relocs=relr and llvm-readelf -r use 32-bit on ELFCLASS32 as well (llvm-project llvm/include/llvm/BinaryFormat/ELF.h)

sys/sys/elf_common.h
622 ↗(On Diff #96962)

I just added an assert for DT_RELRENT.

ld.lld's RELR is robust since 2019-04 (https://reviews.llvm.org/D67164) when I fixed an oscillation bug.

Better test:

% cat relr.c
static int o, x;

#define ELEMS O O O O O O O O X X X X X X X O O X O O X X X E X E E O X O E
#define E 0,

#define O &o,
#define X &x,
void *arr[] = { ELEMS };
#undef O
#undef X

#define O 1,
#define X 2,
static char val[] = { ELEMS };

int main()
{
  for (int i = 0; i < sizeof (arr) / sizeof (arr[0]); i++)
    if (!((arr[i] == 0 && val[i] == 0) ||
          (arr[i] == &o && val[i] == 1) ||
          (arr[i] == &x && val[i] == 2)))
      return 1;
  return 0;
}

% cat /tmp/opt/lib/libc.so 
/* $FreeBSD: releng/12.2/lib/libc/libc.ldscript 258283 2013-11-17 22:52:17Z peter $ */
GROUP ( libc/libc.so.7 libc_nonshared/libc_nonshared.a libssp_nonshared/libssp_nonshared.a )
% clang -Wl,--dynamic-linker=$objdir/libexec/rtld-elf/ld-elf.so.1.full -L/tmp/opt/lib -L$objdir/lib -Wl,-rpath=$objdir/lib/libc -fpie -pie relr.c -o relr -Wl,--pack-dyn-relocs=relr
% ./relr  # good

# Without DT_RELR support, segfault:
% clang -fpie -pie relr.c -o relr -Wl,--pack-dyn-relocs=relr
% ./relr
[1]    45961 segmentation fault (core dumped)  ./relr

Please split the patch into two: one for sys/ adding the needed definitions, and one for rtld. Then send me complete git patches with all metadata and tags filled.

libexec/rtld-elf/rtld.c
3171

There should be a blank line after the local declaration.

3177

If you use local inside block, why not move its declaration into for loop init clause?

3178

!= 0

fbsd-phab_maskray.me marked 2 inline comments as done.

address style issues

rebase on D32526

This revision was not accepted when it landed; it landed in state Needs Review.Oct 16 2021, 11:39 PM
This revision was automatically updated to reflect the committed changes.
arichardson added inline comments.
libexec/rtld-elf/rtld.c
3166

For most code git-clang-format should be almost correct. However rtld still uses different indentation for some functions...

3172

Or we just switch to MK_LLVM_BINUTILS by default :)

libexec/rtld-elf/rtld.c
3172

Or both - even when MK_LLVM_BINUTILS lands we presumably will not MFC it, but people may well run readelf from older FreeBSD branches to inspect objects built with RELR.

@fbsd-phab_maskray.me tell the Linux folks that they don't want to be too far behind FreeBSD :)