-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[ELF] Handle relocations in synthetic .eh_frame with a non-zero offset within the output section #65966
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will only fix the problem for AArch64, but it also affects other targets. Assuming relocateAlloc is the best place to fix this, then it would need applying in all instances of relocateAlloc.
lld/ELF/Arch/AArch64.cpp
Outdated
@@ -770,6 +770,9 @@ void AArch64::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const { | |||
uint64_t secAddr = sec.getOutputSection()->addr; | |||
if (auto *s = dyn_cast<InputSection>(&sec)) | |||
secAddr += s->outSecOff; | |||
else if (auto *eh = dyn_cast<EhInputSection>(&sec)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this problem specific to AArch64? It would also affect the other targets that use .eh_frame such as x86_64.
As I understand it this was triggered by the Picolibc linker script (https://github.com/picolibc/picolibc/blob/main/picolibc.ld.in) with extract:
.except_ordered : {
*(.gcc_except_table *.gcc_except_table.*)
KEEP (*(.eh_frame .eh_frame.*))
*(.ARM.extab* .gnu.linkonce.armextab.*)
} >flash AT>flash :text
Which has the .eh_frame sections after .gcc_except_table.
I made a failing test case for x86_64 by editing the output of GNU ld's --verbose and adding a dummy QUAD statement. I've not included the whole ld --verbose output as it is rather long.
/* Insert QUAD(0) to make .eh_frame start at non 0 offset within ouptut section */
.eh_frame : ONLY_IF_RO { QUAD(0); KEEP (*(.eh_frame)) *(.eh_frame.*) }
With the simple test program
#include <stdio.h>
int main(void) {
try {
throw 55;
}
catch (int i) { printf("Caught int %d\n", i); }
catch (...) { printf("Caught generic\n"); }
return 0;
}
With results:
clang++ -fuse-ld=bfd throw.cpp -o throw.exe -Wl,--script=ld.script
./throw.exe
Caught int 55
clang++ -fuse-ld=lld throw.cpp -o throw.exe -Wl,--script=ld.script
./throw.exe
[1] 444896 abort (core dumped) ./throw.exe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah this affects all other targets (might be X86 and PPC as well).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i could not find the general way of fixing this without modifying target->relocateAlloc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, reading through the code. The .ARM.exidx sections were also InputSections so we could alter their outSecOff to compensate. However EhInputSections are not InputSections so this won't work here. We need to find the outSecOff of the ehframe section (which is usually 0, but with some linker scripts isn't).
I don't know of a legal EhInputSection that would return getParent() == nullptr so it should be possible to remove the nested if.
I suggest use ehIn and ehFrame to make it a bit clearer which sections we are referring to. I've use SyntheticSection as it is what EhInputSection getParent() although that is derived from InputSection so in the code below InputSection would work too.
if (auto *ehIn = dyn_cast<EhInputSection>(&sec) {
SyntheticSection *ehFrame = ehIn->getParent();
SecAddr += ehFrame->outSecOff;
}
d9195a2
to
eb4d993
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for fixing the commits, that makes it easier to review.
I can see that this would need a fix in relocateAlloc. I think you'll need to update the generic one in Target.cpp as well as the Target specific ones for x86_64 and PPC (and any other I've missed).
You'll need to add x86_64, PPC and at least one other target's (for the generic Target) so that we've covered all the code-paths. By replacing the nops with a common data directive you might be able to make the same file work for all 3. However I think it would still need separate files so that the REQUIRES only had one target.
lld/ELF/Arch/AArch64.cpp
Outdated
@@ -770,6 +770,9 @@ void AArch64::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const { | |||
uint64_t secAddr = sec.getOutputSection()->addr; | |||
if (auto *s = dyn_cast<InputSection>(&sec)) | |||
secAddr += s->outSecOff; | |||
else if (auto *eh = dyn_cast<EhInputSection>(&sec)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, reading through the code. The .ARM.exidx sections were also InputSections so we could alter their outSecOff to compensate. However EhInputSections are not InputSections so this won't work here. We need to find the outSecOff of the ehframe section (which is usually 0, but with some linker scripts isn't).
I don't know of a legal EhInputSection that would return getParent() == nullptr so it should be possible to remove the nested if.
I suggest use ehIn and ehFrame to make it a bit clearer which sections we are referring to. I've use SyntheticSection as it is what EhInputSection getParent() although that is derived from InputSection so in the code below InputSection would work too.
if (auto *ehIn = dyn_cast<EhInputSection>(&sec) {
SyntheticSection *ehFrame = ehIn->getParent();
SecAddr += ehFrame->outSecOff;
}
6a61837
to
2e3a0de
Compare
lld/ELF/SyntheticSections.cpp
Outdated
// Adding outSecOff as finalizeAddressDependentContent() | ||
// may have altered the corresponding outSecOff. This is | ||
// required to get the correct PC relative offset. | ||
off = off + outSecOff; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on a second thought, adding outSecOff inside the condition might be more sensible.
if ((enc & 0x70) == DW_EH_PE_pcrel)
return addr + getParent()->addr + off;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, I think
if ((enc & 0x70) == DW_EH_PE_pcrel)
return addr + getParent()->addr + off + outSecOff;
Is easier to understand than modifying off.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you rename the PR and the commit message to remove [AARCH64] as this is now a general change.
I think with this and your own suggestion of moving the change in getFdePc inside the if statement it will be ready for my approval. We'll want to get MaskRay to take a look too.
lld/ELF/SyntheticSections.cpp
Outdated
// Adding outSecOff as finalizeAddressDependentContent() | ||
// may have altered the corresponding outSecOff. This is | ||
// required to get the correct PC relative offset. | ||
off = off + outSecOff; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, I think
if ((enc & 0x70) == DW_EH_PE_pcrel)
return addr + getParent()->addr + off + outSecOff;
Is easier to understand than modifying off.
Done |
@MaskRay Could you please have a look into this patch? |
The convention is to use an imperative sentence and omit the trailing dot. Perhaps |
…ro offset within its output section. When the .eh_frame section is placed at a non-zero offset within its output section, the relocation value within .eh_frame are computed incorrectly. We had similar issue in AArch32 and it has been fixed already in https://reviews.llvm.org/D148033. While applying the relocation using S+A-P, the value of P (the location of the relocation) is getting wrong. P is: P = SecAddr + rel.offset, But SecAddr points to the starting address of the outputsection rather than the starting address of the eh frame section within that output section.
… non-zero offset within its output section.
…offset within its output section. Adding outSecOff in eh_frame_hdr section as finalizeAddressDependentContent() may have altered the corresponding outSecOff. This is required to get the correct initial location of each FDE entries.
…o offset within the output section
Done |
I plan to define RISCV::relocateAllocate in a subsequent change. Add a test to verify `else if (auto *ehIn = dyn_cast<EhInputSection>(&sec)) secAddr += ehIn->getParent()->outSecOff;`
I plan to define RISCV::relocateAllocate in a subsequent change. Add a test to verify `else if (auto *ehIn = dyn_cast<EhInputSection>(&sec)) secAddr += ehIn->getParent()->outSecOff;`
When the .eh_frame section is placed at a non-zero
offset within its output section, the relocation
value within .eh_frame are computed incorrectly.
We had similar issue in .ARM.exidx section and it has been
fixed already in https://reviews.llvm.org/D148033.
While applying the relocation using S+A-P, the value
of P (the location of the relocation) is getting wrong.
P is:
P = SecAddr + rel.offset, But SecAddr points to the
starting address of the outputsection rather than the
starting address of the eh frame section within that
output section.
This issue affects all targets which generates .eh_frame
section. Hence fixing in all the corresponding targets it affecting.