492d29ea1c
This patch splits the TRY_CATCH macro into three, so that we go from this: ~~~ volatile gdb_exception ex; TRY_CATCH (ex, RETURN_MASK_ERROR) { } if (ex.reason < 0) { } ~~~ to this: ~~~ TRY { } CATCH (ex, RETURN_MASK_ERROR) { } END_CATCH ~~~ Thus, we'll be getting rid of the local volatile exception object, and declaring the caught exception in the catch block. This allows reimplementing TRY/CATCH in terms of C++ exceptions when building in C++ mode, while still allowing to build GDB in C mode (using setjmp/longjmp), as a transition step. TBC, after this patch, is it _not_ valid to have code between the TRY and the CATCH blocks, like: TRY { } // some code here. CATCH (ex, RETURN_MASK_ERROR) { } END_CATCH Just like it isn't valid to do that with C++'s native try/catch. By switching to creating the exception object inside the CATCH block scope, we can get rid of all the explicitly allocated volatile exception objects all over the tree, and map the CATCH block more directly to C++'s catch blocks. The majority of the TRY_CATCH -> TRY+CATCH+END_CATCH conversion was done with a script, rerun from scratch at every rebase, no manual editing involved. After the mechanical conversion, a few places needed manual intervention, to fix preexisting cases where we were using the exception object outside of the TRY_CATCH block, and cases where we were using "else" after a 'if (ex.reason) < 0)' [a CATCH after this patch]. The result was folded into this patch so that GDB still builds at each incremental step. END_CATCH is necessary for two reasons: First, because we name the exception object in the CATCH block, which requires creating a scope, which in turn must be closed somewhere. Declaring the exception variable in the initializer field of a for block, like: #define CATCH(EXCEPTION, mask) \ for (struct gdb_exception EXCEPTION; \ exceptions_state_mc_catch (&EXCEPTION, MASK); \ EXCEPTION = exception_none) would avoid needing END_CATCH, but alas, in C mode, we build with C90, which doesn't allow mixed declarations and code. Second, because when TRY/CATCH are wired to real C++ try/catch, as long as we need to handle cleanup chains, even if there's no CATCH block that wants to catch the exception, we need for stop at every frame in the unwind chain and run cleanups, then rethrow. That will be done in END_CATCH. After we require C++, we'll still need TRY/CATCH/END_CATCH until cleanups are completely phased out -- TRY/CATCH in C++ mode will save/restore the current cleanup chain, like in C mode, and END_CATCH catches otherwise uncaugh exceptions, runs cleanups and rethrows, so that C++ cleanups and exceptions can coexist. IMO, this still makes the TRY/CATCH code look a bit more like a newcomer would expect, so IMO worth it even if we weren't considering C++. gdb/ChangeLog. 2015-03-07 Pedro Alves <palves@redhat.com> * common/common-exceptions.c (struct catcher) <exception>: No longer a pointer to volatile exception. Now an exception value. <mask>: Delete field. (exceptions_state_mc_init): Remove all parameters. Adjust. (exceptions_state_mc): No longer pop the catcher here. (exceptions_state_mc_catch): New function. (throw_exception): Adjust. * common/common-exceptions.h (exceptions_state_mc_init): Remove all parameters. (exceptions_state_mc_catch): Declare. (TRY_CATCH): Rename to ... (TRY): ... this. Remove EXCEPTION and MASK parameters. (CATCH, END_CATCH): New. All callers adjusted. gdb/gdbserver/ChangeLog: 2015-03-07 Pedro Alves <palves@redhat.com> Adjust all callers of TRY_CATCH to use TRY/CATCH/END_CATCH instead.
706 lines
21 KiB
C
706 lines
21 KiB
C
/* Copyright (C) 2010-2015 Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "defs.h"
|
|
#include "ia64-tdep.h"
|
|
#include "ia64-hpux-tdep.h"
|
|
#include "solib-ia64-hpux.h"
|
|
#include "solist.h"
|
|
#include "solib.h"
|
|
#include "target.h"
|
|
#include "gdbtypes.h"
|
|
#include "inferior.h"
|
|
#include "gdbcore.h"
|
|
#include "regcache.h"
|
|
#include "opcode/ia64.h"
|
|
#include "symfile.h"
|
|
#include "objfiles.h"
|
|
#include "elf-bfd.h"
|
|
|
|
/* Need to define the following macro in order to get the complete
|
|
load_module_desc struct definition in dlfcn.h Otherwise, it doesn't
|
|
match the size of the struct the loader is providing us during load
|
|
events. */
|
|
#define _LOAD_MODULE_DESC_EXT
|
|
|
|
#include <sys/ttrace.h>
|
|
#include <dlfcn.h>
|
|
#include <elf.h>
|
|
#include <service_mgr.h>
|
|
|
|
/* The following is to have access to the definition of type load_info_t. */
|
|
#include <crt0.h>
|
|
|
|
/* The r32 pseudo-register number.
|
|
|
|
Like all stacked registers, r32 is treated as a pseudo-register,
|
|
because it is not always available for read/write via the ttrace
|
|
interface. */
|
|
/* This is a bit of a hack, as we duplicate something hidden inside
|
|
ia64-tdep.c, but oh well... */
|
|
#define IA64_R32_PSEUDO_REGNUM (IA64_NAT127_REGNUM + 2)
|
|
|
|
/* Our struct so_list private data structure. */
|
|
|
|
struct lm_info
|
|
{
|
|
/* The shared library module descriptor. We extract this structure
|
|
from the loader at the time the shared library gets mapped. */
|
|
struct load_module_desc module_desc;
|
|
|
|
/* The text segment address as defined in the shared library object
|
|
(this is not the address where this segment got loaded). This
|
|
field is initially set to zero, and computed lazily. */
|
|
CORE_ADDR text_start;
|
|
|
|
/* The data segment address as defined in the shared library object
|
|
(this is not the address where this segment got loaded). This
|
|
field is initially set to zero, and computed lazily. */
|
|
CORE_ADDR data_start;
|
|
};
|
|
|
|
/* The list of shared libraries currently mapped by the inferior. */
|
|
|
|
static struct so_list *so_list_head = NULL;
|
|
|
|
/* Create a new so_list element. The result should be deallocated
|
|
when no longer in use. */
|
|
|
|
static struct so_list *
|
|
new_so_list (char *so_name, struct load_module_desc module_desc)
|
|
{
|
|
struct so_list *new_so;
|
|
|
|
new_so = (struct so_list *) XCNEW (struct so_list);
|
|
new_so->lm_info = (struct lm_info *) XCNEW (struct lm_info);
|
|
new_so->lm_info->module_desc = module_desc;
|
|
|
|
strncpy (new_so->so_name, so_name, SO_NAME_MAX_PATH_SIZE - 1);
|
|
new_so->so_name[SO_NAME_MAX_PATH_SIZE - 1] = '\0';
|
|
strcpy (new_so->so_original_name, new_so->so_name);
|
|
|
|
return new_so;
|
|
}
|
|
|
|
/* Return non-zero if the instruction at the current PC is a breakpoint
|
|
part of the dynamic loading process.
|
|
|
|
We identify such instructions by checking that the instruction at
|
|
the current pc is a break insn where no software breakpoint has been
|
|
inserted by us. We also verify that the operands have specific
|
|
known values, to be extra certain.
|
|
|
|
PTID is the ptid of the thread that should be checked, but this
|
|
function also assumes that inferior_ptid is already equal to PTID.
|
|
Ideally, we would like to avoid the requirement on inferior_ptid,
|
|
but many routines still use the inferior_ptid global to access
|
|
the relevant thread's register and memory. We still have the ptid
|
|
as parameter to be able to pass it to the routines that do take a ptid
|
|
- that way we avoid increasing explicit uses of the inferior_ptid
|
|
global. */
|
|
|
|
static int
|
|
ia64_hpux_at_dld_breakpoint_1_p (ptid_t ptid)
|
|
{
|
|
struct regcache *regcache = get_thread_regcache (ptid);
|
|
CORE_ADDR pc = regcache_read_pc (regcache);
|
|
struct address_space *aspace = get_regcache_aspace (regcache);
|
|
ia64_insn t0, t1, slot[3], templ, insn;
|
|
int slotnum;
|
|
bfd_byte bundle[16];
|
|
|
|
/* If this is a regular breakpoint, then it can not be a dld one. */
|
|
if (breakpoint_inserted_here_p (aspace, pc))
|
|
return 0;
|
|
|
|
slotnum = ((long) pc) & 0xf;
|
|
if (slotnum > 2)
|
|
internal_error (__FILE__, __LINE__,
|
|
"invalid slot (%d) for address %s", slotnum,
|
|
paddress (get_regcache_arch (regcache), pc));
|
|
|
|
pc -= (pc & 0xf);
|
|
read_memory (pc, bundle, sizeof (bundle));
|
|
|
|
/* bundles are always in little-endian byte order */
|
|
t0 = bfd_getl64 (bundle);
|
|
t1 = bfd_getl64 (bundle + 8);
|
|
templ = (t0 >> 1) & 0xf;
|
|
slot[0] = (t0 >> 5) & 0x1ffffffffffLL;
|
|
slot[1] = ((t0 >> 46) & 0x3ffff) | ((t1 & 0x7fffff) << 18);
|
|
slot[2] = (t1 >> 23) & 0x1ffffffffffLL;
|
|
|
|
if (templ == 2 && slotnum == 1)
|
|
{
|
|
/* skip L slot in MLI template: */
|
|
slotnum = 2;
|
|
}
|
|
|
|
insn = slot[slotnum];
|
|
|
|
return (insn == 0x1c0c9c0 /* break.i 0x070327 */
|
|
|| insn == 0x3c0c9c0); /* break.i 0x0f0327 */
|
|
}
|
|
|
|
/* Same as ia64_hpux_at_dld_breakpoint_1_p above, with the following
|
|
differences: It temporarily sets inferior_ptid to PTID, and also
|
|
contains any exception being raised. */
|
|
|
|
int
|
|
ia64_hpux_at_dld_breakpoint_p (ptid_t ptid)
|
|
{
|
|
ptid_t saved_ptid = inferior_ptid;
|
|
int result = 0;
|
|
|
|
inferior_ptid = ptid;
|
|
TRY
|
|
{
|
|
result = ia64_hpux_at_dld_breakpoint_1_p (ptid);
|
|
}
|
|
inferior_ptid = saved_ptid;
|
|
CATCH (e, RETURN_MASK_ALL)
|
|
{
|
|
warning (_("error while checking for dld breakpoint: %s"), e.message);
|
|
}
|
|
END_CATCH
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Handler for library load event: Read the information provided by
|
|
the loader, and then use it to read the shared library symbols. */
|
|
|
|
static void
|
|
ia64_hpux_handle_load_event (struct regcache *regcache)
|
|
{
|
|
CORE_ADDR module_desc_addr;
|
|
ULONGEST module_desc_size;
|
|
CORE_ADDR so_path_addr;
|
|
char so_path[PATH_MAX];
|
|
struct load_module_desc module_desc;
|
|
struct so_list *new_so;
|
|
|
|
/* Extract the data provided by the loader as follow:
|
|
- r33: Address of load_module_desc structure
|
|
- r34: size of struct load_module_desc
|
|
- r35: Address of string holding shared library path
|
|
*/
|
|
regcache_cooked_read_unsigned (regcache, IA64_R32_PSEUDO_REGNUM + 1,
|
|
&module_desc_addr);
|
|
regcache_cooked_read_unsigned (regcache, IA64_R32_PSEUDO_REGNUM + 2,
|
|
&module_desc_size);
|
|
regcache_cooked_read_unsigned (regcache, IA64_R32_PSEUDO_REGNUM + 3,
|
|
&so_path_addr);
|
|
|
|
if (module_desc_size != sizeof (struct load_module_desc))
|
|
warning (_("load_module_desc size (%ld) != size returned by kernel (%s)"),
|
|
sizeof (struct load_module_desc),
|
|
pulongest (module_desc_size));
|
|
|
|
read_memory_string (so_path_addr, so_path, PATH_MAX);
|
|
read_memory (module_desc_addr, (gdb_byte *) &module_desc,
|
|
sizeof (module_desc));
|
|
|
|
/* Create a new so_list element and insert it at the start of our
|
|
so_list_head (we insert at the start of the list only because
|
|
it is less work compared to inserting it elsewhere). */
|
|
new_so = new_so_list (so_path, module_desc);
|
|
new_so->next = so_list_head;
|
|
so_list_head = new_so;
|
|
}
|
|
|
|
/* Update the value of the PC to point to the begining of the next
|
|
instruction bundle. */
|
|
|
|
static void
|
|
ia64_hpux_move_pc_to_next_bundle (struct regcache *regcache)
|
|
{
|
|
CORE_ADDR pc = regcache_read_pc (regcache);
|
|
|
|
pc -= pc & 0xf;
|
|
pc += 16;
|
|
ia64_write_pc (regcache, pc);
|
|
}
|
|
|
|
/* Handle loader events.
|
|
|
|
PTID is the ptid of the thread corresponding to the event being
|
|
handled. Similarly to ia64_hpux_at_dld_breakpoint_1_p, this
|
|
function assumes that inferior_ptid is set to PTID. */
|
|
|
|
static void
|
|
ia64_hpux_handle_dld_breakpoint_1 (ptid_t ptid)
|
|
{
|
|
struct regcache *regcache = get_thread_regcache (ptid);
|
|
ULONGEST arg0;
|
|
|
|
/* The type of event is provided by the loaded via r32. */
|
|
regcache_cooked_read_unsigned (regcache, IA64_R32_PSEUDO_REGNUM, &arg0);
|
|
switch (arg0)
|
|
{
|
|
case BREAK_DE_SVC_LOADED:
|
|
/* Currently, the only service loads are uld and dld,
|
|
so we shouldn't need to do anything. Just ignore. */
|
|
break;
|
|
case BREAK_DE_LIB_LOADED:
|
|
ia64_hpux_handle_load_event (regcache);
|
|
solib_add (NULL, 0, ¤t_target, auto_solib_add);
|
|
break;
|
|
case BREAK_DE_LIB_UNLOADED:
|
|
case BREAK_DE_LOAD_COMPLETE:
|
|
case BREAK_DE_BOR:
|
|
/* Ignore for now. */
|
|
break;
|
|
}
|
|
|
|
/* Now that we have handled the event, we can move the PC to
|
|
the next instruction bundle, past the break instruction. */
|
|
ia64_hpux_move_pc_to_next_bundle (regcache);
|
|
}
|
|
|
|
/* Same as ia64_hpux_handle_dld_breakpoint_1 above, with the following
|
|
differences: This function temporarily sets inferior_ptid to PTID,
|
|
and also contains any exception. */
|
|
|
|
void
|
|
ia64_hpux_handle_dld_breakpoint (ptid_t ptid)
|
|
{
|
|
ptid_t saved_ptid = inferior_ptid;
|
|
|
|
inferior_ptid = ptid;
|
|
TRY
|
|
{
|
|
ia64_hpux_handle_dld_breakpoint_1 (ptid);
|
|
}
|
|
inferior_ptid = saved_ptid;
|
|
CATCH (e, RETURN_MASK_ALL)
|
|
{
|
|
warning (_("error detected while handling dld breakpoint: %s"), e.message);
|
|
}
|
|
END_CATCH
|
|
}
|
|
|
|
/* Find the address of the code and data segments in ABFD, and update
|
|
TEXT_START and DATA_START accordingly. */
|
|
|
|
static void
|
|
ia64_hpux_find_start_vma (bfd *abfd, CORE_ADDR *text_start,
|
|
CORE_ADDR *data_start)
|
|
{
|
|
Elf_Internal_Ehdr *i_ehdrp = elf_elfheader (abfd);
|
|
Elf64_Phdr phdr;
|
|
int i;
|
|
|
|
*text_start = 0;
|
|
*data_start = 0;
|
|
|
|
if (bfd_seek (abfd, i_ehdrp->e_phoff, SEEK_SET) == -1)
|
|
error (_("invalid program header offset in %s"), abfd->filename);
|
|
|
|
for (i = 0; i < i_ehdrp->e_phnum; i++)
|
|
{
|
|
if (bfd_bread (&phdr, sizeof (phdr), abfd) != sizeof (phdr))
|
|
error (_("failed to read segment %d in %s"), i, abfd->filename);
|
|
|
|
if (phdr.p_flags & PF_X
|
|
&& (*text_start == 0 || phdr.p_vaddr < *text_start))
|
|
*text_start = phdr.p_vaddr;
|
|
|
|
if (phdr.p_flags & PF_W
|
|
&& (*data_start == 0 || phdr.p_vaddr < *data_start))
|
|
*data_start = phdr.p_vaddr;
|
|
}
|
|
}
|
|
|
|
/* The "relocate_section_addresses" target_so_ops routine for ia64-hpux. */
|
|
|
|
static void
|
|
ia64_hpux_relocate_section_addresses (struct so_list *so,
|
|
struct target_section *sec)
|
|
{
|
|
CORE_ADDR offset = 0;
|
|
|
|
/* If we haven't computed the text & data segment addresses, do so now.
|
|
We do this here, because we now have direct access to the associated
|
|
bfd, whereas we would have had to open our own if we wanted to do it
|
|
while processing the library-load event. */
|
|
if (so->lm_info->text_start == 0 && so->lm_info->data_start == 0)
|
|
ia64_hpux_find_start_vma (sec->the_bfd_section->owner,
|
|
&so->lm_info->text_start,
|
|
&so->lm_info->data_start);
|
|
|
|
/* Determine the relocation offset based on which segment
|
|
the section belongs to. */
|
|
if ((so->lm_info->text_start < so->lm_info->data_start
|
|
&& sec->addr < so->lm_info->data_start)
|
|
|| (so->lm_info->text_start > so->lm_info->data_start
|
|
&& sec->addr >= so->lm_info->text_start))
|
|
offset = so->lm_info->module_desc.text_base - so->lm_info->text_start;
|
|
else if ((so->lm_info->text_start < so->lm_info->data_start
|
|
&& sec->addr >= so->lm_info->data_start)
|
|
|| (so->lm_info->text_start > so->lm_info->data_start
|
|
&& sec->addr < so->lm_info->text_start))
|
|
offset = so->lm_info->module_desc.data_base - so->lm_info->data_start;
|
|
|
|
/* And now apply the relocation. */
|
|
sec->addr += offset;
|
|
sec->endaddr += offset;
|
|
|
|
/* Best effort to set addr_high/addr_low. This is used only by
|
|
'info sharedlibrary'. */
|
|
if (so->addr_low == 0 || sec->addr < so->addr_low)
|
|
so->addr_low = sec->addr;
|
|
|
|
if (so->addr_high == 0 || sec->endaddr > so->addr_high)
|
|
so->addr_high = sec->endaddr;
|
|
}
|
|
|
|
/* The "free_so" target_so_ops routine for ia64-hpux. */
|
|
|
|
static void
|
|
ia64_hpux_free_so (struct so_list *so)
|
|
{
|
|
xfree (so->lm_info);
|
|
}
|
|
|
|
/* The "clear_solib" target_so_ops routine for ia64-hpux. */
|
|
|
|
static void
|
|
ia64_hpux_clear_solib (void)
|
|
{
|
|
struct so_list *so;
|
|
|
|
while (so_list_head != NULL)
|
|
{
|
|
so = so_list_head;
|
|
so_list_head = so_list_head->next;
|
|
|
|
ia64_hpux_free_so (so);
|
|
xfree (so);
|
|
}
|
|
}
|
|
|
|
/* Assuming the inferior just stopped on an EXEC event, return
|
|
the address of the load_info_t structure. */
|
|
|
|
static CORE_ADDR
|
|
ia64_hpux_get_load_info_addr (void)
|
|
{
|
|
struct type *data_ptr_type = builtin_type (target_gdbarch ())->builtin_data_ptr;
|
|
CORE_ADDR addr;
|
|
int status;
|
|
|
|
/* The address of the load_info_t structure is stored in the 4th
|
|
argument passed to the initial thread of the process (in other
|
|
words, in argv[3]). So get the address of these arguments,
|
|
and extract the 4th one. */
|
|
status = ttrace (TT_PROC_GET_ARGS, ptid_get_pid (inferior_ptid),
|
|
0, (uintptr_t) &addr, sizeof (CORE_ADDR), 0);
|
|
if (status == -1 && errno)
|
|
perror_with_name (_("Unable to get argument list"));
|
|
return (read_memory_typed_address (addr + 3 * 8, data_ptr_type));
|
|
}
|
|
|
|
/* A structure used to aggregate some information extracted from
|
|
the dynamic section of the main executable. */
|
|
|
|
struct dld_info
|
|
{
|
|
ULONGEST dld_flags;
|
|
CORE_ADDR load_map;
|
|
};
|
|
|
|
/* Scan the ".dynamic" section referenced by ABFD and DYN_SECT,
|
|
and extract the information needed to fill in INFO. */
|
|
|
|
static void
|
|
ia64_hpux_read_dynamic_info (struct gdbarch *gdbarch, bfd *abfd,
|
|
asection *dyn_sect, struct dld_info *info)
|
|
{
|
|
int sect_size;
|
|
char *buf;
|
|
char *buf_end;
|
|
|
|
/* Make sure that info always has initialized data, even if we fail
|
|
to read the syn_sect section. */
|
|
memset (info, 0, sizeof (struct dld_info));
|
|
|
|
sect_size = bfd_section_size (abfd, dyn_sect);
|
|
buf = alloca (sect_size);
|
|
buf_end = buf + sect_size;
|
|
|
|
if (bfd_seek (abfd, dyn_sect->filepos, SEEK_SET) != 0
|
|
|| bfd_bread (buf, sect_size, abfd) != sect_size)
|
|
error (_("failed to read contents of .dynamic section"));
|
|
|
|
for (; buf < buf_end; buf += sizeof (Elf64_Dyn))
|
|
{
|
|
Elf64_Dyn *dynp = (Elf64_Dyn *) buf;
|
|
Elf64_Sxword d_tag;
|
|
|
|
d_tag = bfd_h_get_64 (abfd, &dynp->d_tag);
|
|
switch (d_tag)
|
|
{
|
|
case DT_HP_DLD_FLAGS:
|
|
info->dld_flags = bfd_h_get_64 (abfd, &dynp->d_un);
|
|
break;
|
|
|
|
case DT_HP_LOAD_MAP:
|
|
{
|
|
CORE_ADDR load_map_addr = bfd_h_get_64 (abfd, &dynp->d_un.d_ptr);
|
|
|
|
if (target_read_memory (load_map_addr,
|
|
(gdb_byte *) &info->load_map,
|
|
sizeof (info->load_map)) != 0)
|
|
error (_("failed to read load map at %s"),
|
|
paddress (gdbarch, load_map_addr));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Wrapper around target_read_memory used with libdl. */
|
|
|
|
static void *
|
|
ia64_hpux_read_tgt_mem (void *buffer, uint64_t ptr, size_t bufsiz, int ident)
|
|
{
|
|
if (target_read_memory (ptr, (gdb_byte *) buffer, bufsiz) != 0)
|
|
return 0;
|
|
else
|
|
return buffer;
|
|
}
|
|
|
|
/* Create a new so_list object for a shared library, and store that
|
|
new so_list object in our SO_LIST_HEAD list.
|
|
|
|
SO_INDEX is an index specifying the placement of the loaded shared
|
|
library in the dynamic loader's search list. Normally, this index
|
|
is strictly positive, but an index of -1 refers to the loader itself.
|
|
|
|
Return nonzero if the so_list object could be created. A null
|
|
return value with a positive SO_INDEX normally means that there are
|
|
no more entries in the dynamic loader's search list at SO_INDEX or
|
|
beyond. */
|
|
|
|
static int
|
|
ia64_hpux_add_so_from_dld_info (struct dld_info info, int so_index)
|
|
{
|
|
struct load_module_desc module_desc;
|
|
uint64_t so_handle;
|
|
char *so_path;
|
|
struct so_list *so;
|
|
|
|
so_handle = dlgetmodinfo (so_index, &module_desc, sizeof (module_desc),
|
|
ia64_hpux_read_tgt_mem, 0, info.load_map);
|
|
|
|
if (so_handle == 0)
|
|
/* No such entry. We probably reached the end of the list. */
|
|
return 0;
|
|
|
|
so_path = dlgetname (&module_desc, sizeof (module_desc),
|
|
ia64_hpux_read_tgt_mem, 0, info.load_map);
|
|
if (so_path == NULL)
|
|
{
|
|
/* Should never happen, but let's not crash if it does. */
|
|
warning (_("unable to get shared library name, symbols not loaded"));
|
|
return 0;
|
|
}
|
|
|
|
/* Create a new so_list and insert it at the start of our list.
|
|
The order is not extremely important, but it's less work to do so
|
|
at the end of the list. */
|
|
so = new_so_list (so_path, module_desc);
|
|
so->next = so_list_head;
|
|
so_list_head = so;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Assuming we just attached to a process, update our list of shared
|
|
libraries (SO_LIST_HEAD) as well as GDB's list. */
|
|
|
|
static void
|
|
ia64_hpux_solib_add_after_attach (void)
|
|
{
|
|
bfd *abfd;
|
|
asection *dyn_sect;
|
|
struct dld_info info;
|
|
int i;
|
|
|
|
if (symfile_objfile == NULL)
|
|
return;
|
|
|
|
abfd = symfile_objfile->obfd;
|
|
dyn_sect = bfd_get_section_by_name (abfd, ".dynamic");
|
|
|
|
if (dyn_sect == NULL || bfd_section_size (abfd, dyn_sect) == 0)
|
|
return;
|
|
|
|
ia64_hpux_read_dynamic_info (get_objfile_arch (symfile_objfile), abfd,
|
|
dyn_sect, &info);
|
|
|
|
if ((info.dld_flags & DT_HP_DEBUG_PRIVATE) == 0)
|
|
{
|
|
warning (_(
|
|
"The shared libraries were not privately mapped; setting a breakpoint\n\
|
|
in a shared library will not work until you rerun the program.\n\
|
|
Use the following command to enable debugging of shared libraries.\n\
|
|
chatr +dbg enable a.out"));
|
|
}
|
|
|
|
/* Read the symbols of the dynamic loader (dld.so). */
|
|
ia64_hpux_add_so_from_dld_info (info, -1);
|
|
|
|
/* Read the symbols of all the other shared libraries. */
|
|
for (i = 1; ; i++)
|
|
if (!ia64_hpux_add_so_from_dld_info (info, i))
|
|
break; /* End of list. */
|
|
|
|
/* Resync the library list at the core level. */
|
|
solib_add (NULL, 1, ¤t_target, auto_solib_add);
|
|
}
|
|
|
|
/* The "create_inferior_hook" target_so_ops routine for ia64-hpux. */
|
|
|
|
static void
|
|
ia64_hpux_solib_create_inferior_hook (int from_tty)
|
|
{
|
|
CORE_ADDR load_info_addr;
|
|
load_info_t load_info;
|
|
|
|
/* Initially, we were thinking about adding a check that the program
|
|
(accessible through symfile_objfile) was linked against some shared
|
|
libraries, by searching for a ".dynamic" section. However, could
|
|
this break in the case of a statically linked program that later
|
|
uses dlopen? Programs that are fully statically linked are very
|
|
rare, and we will worry about them when we encounter one that
|
|
causes trouble. */
|
|
|
|
/* Set the LI_TRACE flag in the load_info_t structure. This enables
|
|
notifications when shared libraries are being mapped. */
|
|
load_info_addr = ia64_hpux_get_load_info_addr ();
|
|
read_memory (load_info_addr, (gdb_byte *) &load_info, sizeof (load_info));
|
|
load_info.li_flags |= LI_TRACE;
|
|
write_memory (load_info_addr, (gdb_byte *) &load_info, sizeof (load_info));
|
|
|
|
/* If we just attached to our process, some shard libraries have
|
|
already been mapped. Find which ones they are... */
|
|
if (current_inferior ()->attach_flag)
|
|
ia64_hpux_solib_add_after_attach ();
|
|
}
|
|
|
|
/* The "special_symbol_handling" target_so_ops routine for ia64-hpux. */
|
|
|
|
static void
|
|
ia64_hpux_special_symbol_handling (void)
|
|
{
|
|
/* Nothing to do. */
|
|
}
|
|
|
|
/* The "current_sos" target_so_ops routine for ia64-hpux. */
|
|
|
|
static struct so_list *
|
|
ia64_hpux_current_sos (void)
|
|
{
|
|
/* Return a deep copy of our own list. */
|
|
struct so_list *new_head = NULL, *prev_new_so = NULL;
|
|
struct so_list *our_so;
|
|
|
|
for (our_so = so_list_head; our_so != NULL; our_so = our_so->next)
|
|
{
|
|
struct so_list *new_so;
|
|
|
|
new_so = new_so_list (our_so->so_name, our_so->lm_info->module_desc);
|
|
if (prev_new_so != NULL)
|
|
prev_new_so->next = new_so;
|
|
prev_new_so = new_so;
|
|
if (new_head == NULL)
|
|
new_head = new_so;
|
|
}
|
|
|
|
return new_head;
|
|
}
|
|
|
|
/* The "open_symbol_file_object" target_so_ops routine for ia64-hpux. */
|
|
|
|
static int
|
|
ia64_hpux_open_symbol_file_object (void *from_ttyp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* The "in_dynsym_resolve_code" target_so_ops routine for ia64-hpux. */
|
|
|
|
static int
|
|
ia64_hpux_in_dynsym_resolve_code (CORE_ADDR pc)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* If FADDR is the address of a function inside one of the shared
|
|
libraries, return the shared library linkage address. */
|
|
|
|
CORE_ADDR
|
|
ia64_hpux_get_solib_linkage_addr (CORE_ADDR faddr)
|
|
{
|
|
struct so_list *so = so_list_head;
|
|
|
|
while (so != NULL)
|
|
{
|
|
struct load_module_desc module_desc = so->lm_info->module_desc;
|
|
|
|
if (module_desc.text_base <= faddr
|
|
&& (module_desc.text_base + module_desc.text_size) > faddr)
|
|
return module_desc.linkage_ptr;
|
|
|
|
so = so->next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Create a new target_so_ops structure suitable for ia64-hpux, and
|
|
return its address. */
|
|
|
|
static struct target_so_ops *
|
|
ia64_hpux_target_so_ops (void)
|
|
{
|
|
struct target_so_ops *ops = XCNEW (struct target_so_ops);
|
|
|
|
ops->relocate_section_addresses = ia64_hpux_relocate_section_addresses;
|
|
ops->free_so = ia64_hpux_free_so;
|
|
ops->clear_solib = ia64_hpux_clear_solib;
|
|
ops->solib_create_inferior_hook = ia64_hpux_solib_create_inferior_hook;
|
|
ops->special_symbol_handling = ia64_hpux_special_symbol_handling;
|
|
ops->current_sos = ia64_hpux_current_sos;
|
|
ops->open_symbol_file_object = ia64_hpux_open_symbol_file_object;
|
|
ops->in_dynsym_resolve_code = ia64_hpux_in_dynsym_resolve_code;
|
|
ops->bfd_open = solib_bfd_open;
|
|
|
|
return ops;
|
|
}
|
|
|
|
/* Prevent warning from -Wmissing-prototypes. */
|
|
void _initialize_solib_ia64_hpux (void);
|
|
|
|
void
|
|
_initialize_solib_ia64_hpux (void)
|
|
{
|
|
ia64_hpux_so_ops = ia64_hpux_target_so_ops ();
|
|
}
|