freebsd-dev/usr.sbin/kvm_mkdb/nlist.c
John Polstra a150a878d5 Handle ELF symbols better. This fixes "vmstat -i" for the case
where "/var/db/kvm_kernel.db" exists.

Note, kvm_mkdb tries to be clever, and skips rebuilding the database
if it thinks it's already up to date.  To see the effects of this
fix, you may need to manually delete "/var/db/kvm_kernel.db" and
then run "kvm_mkdb".
1998-10-28 06:39:41 +00:00

369 lines
9.0 KiB
C

/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef lint
#if 0
static char sccsid[] = "@(#)from: nlist.c 8.1 (Berkeley) 6/6/93";
#endif
static const char rcsid[] =
"$Id: nlist.c,v 1.9 1998/08/17 08:46:46 dfr Exp $";
#endif /* not lint */
#include <sys/param.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <a.out.h>
#include <db.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <kvm.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "extern.h"
#ifdef DO_ELF
#include <elf.h>
#endif
typedef struct nlist NLIST;
#define _strx n_un.n_strx
#define _name n_un.n_name
#define badfmt(str) errx(1, "%s: %s: %s", kfile, str, strerror(EFTYPE))
static char *kfile;
#if defined(DO_AOUT)
int
__aout_knlist(name, db)
char *name;
DB *db;
{
register int nsyms;
struct exec *ebuf;
NLIST *nbuf;
DBT data, key;
int fd;
char *strtab;
u_char *filep;
char *vp;
struct stat sst;
long cur_off, voff;
kfile = name;
if ((fd = open(name, O_RDONLY, 0)) < 0)
err(1, "%s", name);
fstat(fd,&sst);
filep = (u_char*)mmap(0, sst.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (filep == (u_char*)MAP_FAILED)
err(1, "mmap failed");
/* Read in exec structure. */
ebuf = (struct exec *) filep;
/* Check magic number and symbol count. */
if (N_BADMAG(*ebuf))
badfmt("bad magic number");
if (!ebuf->a_syms)
badfmt("stripped");
strtab = filep + N_STROFF(*ebuf) + sizeof (int);
/* Seek to symbol table. */
cur_off = N_SYMOFF(*ebuf);
/* Read each symbol and enter it into the database. */
nsyms = ebuf->a_syms / sizeof(struct nlist);
while (nsyms--) {
nbuf = (NLIST *)(filep + cur_off);
cur_off += sizeof(NLIST);
if (!nbuf->_strx || nbuf->n_type&N_STAB)
continue;
key.data = (u_char *)strtab + nbuf->_strx - sizeof(long);
key.size = strlen((char *)key.data);
data.data = (u_char *)nbuf;
data.size = sizeof(NLIST);
if (db->put(db, &key, &data, 0))
err(1, "record enter");
if (1 && strcmp((char *)key.data, VRS_SYM) == 0) {
#ifndef KERNTEXTOFF
/*
* XXX
* The FreeBSD bootloader loads the kernel at the a_entry address, meaning
* that this is where the kernel starts. (not at KERNBASE)
*
* This may be introducing an i386 dependency.
*/
#if defined(__FreeBSD__)
#define KERNTEXTOFF ebuf->a_entry
#else
#define KERNTEXTOFF KERNBASE
#endif
#endif
/*
* Calculate offset relative to a normal (non-kernel)
* a.out. KERNTEXTOFF is where the kernel is really
* loaded; N_TXTADDR is where a normal file is loaded.
* From there, locate file offset in text or data.
*/
voff = nbuf->n_value - KERNTEXTOFF + N_TXTADDR(*ebuf);
if ((nbuf->n_type & N_TYPE) == N_TEXT)
voff += N_TXTOFF(*ebuf) - N_TXTADDR(*ebuf);
else
voff += N_DATOFF(*ebuf) - N_DATADDR(*ebuf);
vp = filep + voff;
key.data = (u_char *)VRS_KEY;
key.size = sizeof(VRS_KEY) - 1;
data.data = vp;
data.size = strchr(vp, '\n') - vp + 1;
if (db->put(db, &key, &data, 0))
err(1, "record enter");
/* Restore to original values. */
data.size = sizeof(NLIST);
}
}
return(0);
}
#endif /* DO_AOUT */
#ifdef DO_ELF
static void elf_sym_to_nlist __P((struct nlist *, Elf_Sym *, Elf_Shdr *, int));
int
__elf_knlist(name, db)
char *name;
DB *db;
{
register caddr_t strtab;
register off_t symstroff = 0, symoff = 0;
register u_long symsize = 0;
register u_long kernvma, kernoffs;
register int i;
Elf_Sym *sbuf;
size_t symstrsize;
char *shstr, buf[1024];
Elf_Ehdr *eh;
Elf_Shdr *sh = NULL;
DBT data, key;
NLIST nbuf;
int fd;
u_char *filep;
struct stat sst;
kfile = name;
if ((fd = open(name, O_RDONLY, 0)) < 0)
err(1, "%s", name);
fstat(fd, &sst);
/* Check for files too large to mmap. */
/* XXX is this really possible? */
if (sst.st_size > SIZE_T_MAX) {
badfmt("corrupt file");
}
filep = (u_char*)mmap(0, sst.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (filep == (u_char*)MAP_FAILED)
err(1, "mmap failed");
/* Read in exec structure. */
eh = (Elf_Ehdr *) filep;
if (!IS_ELF(*eh))
return(-1);
sh = (Elf_Shdr *)&filep[eh->e_shoff];
shstr = (char *)&filep[sh[eh->e_shstrndx].sh_offset];
for (i = 0; i < eh->e_shnum; i++) {
if (strcmp (shstr + sh[i].sh_name, ".strtab") == 0) {
symstroff = sh[i].sh_offset;
symstrsize = sh[i].sh_size;
}
else if (strcmp (shstr + sh[i].sh_name, ".symtab") == 0) {
symoff = sh[i].sh_offset;
symsize = sh[i].sh_size;
}
else if (strcmp (shstr + sh[i].sh_name, ".text") == 0) {
kernvma = sh[i].sh_addr;
kernoffs = sh[i].sh_offset;
}
}
if (symsize == 0)
badfmt("stripped");
strtab = (char *)&filep[symstroff];
data.data = (u_char *)&nbuf;
data.size = sizeof(NLIST);
/* Read each symbol and enter it into the database. */
for (i = 0; symsize > 0; i++, symsize -= sizeof(Elf_Sym)) {
sbuf = (Elf_Sym *)&filep[symoff + i * sizeof(*sbuf)];
if (!sbuf->st_name)
continue;
elf_sym_to_nlist(&nbuf, sbuf, sh, eh->e_shnum);
key.data = (u_char *)(strtab + sbuf->st_name);
key.size = strlen((char *)key.data);
if (db->put(db, &key, &data, 0))
err(1, "record enter");
/* also put in name prefixed with _ */
*buf = '_';
strcpy(buf + 1, strtab + sbuf->st_name);
key.data = (u_char *)buf;
key.size = strlen((char *)key.data);
if (db->put(db, &key, &data, 0))
err(1, "record enter");
/* Special processing for "_version" (depends on above) */
if (strcmp((char *)key.data, VRS_SYM) == 0) {
char *vp;
key.data = (u_char *)VRS_KEY;
key.size = sizeof(VRS_KEY) - 1;
/* Find the version string, relative to its section */
data.data = strdup(&filep[nbuf.n_value -
sh[sbuf->st_shndx].sh_addr +
sh[sbuf->st_shndx].sh_offset]);
/* assumes newline terminates version. */
if ((vp = strchr(data.data, '\n')) != NULL)
*vp = '\0';
data.size = strlen((char *)data.data);
if (db->put(db, &key, &data, 0))
err(1, "record enter");
/* Restore to original values. */
data.data = (u_char *)&nbuf;
data.size = sizeof(NLIST);
}
}
munmap(filep, sst.st_size);
(void)close(fd);
return(0);
}
/*
* Convert an Elf_Sym into an nlist structure. This fills in only the
* n_value and n_type members.
*/
static void
elf_sym_to_nlist(nl, s, shdr, shnum)
struct nlist *nl;
Elf_Sym *s;
Elf_Shdr *shdr;
int shnum;
{
nl->n_value = s->st_value;
switch (s->st_shndx) {
case SHN_UNDEF:
case SHN_COMMON:
nl->n_type = N_UNDF;
break;
case SHN_ABS:
nl->n_type = ELF_ST_TYPE(s->st_info) == STT_FILE ?
N_FN : N_ABS;
break;
default:
if (s->st_shndx >= shnum)
nl->n_type = N_UNDF;
else {
Elf_Shdr *sh = shdr + s->st_shndx;
nl->n_type = sh->sh_type == SHT_PROGBITS ?
(sh->sh_flags & SHF_WRITE ? N_DATA : N_TEXT) :
(sh->sh_type == SHT_NOBITS ? N_BSS : N_UNDF);
}
break;
}
if (ELF_ST_BIND(s->st_info) == STB_GLOBAL ||
ELF_ST_BIND(s->st_info) == STB_WEAK)
nl->n_type |= N_EXT;
}
#endif /* DO_ELF */
static struct knlist_handlers {
int (*fn) __P((char *name, DB *db));
} nlist_fn[] = {
#ifdef DO_ELF
{ __elf_knlist },
#endif
#ifdef DO_AOUT
{ __aout_knlist },
#endif
};
void
create_knlist(name, db)
char *name;
DB *db;
{
int n, i;
for (i = 0; i < sizeof(nlist_fn)/sizeof(nlist_fn[0]); i++) {
n = (nlist_fn[i].fn)(name, db);
if (n != -1)
break;
}
}