#include "defs.h"
#include <sys/types.h>
#include <signal.h>
#include "gdb_string.h"
#include <fcntl.h>
#include "symtab.h"
#include "bfd.h"
#include "symfile.h"
#include "objfiles.h"
#include "gdbcore.h"
#include "command.h"
#include "target.h"
#include "frame.h"
#include "gdb_regex.h"
#include "inferior.h"
#include "language.h"
#include "gdbcmd.h"
#define MAX_PATH_SIZE 1024
#ifndef USE_LDR_ROUTINES
#define RLD_CONTEXT_ADDRESS 0x3ffc0000000
typedef struct
{
CORE_ADDR next;
CORE_ADDR previous;
CORE_ADDR unknown1;
char *module_name;
CORE_ADDR modinfo_addr;
long module_id;
CORE_ADDR unknown2;
CORE_ADDR unknown3;
long region_count;
CORE_ADDR regioninfo_addr;
}
ldr_module_info_t;
typedef struct
{
long unknown1;
CORE_ADDR regionname_addr;
long protection;
CORE_ADDR vaddr;
CORE_ADDR mapaddr;
long size;
long unknown2[5];
}
ldr_region_info_t;
typedef struct
{
CORE_ADDR unknown1;
CORE_ADDR unknown2;
CORE_ADDR head;
CORE_ADDR tail;
}
ldr_context_t;
static ldr_context_t ldr_context;
#else
#include <loader.h>
static ldr_process_t fake_ldr_process;
static int ldr_read_memory (CORE_ADDR, char *, int, int);
static int
ldr_read_memory (CORE_ADDR memaddr, char *myaddr, int len, int readstring)
{
int result;
char *buffer;
if (readstring)
{
target_read_string (memaddr, &buffer, len, &result);
if (result == 0)
strcpy (myaddr, buffer);
xfree (buffer);
}
else
result = target_read_memory (memaddr, myaddr, len);
if (result != 0)
result = -result;
return result;
}
#endif
struct link_map
{
CORE_ADDR l_offset;
char *l_name;
ldr_module_info_t module_info;
};
#define LM_OFFSET(so) ((so) -> lm.l_offset)
#define LM_NAME(so) ((so) -> lm.l_name)
struct so_list
{
struct so_list *next;
struct link_map lm;
struct link_map *lmaddr;
CORE_ADDR lmend;
char so_name[MAX_PATH_SIZE];
char symbols_loaded;
char from_tty;
struct objfile *objfile;
struct section_table *sections;
struct section_table *sections_end;
struct section_table *textsection;
bfd *abfd;
};
static struct so_list *so_list_head;
extern int fdmatch (int, int);
static void sharedlibrary_command (char *, int);
static void info_sharedlibrary_command (char *, int);
static int symbol_add_stub (char *);
static struct so_list *find_solib (struct so_list *);
static struct link_map *first_link_map_member (void);
static struct link_map *next_link_map_member (struct so_list *);
static void xfer_link_map_member (struct so_list *, struct link_map *);
static int solib_map_sections (char *);
static int
solib_map_sections (char *arg)
{
struct so_list *so = (struct so_list *) arg;
char *filename;
char *scratch_pathname;
int scratch_chan;
struct section_table *p;
struct cleanup *old_chain;
bfd *abfd;
filename = tilde_expand (so->so_name);
old_chain = make_cleanup (xfree, filename);
scratch_chan = openp (getenv ("PATH"), 1, filename, O_RDONLY, 0,
&scratch_pathname);
if (scratch_chan < 0)
{
scratch_chan = openp (getenv ("LD_LIBRARY_PATH"), 1, filename,
O_RDONLY, 0, &scratch_pathname);
}
if (scratch_chan < 0)
{
perror_with_name (filename);
}
abfd = bfd_fdopenr (scratch_pathname, gnutarget, scratch_chan);
if (!abfd)
{
close (scratch_chan);
error ("Could not open `%s' as an executable file: %s",
scratch_pathname, bfd_errmsg (bfd_get_error ()));
}
so->abfd = abfd;
abfd->cacheable = 1;
if (!bfd_check_format (abfd, bfd_object))
{
error ("\"%s\": not in executable format: %s.",
scratch_pathname, bfd_errmsg (bfd_get_error ()));
}
if (build_section_table (abfd, &so->sections, &so->sections_end))
{
error ("Can't find the file sections in `%s': %s",
bfd_get_filename (exec_bfd), bfd_errmsg (bfd_get_error ()));
}
for (p = so->sections; p < so->sections_end; p++)
{
p->addr += LM_OFFSET (so);
p->endaddr += LM_OFFSET (so);
so->lmend = (CORE_ADDR) max (p->endaddr, so->lmend);
if (STREQ (p->the_bfd_section->name, ".text"))
{
so->textsection = p;
}
}
do_cleanups (old_chain);
return (1);
}
static struct link_map *
first_link_map_member (void)
{
struct link_map *lm = NULL;
static struct link_map first_lm;
#ifdef USE_LDR_ROUTINES
ldr_module_t mod_id = LDR_NULL_MODULE;
size_t retsize;
fake_ldr_process = ldr_core_process ();
ldr_set_core_reader (ldr_read_memory);
ldr_xdetach (fake_ldr_process);
if (ldr_xattach (fake_ldr_process) != 0
|| ldr_next_module (fake_ldr_process, &mod_id) != 0
|| mod_id == LDR_NULL_MODULE
|| ldr_inq_module (fake_ldr_process, mod_id,
&first_lm.module_info, sizeof (ldr_module_info_t),
&retsize) != 0)
return lm;
#else
CORE_ADDR ldr_context_addr;
if (target_read_memory ((CORE_ADDR) RLD_CONTEXT_ADDRESS,
(char *) &ldr_context_addr,
sizeof (CORE_ADDR)) != 0
|| target_read_memory (ldr_context_addr,
(char *) &ldr_context,
sizeof (ldr_context_t)) != 0
|| target_read_memory ((CORE_ADDR) ldr_context.head,
(char *) &first_lm.module_info,
sizeof (ldr_module_info_t)) != 0)
return lm;
#endif
lm = &first_lm;
lm->l_name = NULL;
return lm;
}
static struct link_map *
next_link_map_member (struct so_list *so_list_ptr)
{
struct link_map *lm = NULL;
static struct link_map next_lm;
#ifdef USE_LDR_ROUTINES
ldr_module_t mod_id = so_list_ptr->lm.module_info.lmi_modid;
size_t retsize;
if (ldr_next_module (fake_ldr_process, &mod_id) != 0
|| mod_id == LDR_NULL_MODULE
|| ldr_inq_module (fake_ldr_process, mod_id,
&next_lm.module_info, sizeof (ldr_module_info_t),
&retsize) != 0)
return lm;
lm = &next_lm;
lm->l_name = lm->module_info.lmi_name;
#else
CORE_ADDR ldr_context_addr;
if (target_read_memory ((CORE_ADDR) RLD_CONTEXT_ADDRESS,
(char *) &ldr_context_addr,
sizeof (CORE_ADDR)) != 0
|| target_read_memory (ldr_context_addr,
(char *) &ldr_context,
sizeof (ldr_context_t)) != 0
|| so_list_ptr->lm.module_info.modinfo_addr == ldr_context.tail
|| target_read_memory (so_list_ptr->lm.module_info.next,
(char *) &next_lm.module_info,
sizeof (ldr_module_info_t)) != 0)
return lm;
lm = &next_lm;
lm->l_name = lm->module_info.module_name;
#endif
return lm;
}
static void
xfer_link_map_member (struct so_list *so_list_ptr, struct link_map *lm)
{
int i;
so_list_ptr->lm = *lm;
LM_OFFSET (so_list_ptr) = 0;
if (LM_NAME (so_list_ptr) != 0)
{
#ifdef USE_LDR_ROUTINES
int len = strlen (LM_NAME (so_list_ptr) + 1);
if (len > MAX_PATH_SIZE)
len = MAX_PATH_SIZE;
strncpy (so_list_ptr->so_name, LM_NAME (so_list_ptr), MAX_PATH_SIZE);
so_list_ptr->so_name[MAX_PATH_SIZE - 1] = '\0';
for (i = 0; i < lm->module_info.lmi_nregion; i++)
{
ldr_region_info_t region_info;
size_t retsize;
CORE_ADDR region_offset;
if (ldr_inq_region (fake_ldr_process, lm->module_info.lmi_modid,
i, ®ion_info, sizeof (region_info),
&retsize) != 0)
break;
region_offset = (CORE_ADDR) region_info.lri_mapaddr
- (CORE_ADDR) region_info.lri_vaddr;
if (i == 0)
LM_OFFSET (so_list_ptr) = region_offset;
else if (LM_OFFSET (so_list_ptr) != region_offset)
warning ("cannot handle shared library relocation for %s (%s)",
so_list_ptr->so_name, region_info.lri_name);
}
#else
int errcode;
char *buffer;
target_read_string ((CORE_ADDR) LM_NAME (so_list_ptr), &buffer,
MAX_PATH_SIZE - 1, &errcode);
if (errcode != 0)
error ("xfer_link_map_member: Can't read pathname for load map: %s\n",
safe_strerror (errcode));
strncpy (so_list_ptr->so_name, buffer, MAX_PATH_SIZE - 1);
xfree (buffer);
so_list_ptr->so_name[MAX_PATH_SIZE - 1] = '\0';
for (i = 0; i < lm->module_info.region_count; i++)
{
ldr_region_info_t region_info;
CORE_ADDR region_offset;
if (target_read_memory (lm->module_info.regioninfo_addr
+ i * sizeof (region_info),
(char *) ®ion_info,
sizeof (region_info)) != 0)
break;
region_offset = region_info.mapaddr - region_info.vaddr;
if (i == 0)
LM_OFFSET (so_list_ptr) = region_offset;
else if (LM_OFFSET (so_list_ptr) != region_offset)
{
char *region_name;
target_read_string (region_info.regionname_addr, &buffer,
MAX_PATH_SIZE - 1, &errcode);
if (errcode == 0)
region_name = buffer;
else
region_name = "??";
warning ("cannot handle shared library relocation for %s (%s)",
so_list_ptr->so_name, region_name);
xfree (buffer);
}
}
#endif
catch_errors (solib_map_sections, (char *) so_list_ptr,
"Error while mapping shared library sections:\n",
RETURN_MASK_ALL);
}
}
static struct so_list *
find_solib (struct so_list *so_list_ptr)
{
struct so_list *so_list_next = NULL;
struct link_map *lm = NULL;
struct so_list *new;
if (so_list_ptr == NULL)
{
if ((so_list_next = so_list_head) == NULL)
{
lm = first_link_map_member ();
}
}
else
{
lm = next_link_map_member (so_list_ptr);
so_list_next = so_list_ptr->next;
}
if ((so_list_next == NULL) && (lm != NULL))
{
new = (struct so_list *) xmalloc (sizeof (struct so_list));
memset ((char *) new, 0, sizeof (struct so_list));
new->lmaddr = lm;
if (so_list_ptr != NULL)
{
so_list_ptr->next = new;
}
else
{
so_list_head = new;
}
so_list_next = new;
xfer_link_map_member (new, lm);
}
return (so_list_next);
}
static int
symbol_add_stub (char *arg)
{
register struct so_list *so = (struct so_list *) arg;
CORE_ADDR text_addr = 0;
struct section_addr_info section_addrs;
memset (§ion_addrs, 0, sizeof (section_addrs));
if (so->textsection)
text_addr = so->textsection->addr;
else if (so->abfd != NULL)
{
asection *lowest_sect;
lowest_sect = bfd_get_section_by_name (so->abfd, ".text");
if (lowest_sect == NULL)
bfd_map_over_sections (so->abfd, find_lowest_section,
(PTR) &lowest_sect);
if (lowest_sect)
text_addr = bfd_section_vma (so->abfd, lowest_sect) + LM_OFFSET (so);
}
section_addrs.other[0].addr = text_addr;
section_addrs.other[0].name = ".text";
so->objfile = symbol_file_add (so->so_name, so->from_tty,
§ion_addrs, 0, OBJF_SHARED);
return (1);
}
void
solib_add (char *arg_string, int from_tty, struct target_ops *target, int readsyms)
{
register struct so_list *so = NULL;
struct so_list *so_last = NULL;
char *re_err;
int count;
int old;
if (!readsyms)
return;
if ((re_err = re_comp (arg_string ? arg_string : ".")) != NULL)
{
error ("Invalid regexp: %s", re_err);
}
if (target)
{
so = NULL;
count = 0;
while ((so = find_solib (so)) != NULL)
{
if (so->so_name[0])
{
count += so->sections_end - so->sections;
}
}
if (count)
{
old = target_resize_to_sections (target, count);
while ((so = find_solib (so)) != NULL)
{
if (so->so_name[0])
{
count = so->sections_end - so->sections;
memcpy ((char *) (target->to_sections + old),
so->sections,
(sizeof (struct section_table)) * count);
old += count;
}
}
}
}
so = NULL;
while ((so = find_solib (so)) != NULL)
{
if (so->so_name[0] && re_exec (so->so_name))
{
so->from_tty = from_tty;
if (so->symbols_loaded)
{
if (from_tty)
{
printf_unfiltered ("Symbols already loaded for %s\n", so->so_name);
}
}
else if (catch_errors
(symbol_add_stub, (char *) so,
"Error while reading shared library symbols:\n",
RETURN_MASK_ALL))
{
so_last = so;
so->symbols_loaded = 1;
}
}
}
if (so_last)
reinit_frame_cache ();
}
static void
info_sharedlibrary_command (char *ignore, int from_tty)
{
register struct so_list *so = NULL;
int header_done = 0;
if (exec_bfd == NULL)
{
printf_unfiltered ("No executable file.\n");
return;
}
while ((so = find_solib (so)) != NULL)
{
if (so->so_name[0])
{
unsigned long txt_start = 0;
unsigned long txt_end = 0;
if (!header_done)
{
printf_unfiltered ("%-20s%-20s%-12s%s\n", "From", "To", "Syms Read",
"Shared Object Library");
header_done++;
}
if (so->textsection)
{
txt_start = (unsigned long) so->textsection->addr;
txt_end = (unsigned long) so->textsection->endaddr;
}
printf_unfiltered ("%-20s", local_hex_string_custom (txt_start, "08l"));
printf_unfiltered ("%-20s", local_hex_string_custom (txt_end, "08l"));
printf_unfiltered ("%-12s", so->symbols_loaded ? "Yes" : "No");
printf_unfiltered ("%s\n", so->so_name);
}
}
if (so_list_head == NULL)
{
printf_unfiltered ("No shared libraries loaded at this time.\n");
}
}
char *
solib_address (CORE_ADDR address)
{
register struct so_list *so = 0;
while ((so = find_solib (so)) != NULL)
{
if (so->so_name[0] && so->textsection)
{
if ((address >= (CORE_ADDR) so->textsection->addr) &&
(address < (CORE_ADDR) so->textsection->endaddr))
return (so->so_name);
}
}
return (0);
}
void
clear_solib (void)
{
struct so_list *next;
char *bfd_filename;
disable_breakpoints_in_shlibs (1);
while (so_list_head)
{
if (so_list_head->sections)
{
xfree (so_list_head->sections);
}
if (so_list_head->abfd)
{
remove_target_sections (so_list_head->abfd);
bfd_filename = bfd_get_filename (so_list_head->abfd);
if (!bfd_close (so_list_head->abfd))
warning ("cannot close \"%s\": %s",
bfd_filename, bfd_errmsg (bfd_get_error ()));
}
else
bfd_filename = NULL;
next = so_list_head->next;
if (bfd_filename)
xfree (bfd_filename);
xfree (so_list_head);
so_list_head = next;
}
}
void
solib_create_inferior_hook (void)
{
if (symfile_objfile == NULL
|| symfile_objfile->obfd == NULL
|| ((bfd_get_file_flags (symfile_objfile->obfd) & DYNAMIC) == 0))
return;
clear_proceed_status ();
stop_soon_quietly = 1;
stop_signal = TARGET_SIGNAL_0;
do
{
target_resume (minus_one_ptid, 0, stop_signal);
wait_for_inferior ();
}
while (stop_signal != TARGET_SIGNAL_TRAP);
solib_add ((char *) 0, 0, (struct target_ops *) 0, auto_solib_add);
stop_soon_quietly = 0;
}
static void
sharedlibrary_command (char *args, int from_tty)
{
dont_repeat ();
solib_add (args, from_tty, (struct target_ops *) 0, 1);
}
void
_initialize_solib (void)
{
add_com ("sharedlibrary", class_files, sharedlibrary_command,
"Load shared object library symbols for files matching REGEXP.");
add_info ("sharedlibrary", info_sharedlibrary_command,
"Status of loaded shared object libraries.");
add_show_from_set
(add_set_cmd ("auto-solib-add", class_support, var_boolean,
(char *) &auto_solib_add,
"Set autoloading of shared library symbols.\n\
If \"on\", symbols from all shared object libraries will be loaded\n\
automatically when the inferior begins execution, when the dynamic linker\n\
informs gdb that a new library has been loaded, or when attaching to the\n\
inferior. Otherwise, symbols must be loaded manually, using `sharedlibrary'.",
&setlist),
&showlist);
}