This is an archive of the discontinued LLVM Phabricator instance.

[lld][AArch64] Add BTI landing pad to PLT entries when the symbol is exported.
ClosedPublic

Authored by danielkiss on Jun 19 2023, 4:17 AM.

Details

Summary

With relative vtables the caller jumps directly to the plt entries in the shared object,
therefore landing pad is need for these entries.

Reproducer:
main.cpp

#include "v.hpp"
int main() {
    A* a = new B();
    a->do_something2();
    return 0;
}

v.hpp

struct A {
    virtual void do_something() = 0;
    virtual void do_something2();
};
struct B : public A {
    void do_something() override;
    void do_something2() override;
};

v.cpp

#include "v.hpp"
void A::do_something2() { }
void B::do_something() { }
void B::do_something2() { }
CC="clang++ --target=aarch64-unknown-linux-gnu -fuse-ld=lld -mbranch-protection=bti"
F=-fexperimental-relative-c++-abi-vtables
${=CC} $F -shared v.cpp -o v.so -z force-bti
${=CC} $F main.cpp -L./ v.so -Wl,-rpath=. -z force-bti
qemu-aarch64-static -L /usr/aarch64-linux-gnu -cpu max ./a.out

For v.so, the regular vtable entry is relocated by an R_AARCH64_ABS64 relocation referencing _ZN1B13do_something2Ev.

_ZTV1B:
.xword  _ZN1B13do_something2Ev

Using relative vtable entry for a DSO has a downside of creating many PLT entries and making their addresses escape.
The relative vtable entry references a PLT entry _ZN1B13do_something2Ev@plt.

.L_ZTV1A.local:
        .word   (_ZN1A13do_something2Ev@PLT-.L_ZTV1A.local)-8

fixes: #63580

Diff Detail

Event Timeline

danielkiss created this revision.Jun 19 2023, 4:17 AM
danielkiss requested review of this revision.Jun 19 2023, 4:17 AM

minimised reproducer (crashes on a BTI enabled system without the patch, work with the patch)

main.cpp

#include "v.hpp"

int main()
{
    A* a = new B();
    a->do_something2();
    return 0;
}

v.hpp

struct A
{
    virtual void do_something() = 0;
    virtual void do_something2();
};

struct B : public A
{
    void do_something() override;
    void do_something2() override;
};

v.cpp

#include "v.hpp"
void A::do_something2() { }
void B::do_something() { }
void B::do_something2() { }

bin/clang++ -O0 -fuse-ld=lld -mbranch-protection=bti -fexperimental-relative-c++-abi-vtables -shared v.cpp -o v.so
bin/clang++ -O0 -fuse-ld=lld -mbranch-protection=bti -fexperimental-relative-c++-abi-vtables vmain.cpp -L./ v.so -Wl,-rpath=./
qemu-aarch64 -cpu max a.out

Thanks for the example (needs a --target=aarch64-linux-gnu if cross-compiling). IIUC the problem is mostly visible within v.so. The relative vtable has an offset to the PLT entry for the virtual functions as these can be pre-empted by an interposing shared object. The code in vmain will use the relative vtable entry and indirectly branch to the PLT which without the patch doesn't have a PLT entry.

I've left a suggestion that if it is simple to implement should be more specific. It may not be worth the complexity if it is a lot of work though.

lld/ELF/Arch/AArch64.cpp
919

Could you alter the comment to include the condition. Something like.

// NEEDS_COPY indicates a non-ifunc canonical PLT entry whose address may
// escape to shared objects. isInIplt indicates a non-preemptible ifunc. Its
// address may escape if referenced by a direct relocation. If relative vtables
// are used then if the vtable is in a shared object the offsets will be to the PLT entry.
// The condition is conservative.
920

IIUC this will put BTI at the start of most PLT entries in a shared object. One possibility is to overload thunkAccessed (a relative vtable is kind of thunk :-) , or rename it) so that in Relocations.cpp we can follow the R_AARCH64_PLT32 relocations (specific to relative vtables) and tag affected symbols with thunkAccessed.

Adding Petr Hosek as a reviewer as I think Fuchsia uses relative vtables by default.

danielkiss planned changes to this revision.Jun 19 2023, 1:13 PM
danielkiss added inline comments.
lld/ELF/Arch/AArch64.cpp
920

Looks, could be done in that way too, hopefully with less impact on the number of landing pads.

Adding the example to the description will be useful.

Thank you for the example. I managed to reproduce with:

CC="/tmp/Rel/bin/clang++ --target=aarch64-unknown-linux-gnu -fuse-ld=lld -mbranch-protection=bti"
F=-fexperimental-relative-c++-abi-vtables

${=CC} $F -shared v.cpp -o v.so -z force-bti
${=CC} $F main.cpp -L./ v.so -Wl,-rpath=. -z force-bti
qemu-aarch64-static -L /usr/aarch64-linux-gnu -cpu max ./a.out

For v.so, the regular vtable entry is relocated by an R_AARCH64_ABS64 relocation referencing _ZN1B13do_something2Ev.

_ZTV1B:
...
.xword  _ZN1B13do_something2Ev

Using relative vtable entry for a DSO has a downside of creating many PLT entries and making their addresses escape.
The relative vtable entry references a PLT entry _ZN1B13do_something2Ev@plt.

.L_ZTV1A.local:
...
        .word   (_ZN1A13do_something2Ev@PLT-.L_ZTV1A.local)-8

I agree that we can reuse thunkAccessed. We can do:

case R_AARCH64_PLT32:
  const_cast<Symbol &>(sym).thunkAccessed = true;
  return R_PLT_PC;
danielkiss edited the summary of this revision. (Show Details)

@peter.smith & @MaskRay Thanks for the comments, patch is updated.
Moved the example to a bug.

MaskRay accepted this revision.Jun 28 2023, 1:47 PM

@peter.smith & @MaskRay Thanks for the comments, patch is updated.
Moved the example to a bug.

LGTM. I think some description from https://reviews.llvm.org/D153264#4439762 will be useful in the summary. Feel free to add them.

lld/test/ELF/aarch64-feature-bti.s
164

Indent to align with -NEXT: directives below.

# RELV-LABEL: <func1>:
# RELV-NEXT:    10380: bl     0x103b0 <func2@plt>

The idea is, <func1> will always match. If the section layout has a small disturbance, FileCheck will know for sure that the issue is on # RELV-NEXT: 10380: bl 0x103b0 <func2@plt>, and it will be straightforward to update it.

If you do # RELV: 0000000000010380 <func1>:, there is some chance that FileCheck isn't smart enough to locate the <func1> line

This revision is now accepted and ready to land.Jun 28 2023, 1:47 PM
danielkiss edited the summary of this revision. (Show Details)
danielkiss marked an inline comment as done.
MaskRay accepted this revision.Jun 29 2023, 9:07 AM
MaskRay added inline comments.
lld/test/ELF/aarch64-feature-bti.s
169

If "Disassembly of section .text:" is kept, ensure that it aligns with <.plt>: and <.text>:. It will look nicer.

danielkiss marked an inline comment as done.
peter.smith accepted this revision.Jun 29 2023, 10:34 AM

LGTM too. Thanks for the update.

MaskRay accepted this revision.Jun 29 2023, 11:17 AM
MaskRay added inline comments.
lld/ELF/Arch/AArch64.cpp
920

I think the comment is still worth updating.

danielkiss marked an inline comment as done.Jun 29 2023, 1:15 PM
danielkiss added inline comments.
lld/ELF/Arch/AArch64.cpp
920

I'll land it with the above comment.

Herald added a project: Restricted Project. · View Herald TranscriptJun 29 2023, 1:17 PM