File: [local] / src / usr.bin / nm / elf.c (download)
Revision 1.20, Wed Sep 28 19:58:14 2011 UTC (12 years, 7 months ago) by uwe
Branch: MAIN
CVS Tags: OPENBSD_5_3_BASE, OPENBSD_5_3, OPENBSD_5_2_BASE, OPENBSD_5_2, OPENBSD_5_1_BASE, OPENBSD_5_1 Changes since 1.19: +9 -2 lines
Support symbols in .tbss and .tdata ELF sections
With this change nm(1) prints the expected symbol type ('B' or 'D')
for variables in the .tbss and .tdata sections respectively, instead
of '?'. This would be what binutils do for non-automatic variables
that are marked as thread-local with the __thread attribute in GCC.
help and ok miod@, guenther@
|
/* $OpenBSD: elf.c,v 1.20 2011/09/28 19:58:14 uwe Exp $ */
/*
* Copyright (c) 2003 Michael Shalayeff
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR OR HIS RELATIVES 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 MIND, 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.
*/
#include <sys/param.h>
#include <sys/mman.h>
#include <unistd.h>
#include <a.out.h>
#include <elf_abi.h>
#include <errno.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "elfuncs.h"
#include "util.h"
#if ELFSIZE == 32
#define swap_addr swap32
#define swap_off swap32
#define swap_sword swap32
#define swap_word swap32
#define swap_sxword swap32
#define swap_xword swap32
#define swap_half swap16
#define swap_quarter swap16
#define elf_fix_header elf32_fix_header
#define elf_load_shdrs elf32_load_shdrs
#define elf_load_phdrs elf32_load_phdrs
#define elf_fix_shdrs elf32_fix_shdrs
#define elf_fix_phdrs elf32_fix_phdrs
#define elf_fix_sym elf32_fix_sym
#define elf_size elf32_size
#define elf_symloadx elf32_symloadx
#define elf_symload elf32_symload
#define elf2nlist elf32_2nlist
#define elf_shn2type elf32_shn2type
#elif ELFSIZE == 64
#define swap_addr swap64
#define swap_off swap64
#ifdef __alpha__
#define swap_sword swap64
#define swap_word swap64
#else
#define swap_sword swap32
#define swap_word swap32
#endif
#define swap_sxword swap64
#define swap_xword swap64
#define swap_half swap64
#define swap_quarter swap16
#define elf_fix_header elf64_fix_header
#define elf_load_shdrs elf64_load_shdrs
#define elf_load_phdrs elf64_load_phdrs
#define elf_fix_shdrs elf64_fix_shdrs
#define elf_fix_phdrs elf64_fix_phdrs
#define elf_fix_sym elf64_fix_sym
#define elf_size elf64_size
#define elf_symloadx elf64_symloadx
#define elf_symload elf64_symload
#define elf2nlist elf64_2nlist
#define elf_shn2type elf64_shn2type
#else
#error "Unsupported ELF class"
#endif
#define ELF_SDATA ".sdata"
#define ELF_TDATA ".tdata"
#define ELF_SBSS ".sbss"
#define ELF_TBSS ".tbss"
#define ELF_PLT ".plt"
#ifndef SHN_MIPS_ACOMMON
#define SHN_MIPS_ACOMMON SHN_LOPROC + 0
#endif
#ifndef SHN_MIPS_TEXT
#define SHN_MIPS_TEXT SHN_LOPROC + 1
#endif
#ifndef SHN_MIPS_DATA
#define SHN_MIPS_DATA SHN_LOPROC + 2
#endif
#ifndef SHN_MIPS_SUNDEFINED
#define SHN_MIPS_SUNDEFINED SHN_LOPROC + 4
#endif
#ifndef SHN_MIPS_SCOMMON
#define SHN_MIPS_SCOMMON SHN_LOPROC + 3
#endif
#ifndef STT_PARISC_MILLI
#define STT_PARISC_MILLI STT_LOPROC + 0
#endif
int
elf_fix_header(Elf_Ehdr *eh)
{
/* nothing to do */
if (eh->e_ident[EI_DATA] == ELF_TARG_DATA)
return (0);
eh->e_type = swap16(eh->e_type);
eh->e_machine = swap16(eh->e_machine);
eh->e_version = swap32(eh->e_version);
eh->e_entry = swap_addr(eh->e_entry);
eh->e_phoff = swap_off(eh->e_phoff);
eh->e_shoff = swap_off(eh->e_shoff);
eh->e_flags = swap32(eh->e_flags);
eh->e_ehsize = swap16(eh->e_ehsize);
eh->e_phentsize = swap16(eh->e_phentsize);
eh->e_phnum = swap16(eh->e_phnum);
eh->e_shentsize = swap16(eh->e_shentsize);
eh->e_shnum = swap16(eh->e_shnum);
eh->e_shstrndx = swap16(eh->e_shstrndx);
return (1);
}
Elf_Shdr *
elf_load_shdrs(const char *name, FILE *fp, off_t foff, Elf_Ehdr *head)
{
Elf_Shdr *shdr;
elf_fix_header(head);
if ((shdr = calloc(head->e_shentsize, head->e_shnum)) == NULL) {
warn("%s: malloc shdr", name);
return (NULL);
}
if (fseeko(fp, foff + head->e_shoff, SEEK_SET)) {
warn("%s: fseeko", name);
free(shdr);
return (NULL);
}
if (fread(shdr, head->e_shentsize, head->e_shnum, fp) != head->e_shnum) {
warnx("%s: premature EOF", name);
free(shdr);
return (NULL);
}
elf_fix_shdrs(head, shdr);
return (shdr);
}
Elf_Phdr *
elf_load_phdrs(const char *name, FILE *fp, off_t foff, Elf_Ehdr *head)
{
Elf_Phdr *phdr;
if ((phdr = calloc(head->e_phentsize, head->e_phnum)) == NULL) {
warn("%s: malloc phdr", name);
return (NULL);
}
if (fseeko(fp, foff + head->e_phoff, SEEK_SET)) {
warn("%s: fseeko", name);
free(phdr);
return (NULL);
}
if (fread(phdr, head->e_phentsize, head->e_phnum, fp) != head->e_phnum) {
warnx("%s: premature EOF", name);
free(phdr);
return (NULL);
}
elf_fix_phdrs(head, phdr);
return (phdr);
}
int
elf_fix_shdrs(Elf_Ehdr *eh, Elf_Shdr *shdr)
{
int i;
/* nothing to do */
if (eh->e_ident[EI_DATA] == ELF_TARG_DATA)
return (0);
for (i = eh->e_shnum; i--; shdr++) {
shdr->sh_name = swap32(shdr->sh_name);
shdr->sh_type = swap32(shdr->sh_type);
shdr->sh_flags = swap_xword(shdr->sh_flags);
shdr->sh_addr = swap_addr(shdr->sh_addr);
shdr->sh_offset = swap_off(shdr->sh_offset);
shdr->sh_size = swap_xword(shdr->sh_size);
shdr->sh_link = swap32(shdr->sh_link);
shdr->sh_info = swap32(shdr->sh_info);
shdr->sh_addralign = swap_xword(shdr->sh_addralign);
shdr->sh_entsize = swap_xword(shdr->sh_entsize);
}
return (1);
}
int
elf_fix_phdrs(Elf_Ehdr *eh, Elf_Phdr *phdr)
{
int i;
/* nothing to do */
if (eh->e_ident[EI_DATA] == ELF_TARG_DATA)
return (0);
for (i = eh->e_phnum; i--; phdr++) {
phdr->p_type = swap32(phdr->p_type);
phdr->p_flags = swap32(phdr->p_flags);
phdr->p_offset = swap_off(phdr->p_offset);
phdr->p_vaddr = swap_addr(phdr->p_vaddr);
phdr->p_paddr = swap_addr(phdr->p_paddr);
phdr->p_filesz = swap_xword(phdr->p_filesz);
phdr->p_memsz = swap_xword(phdr->p_memsz);
phdr->p_align = swap_xword(phdr->p_align);
}
return (1);
}
int
elf_fix_sym(Elf_Ehdr *eh, Elf_Sym *sym)
{
/* nothing to do */
if (eh->e_ident[EI_DATA] == ELF_TARG_DATA)
return (0);
sym->st_name = swap32(sym->st_name);
sym->st_shndx = swap16(sym->st_shndx);
sym->st_value = swap_addr(sym->st_value);
sym->st_size = swap_xword(sym->st_size);
return (1);
}
int
elf_shn2type(Elf_Ehdr *eh, u_int shn, const char *sn)
{
switch (shn) {
case SHN_MIPS_SUNDEFINED:
if (eh->e_machine == EM_MIPS)
return (N_UNDF | N_EXT);
break;
case SHN_UNDEF:
return (N_UNDF | N_EXT);
case SHN_ABS:
return (N_ABS);
case SHN_MIPS_ACOMMON:
if (eh->e_machine == EM_MIPS)
return (N_COMM);
break;
case SHN_MIPS_SCOMMON:
if (eh->e_machine == EM_MIPS)
return (N_COMM);
break;
case SHN_COMMON:
return (N_COMM);
case SHN_MIPS_TEXT:
if (eh->e_machine == EM_MIPS)
return (N_TEXT);
break;
case SHN_MIPS_DATA:
if (eh->e_machine == EM_MIPS)
return (N_DATA);
break;
default:
/* TODO: beyond 8 a table-driven binsearch should be used */
if (sn == NULL)
return (-1);
else if (!strcmp(sn, ELF_TEXT))
return (N_TEXT);
else if (!strcmp(sn, ELF_RODATA))
return (N_SIZE);
else if (!strcmp(sn, ELF_DATA))
return (N_DATA);
else if (!strcmp(sn, ELF_SDATA))
return (N_DATA);
else if (!strcmp(sn, ELF_TDATA))
return (N_DATA);
else if (!strcmp(sn, ELF_BSS))
return (N_BSS);
else if (!strcmp(sn, ELF_SBSS))
return (N_BSS);
else if (!strcmp(sn, ELF_TBSS))
return (N_BSS);
else if (!strncmp(sn, ELF_GOT, sizeof(ELF_GOT) - 1))
return (N_DATA);
else if (!strncmp(sn, ELF_PLT, sizeof(ELF_PLT) - 1))
return (N_DATA);
}
return (-1);
}
/*
* Devise nlist's type from Elf_Sym.
* XXX this task is done as well in libc and kvm_mkdb.
*/
int
elf2nlist(Elf_Sym *sym, Elf_Ehdr *eh, Elf_Shdr *shdr, char *shstr, struct nlist *np)
{
u_char stt;
const char *sn;
int type;
if (sym->st_shndx < eh->e_shnum)
sn = shstr + shdr[sym->st_shndx].sh_name;
else
sn = NULL;
#if 0
{
extern char *stab;
printf("%d:%s %d %s\n", sym->st_shndx, sn? sn : "",
ELF_ST_TYPE(sym->st_info), stab + sym->st_name);
}
#endif
switch (stt = ELF_ST_TYPE(sym->st_info)) {
case STT_NOTYPE:
case STT_OBJECT:
case STT_TLS:
type = elf_shn2type(eh, sym->st_shndx, sn);
if (type < 0) {
if (sn == NULL)
np->n_other = '?';
else
np->n_type = stt == STT_NOTYPE? N_COMM : N_DATA;
} else {
/* a hack for .rodata check (; */
if (type == N_SIZE) {
np->n_type = N_DATA;
np->n_other = 'r';
} else
np->n_type = type;
}
break;
case STT_FUNC:
type = elf_shn2type(eh, sym->st_shndx, NULL);
np->n_type = type < 0? N_TEXT : type;
if (ELF_ST_BIND(sym->st_info) == STB_WEAK) {
np->n_type = N_INDR;
np->n_other = 'W';
} else if (sn != NULL && *sn != 0 &&
strcmp(sn, ELF_INIT) &&
strcmp(sn, ELF_TEXT) &&
strcmp(sn, ELF_FINI)) /* XXX GNU compat */
np->n_other = '?';
break;
case STT_SECTION:
type = elf_shn2type(eh, sym->st_shndx, NULL);
if (type < 0)
np->n_other = '?';
else
np->n_type = type;
break;
case STT_FILE:
np->n_type = N_FN | N_EXT;
break;
case STT_PARISC_MILLI:
if (eh->e_machine == EM_PARISC)
np->n_type = N_TEXT;
else
np->n_other = '?';
break;
default:
np->n_other = '?';
break;
}
if (np->n_type != N_UNDF && ELF_ST_BIND(sym->st_info) != STB_LOCAL) {
np->n_type |= N_EXT;
if (np->n_other)
np->n_other = toupper(np->n_other);
}
return (0);
}
int
elf_size(Elf_Ehdr *head, Elf_Shdr *shdr,
u_long *ptext, u_long *pdata, u_long *pbss)
{
int i;
*ptext = *pdata = *pbss = 0;
for (i = 0; i < head->e_shnum; i++) {
if (!(shdr[i].sh_flags & SHF_ALLOC))
;
else if (shdr[i].sh_flags & SHF_EXECINSTR ||
!(shdr[i].sh_flags & SHF_WRITE))
*ptext += shdr[i].sh_size;
else if (shdr[i].sh_type == SHT_NOBITS)
*pbss += shdr[i].sh_size;
else
*pdata += shdr[i].sh_size;
}
return (0);
}
int
elf_symloadx(const char *name, FILE *fp, off_t foff, Elf_Ehdr *eh,
Elf_Shdr *shdr, char *shstr, struct nlist **pnames,
struct nlist ***psnames, size_t *pstabsize, int *pnrawnames,
const char *strtab, const char *symtab)
{
long symsize;
struct nlist *np;
Elf_Sym sbuf;
int i;
for (i = 0; i < eh->e_shnum; i++) {
if (!strcmp(shstr + shdr[i].sh_name, strtab)) {
*pstabsize = shdr[i].sh_size;
if (*pstabsize > SIZE_T_MAX) {
warnx("%s: corrupt file", name);
return (1);
}
MMAP(stab, *pstabsize, PROT_READ, MAP_PRIVATE|MAP_FILE,
fileno(fp), foff + shdr[i].sh_offset);
if (stab == MAP_FAILED)
return (1);
}
}
for (i = 0; i < eh->e_shnum; i++) {
if (!strcmp(shstr + shdr[i].sh_name, symtab)) {
symsize = shdr[i].sh_size;
if (fseeko(fp, foff + shdr[i].sh_offset, SEEK_SET)) {
warn("%s: fseeko", name);
if (stab)
MUNMAP(stab, *pstabsize);
return (1);
}
*pnrawnames = symsize / sizeof(sbuf);
if ((*pnames = calloc(*pnrawnames, sizeof(*np))) == NULL) {
warn("%s: malloc names", name);
if (stab)
MUNMAP(stab, *pstabsize);
return (1);
}
if ((*psnames = calloc(*pnrawnames, sizeof(np))) == NULL) {
warn("%s: malloc snames", name);
if (stab)
MUNMAP(stab, *pstabsize);
free(*pnames);
return (1);
}
for (np = *pnames; symsize > 0; symsize -= sizeof(sbuf)) {
if (fread(&sbuf, 1, sizeof(sbuf),
fp) != sizeof(sbuf)) {
warn("%s: read symbol", name);
if (stab)
MUNMAP(stab, *pstabsize);
free(*pnames);
free(*psnames);
return (1);
}
elf_fix_sym(eh, &sbuf);
if (!sbuf.st_name ||
sbuf.st_name > *pstabsize)
continue;
elf2nlist(&sbuf, eh, shdr, shstr, np);
np->n_value = sbuf.st_value;
np->n_un.n_strx = sbuf.st_name;
np++;
}
*pnrawnames = np - *pnames;
}
}
}
int
elf_symload(const char *name, FILE *fp, off_t foff, Elf_Ehdr *eh,
Elf_Shdr *shdr, struct nlist **pnames, struct nlist ***psnames,
size_t *pstabsize, int *pnrawnames)
{
long shstrsize;
char *shstr;
shstrsize = shdr[eh->e_shstrndx].sh_size;
if (shstrsize == 0) {
warnx("%s: no name list", name);
return (1);
}
if ((shstr = malloc(shstrsize)) == NULL) {
warn("%s: malloc shsrt", name);
return (1);
}
if (fseeko(fp, foff + shdr[eh->e_shstrndx].sh_offset, SEEK_SET)) {
warn("%s: fseeko", name);
free(shstr);
return (1);
}
if (fread(shstr, 1, shstrsize, fp) != shstrsize) {
warnx("%s: premature EOF", name);
free(shstr);
return(1);
}
stab = NULL;
*pnames = NULL; *psnames = NULL;
elf_symloadx(name, fp, foff, eh, shdr, shstr, pnames,
psnames, pstabsize, pnrawnames, ELF_STRTAB, ELF_SYMTAB);
if (stab == NULL) {
elf_symloadx(name, fp, foff, eh, shdr, shstr, pnames,
psnames, pstabsize, pnrawnames, ELF_DYNSTR, ELF_DYNSYM);
}
free(shstr);
if (stab == NULL) {
warnx("%s: no name list", name);
if (*pnames)
free(*pnames);
if (*psnames)
free(*psnames);
return (1);
}
return (0);
}