-
Notifications
You must be signed in to change notification settings - Fork 14.3k
LLD: Fix getFdePC with sdata2 or sdata4 #92228
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
LLD uses uint64_t to hold PC value. If an Fde is DW_EH_PE_absptr with data type sdata, getFdePc may return a negtive value and then convert to uint64_t. It will fail to so some add/sub with other address values: PC offset is too large: 0xffffffff00000040 is expected. So if Fde is DW_EH_PE_absptr, let's convert its value to uint16_t(sdata2) or uint32_t(sdata4). We also do similiar things to DW_EH_PE_pcrel: the value of it should be a signed offset, thus, let's convert them to signed values before use them to add with other address values. Fixes: llvm#88852.
@llvm/pr-subscribers-lld-elf @llvm/pr-subscribers-lld Author: YunQiang Su (wzssyqa) ChangesLLD uses uint64_t to hold PC value. If an Fde is DW_EH_PE_absptr with data type sdata, getFdePc may return a negtive value and then convert to uint64_t. It will fail to so some add/sub with other address values: So if Fde is DW_EH_PE_absptr, let's convert its value to uint16_t(sdata2) or uint32_t(sdata4). We also do similiar things to DW_EH_PE_pcrel: the value of it should be a signed offset, thus, let's convert them to signed values before use them to add with other address values. Fixes: #88852. Full diff: https://github.com/llvm/llvm-project/pull/92228.diff 3 Files Affected:
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 7b9ada40c0f67..9749e601d8b16 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -612,10 +612,23 @@ uint64_t EhFrameSection::getFdePc(uint8_t *buf, size_t fdeOff,
// the .eh_frame section.
size_t off = fdeOff + 8;
uint64_t addr = readFdeAddr(buf + off, enc & 0xf);
- if ((enc & 0x70) == DW_EH_PE_absptr)
- return addr;
- if ((enc & 0x70) == DW_EH_PE_pcrel)
- return addr + getParent()->addr + off + outSecOff;
+ if ((enc & 0x70) == DW_EH_PE_absptr) {
+ if ((enc & 0xf) == DW_EH_PE_sdata2)
+ return (uint64_t)(uint16_t)addr;
+ else if ((enc & 0xf) == DW_EH_PE_sdata4)
+ return (uint64_t)(uint32_t)addr;
+ else
+ return addr;
+ }
+ if ((enc & 0x70) == DW_EH_PE_pcrel) {
+ uint64_t addr_base = getParent()->addr + off + outSecOff;
+ if ((enc & 0xf) == DW_EH_PE_sdata2)
+ return (int16_t)addr + addr_base;
+ else if ((enc & 0xf) == DW_EH_PE_sdata4)
+ return (int32_t)addr + addr_base;
+ else
+ return addr + addr_base;
+ }
fatal("unknown FDE size relative encoding");
}
diff --git a/lld/test/ELF/Inputs/mips32-kseg0.lds b/lld/test/ELF/Inputs/mips32-kseg0.lds
new file mode 100644
index 0000000000000..613194adcca7c
--- /dev/null
+++ b/lld/test/ELF/Inputs/mips32-kseg0.lds
@@ -0,0 +1,8 @@
+OUTPUT_ARCH(mips)
+SECTIONS
+{
+ . = 0x80000000;
+ .text : { *(.text) }
+ .data : { *(.data) }
+ .bss : { *(.bss) }
+}
diff --git a/lld/test/ELF/mips-eh_frame-pic.s b/lld/test/ELF/mips-eh_frame-pic.s
index 79076e74a7e3f..b400114c8b7b7 100644
--- a/lld/test/ELF/mips-eh_frame-pic.s
+++ b/lld/test/ELF/mips-eh_frame-pic.s
@@ -23,15 +23,20 @@
# RUN: llvm-dwarfdump --eh-frame %t-nopic32.o | FileCheck %s --check-prefix=ABS32-EH-FRAME
# RUN: llvm-readobj -r %t-nopic32.o | FileCheck %s --check-prefixes=RELOCS,ABS32-RELOCS
# RUN: not ld.lld -shared %t-nopic32.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=NOPIC32-ERR
+# RUN: ld.lld %t-nopic32.o -T %p/Inputs/mips32-kseg0.lds -eh-frame-hdr -static -o /dev/null 2>&1 \
+# RUN: | FileCheck %s --check-prefix=NOPIC32-ABSPTR
## Note: ld.bfd can link this file because it rewrites the .eh_frame section to use
## relative addressing.
# NOPIC32-ERR: ld.lld: error: relocation R_MIPS_32 cannot be used against local symbol
+# NOPIC32-ABSPTR: cannot find entry symbol __start
## For -fPIC, .eh_frame should contain DW_EH_PE_pcrel | DW_EH_PE_sdata4 values:
# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux --position-independent %s -o %t-pic32.o
# RUN: llvm-readobj -r %t-pic32.o | FileCheck %s --check-prefixes=RELOCS,PIC32-RELOCS
# RUN: ld.lld -shared %t-pic32.o -o %t-pic32.so
+# RUN: ld.lld -shared -T %p/Inputs/mips32-kseg0.lds %t-pic32.o -o %t-pic32-kseg0.so
# RUN: llvm-dwarfdump --eh-frame %t-pic32.so | FileCheck %s --check-prefix=PIC32-EH-FRAME
+# RUN: llvm-dwarfdump --eh-frame %t-pic32-kseg0.so | FileCheck %s --check-prefix=PIC32-EH-FRAME
# RELOCS: .rel{{a?}}.eh_frame {
# ABS32-RELOCS-NEXT: 0x1C R_MIPS_32 .text
|
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.
.
if ((enc & 0x70) == DW_EH_PE_pcrel) { | ||
uint64_t addr_base = getParent()->addr + off + outSecOff; | ||
if ((enc & 0xf) == DW_EH_PE_sdata2) | ||
return (int16_t)addr + addr_base; |
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 is unnecessary.
if ((enc & 0x70) == DW_EH_PE_pcrel) | ||
return addr + getParent()->addr + off + outSecOff; | ||
if ((enc & 0x70) == DW_EH_PE_absptr) { | ||
if ((enc & 0xf) == DW_EH_PE_sdata2) |
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 is probably not desired for ELFCLASS64.
See the alternative fix: #92438 , but thanks for investigation.
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.
Ohh, yes. we won't support any platform with 16bit absptr with ELF.
LLD uses uint64_t to hold PC value. If an Fde is DW_EH_PE_absptr with data type sdata, getFdePc may return a negtive value and then convert to uint64_t. It will fail to so some add/sub with other address values:
PC offset is too large: 0xffffffff00000040 is expected.
So if Fde is DW_EH_PE_absptr, let's convert its value to uint16_t(sdata2) or uint32_t(sdata4).
We also do similiar things to DW_EH_PE_pcrel: the value of it should be a signed offset, thus, let's convert them to signed values before use them to add with other address values.
Fixes: #88852.