/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1994, by Sun Microsytems, Inc.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Routines that
* - return an address for a symbol name
* - return a symbol name for an address
*/
#ifndef DEBUG
#define NDEBUG 1
#endif
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/procfs.h>
#include <sys/stat.h>
#include <assert.h>
#include <note.h>
#include "tnfctl_int.h"
#include "dbg.h"
/*
* Typedefs
*/
typedef struct sym_args {
char *sa_name;
uintptr_t sa_addr;
} sym_args_t;
NOTE(SCHEME_PROTECTS_DATA("always automatic", sym_args))
/*
* Declarations
*/
static tnfctl_errcode_t sym_findname_in_obj(int objfd, uintptr_t baseaddr,
uintptr_t symaddr, char **symname);
static tnfctl_errcode_t sym_match(char *name, uintptr_t addr, void *sym_entry,
tnfctl_elf_search_t *search_info_p);
static tnfctl_errcode_t sym_matchname(char *name, uintptr_t addr,
void *sym_entry,
tnfctl_elf_search_t *search_info_p);
/* ---------------------------------------------------------------- */
/* ----------------------- Public Functions ----------------------- */
/* ---------------------------------------------------------------- */
/*
* _tnfctl_sym_find_in_obj() - determines the virtual address of the supplied
* symbol in the object file specified by fd.
*/
tnfctl_errcode_t
_tnfctl_sym_find_in_obj(int objfd, uintptr_t baseaddr, const char *symname,
uintptr_t *symaddr)
{
tnfctl_errcode_t prexstat = TNFCTL_ERR_NONE;
sym_args_t symargs;
tnfctl_elf_search_t search_info;
DBG_TNF_PROBE_1(_tnfctl_sym_find_in_obj_1, "libtnfctl",
"sunw%verbosity 3",
tnf_string, searching_for, symname);
symargs.sa_name = (char *) symname;
/* clear output argument in advance */
symargs.sa_addr = 0;
search_info.section_func = _tnfctl_traverse_dynsym;
search_info.record_func = sym_match;
search_info.record_data = &symargs;
prexstat = _tnfctl_traverse_object(objfd, baseaddr, &search_info);
if (prexstat)
return (prexstat);
/* check if we found symbol address */
if (symargs.sa_addr == 0) {
return (TNFCTL_ERR_BADARG);
}
*symaddr = symargs.sa_addr;
return (TNFCTL_ERR_NONE);
}
/*
* _tnfctl_sym_find() - determines the virtual address of the supplied symbol
* in the process.
*/
tnfctl_errcode_t
_tnfctl_sym_find(tnfctl_handle_t *hndl, const char *symname, uintptr_t *symaddr)
{
boolean_t release_lock;
tnfctl_errcode_t prexstat = TNFCTL_ERR_NONE;
objlist_t *obj;
DBG_TNF_PROBE_1(_tnfctl_sym_find_start, "libtnfctl",
"start _tnfctl_sym_find; sunw%verbosity 3",
tnf_string, searching_for, symname);
/*LINTED statement has no consequent: else*/
LOCK(hndl, prexstat, release_lock);
/* for every object in list, search for symbol */
for (obj = hndl->objlist; obj; obj = obj->next) {
if (obj->old == B_TRUE)
continue; /* don't examine dlclose'd libs */
/* return value of TNFCTL_ERR_BADARG means symbol not found */
prexstat = _tnfctl_sym_find_in_obj(obj->objfd,
obj->baseaddr, symname, symaddr);
if (prexstat == TNFCTL_ERR_NONE)
/* symbol found */
break;
else if (prexstat != TNFCTL_ERR_BADARG)
/* error condition */
break;
/* continue loop on TNFCTL_ERR_BADARG */
}
/*LINTED statement has no consequent: else*/
UNLOCK(hndl, release_lock);
DBG_TNF_PROBE_0(_tnfctl_sym_find_end, "libtnfctl",
"end _tnfctl_sym_find; sunw%verbosity 3");
return (prexstat);
}
/*
* _tnfctl_sym_obj_find() - determines the virtual address of the supplied
* symbol in the object specified by base name
*/
tnfctl_errcode_t
_tnfctl_sym_obj_find(tnfctl_handle_t *hndl, const char *lib_base_name,
const char *symname, uintptr_t *symaddr)
{
tnfctl_errcode_t prexstat = TNFCTL_ERR_NONE;
objlist_t *obj, *found_obj;
const char *str_ptr;
assert((hndl->mode == INTERNAL_MODE) ?
(MUTEX_HELD(&_tnfctl_lmap_lock)) : 1);
DBG_TNF_PROBE_1(_tnfctl_sym_obj_find_start, "libtnfctl",
"start _tnfctl_sym_obj_find; sunw%verbosity 3",
tnf_string, searching_for, symname);
found_obj = NULL;
/* for every object in list ... */
for (obj = hndl->objlist; obj; obj = obj->next) {
if (obj->old == B_TRUE)
continue; /* don't examine dlclose'd libs */
if (obj->objname == NULL)
continue;
/* find the last occurrence of / in the name */
str_ptr = strrchr(obj->objname, '/');
if (str_ptr == NULL) {
str_ptr = obj->objname;
} else {
str_ptr++; /* bump up past '/' */
}
/* XXX - use strcoll ? */
if (strcmp(str_ptr, lib_base_name) == 0) {
found_obj = obj;
break;
}
}
/* return value of TNFCTL_ERR_BADARG means symbol not found */
if (found_obj == NULL)
return (TNFCTL_ERR_BADARG);
prexstat = _tnfctl_sym_find_in_obj(found_obj->objfd,
found_obj->baseaddr, symname, symaddr);
DBG_TNF_PROBE_0(_tnfctl_sym_obj_find_end, "libtnfctl",
"end _tnfctl_sym_obj_find; sunw%verbosity 3");
return (prexstat);
}
/*
* _tnfctl_sym_findname() - determines the name of a function from its address.
*/
tnfctl_errcode_t
_tnfctl_sym_findname(tnfctl_handle_t *hndl, uintptr_t symaddr,
char **symname)
{
boolean_t release_lock;
tnfctl_errcode_t prexstat = TNFCTL_ERR_NONE;
objlist_t *obj;
DBG_TNF_PROBE_1(_tnfctl_sym_findname_start, "libtnfctl",
"start _tnfctl_sym_findname; sunw%verbosity 3",
tnf_opaque, searching_for, symaddr);
/*LINTED statement has no consequent: else*/
LOCK(hndl, prexstat, release_lock);
/* for every object in list, search for name */
for (obj = hndl->objlist; obj; obj = obj->next) {
if (obj->old == B_TRUE)
continue; /* don't examine dlclose'd libs */
/* return value of TNFCTL_ERR_BADARG means symbol not found */
prexstat = sym_findname_in_obj(obj->objfd,
obj->baseaddr, symaddr, symname);
if (prexstat == TNFCTL_ERR_NONE)
/* symbol found */
break;
else if (prexstat != TNFCTL_ERR_BADARG)
/* error condition */
break;
/* continue loop on TNFCTL_ERR_BADARG */
}
/*LINTED statement has no consequent: else*/
UNLOCK(hndl, release_lock);
DBG_TNF_PROBE_0(_tnfctl_sym_findname_end, "libtnfctl",
"end _tnfctl_sym_findname; sunw%verbosity 3");
return (prexstat);
}
/* ---------------------------------------------------------------- */
/* ----------------------- Private Functions ---------------------- */
/* ---------------------------------------------------------------- */
/*
* sym_findname_in_obj() - determines the name of the supplied
* address in the specified object file.
*/
static tnfctl_errcode_t
sym_findname_in_obj(int objfd, uintptr_t baseaddr, uintptr_t symaddr,
char **symname)
{
tnfctl_errcode_t prexstat = TNFCTL_ERR_NONE;
sym_args_t symargs;
tnfctl_elf_search_t search_info;
DBG_TNF_PROBE_1(sym_findname_in_obj_1, "libtnfctl",
"sunw%verbosity 3",
tnf_opaque, searching_for, symaddr);
/* clear output argument in advance */
symargs.sa_name = NULL;
symargs.sa_addr = symaddr;
search_info.section_func = _tnfctl_traverse_dynsym;
search_info.record_func = sym_matchname;
search_info.record_data = &symargs;
prexstat = _tnfctl_traverse_object(objfd, baseaddr, &search_info);
if (prexstat)
return (prexstat);
/* check if we found symbol address */
if (symargs.sa_name == NULL) {
return (TNFCTL_ERR_BADARG);
}
*symname = symargs.sa_name;
return (TNFCTL_ERR_NONE);
}
/*
* sym_match() - function to be called on each symbol in a dynsym section.
* Used to find the address of a symbol.
*/
static tnfctl_errcode_t
sym_match(char *name, uintptr_t addr, void *sym_entry,
tnfctl_elf_search_t *search_info_p)
{
sym_args_t *symargs_p = (sym_args_t *) search_info_p->record_data;
Elf3264_Sym *sym = (Elf3264_Sym *) sym_entry;
#if 0
printf("enter sym_match: \n");
if (symargs_p->sa_name != 0)
printf("(symargs_p->sa_name) = %s\n", symargs_p->sa_name);
else
printf("symargs_p->sa_name = 0\n");
if (name != 0)
printf("(name) = %s\n", name);
else
printf("name = 0\n");
#endif
#ifdef VERYVERBOSE
(void) fprintf(stderr, "sym_match: checking \"%s\"\n", name);
#endif
if ((sym->st_shndx != SHN_UNDEF) &&
(strcmp(name, symargs_p->sa_name) == 0)) {
DBG_TNF_PROBE_2(sym_match_1, "libtnfctl",
"sunw%verbosity 2; sunw%debug '\tMatched Symbol'",
tnf_string, symbol, name,
tnf_opaque, address_found, addr);
symargs_p->sa_addr = addr;
}
#if 0
printf("leaving sym_match\n");
#endif
return (TNFCTL_ERR_NONE);
}
/*
* sym_matchname() - function to be called on each symbol in a dynsym
* section. Used to find the name of a symbol whose address is known.
*/
static tnfctl_errcode_t
sym_matchname(char *name, uintptr_t addr, void *sym_entry,
tnfctl_elf_search_t * search_info_p)
{
sym_args_t *symargs_p = (sym_args_t *) search_info_p->record_data;
Elf3264_Sym *sym = (Elf3264_Sym *) sym_entry;
#ifdef VERYVERBOSE
(void) fprintf(stderr, "sym_matchname: checking \"%s\"\n", name);
#endif
if ((sym->st_shndx != SHN_UNDEF) &&
symargs_p->sa_addr == addr) {
DBG_TNF_PROBE_2(sym_matchname_1, "libtnfctl",
"sunw%verbosity 2; sunw%debug '\tMatched Name'",
tnf_string, symbol_found, name,
tnf_opaque, address, addr);
symargs_p->sa_name = strdup(name);
}
return (TNFCTL_ERR_NONE);
}