summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJim Blandy <jimb@codesourcery.com>2003-06-12 23:13:40 +0000
committerJim Blandy <jimb@codesourcery.com>2003-06-12 23:13:40 +0000
commitf907be2bf54c9e14155442e85258972fc88849f2 (patch)
treec614c865a82c737381a8406137d1fc23e4dfc714
parent70cc9f953f9c5decc733b2b65431b83bb4cc4d34 (diff)
downloadgdb-f907be2bf54c9e14155442e85258972fc88849f2.tar.gz
* ppc-linux-tdep.c (ppc64_linux_bfd_entry_point): New function.
(ppc_linux_init_abi): Register it as our bfd_entry_point address.
-rw-r--r--gdb/ChangeLog3
-rw-r--r--gdb/ppc-linux-tdep.c102
2 files changed, 105 insertions, 0 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 272888225e7..1690b4727d8 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,8 @@
2003-06-12 Jim Blandy <jimb@redhat.com>
+ * ppc-linux-tdep.c (ppc64_linux_bfd_entry_point): New function.
+ (ppc_linux_init_abi): Register it as our bfd_entry_point address.
+
* gdbarch.sh (gdbarch_bfd_entry_point): New gdbarch method.
* arch-utils.c (generic_bfd_entry_point): New function.
* arch-utils.h (generic_bfd_entry_point): New declaration.
diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c
index 7e6306e821b..6ee0e5a5f08 100644
--- a/gdb/ppc-linux-tdep.c
+++ b/gdb/ppc-linux-tdep.c
@@ -941,6 +941,106 @@ ppc64_call_dummy_address (void)
}
+/* Return the unrelocated code address at which execution begins for
+ ABFD, under the 64-bit PowerPC Linux ABI. On that system, the ELF
+ header e_entry field (which is what bfd_get_start_address gives
+ you) is the address of the function descriptor for the startup
+ function, not the address of the actual machine instruction you
+ jump to.
+
+ This function doesn't just go and read the entry point from the
+ function descriptor. We need it to work when ABFD is the dynamic
+ linker, immediately after an exec. But ld.so is a dynamic
+ executable itself on PPC64 Linux, so it appears in memory whereever
+ the kernel drops it; this means that bfd_get_start_address's result
+ needs to be adjusted --- by some offset we don't know. So we can't
+ find the descriptor's address in memory to read the entry point
+ from it.
+
+ Instead, we do it all based on ABFD's symbol table. We take the
+ address from bfd_get_start_address, find each symbol at that
+ address, stick a '.' on the front of its name to get the entry
+ point symbol name, try to look that up, and return the value of
+ what we find, if anything. We never touch memory, or talk with the
+ kernel about the inferior at all.
+
+ Now, this address we return is straight from the symbol table, so
+ it hasn't been adjusted to take into account where ABFD was loaded.
+ But that's okay --- our job is just to return the unrelocated code
+ address. */
+static CORE_ADDR
+ppc64_linux_bfd_entry_point (struct gdbarch *gdbarch, bfd *abfd)
+{
+ long storage_needed;
+
+ storage_needed = bfd_get_symtab_upper_bound (abfd);
+
+ if (storage_needed > 0)
+ {
+ asymbol **symbol_table;
+ unsigned int symbol_table_len;
+ struct cleanup *back_to;
+ unsigned int i;
+ CORE_ADDR start_address;
+
+ symbol_table = (asymbol **) xmalloc (storage_needed);
+ back_to = make_cleanup (xfree, symbol_table);
+ symbol_table_len = bfd_canonicalize_symtab (abfd, symbol_table);
+
+ /* Find the symbol naming the start function's descriptor. Its
+ value must be the BFD's start address, and it must be in a
+ data section.
+
+ Also, the symbol's name must be non-empty. A lot of symtabs
+ seem to contain a bunch of symbols with no name whose value
+ is zero relative to the start of the data section; if the
+ start function descriptor is the first thing in the data
+ section, we'll get more false positives than we'd like. */
+ start_address = bfd_get_start_address (abfd);
+ for (i = 0; i < symbol_table_len; i++)
+ if (bfd_asymbol_value (symbol_table[i]) == start_address
+ && symbol_table[i]->section->flags & SEC_DATA
+ && bfd_asymbol_name (symbol_table[i])[0] != '\0')
+ {
+ /* Okay, we've found a symbol whose value and section are
+ right. Construct the name of the corresponding entry
+ point symbol and see if we can find a symbol with that
+ name in a code section. */
+ const char *desc_name = bfd_asymbol_name (symbol_table[i]);
+ char *entry_pt_name = alloca (strlen (desc_name) + 2);
+ int j;
+
+ entry_pt_name[0] = '.';
+ strcpy (entry_pt_name + 1, desc_name);
+
+ for (j = 0; j < symbol_table_len; j++)
+ if ((strcmp (bfd_asymbol_name (symbol_table[j]), entry_pt_name)
+ == 0)
+ && symbol_table[j]->section->flags & SEC_CODE)
+ /* Yay! What a coincidence. Let's assume this is the
+ entry point symbol. */
+ {
+ CORE_ADDR entry_point = bfd_asymbol_value (symbol_table[j]);
+ do_cleanups (back_to);
+ return entry_point;
+ }
+
+ /* No good --- there's no symbol by that name. Perhaps
+ symbol_table[i] is just coincidentally equal to the
+ start address; after all, there could be many symbols
+ with the same value. Continue the search. */
+ }
+
+ /* No good --- there's no symbol pointing at the start
+ address. */
+ do_cleanups (back_to);
+ }
+
+ /* No good --- this BFD has no symbols at all. We give up! */
+ return 0;
+}
+
+
enum {
ELF_NGREG = 48,
ELF_NFPREG = 33,
@@ -1068,6 +1168,8 @@ ppc_linux_init_abi (struct gdbarch_info info,
(gdbarch, ppc64_linux_convert_from_func_ptr_addr);
set_gdbarch_call_dummy_address (gdbarch, ppc64_call_dummy_address);
+
+ set_gdbarch_bfd_entry_point (gdbarch, ppc64_linux_bfd_entry_point);
set_gdbarch_in_solib_call_trampoline
(gdbarch, ppc64_in_solib_call_trampoline);