Skip to content

[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

Merged
merged 4 commits into from
Oct 3, 2023

Conversation

simpal01
Copy link
Contributor

@simpal01 simpal01 commented Sep 11, 2023

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.

@simpal01 simpal01 marked this pull request as draft September 11, 2023 14:20
@simpal01 simpal01 marked this pull request as ready for review September 11, 2023 15:14
@simpal01 simpal01 self-assigned this Sep 11, 2023
Copy link
Collaborator

@smithp35 smithp35 left a 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.

@@ -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))
Copy link
Collaborator

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

Copy link
Contributor Author

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).

Copy link
Contributor Author

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.

Copy link
Collaborator

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; 
}

@simpal01 simpal01 force-pushed the main branch 2 times, most recently from d9195a2 to eb4d993 Compare September 12, 2023 10:35
@simpal01 simpal01 requested a review from amilendra September 12, 2023 12:17
Copy link
Collaborator

@smithp35 smithp35 left a 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.

@@ -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))
Copy link
Collaborator

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; 
}

@simpal01 simpal01 force-pushed the main branch 2 times, most recently from 6a61837 to 2e3a0de Compare September 22, 2023 15:19
// Adding outSecOff as finalizeAddressDependentContent()
// may have altered the corresponding outSecOff. This is
// required to get the correct PC relative offset.
off = off + outSecOff;
Copy link
Contributor Author

@simpal01 simpal01 Sep 22, 2023

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;

Copy link
Collaborator

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.

Copy link
Collaborator

@smithp35 smithp35 left a 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.

// Adding outSecOff as finalizeAddressDependentContent()
// may have altered the corresponding outSecOff. This is
// required to get the correct PC relative offset.
off = off + outSecOff;
Copy link
Collaborator

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.

@simpal01 simpal01 changed the title [LLD][AARCH64] lld incorrectly handles .eh_frame when it has a non-zero offset within its output section. [LLD]lld incorrectly handles .eh_frame when it has a non-zero offset within its output section. Sep 30, 2023
@simpal01
Copy link
Contributor Author

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.

Done

@simpal01
Copy link
Contributor Author

@MaskRay Could you please have a look into this patch?

@MaskRay
Copy link
Member

MaskRay commented Sep 30, 2023

[LLD] lld incorrectly handles .eh_frame when it has a non-zero offset within its output section.

The convention is to use an imperative sentence and omit the trailing dot.

Perhaps [ELF] Handle relocations in synthetic .eh_frame with a non-zero offset within the output section will be better.
(Some patches use [LLD][ELF] or [lld][ELF] but I find [lld] redundant.)

@simpal01 simpal01 changed the title [LLD]lld incorrectly handles .eh_frame when it has a non-zero offset within its output section. [ELF] Handle relocations in synthetic .eh_frame with a non-zero offset within the output section Sep 30, 2023
…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.
…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.
@simpal01
Copy link
Contributor Author

simpal01 commented Oct 2, 2023

[LLD] lld incorrectly handles .eh_frame when it has a non-zero offset within its output section.

The convention is to use an imperative sentence and omit the trailing dot.

Perhaps [ELF] Handle relocations in synthetic .eh_frame with a non-zero offset within the output section will be better. (Some patches use [LLD][ELF] or [lld][ELF] but I find [lld] redundant.)

Done

@simpal01 simpal01 merged commit 3cde1d8 into llvm:main Oct 3, 2023
MaskRay added a commit that referenced this pull request Jan 8, 2024
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;`
justinfargnoli pushed a commit to justinfargnoli/llvm-project that referenced this pull request Jan 28, 2024
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;`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants