[lld] LLD: Fix getFdePC with sdata2 or sdata4 (PR #92228)

YunQiang Su via llvm-commits llvm-commits at lists.llvm.org
Wed May 15 01:44:48 PDT 2024


https://github.com/wzssyqa created https://github.com/llvm/llvm-project/pull/92228

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.

>From 018dfac68306658c586444cd264bb4a1280b1ff0 Mon Sep 17 00:00:00 2001
From: YunQiang Su <syq at gcc.gnu.org>
Date: Wed, 15 May 2024 15:41:39 +0800
Subject: [PATCH] LLD: Fix getFdePC with sdata2 or sdata4

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.
---
 lld/ELF/SyntheticSections.cpp        | 21 +++++++++++++++++----
 lld/test/ELF/Inputs/mips32-kseg0.lds |  8 ++++++++
 lld/test/ELF/mips-eh_frame-pic.s     |  5 +++++
 3 files changed, 30 insertions(+), 4 deletions(-)
 create mode 100644 lld/test/ELF/Inputs/mips32-kseg0.lds

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



More information about the llvm-commits mailing list