/* Target-dependent code for Linux running on i386's, for GDB.
- Copyright (C) 2000 Free Software Foundation, Inc.
+ Copyright 2000, 2001 Free Software Foundation, Inc.
This file is part of GDB.
#include "gdbcore.h"
#include "frame.h"
#include "value.h"
+#include "regcache.h"
+
+/* For i386_linux_skip_solib_resolver. */
+#include "symtab.h"
+#include "symfile.h"
+#include "objfiles.h"
+
+#include "solib-svr4.h" /* For struct link_map_offsets. */
\f
/* Recognizing signal handler frames. */
/* Assuming FRAME is for a Linux sigtramp routine, return the saved
program counter. */
-CORE_ADDR
+static CORE_ADDR
i386_linux_sigtramp_saved_pc (struct frame_info *frame)
{
CORE_ADDR addr;
/* Assuming FRAME is for a Linux sigtramp routine, return the saved
stack pointer. */
-CORE_ADDR
+static CORE_ADDR
i386_linux_sigtramp_saved_sp (struct frame_info *frame)
{
CORE_ADDR addr;
return read_memory_integer (addr + LINUX_SIGCONTEXT_SP_OFFSET, 4);
}
+/* Signal trampolines don't have a meaningful frame. As in
+ "i386/tm-i386.h", the frame pointer value we use is actually the
+ frame pointer of the calling frame -- that is, the frame which was
+ in progress when the signal trampoline was entered. GDB mostly
+ treats this frame pointer value as a magic cookie. We detect the
+ case of a signal trampoline by looking at the SIGNAL_HANDLER_CALLER
+ field, which is set based on IN_SIGTRAMP.
+
+ When a signal trampoline is invoked from a frameless function, we
+ essentially have two frameless functions in a row. In this case,
+ we use the same magic cookie for three frames in a row. We detect
+ this case by seeing whether the next frame has
+ SIGNAL_HANDLER_CALLER set, and, if it does, checking whether the
+ current frame is actually frameless. In this case, we need to get
+ the PC by looking at the SP register value stored in the signal
+ context.
+
+ This should work in most cases except in horrible situations where
+ a signal occurs just as we enter a function but before the frame
+ has been set up. */
+
+#define FRAMELESS_SIGNAL(frame) \
+ ((frame)->next != NULL \
+ && (frame)->next->signal_handler_caller \
+ && frameless_look_for_prologue (frame))
+
+CORE_ADDR
+i386_linux_frame_chain (struct frame_info *frame)
+{
+ if (frame->signal_handler_caller || FRAMELESS_SIGNAL (frame))
+ return frame->frame;
+
+ if (! inside_entry_file (frame->pc))
+ return read_memory_unsigned_integer (frame->frame, 4);
+
+ return 0;
+}
+
+/* Return the saved program counter for FRAME. */
+
+CORE_ADDR
+i386_linux_frame_saved_pc (struct frame_info *frame)
+{
+ if (frame->signal_handler_caller)
+ return i386_linux_sigtramp_saved_pc (frame);
+
+ if (FRAMELESS_SIGNAL (frame))
+ {
+ CORE_ADDR sp = i386_linux_sigtramp_saved_sp (frame->next);
+ return read_memory_unsigned_integer (sp, 4);
+ }
+
+ return read_memory_unsigned_integer (frame->frame + 4, 4);
+}
+
/* Immediately after a function call, return the saved pc. */
CORE_ADDR
if (frame->signal_handler_caller)
return i386_linux_sigtramp_saved_pc (frame);
- return read_memory_integer (read_register (SP_REGNUM), 4);
+ return read_memory_unsigned_integer (read_register (SP_REGNUM), 4);
+}
+\f
+
+/* Calling functions in shared libraries. */
+/* Find the minimal symbol named NAME, and return both the minsym
+ struct and its objfile. This probably ought to be in minsym.c, but
+ everything there is trying to deal with things like C++ and
+ SOFUN_ADDRESS_MAYBE_TURQUOISE, ... Since this is so simple, it may
+ be considered too special-purpose for general consumption. */
+
+static struct minimal_symbol *
+find_minsym_and_objfile (char *name, struct objfile **objfile_p)
+{
+ struct objfile *objfile;
+
+ ALL_OBJFILES (objfile)
+ {
+ struct minimal_symbol *msym;
+
+ ALL_OBJFILE_MSYMBOLS (objfile, msym)
+ {
+ if (SYMBOL_NAME (msym)
+ && STREQ (SYMBOL_NAME (msym), name))
+ {
+ *objfile_p = objfile;
+ return msym;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static CORE_ADDR
+skip_hurd_resolver (CORE_ADDR pc)
+{
+ /* The HURD dynamic linker is part of the GNU C library, so many
+ GNU/Linux distributions use it. (All ELF versions, as far as I
+ know.) An unresolved PLT entry points to "_dl_runtime_resolve",
+ which calls "fixup" to patch the PLT, and then passes control to
+ the function.
+
+ We look for the symbol `_dl_runtime_resolve', and find `fixup' in
+ the same objfile. If we are at the entry point of `fixup', then
+ we set a breakpoint at the return address (at the top of the
+ stack), and continue.
+
+ It's kind of gross to do all these checks every time we're
+ called, since they don't change once the executable has gotten
+ started. But this is only a temporary hack --- upcoming versions
+ of Linux will provide a portable, efficient interface for
+ debugging programs that use shared libraries. */
+
+ struct objfile *objfile;
+ struct minimal_symbol *resolver
+ = find_minsym_and_objfile ("_dl_runtime_resolve", &objfile);
+
+ if (resolver)
+ {
+ struct minimal_symbol *fixup
+ = lookup_minimal_symbol ("fixup", 0, objfile);
+
+ if (fixup && SYMBOL_VALUE_ADDRESS (fixup) == pc)
+ return (SAVED_PC_AFTER_CALL (get_current_frame ()));
+ }
+
+ return 0;
+}
+
+/* See the comments for SKIP_SOLIB_RESOLVER at the top of infrun.c.
+ This function:
+ 1) decides whether a PLT has sent us into the linker to resolve
+ a function reference, and
+ 2) if so, tells us where to set a temporary breakpoint that will
+ trigger when the dynamic linker is done. */
+
+CORE_ADDR
+i386_linux_skip_solib_resolver (CORE_ADDR pc)
+{
+ CORE_ADDR result;
+
+ /* Plug in functions for other kinds of resolvers here. */
+ result = skip_hurd_resolver (pc);
+ if (result)
+ return result;
+
+ return 0;
+}
+
+/* Fetch (and possibly build) an appropriate link_map_offsets
+ structure for native Linux/x86 targets using the struct offsets
+ defined in link.h (but without actual reference to that file).
+
+ This makes it possible to access Linux/x86 shared libraries from a
+ GDB that was not built on an Linux/x86 host (for cross debugging). */
+
+struct link_map_offsets *
+i386_linux_svr4_fetch_link_map_offsets (void)
+{
+ static struct link_map_offsets lmo;
+ static struct link_map_offsets *lmp = NULL;
+
+ if (lmp == NULL)
+ {
+ lmp = &lmo;
+
+ lmo.r_debug_size = 8; /* The actual size is 20 bytes, but
+ this is all we need. */
+ lmo.r_map_offset = 4;
+ lmo.r_map_size = 4;
+
+ lmo.link_map_size = 20; /* The actual size is 552 bytes, but
+ this is all we need. */
+ lmo.l_addr_offset = 0;
+ lmo.l_addr_size = 4;
+
+ lmo.l_name_offset = 4;
+ lmo.l_name_size = 4;
+
+ lmo.l_next_offset = 12;
+ lmo.l_next_size = 4;
+
+ lmo.l_prev_offset = 16;
+ lmo.l_prev_size = 4;
+ }
+
+ return lmp;
}