#include <dlfcn.h>
#include <elf.h>
#include <elf_hp.h>
#include "defs.h"
#include "frame.h"
#include "bfd.h"
#include "libhppa.h"
#include "gdbcore.h"
#include "symtab.h"
#include "breakpoint.h"
#include "symfile.h"
#include "objfiles.h"
#include "inferior.h"
#include "gdb-stabs.h"
#include "gdb_stat.h"
#include "gdbcmd.h"
#include "language.h"
#include "regcache.h"
#include "exec.h"
#include <fcntl.h>
#ifndef O_BINARY
#define O_BINARY 0
#endif
static CORE_ADDR bfd_lookup_symbol (bfd *, char *);
extern struct unwind_table_entry *find_unwind_entry (CORE_ADDR pc);
#define DLD_CB_LOAD 1
#define DLD_CB_UNLOAD 0
struct so_list
{
bfd *abfd;
char *name;
struct so_list *next;
struct objfile *objfile;
CORE_ADDR pa64_solib_desc_addr;
struct load_module_desc pa64_solib_desc;
struct section_table *sections;
struct section_table *sections_end;
int loaded;
};
static struct so_list *so_list_head;
static LONGEST pa64_solib_total_st_size;
static int pa64_solib_st_size_threshold_exceeded;
typedef struct
{
CORE_ADDR dld_flags_addr;
LONGEST dld_flags;
struct bfd_section *dyninfo_sect;
int have_read_dld_descriptor;
int is_valid;
CORE_ADDR load_map;
CORE_ADDR load_map_addr;
struct load_module_desc dld_desc;
}
dld_cache_t;
static dld_cache_t dld_cache;
static void pa64_sharedlibrary_info_command (char *, int);
static void pa64_solib_sharedlibrary_command (char *, int);
static void *pa64_target_read_memory (void *, CORE_ADDR, size_t, int);
static int read_dld_descriptor (struct target_ops *, int readsyms);
static int read_dynamic_info (asection *, dld_cache_t *);
static void add_to_solist (int, char *, int, struct load_module_desc *,
CORE_ADDR, struct target_ops *);
static char *pa64_debug_section_names[] = {
".debug_header", ".debug_gntt", ".debug_lntt", ".debug_slt", ".debug_vt",
".stabs", ".stabstr", ".debug_info", ".debug_abbrev", ".debug_aranges",
".debug_macinfo", ".debug_line", ".debug_loc", ".debug_pubnames",
".debug_str", NULL
};
static LONGEST
pa64_solib_sizeof_symbol_table (char *filename)
{
bfd *abfd;
int i;
int desc;
char *absolute_name;
LONGEST st_size = (LONGEST) 0;
asection *sect;
desc = openp (getenv ("PATH"), 1, filename, O_RDONLY | O_BINARY,
0, &absolute_name);
if (desc < 0)
{
perror_with_name (filename);
}
filename = absolute_name;
abfd = bfd_fdopenr (filename, gnutarget, desc);
if (!abfd)
{
close (desc);
make_cleanup (xfree, filename);
error ("\"%s\": can't open to read symbols: %s.", filename,
bfd_errmsg (bfd_get_error ()));
}
if (!bfd_check_format (abfd, bfd_object))
{
bfd_close (abfd);
make_cleanup (xfree, filename);
error ("\"%s\": can't read symbols: %s.", filename,
bfd_errmsg (bfd_get_error ()));
}
for (i = 0; pa64_debug_section_names[i] != NULL; i++)
{
asection *sect;
sect = bfd_get_section_by_name (abfd, pa64_debug_section_names[i]);
if (sect)
st_size += (LONGEST)bfd_section_size (abfd, sect);
}
bfd_close (abfd);
xfree (filename);
return st_size * (LONGEST) 10;
}
static void
pa64_solib_add_solib_objfile (struct so_list *so, char *name, int from_tty,
CORE_ADDR text_addr)
{
bfd *tmp_bfd;
asection *sec;
obj_private_data_t *obj_private;
struct section_addr_info *section_addrs;
struct cleanup *my_cleanups;
tmp_bfd = bfd_openr (name, gnutarget);
if (tmp_bfd == NULL)
{
perror_with_name (name);
return;
}
if (!bfd_check_format (tmp_bfd, bfd_object))
{
bfd_close (tmp_bfd);
error ("\"%s\" is not an object file: %s", name,
bfd_errmsg (bfd_get_error ()));
}
sec = bfd_get_section_by_name (tmp_bfd, ".text");
text_addr += bfd_section_vma (tmp_bfd, sec);
sec = NULL;
bfd_map_over_sections (tmp_bfd, find_lowest_section, &sec);
if (sec)
{
text_addr -= bfd_section_vma (tmp_bfd, sec);
text_addr += sec->filepos;
}
section_addrs = alloc_section_addr_info (bfd_count_sections (tmp_bfd));
my_cleanups = make_cleanup (xfree, section_addrs);
bfd_close (tmp_bfd);
tmp_bfd = NULL;
section_addrs->other[0].addr = text_addr;
section_addrs->other[0].name = ".text";
so->objfile = symbol_file_add (name, from_tty, section_addrs, 0, OBJF_SHARED);
so->abfd = so->objfile->obfd;
so->objfile->flags |= OBJF_SHARED;
if (so->objfile->obj_private == NULL)
{
obj_private = (obj_private_data_t *)
obstack_alloc (&so->objfile->objfile_obstack,
sizeof (obj_private_data_t));
obj_private->unwind_info = NULL;
obj_private->so_info = NULL;
so->objfile->obj_private = obj_private;
}
obj_private = (obj_private_data_t *) so->objfile->obj_private;
obj_private->so_info = so;
obj_private->dp = so->pa64_solib_desc.linkage_ptr;
do_cleanups (my_cleanups);
}
static void
pa64_solib_load_symbols (struct so_list *so, char *name, int from_tty,
CORE_ADDR text_addr, struct target_ops *target)
{
struct section_table *p;
asection *sec;
int status;
char buf[4];
CORE_ADDR presumed_data_start;
if (text_addr == 0)
text_addr = so->pa64_solib_desc.text_base;
pa64_solib_add_solib_objfile (so, name, from_tty, text_addr);
if (build_section_table (so->abfd,
&so->sections,
&so->sections_end))
{
error ("Unable to build section table for shared library\n.");
return;
}
(so->objfile->section_offsets)->offsets[SECT_OFF_TEXT (so->objfile)]
= so->pa64_solib_desc.text_base;
(so->objfile->section_offsets)->offsets[SECT_OFF_DATA (so->objfile)]
= so->pa64_solib_desc.data_base;
for (p = so->sections; p < so->sections_end; p++)
{
if (p->the_bfd_section->flags & SEC_CODE)
{
p->addr += ANOFFSET (so->objfile->section_offsets, SECT_OFF_TEXT (so->objfile));
p->endaddr += ANOFFSET (so->objfile->section_offsets, SECT_OFF_TEXT (so->objfile));
}
else if (p->the_bfd_section->flags & SEC_DATA)
{
p->addr += ANOFFSET (so->objfile->section_offsets, SECT_OFF_DATA (so->objfile));
p->endaddr += ANOFFSET (so->objfile->section_offsets, SECT_OFF_DATA (so->objfile));
}
}
status = target_read_memory (text_addr, buf, 4);
if (status != 0)
{
int new, old;
new = so->sections_end - so->sections;
old = target_resize_to_sections (target, new);
memcpy ((char *) (target->to_sections + old),
so->sections,
((sizeof (struct section_table)) * new));
}
}
void
pa64_solib_add (char *arg_string, int from_tty, struct target_ops *target, int readsyms)
{
struct minimal_symbol *msymbol;
CORE_ADDR addr;
asection *shlib_info;
int status;
unsigned int dld_flags;
char buf[4], *re_err;
int threshold_warning_given = 0;
int dll_index;
struct load_module_desc dll_desc;
char *dll_path;
if ((re_err = re_comp (arg_string ? arg_string : ".")) != NULL)
{
error ("Invalid regexp: %s", re_err);
}
if (symfile_objfile == NULL)
return;
shlib_info = bfd_get_section_by_name (symfile_objfile->obfd, ".dynamic");
if (!shlib_info)
return;
if (bfd_section_size (symfile_objfile->obfd, shlib_info) == 0)
return;
if (! dld_cache.have_read_dld_descriptor)
if (! read_dld_descriptor (target, readsyms))
return;
if ((dld_cache.dld_flags & DT_HP_DEBUG_PRIVATE) == 0)
warning ("The shared libraries were not privately mapped; setting a\nbreakpoint in a shared library will not work until you rerun the program.\n");
for (dll_index = 1; ; dll_index++)
{
if (dlgetmodinfo (dll_index, &dll_desc, sizeof (dll_desc),
pa64_target_read_memory, 0, dld_cache.load_map)
== 0)
return;
dll_path = (char *)dlgetname (&dll_desc, sizeof (dll_desc),
pa64_target_read_memory,
0, dld_cache.load_map);
if (!dll_path)
error ("pa64_solib_add, unable to read shared library path.");
add_to_solist (from_tty, dll_path, readsyms, &dll_desc, 0, target);
}
}
void
pa64_solib_create_inferior_hook (void)
{
struct minimal_symbol *msymbol;
unsigned int dld_flags, status;
asection *shlib_info, *interp_sect;
char buf[4];
struct objfile *objfile;
CORE_ADDR anaddr;
remove_solib_event_breakpoints ();
if (symfile_objfile == NULL)
return;
shlib_info = bfd_get_section_by_name (symfile_objfile->obfd, ".dynamic");
if (!shlib_info)
return;
if (bfd_section_size (symfile_objfile->obfd, shlib_info) == 0)
return;
if (! read_dynamic_info (shlib_info, &dld_cache))
error ("Unable to read the .dynamic section.");
dld_cache.dld_flags |= DT_HP_DEBUG_PRIVATE;
dld_cache.dld_flags |= DT_HP_DEBUG_CALLBACK;
status = target_write_memory (dld_cache.dld_flags_addr,
(char *) &dld_cache.dld_flags,
sizeof (dld_cache.dld_flags));
if (status != 0)
error ("Unable to modify dynamic linker flags.");
interp_sect = bfd_get_section_by_name (exec_bfd, ".interp");
if (interp_sect)
{
unsigned int interp_sect_size;
char *buf;
CORE_ADDR load_addr;
bfd *tmp_bfd;
CORE_ADDR sym_addr = 0;
interp_sect_size = bfd_section_size (exec_bfd, interp_sect);
buf = alloca (interp_sect_size);
bfd_get_section_contents (exec_bfd, interp_sect,
buf, 0, interp_sect_size);
tmp_bfd = bfd_openr (buf, gnutarget);
if (tmp_bfd == NULL)
goto get_out;
if (!bfd_check_format (tmp_bfd, bfd_object))
{
warning ("Unable to grok dynamic linker %s as an object file", buf);
bfd_close (tmp_bfd);
goto get_out;
}
load_addr = read_pc () - tmp_bfd->start_address;
sym_addr = bfd_lookup_symbol (tmp_bfd, "__dld_break");
sym_addr = load_addr + sym_addr + 4;
{
struct breakpoint *b
= create_solib_event_breakpoint (sym_addr);
make_breakpoint_permanent (b);
}
bfd_close (tmp_bfd);
}
get_out:
while (so_list_head)
{
struct so_list *temp;
temp = so_list_head;
xfree (so_list_head);
so_list_head = temp->next;
}
clear_symtab_users ();
}
void
pa64_solib_remove_inferior_hook (int pid)
{
dld_cache.dld_flags &= ~DT_HP_DEBUG_CALLBACK;
target_write_memory (dld_cache.dld_flags_addr,
(char *)&dld_cache.dld_flags,
sizeof (dld_cache.dld_flags));
}
void
pa64_solib_create_catch_load_hook (int pid, int tempflag, char *filename,
char *cond_string)
{
create_solib_load_event_breakpoint ("", tempflag, filename, cond_string);
}
void
pa64_solib_create_catch_unload_hook (int pid, int tempflag, char *filename,
char *cond_string)
{
create_solib_unload_event_breakpoint ("", tempflag, filename, cond_string);
}
int
pa64_solib_have_load_event (int pid)
{
CORE_ADDR event_kind;
event_kind = read_register (ARG0_REGNUM);
return (event_kind == DLD_CB_LOAD);
}
int
pa64_solib_have_unload_event (int pid)
{
CORE_ADDR event_kind;
event_kind = read_register (ARG0_REGNUM);
return (event_kind == DLD_CB_UNLOAD);
}
char *
pa64_solib_loaded_library_pathname (int pid)
{
static char dll_path[MAXPATHLEN];
CORE_ADDR dll_path_addr = read_register (ARG3_REGNUM);
read_memory_string (dll_path_addr, dll_path, MAXPATHLEN);
return dll_path;
}
char *
pa64_solib_unloaded_library_pathname (int pid)
{
static char dll_path[MAXPATHLEN];
CORE_ADDR dll_path_addr = read_register (ARG3_REGNUM);
read_memory_string (dll_path_addr, dll_path, MAXPATHLEN);
return dll_path;
}
int
pa64_solib_in_dynamic_linker (int pid, CORE_ADDR pc)
{
asection *shlib_info;
if (symfile_objfile == NULL)
return 0;
if (!dld_cache.have_read_dld_descriptor)
if (!read_dld_descriptor (¤t_target, auto_solib_add))
return 0;
return (pc >= dld_cache.dld_desc.text_base
&& pc < dld_cache.dld_desc.text_base + dld_cache.dld_desc.text_size);
}
CORE_ADDR
pa64_solib_get_got_by_pc (CORE_ADDR addr)
{
struct so_list *so_list = so_list_head;
CORE_ADDR got_value = 0;
while (so_list)
{
if (so_list->pa64_solib_desc.text_base <= addr
&& ((so_list->pa64_solib_desc.text_base
+ so_list->pa64_solib_desc.text_size)
> addr))
{
got_value = so_list->pa64_solib_desc.linkage_ptr;
break;
}
so_list = so_list->next;
}
return got_value;
}
CORE_ADDR
pa64_solib_get_solib_by_pc (CORE_ADDR addr)
{
struct so_list *so_list = so_list_head;
CORE_ADDR retval = 0;
while (so_list)
{
if (so_list->pa64_solib_desc.text_base <= addr
&& ((so_list->pa64_solib_desc.text_base
+ so_list->pa64_solib_desc.text_size)
> addr))
{
retval = so_list->pa64_solib_desc_addr;
break;
}
so_list = so_list->next;
}
return retval;
}
static void
pa64_sharedlibrary_info_command (char *ignore, int from_tty)
{
struct so_list *so_list = so_list_head;
if (exec_bfd == NULL)
{
printf_unfiltered ("No executable file.\n");
return;
}
if (so_list == NULL)
{
printf_unfiltered ("No shared libraries loaded at this time.\n");
return;
}
printf_unfiltered ("Shared Object Libraries\n");
printf_unfiltered (" %-19s%-19s%-19s%-19s\n",
" text start", " text end",
" data start", " data end");
while (so_list)
{
unsigned int flags;
printf_unfiltered ("%s", so_list->name);
if (so_list->objfile == NULL)
printf_unfiltered (" (symbols not loaded)");
if (so_list->loaded == 0)
printf_unfiltered (" (shared library unloaded)");
printf_unfiltered (" %-18s",
local_hex_string_custom (so_list->pa64_solib_desc.linkage_ptr,
"016l"));
printf_unfiltered ("\n");
printf_unfiltered ("%-18s",
local_hex_string_custom (so_list->pa64_solib_desc.text_base,
"016l"));
printf_unfiltered (" %-18s",
local_hex_string_custom ((so_list->pa64_solib_desc.text_base
+ so_list->pa64_solib_desc.text_size),
"016l"));
printf_unfiltered (" %-18s",
local_hex_string_custom (so_list->pa64_solib_desc.data_base,
"016l"));
printf_unfiltered (" %-18s\n",
local_hex_string_custom ((so_list->pa64_solib_desc.data_base
+ so_list->pa64_solib_desc.data_size),
"016l"));
so_list = so_list->next;
}
}
static void
pa64_solib_sharedlibrary_command (char *args, int from_tty)
{
dont_repeat ();
pa64_solib_add (args, from_tty, (struct target_ops *) 0, 1);
}
char *
pa64_solib_address (CORE_ADDR addr)
{
struct so_list *so = so_list_head;
while (so)
{
if (addr >= so->pa64_solib_desc.text_base
&& addr < (so->pa64_solib_desc.text_base
| so->pa64_solib_desc.text_size))
return so->name;
so = so->next;
}
return NULL;
}
void
pa64_solib_restart (void)
{
struct so_list *sl = so_list_head;
disable_breakpoints_in_shlibs (0);
while (sl)
{
struct so_list *next_sl = sl->next;
xfree (sl);
sl = next_sl;
}
so_list_head = NULL;
pa64_solib_total_st_size = (LONGEST) 0;
pa64_solib_st_size_threshold_exceeded = 0;
dld_cache.is_valid = 0;
dld_cache.have_read_dld_descriptor = 0;
dld_cache.dld_flags_addr = 0;
dld_cache.load_map = 0;
dld_cache.load_map_addr = 0;
dld_cache.dld_desc.data_base = 0;
dld_cache.dld_flags = 0;
dld_cache.dyninfo_sect = 0;
}
void
_initialize_pa64_solib (void)
{
add_com ("sharedlibrary", class_files, pa64_solib_sharedlibrary_command,
"Load shared object library symbols for files matching REGEXP.");
add_info ("sharedlibrary", pa64_sharedlibrary_info_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);
add_show_from_set
(add_set_cmd ("auto-solib-limit", class_support, var_zinteger,
(char *) &auto_solib_limit,
"Set threshold (in Mb) for autoloading shared library symbols.\n\
When shared library autoloading is enabled, new libraries will be loaded\n\
only until the total size of shared library symbols exceeds this\n\
threshold in megabytes. Is ignored when using `sharedlibrary'.",
&setlist),
&showlist);
auto_solib_limit = 100;
pa64_solib_restart ();
}
CORE_ADDR
so_lib_thread_start_addr (struct so_list *so)
{
return so->pa64_solib_desc.tls_start_addr;
}
static int
read_dld_descriptor (struct target_ops *target, int readsyms)
{
char *dll_path;
asection *dyninfo_sect;
if (!dld_cache.is_valid)
{
if (symfile_objfile == NULL)
error ("No object file symbols.");
dyninfo_sect = bfd_get_section_by_name (symfile_objfile->obfd,
".dynamic");
if (!dyninfo_sect)
{
return 0;
}
if (!read_dynamic_info (dyninfo_sect, &dld_cache))
error ("Unable to read in .dynamic section information.");
}
if (target_read_memory (dld_cache.load_map_addr,
(char*) &dld_cache.load_map,
sizeof(dld_cache.load_map))
!= 0)
{
error ("Error while reading in load map pointer.");
}
if (dlgetmodinfo (-1,
&dld_cache.dld_desc,
sizeof(dld_cache.dld_desc),
pa64_target_read_memory,
0,
dld_cache.load_map)
== 0)
{
error ("Error trying to get information about dynamic linker.");
}
dld_cache.have_read_dld_descriptor = 1;
dll_path = dlgetname (&dld_cache.dld_desc,
sizeof(dld_cache.dld_desc),
pa64_target_read_memory,
0,
dld_cache.load_map);
add_to_solist(0, dll_path, readsyms, &dld_cache.dld_desc, 0, target);
return 1;
}
static int
read_dynamic_info (asection *dyninfo_sect, dld_cache_t *dld_cache_p)
{
char *buf;
char *bufend;
CORE_ADDR dyninfo_addr;
int dyninfo_sect_size;
CORE_ADDR entry_addr;
dyninfo_addr = bfd_section_vma (symfile_objfile->obfd, dyninfo_sect);
dyninfo_sect_size = bfd_section_size (exec_bfd, dyninfo_sect);
buf = alloca (dyninfo_sect_size);
if (target_read_memory (dyninfo_addr, buf, dyninfo_sect_size))
return 0;
for (bufend = buf + dyninfo_sect_size, entry_addr = dyninfo_addr;
buf < bufend;
buf += sizeof (Elf64_Dyn), entry_addr += sizeof (Elf64_Dyn))
{
Elf64_Dyn *x_dynp = (Elf64_Dyn*)buf;
Elf64_Sxword dyn_tag;
CORE_ADDR dyn_ptr;
char *pbuf;
pbuf = alloca (TARGET_PTR_BIT / HOST_CHAR_BIT);
dyn_tag = bfd_h_get_64 (symfile_objfile->obfd,
(bfd_byte*) &x_dynp->d_tag);
if (dyn_tag == DT_NULL)
break;
else if (dyn_tag == DT_HP_DLD_FLAGS)
{
dld_cache_p->dld_flags_addr = entry_addr + offsetof(Elf64_Dyn, d_un);
if (target_read_memory (dld_cache_p->dld_flags_addr,
(char*) &dld_cache_p->dld_flags,
sizeof(dld_cache_p->dld_flags))
!= 0)
{
error ("Error while reading in .dynamic section of the program.");
}
}
else if (dyn_tag == DT_HP_LOAD_MAP)
{
if (target_read_memory (entry_addr + offsetof(Elf64_Dyn,
d_un.d_ptr),
(char*) &dld_cache_p->load_map_addr,
sizeof(dld_cache_p->load_map_addr))
!= 0)
{
error ("Error while reading in .dynamic section of the program.");
}
}
else
{
}
}
dld_cache_p->dyninfo_sect = dyninfo_sect;
if (dld_cache_p->dld_flags_addr != 0 && dld_cache_p->load_map_addr != 0)
dld_cache_p->is_valid = 1;
else
return 0;
return 1;
}
static void *
pa64_target_read_memory (void *buffer, CORE_ADDR ptr, size_t bufsiz, int ident)
{
if (target_read_memory (ptr, buffer, bufsiz) != 0)
return 0;
return buffer;
}
static void
add_to_solist (int from_tty, char *dll_path, int readsyms,
struct load_module_desc *load_module_desc_p,
CORE_ADDR load_module_desc_addr, struct target_ops *target)
{
struct so_list *new_so, *so_list_tail;
int pa64_solib_st_size_threshhold_exceeded;
LONGEST st_size;
if (symfile_objfile == NULL)
return;
so_list_tail = so_list_head;
while (so_list_tail && so_list_tail->next)
{
if (strcmp (so_list_tail->name, dll_path) == 0)
return;
so_list_tail = so_list_tail->next;
}
if (so_list_tail && strcmp (so_list_tail->name, dll_path) == 0)
return;
new_so = (struct so_list *) xmalloc (sizeof (struct so_list));
memset ((char *)new_so, 0, sizeof (struct so_list));
if (so_list_head == NULL)
{
so_list_head = new_so;
so_list_tail = new_so;
}
else
{
so_list_tail->next = new_so;
so_list_tail = new_so;
}
if (load_module_desc_p)
{
new_so->pa64_solib_desc = *load_module_desc_p;
}
else
{
if (target_read_memory (load_module_desc_addr,
(char*) &new_so->pa64_solib_desc,
sizeof(struct load_module_desc))
!= 0)
{
error ("Error while reading in dynamic library %s", dll_path);
}
}
new_so->pa64_solib_desc_addr = load_module_desc_addr;
new_so->loaded = 1;
new_so->name = obsavestring (dll_path, strlen(dll_path),
&symfile_objfile->objfile_obstack);
st_size = pa64_solib_sizeof_symbol_table (dll_path);
pa64_solib_st_size_threshhold_exceeded =
!from_tty
&& readsyms
&& ( (st_size + pa64_solib_total_st_size)
> (auto_solib_limit * (LONGEST) (1024 * 1024)));
if (pa64_solib_st_size_threshhold_exceeded)
{
pa64_solib_add_solib_objfile (new_so, dll_path, from_tty, 1);
return;
}
pa64_solib_total_st_size += st_size;
pa64_solib_load_symbols (new_so,
dll_path,
from_tty,
0,
target);
return;
}
static CORE_ADDR
bfd_lookup_symbol (bfd *abfd, char *symname)
{
unsigned int storage_needed;
asymbol *sym;
asymbol **symbol_table;
unsigned int number_of_symbols;
unsigned int i;
struct cleanup *back_to;
CORE_ADDR symaddr = 0;
storage_needed = bfd_get_symtab_upper_bound (abfd);
if (storage_needed > 0)
{
symbol_table = (asymbol **) xmalloc (storage_needed);
back_to = make_cleanup (xfree, symbol_table);
number_of_symbols = bfd_canonicalize_symtab (abfd, symbol_table);
for (i = 0; i < number_of_symbols; i++)
{
sym = *symbol_table++;
if (strcmp (sym->name, symname) == 0)
{
symaddr = sym->value + sym->section->vma;
break;
}
}
do_cleanups (back_to);
}
return (symaddr);
}