Skip to content

[JITLink][AArch32] Add GOT builder and implement R_ARM_GOT_PREL relocations for ELF #78753

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ enum EdgeKind_aarch32 : Edge::Kind {
/// Absolute 32-bit value relocation
Data_Pointer32,

LastDataRelocation = Data_Pointer32,
/// Create GOT entry and store offset
Data_RequestGOTAndTransformToDelta32,

LastDataRelocation = Data_RequestGOTAndTransformToDelta32,

///
/// Relocations of class Arm (covers fixed-width 4-byte instruction subset)
Expand Down Expand Up @@ -318,6 +321,18 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
llvm_unreachable("Relocation must be of class Data, Arm or Thumb");
}

/// Populate a Global Offset Table from edges that request it.
class GOTBuilder : public TableManager<GOTBuilder> {
public:
static StringRef getSectionName() { return "$__GOT"; }

bool visitEdge(LinkGraph &G, Block *B, Edge &E);
Symbol &createEntry(LinkGraph &G, Symbol &Target);

private:
Section *GOTSection = nullptr;
};

/// Stubs builder for v7 emits non-position-independent Thumb stubs.
///
/// Right now we only have one default stub kind, but we want to extend this
Expand Down
7 changes: 7 additions & 0 deletions llvm/lib/ExecutionEngine/JITLink/ELF_aarch32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ Expected<aarch32::EdgeKind_aarch32> getJITLinkEdgeKind(uint32_t ELFType) {
switch (ELFType) {
case ELF::R_ARM_ABS32:
return aarch32::Data_Pointer32;
case ELF::R_ARM_GOT_PREL:
return aarch32::Data_RequestGOTAndTransformToDelta32;
case ELF::R_ARM_REL32:
return aarch32::Data_Delta32;
case ELF::R_ARM_CALL:
Expand Down Expand Up @@ -71,6 +73,8 @@ Expected<uint32_t> getELFRelocationType(Edge::Kind Kind) {
return ELF::R_ARM_REL32;
case aarch32::Data_Pointer32:
return ELF::R_ARM_ABS32;
case aarch32::Data_RequestGOTAndTransformToDelta32:
return ELF::R_ARM_GOT_PREL;
case aarch32::Arm_Call:
return ELF::R_ARM_CALL;
case aarch32::Arm_Jump24:
Expand Down Expand Up @@ -222,6 +226,9 @@ Error buildTables_ELF_aarch32(LinkGraph &G) {

StubsManagerType StubsManager;
visitExistingEdges(G, StubsManager);
aarch32::GOTBuilder GOT;
visitExistingEdges(G, GOT);

return Error::success();
}

Expand Down
71 changes: 59 additions & 12 deletions llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ Expected<int64_t> readAddendData(LinkGraph &G, Block &B, Edge::OffsetT Offset,
switch (Kind) {
case Data_Delta32:
case Data_Pointer32:
case Data_RequestGOTAndTransformToDelta32:
return SignExtend64<32>(support::endian::read32(FixupPtr, Endian));
default:
return make_error<JITLinkError>(
Expand Down Expand Up @@ -464,15 +465,6 @@ Error applyFixupData(LinkGraph &G, Block &B, const Edge &E) {
char *BlockWorkingMem = B.getAlreadyMutableContent().data();
char *FixupPtr = BlockWorkingMem + E.getOffset();

auto Write32 = [FixupPtr, Endian = G.getEndianness()](int64_t Value) {
assert(isInt<32>(Value) && "Must be in signed 32-bit range");
uint32_t Imm = static_cast<int32_t>(Value);
if (LLVM_LIKELY(Endian == endianness::little))
endian::write32<endianness::little>(FixupPtr, Imm);
else
endian::write32<endianness::big>(FixupPtr, Imm);
};

Edge::Kind Kind = E.getKind();
uint64_t FixupAddress = (B.getAddress() + E.getOffset()).getValue();
int64_t Addend = E.getAddend();
Expand All @@ -487,16 +479,24 @@ Error applyFixupData(LinkGraph &G, Block &B, const Edge &E) {
int64_t Value = TargetAddress - FixupAddress + Addend;
if (!isInt<32>(Value))
return makeTargetOutOfRangeError(G, B, E);
Write32(Value);
if (LLVM_LIKELY(G.getEndianness() == endianness::little))
endian::write32le(FixupPtr, Value);
else
endian::write32be(FixupPtr, Value);
return Error::success();
}
case Data_Pointer32: {
int64_t Value = TargetAddress + Addend;
if (!isInt<32>(Value))
if (!isUInt<32>(Value))
return makeTargetOutOfRangeError(G, B, E);
Write32(Value);
if (LLVM_LIKELY(G.getEndianness() == endianness::little))
endian::write32le(FixupPtr, Value);
else
endian::write32be(FixupPtr, Value);
return Error::success();
}
case Data_RequestGOTAndTransformToDelta32:
llvm_unreachable("Should be transformed");
default:
return make_error<JITLinkError>(
"In graph " + G.getName() + ", section " + B.getSection().getName() +
Expand Down Expand Up @@ -678,6 +678,52 @@ Error applyFixupThumb(LinkGraph &G, Block &B, const Edge &E,
}
}

const uint8_t GOTEntryInit[] = {
0x00,
0x00,
0x00,
0x00,
};

/// Create a new node in the link-graph for the given pointer value.
template <size_t Size>
static Block &allocPointer(LinkGraph &G, Section &S,
const uint8_t (&Content)[Size]) {
static_assert(Size == 4, "Pointers are 32-bit");
constexpr uint64_t Alignment = 4;
ArrayRef<char> Init(reinterpret_cast<const char *>(Content), Size);
return G.createContentBlock(S, Init, orc::ExecutorAddr(), Alignment, 0);
}

Symbol &GOTBuilder::createEntry(LinkGraph &G, Symbol &Target) {
if (!GOTSection)
GOTSection = &G.createSection(getSectionName(), orc::MemProt::Read);
Block &B = allocPointer(G, *GOTSection, GOTEntryInit);
constexpr int64_t GOTEntryAddend = 0;
B.addEdge(Data_Pointer32, 0, Target, GOTEntryAddend);
return G.addAnonymousSymbol(B, 0, B.getSize(), false, false);
}

bool GOTBuilder::visitEdge(LinkGraph &G, Block *B, Edge &E) {
Edge::Kind KindToSet = Edge::Invalid;
switch (E.getKind()) {
case aarch32::Data_RequestGOTAndTransformToDelta32: {
KindToSet = aarch32::Data_Delta32;
break;
}
default:
return false;
}
LLVM_DEBUG(dbgs() << " Transforming " << G.getEdgeKindName(E.getKind())
<< " edge at " << B->getFixupAddress(E) << " ("
<< B->getAddress() << " + "
<< formatv("{0:x}", E.getOffset()) << ") into "
<< G.getEdgeKindName(KindToSet) << "\n");
E.setKind(KindToSet);
E.setTarget(getEntryForTarget(G, E.getTarget()));
return true;
}

const uint8_t Thumbv7ABS[] = {
0x40, 0xf2, 0x00, 0x0c, // movw r12, #0x0000 ; lower 16-bit
0xc0, 0xf2, 0x00, 0x0c, // movt r12, #0x0000 ; upper 16-bit
Expand Down Expand Up @@ -709,6 +755,7 @@ const char *getEdgeKindName(Edge::Kind K) {
switch (K) {
KIND_NAME_CASE(Data_Delta32)
KIND_NAME_CASE(Data_Pointer32)
KIND_NAME_CASE(Data_RequestGOTAndTransformToDelta32)
KIND_NAME_CASE(Arm_Call)
KIND_NAME_CASE(Arm_Jump24)
KIND_NAME_CASE(Arm_MovwAbsNC)
Expand Down
50 changes: 42 additions & 8 deletions llvm/test/ExecutionEngine/JITLink/AArch32/ELF_relocations_data.s
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# RUN: llvm-mc -triple=armv7-none-linux-gnueabi -arm-add-build-attributes -filetype=obj -o %t_armv7.o %s
# RUN: llvm-objdump -r %t_armv7.o | FileCheck --check-prefix=CHECK-TYPE %s
# RUN: rm -rf %t && mkdir -p %t/armv7 && mkdir -p %t/thumbv7
# RUN: llvm-mc -triple=armv7-none-linux-gnueabi -arm-add-build-attributes -filetype=obj -o %t/armv7/out.o %s
# RUN: llvm-objdump -r %t/armv7/out.o | FileCheck --check-prefix=CHECK-TYPE %s
# RUN: llvm-jitlink -noexec -slab-address 0x76ff0000 -slab-allocate 10Kb -slab-page-size 4096 \
# RUN: -abs target=0x76bbe88f -check %s %t_armv7.o
# RUN: -abs target=0x76bbe88f -check %s %t/armv7/out.o

# RUN: llvm-mc -triple=thumbv7-none-linux-gnueabi -arm-add-build-attributes -filetype=obj -o %t_thumbv7.o %s
# RUN: llvm-objdump -r %t_thumbv7.o | FileCheck --check-prefix=CHECK-TYPE %s
# RUN: llvm-mc -triple=thumbv7-none-linux-gnueabi -arm-add-build-attributes -filetype=obj -o %t/thumbv7/out.o %s
# RUN: llvm-objdump -r %t/thumbv7/out.o | FileCheck --check-prefix=CHECK-TYPE %s
# RUN: llvm-jitlink -noexec -slab-address 0x76ff0000 -slab-allocate 10Kb -slab-page-size 4096 \
# RUN: -abs target=0x76bbe88f -check %s %t_thumbv7.o
# RUN: -abs target=0x76bbe88f -check %s %t/thumbv7/out.o

.data
.global target
Expand All @@ -28,10 +29,43 @@ rel32:
.word target - .
.size rel32, .-rel32

# Empty main function for jitlink to be happy
# CHECK-TYPE: {{[0-9a-f]+}} R_ARM_GOT_PREL target
#
# The GOT entry contains the absolute address of the external:
# jitlink-check: *{4}(got_addr(out.o, target)) = target
#
# The embedded offset value contains the offset to the GOT entry relative to pc.
# The +12 accounts for the ARM branch offset (8) and the .LPC offset (4), which
# is stored as initial addend inline.
# FIXME: We shouldn't need to substract the 64-bit sign-extension manually.
# jitlink-check: *{4}got_prel_offset = got_addr(out.o, target) - (got_prel + 12) - 0xffffffff00000000
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The offset is a signed 32-bit value and it doesn't get sign-extended to JITLink's 64-bit target address type. We should probably fix that, but it seems outside the scope of this PR.

.globl got_prel
.type got_prel,%function
.p2align 2
.code 32
got_prel:
ldr r0, .LCPI
.LPC:
ldr r0, [pc, r0]
ldr r0, [r0]
bx lr
# Actual relocation site is on the embedded offset value:
.globl got_prel_offset
got_prel_offset:
.LCPI:
.long target(GOT_PREL)-((.LPC+8)-.LCPI)
.size got_prel_offset, .-got_prel_offset
.size got_prel, .-got_prel

# This test is executable with any 4-byte external target:
# > echo "unsigned target = 42;" | clang -target armv7-linux-gnueabihf -o target.o -c -xc -
# > llvm-jitlink target.o armv7/out.o
#
.globl main
.type main, %function
.p2align 2
main:
bx lr
push {lr}
bl got_prel
pop {pc}
.size main, .-main