@@ -34,6 +34,7 @@
#include "breakpoint.h"
#include "auxv.h"
#include "xml-syscall.h"
+#include "objfiles.h"
#include "arm-tdep.h"
#include "arm-linux-tdep.h"
@@ -1351,6 +1352,69 @@ arm_linux_syscall_record (struct regcache *regcache, unsigned long svc_number)
return 0;
}
+/* This is used to skip the arm entry points in thumb shared library.
+ GNU ld adds the stub for the armv4t thumb shared library. If we call
+ 'foo' from a shared library, 'foo' becomes the stub and '__real_foo' is
+ the real function.
+
+ 0x75c <__real_foo>: foo function
+
+ 0x7b4 <foo>: ldr r12, [pc, #4] ; 0x7c0 <pendfunc1+12>
+ 0x7b8 <foo+4>: add r12, r12, pc
+ 0x7bc <foo+8>: bx r12 ; branch to __real_foo.
+
+ When ADDR is foo's address, this function is to adjust the address to
+ __real_foo's, the address of the function foo. */
+
+static CORE_ADDR
+arm_linux_skip_armv4t_thumb_shlib_stub (struct gdbarch *gdbarch,
+ CORE_ADDR addr)
+{
+ struct bound_minimal_symbol msym = lookup_minimal_symbol_by_pc (addr);
+
+ if (msym.minsym != NULL
+ && BMSYMBOL_VALUE_ADDRESS (msym) == addr
+ && MSYMBOL_LINKAGE_NAME (msym.minsym) != NULL)
+ {
+ const char *name = MSYMBOL_LINKAGE_NAME (msym.minsym);
+ char *real = xmalloc (strlen (name) + sizeof "__real_");
+
+ strcpy (real, "__real_");
+ strcpy (real + sizeof "__real_" - 1, name);
+
+ /* Look for __real_foo within the same objfile. */
+ msym = lookup_minimal_symbol (real, NULL, msym.objfile);
+
+ xfree (real);
+ if (msym.minsym != NULL)
+ {
+ int i;
+ static const unsigned int stub_code[] =
+ {
+ 0xe59fc004, /* ldr r12, [pc, #4] */
+ 0xe08cc00f, /* add r12, r12, pc */
+ 0xe12fff1c, /* bx r12 */
+ 0
+ };
+
+ for (i = 0; stub_code[i] != 0; i++)
+ {
+ unsigned int insn;
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+
+ insn = read_memory_integer (addr + (i * 4), 4, byte_order);
+ if (insn != stub_code[i])
+ return addr;
+ }
+
+ /* Return the address of __real_foo if it is found. */
+ return BMSYMBOL_VALUE_ADDRESS (msym);
+ }
+ }
+
+ return addr;
+}
+
/* Implement the skip_trampoline_code gdbarch method. */
static CORE_ADDR
@@ -1364,6 +1428,16 @@ arm_linux_skip_trampoline_code (struct frame_info *frame, CORE_ADDR pc)
return find_solib_trampoline_target (frame, pc);
}
+/* Implement the convert_from_func_ptr_addr gdbarch method. */
+
+static CORE_ADDR
+arm_linux_convert_from_func_ptr_addr (struct gdbarch *gdbarch,
+ CORE_ADDR addr,
+ struct target_ops *targ)
+{
+ return arm_linux_skip_armv4t_thumb_shlib_stub (gdbarch, addr);
+}
+
static void
arm_linux_init_abi (struct gdbarch_info info,
struct gdbarch *gdbarch)
@@ -1431,6 +1505,8 @@ arm_linux_init_abi (struct gdbarch_info info,
/* Shared library handling. */
set_gdbarch_skip_trampoline_code (gdbarch, arm_linux_skip_trampoline_code);
set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);
+ set_gdbarch_convert_from_func_ptr_addr
+ (gdbarch, arm_linux_convert_from_func_ptr_addr);
/* Enable TLS support. */
set_gdbarch_fetch_tls_load_module_address (gdbarch,
@@ -100,7 +100,31 @@ proc do_test { lib1opts lib2opts lib1first } {
gdb_breakpoint "bar"
- gdb_test "continue" "Breakpoint .* \\.?bar .*${expected_file}\\..*" \
+ set pattern "\\.?bar"
+
+ global gdb_prompt hex
+ set test "p __real_bar"
+ # Test symbol __real_bar exists or not.
+ gdb_test_multiple $test $test {
+ -re "<text variable, no debug info>.*$gdb_prompt $" {
+ # If the first appeared library doesn't have debug info,
+ # the minimal symbol is used. The address is resolved
+ # to __real_bar.
+ if {$lib1first} {
+ if {$lib1opts == ""} {
+ set pattern "__real_bar"
+ }
+ } else {
+ if {$lib2opts == ""} {
+ set pattern "__real_bar"
+ }
+ }
+ }
+ -re "$gdb_prompt $" {
+ }
+ }
+
+ gdb_test "continue" "Breakpoint .* $pattern .*${expected_file}\\..*" \
"run to breakpoint - $testopts"
}