Skip to content

Commit

Permalink
Relax R_LARCH_TLS_LE_{HI20_R,ADD_R,LO12_R}
Browse files Browse the repository at this point in the history
  • Loading branch information
rui314 committed Jul 27, 2024
1 parent 98a7cff commit 36e5b4b
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 7 deletions.
52 changes: 49 additions & 3 deletions elf/arch-loongarch.cc
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ static void write_d10k16(u8 *loc, u32 val) {
*(ul32 *)loc |= bits(val, 25, 16);
}

static void set_rj(u8 *loc, u32 rj) {
assert(rj < 32);
*(ul32 *)loc &= 0b111111'1111111111111111'00000'11111;
*(ul32 *)loc |= rj << 5;
}

template <>
void write_plt_header<E>(Context<E> &ctx, u8 *buf) {
constexpr ul32 insn_64[] = {
Expand Down Expand Up @@ -459,11 +465,19 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
overwrite_uleb(loc, read_uleb(loc) - S - A);
break;
case R_LARCH_TLS_LE_HI20_R:
write_j20(loc, (S + A + 0x800 - ctx.tp_addr) >> 12);
if (removed_bytes == 0)
write_j20(loc, (S + A + 0x800 - ctx.tp_addr) >> 12);
break;
case R_LARCH_TLS_LE_LO12_R:
write_k12(loc, S + A - ctx.tp_addr);
case R_LARCH_TLS_LE_LO12_R: {
i64 val = S + A - ctx.tp_addr;
write_k12(loc, val);

// Rewrite `addi.d $t0, $t0, <offset>` with `addi.d $t0, $tp, <offset>`
// if the offset is directly accessible using tp. tp is r2.
if (sign_extend(val, 11) == val)
set_rj(loc, 2);
break;
}
case R_LARCH_TLS_LE_ADD_R:
break;
default:
Expand Down Expand Up @@ -677,8 +691,10 @@ void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc) {

for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &r = rels[i];
Symbol<E> &sym = *isec.file.symbols[r.r_sym];
isec.extra.r_deltas[i] = delta;

// Handling R_LARCH_ALIGN is mandatory.
if (r.r_type == R_LARCH_ALIGN) {
i64 nop_size;
if (r.r_sym) {
Expand All @@ -699,6 +715,36 @@ void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc) {
delta += next_loc - align_to(loc, alignment);
continue;
}

// Handling other relocations is optional.
if (!ctx.arg.relax || i == rels.size() - 1 ||
rels[i + 1].r_type != R_LARCH_RELAX)
continue;

// Skip linker-synthesized symbols because their final addresses
// are not fixed yet.
if (sym.file == ctx.internal_obj)
continue;

switch (r.r_type) {
case R_LARCH_TLS_LE_HI20_R:
case R_LARCH_TLS_LE_ADD_R:
// LoongArch uses the following three instructions to access
// TP ± 2 GiB.
//
// lu12i.w $t0, 0 # R_LARCH_TLS_LE_HI20_R
// add.d $t0, $t0, $tp # R_LARCH_TLS_LE_ADD_R
// addi.d $t0, $t0, 0 # R_LARCH_TLS_LE_LO12_R
//
// If the thread-local variable is within TP ± 2 KiB, we can
// relax them into the following single instruction.
//
// addi.d $t0, $tp, <tp-offset>
if (i64 val = sym.get_addr(ctx) + r.r_addend - ctx.tp_addr;
sign_extend(val, 11) == val)
delta += 4;
break;
}
}

isec.extra.r_deltas[rels.size()] = delta;
Expand Down
8 changes: 4 additions & 4 deletions test/elf/tls-le.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ cat <<EOF | $GCC -fPIC -c -o $t/b.o -xc -
__attribute__((tls_model("local-exec"))) _Thread_local int foo = 3;
EOF

$CC -B. -o $t/exe $t/a.o $t/b.o
$QEMU $t/exe | grep -q '3 5 3 5'
$CC -B. -o $t/exe1 $t/a.o $t/b.o
$QEMU $t/exe1 | grep -q '3 5 3 5'

$CC -B. -o $t/exe $t/a.o $t/b.o -Wl,-no-relax
$QEMU $t/exe | grep -q '3 5 3 5'
$CC -B. -o $t/exe2 $t/a.o $t/b.o -Wl,-no-relax
$QEMU $t/exe2 | grep -q '3 5 3 5'

0 comments on commit 36e5b4b

Please sign in to comment.