diff --git a/include/eld/Diagnostics/DiagRelocations.inc b/include/eld/Diagnostics/DiagRelocations.inc
index 57296edee..765b324ae 100644
--- a/include/eld/Diagnostics/DiagRelocations.inc
+++ b/include/eld/Diagnostics/DiagRelocations.inc
@@ -30,7 +30,7 @@ DIAG(unknown_reloc_section_type, DiagnosticEngine::Unreachable,
"unknown relocation section type: `%0' in section `%1'")
DIAG(unsupport_cond_branch_reloc, DiagnosticEngine::Error,
"applying relocation `%0', conditional branch to PLT in THUMB-2 not "
- "supported yet")
+ "supported yet for symbol `%1' referred from %2[%3]")
DIAG(copyrelocs_is_error, DiagnosticEngine::Error,
"Cannot copy symbol %0 referenced in %1 from dynamic library %1 due to -z "
"nocopyrelocs ")
diff --git a/include/eld/Driver/GnuLinkerOptions.td b/include/eld/Driver/GnuLinkerOptions.td
index 6f08f0db0..d5f1b9396 100644
--- a/include/eld/Driver/GnuLinkerOptions.td
+++ b/include/eld/Driver/GnuLinkerOptions.td
@@ -225,6 +225,16 @@ defm Ttext_segment
"Specify an address for the .text-segment segment">,
MetaVarName<"
">,
Group;
+defm Trodata_segment
+ : dashEqWithOpt<"Trodata-segment", "Trodata-segment", "Trodata_segment",
+ "Specify an address for the first byte of the read-only data segment">,
+ MetaVarName<"">,
+ Group;
+defm Tldata_segment
+ : dashEqWithOpt<"Tldata-segment", "Tldata-segment", "Tldata_segment",
+ "Specify an address for the first byte of the ldata segment">,
+ MetaVarName<"">,
+ Group;
defm Tdata
: smDash<"Tdata", "Tdata", "Specify an address for the .data section">,
MetaVarName<"">,
diff --git a/lib/Target/ARM/ARMRelocator.cpp b/lib/Target/ARM/ARMRelocator.cpp
index ecf3df0aa..76ec2cb40 100644
--- a/lib/Target/ARM/ARMRelocator.cpp
+++ b/lib/Target/ARM/ARMRelocator.cpp
@@ -667,6 +667,27 @@ void ARMRelocator::scanGlobalReloc(InputFile &pInput, Relocation::Type Type,
case llvm::ELF::R_ARM_THM_JUMP11:
case llvm::ELF::R_ARM_THM_JUMP8: {
std::lock_guard relocGuard(m_RelocMutex);
+
+ // Check if this symbol already has a PLT, or is about to be granted one
+ bool needsPLT = false;
+ if (rsym->reserved() & ReservePLT) {
+ needsPLT = true;
+ } else if ((rsym->type() == ResolveInfo::IndirectFunc) && config().isCodeStatic()) {
+ needsPLT = true;
+ } else if (getTarget().isSymbolPreemptible(*rsym) && rsym->visibility() != ResolveInfo::Hidden) {
+ needsPLT = true;
+ }
+
+ // If a PLT is involved and this is a JUMP19, throw our detailed error immediately!
+ if (needsPLT && pReloc.type() == llvm::ELF::R_ARM_THM_JUMP19) {
+ config().raise(Diag::unsupport_cond_branch_reloc)
+ << (int)pReloc.type()
+ << rsym->name()
+ << pReloc.getSourcePath(config().options())
+ << pSection.name();
+ return;
+ }
+
// These are branch relocation (except PREL31)
// A PLT entry is needed when building shared library
@@ -1100,12 +1121,6 @@ Relocator::Result thm_jump19(Relocation &pReloc, ARMRelocator &pParent) {
helper_clear_thumb_bit(S);
}
- if (0x0 == T) {
- // FIXME: conditional branch to PLT in THUMB-2 not supported yet
- DiagEngine->raise(Diag::unsupport_cond_branch_reloc) << (int)pReloc.type();
- return Relocator::BadReloc;
- }
-
Relocator::DWord X = ((S + A) | T) - P;
int32_t SignedValue = static_cast(X);
if (!llvm::isInt<21>(SignedValue))
diff --git a/lib/Target/ARM/THMToARMStub.cpp b/lib/Target/ARM/THMToARMStub.cpp
index ef30e863d..e291dfa2c 100644
--- a/lib/Target/ARM/THMToARMStub.cpp
+++ b/lib/Target/ARM/THMToARMStub.cpp
@@ -89,7 +89,8 @@ bool THMToARMStub::isNeeded(const Relocation *pReloc, int64_t pTargetValue,
!(pReloc->symInfo()->reserved() & Relocator::ReservePLT))
return false;
// always need a stub to switch mode for JUMp24
- if (pReloc->type() == llvm::ELF::R_ARM_THM_JUMP24) {
+ if (pReloc->type() == llvm::ELF::R_ARM_THM_JUMP24 ||
+ pReloc->type() == llvm::ELF::R_ARM_THM_JUMP19) {
return true;
}
// Other relocation needs stub only if it cannot reach
@@ -104,11 +105,13 @@ bool THMToARMStub::isRelocInRange(const class Relocation *pReloc,
Offset = pTargetValue + addend - pReloc->place(Module);
switch (pReloc->type()) {
case llvm::ELF::R_ARM_THM_JUMP24:
- case llvm::ELF::R_ARM_THM_CALL: {
+ case llvm::ELF::R_ARM_THM_CALL:{
if (m_Target->isJ1J2BranchEncoding())
return llvm::isInt(Offset);
return llvm::isInt(Offset);
}
+ case llvm::ELF::R_ARM_THM_JUMP19:
+ return llvm::isInt<21>(Offset);
default:
break;
}
diff --git a/lib/Target/ARM/THMToTHMStub.cpp b/lib/Target/ARM/THMToTHMStub.cpp
index 7013368b8..1fb10f0c1 100644
--- a/lib/Target/ARM/THMToTHMStub.cpp
+++ b/lib/Target/ARM/THMToTHMStub.cpp
@@ -92,7 +92,9 @@ bool THMToTHMStub::isNeeded(const Relocation *pReloc, int64_t pTargetValue,
Module &Module) const {
int64_t Offset = 0;
// This stub cannot be used for ARM target.
- if ((pTargetValue & 0x1) == 0)
+ if ((pTargetValue & 0x1) == 0 &&
+ pReloc->type() != llvm::ELF::R_ARM_THM_JUMP19 &&
+ pReloc->type() != llvm::ELF::R_ARM_THM_JUMP24)
return false;
// The stub is needed only if the target is unreachable
return !isRelocInRange(pReloc, pTargetValue, Offset, Module);
@@ -107,11 +109,13 @@ bool THMToTHMStub::isRelocInRange(const Relocation *pReloc,
Offset = pTargetValue + addend - pReloc->place(Module);
switch (pReloc->type()) {
case llvm::ELF::R_ARM_THM_JUMP24:
- case llvm::ELF::R_ARM_THM_CALL: {
+ case llvm::ELF::R_ARM_THM_CALL:{
if (m_Target->isJ1J2BranchEncoding())
return llvm::isInt(Offset);
return llvm::isInt(Offset);
}
+ case llvm::ELF::R_ARM_THM_JUMP19:
+ return llvm::isInt<21>(Offset);
default:
break;
}
diff --git a/test/ARM/standalone/THMJump19Veneer/thm-jump19-basic.s b/test/ARM/standalone/THMJump19Veneer/thm-jump19-basic.s
new file mode 100644
index 000000000..ee462f2ca
--- /dev/null
+++ b/test/ARM/standalone/THMJump19Veneer/thm-jump19-basic.s
@@ -0,0 +1,30 @@
+// Tests that R_ARM_THM_JUMP19 (conditional branch) links successfully
+// for an in-range target without requiring a stub.
+// Regression test for https://github.com/qualcomm/eld/issues/1005
+
+// RUN: %clang %clangopts %s -o %t.o -c --target=arm-none-eabi -mcpu=cortex-m33 -mfloat-abi=soft
+// RUN: %link %linkopts %t.o -o %t.out --entry=my_func -static
+// RUN: %objdump -d %t.out | %filecheck %s
+
+// CHECK: :
+// CHECK: {{.*}} beq.w {{.*}}
+
+// CHECK: :
+// CHECK-NEXT: {{.*}} bx lr
+
+ .syntax unified
+ .thumb
+
+ .section .text.caller, "ax", %progbits
+ .thumb_func
+ .global my_func
+ .type my_func, %function
+my_func:
+ cmp r0, #0
+ beq plain_entry
+ bx lr
+
+ .section .text.target, "ax", %progbits
+ .global plain_entry
+plain_entry:
+ bx lr
\ No newline at end of file
diff --git a/test/ARM/standalone/THMJump19Veneer/thm-jump19-plt-diag.s b/test/ARM/standalone/THMJump19Veneer/thm-jump19-plt-diag.s
new file mode 100644
index 000000000..357a5b3b8
--- /dev/null
+++ b/test/ARM/standalone/THMJump19Veneer/thm-jump19-plt-diag.s
@@ -0,0 +1,22 @@
+// Tests that R_ARM_THM_JUMP19 correctly emits a detailed error
+// when attempting to branch to a PLT entry.
+// Regression test for https://github.com/qualcomm/eld/issues/1005
+
+// RUN: %clang %clangopts %s -o %t.o -c --target=arm-none-eabi -march=armv7-a
+// RUN: not %link %linkopts -shared %t.o -o %t.so 2>&1 | %filecheck %s
+
+// CHECK: Error:
+// CHECK-SAME: conditional branch to PLT in THUMB-2 not supported yet for symbol `extern_func'
+// CHECK-SAME: [.text.caller][.text.caller]
+
+ .syntax unified
+ .thumb
+
+ .section .text.caller, "ax", %progbits
+ .thumb_func
+ .global my_func
+ .type my_func, %function
+my_func:
+ cmp r0, #0
+ beq extern_func // Conditional branch to an undefined external symbol
+ bx lr
diff --git a/test/ARM/standalone/THMJump19Veneer/thm-jump19-veneer.s b/test/ARM/standalone/THMJump19Veneer/thm-jump19-veneer.s
new file mode 100644
index 000000000..b4ff1b8db
--- /dev/null
+++ b/test/ARM/standalone/THMJump19Veneer/thm-jump19-veneer.s
@@ -0,0 +1,41 @@
+// Tests that R_ARM_THM_JUMP19 (conditional branch) produces both T2T and T2A veneers
+// when the targets are out of range (beyond 21-bit range).
+// Regression test for https://github.com/qualcomm/eld/issues/1005
+
+// RUN: %clang %clangopts %s -o %t.o -c --target=arm-none-eabi -march=armv7-a -mfloat-abi=soft
+// RUN: echo "SECTIONS { .text.caller 0x0 : { *(.text.caller) } .text.thumb_target 0x200000 : { *(.text.thumb_target) } .text.arm_target 0x400000 : { *(.text.arm_target) } }" > %t.script
+// RUN: %link %linkopts -T %t.script %t.o -o %t.out --entry=my_func -static --trace=trampolines 2>&1 | %filecheck %s
+
+// CHECK: Creating Stub __thumb_target_{{.*}}veneer@island-{{.*}}
+// CHECK: Creating Stub __arm_target_{{.*}}veneer@island-{{.*}}
+
+.syntax unified
+
+// --- CALLER (Thumb) ---
+.section .text.caller, "ax", %progbits
+.thumb
+.thumb_func
+.global my_func
+.type my_func, %function
+my_func:
+ cmp r0, #0
+ beq thumb_target // Triggers T2T Veneer
+ beq arm_target // Triggers T2A Veneer
+ bx lr
+
+// --- TARGET 1 (Thumb) ---
+.section .text.thumb_target, "ax", %progbits
+.thumb
+.thumb_func
+.global thumb_target
+.type thumb_target, %function
+thumb_target:
+ bx lr
+
+// --- TARGET 2 (ARM) ---
+.section .text.arm_target, "ax", %progbits
+.arm
+.global arm_target
+.type arm_target, %function
+arm_target:
+ bx lr
\ No newline at end of file