Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / system / blaze / elfsym.cc
// ========== Copyright Header Begin ==========================================
//
// OpenSPARC T2 Processor File: elfsym.cc
// Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
//
// The above named program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License version 2 as published by the Free Software Foundation.
//
// The above named program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this work; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
// ========== Copyright Header End ============================================
///////////////////////////////////////////////////////////////
//
// elfsym: Extract Symbols for .text, .data and .bss
// sections of ELF file using libelf interface.
//
// Note: use 'Extended ELF Section indexes' if required,
// decode a corresponding SHT_SYMTAB_SHNDX
// section.
//
// Copyright 2006 Sun Microsystems, Inc. All rights reserved.
// Use is subject to license terms.
//
#include <stdio.h>
#include <libelf.h>
#include <gelf.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "ui.h"
// only one routine in exported interface
int load_symbols ( char* elf_fname, uint64_t text_base, uint64_t data_base, uint32_t context );
// set level of debug info
static int Verbose = 0;
// If you need to build a standalong elf_file utility
// set STANDALONG to be defined, build it with command
// CC -o <util_name> elf_file.cc
// usage: <util_name> <path/elf_file_name>
// output elf symbol info to the terminal
#ifndef STANDALONG
#include "symbols.h"
// imported symtable
extern SymTable *g_sym_table; // symbol table
#else // STANDALONG
int main(int argc, char **argv)
{
int i;
Verbose = 1;
if (argc != 2)
{
ui->output("usage: %s elf_file \n", argv[0]);
return(1);
}
char *fname = argv[1];
return load_symbols(fname,0,0,Symbol::ANY_CONTEXT);
}
#endif // STANDALONG
//////////////////////////////////////////////////////
//
// Extract defined symbols from
// .symtab and .dynsym sections
//
static void get_symbols
(
Elf *elf,
char *file,
uint64_t text_base,
uint64_t data_base,
uint32_t context
)
{
Elf_Scn *scn;
GElf_Shdr shdr;
GElf_Ehdr ehdr;
// get module name by stripping path from file name
char *path_name=strtok(file, "/");
char *module_name = file;
while (path_name=strtok(NULL, "/"))
{
module_name = path_name;
}
if (gelf_getehdr(elf, &ehdr) == 0)
{
ui->error( "%s: elf_getehdr() failed: %s\n",
file, elf_errmsg(0));
return;
}
// check the file type
char *filetype = "Other";
switch (ehdr.e_type)
{
case ET_REL:
filetype = "Reallocatable";
break;
case ET_EXEC:
filetype = "Executable";
break;
case ET_DYN:
filetype = "Shared Object";
break;
default:
break;
}
ui->output("loading symbols for %s - %s file.\n", module_name, filetype);
size_t shstrndx;
if (elf_getshstrndx(elf, &shstrndx) == 0)
{
ui->error( "%s: elf_getshstrndx() failed: %s\n",
file, elf_errmsg(0));
return;
}
// find .text, .data and .bss section headers
GElf_Shdr text_shdr;
GElf_Shdr data_shdr;
GElf_Shdr bss_shdr;
text_shdr.sh_type = SHT_NULL;
data_shdr.sh_type = SHT_NULL;
bss_shdr.sh_type = SHT_NULL;
scn = 0;
uint32_t idx = 1;
uint32_t text_idx, data_idx, bss_idx;
while ((scn = elf_nextscn(elf, scn)) != 0)
{
if (gelf_getshdr(scn, &shdr) == 0)
{
(void) ui->error(
"%s: elf_getshdr() failed: %s\n",
file, elf_errmsg(0));
return;
}
// get section name
char *sname = elf_strptr(elf, shstrndx, shdr.sh_name);
if (strcmp(sname, ".data")==0)
{
data_shdr = shdr;
data_idx = idx;
}
else if (strcmp(sname, ".bss" )==0)
{
bss_shdr = shdr;
bss_idx = idx;
}
else if (strcmp(sname, ".text")==0)
{
text_shdr = shdr;
text_idx = idx;
}
idx++;
continue;
}
if (text_shdr.sh_type ==SHT_NULL)
{
ui->error(" there is no text section \n");
return;
}
else if (Verbose)
ui->output(".text #%d sh_addr=0x%llx, sh_size=0x%llx, sh_offset=0x%llx, sh_addralign=0x%llx \n",
text_idx,text_shdr.sh_addr, text_shdr.sh_size, text_shdr.sh_offset, text_shdr.sh_addralign );
if (Verbose && data_shdr.sh_type !=SHT_NULL)
ui->output(".data #%d sh_addr=0x%llx, sh_size=0x%llx, sh_offset=0x%llx, sh_addralign=0x%llx \n",
data_idx,data_shdr.sh_addr, data_shdr.sh_size, data_shdr.sh_offset, data_shdr.sh_addralign );
if (Verbose && bss_shdr.sh_type !=SHT_NULL)
ui->output(".bss #%d sh_addr=0x%llx, sh_size=0x%llx, sh_offset=0x%llx, sh_addralign=0x%llx \n",
bss_idx,bss_shdr.sh_addr, bss_shdr.sh_size, bss_shdr.sh_offset, bss_shdr.sh_addralign );
// uninitialized data (bss) segment is located immediately
// after initialized data segment
uint64_t bss_offset = 0;
if (
(data_shdr.sh_type !=SHT_NULL) // data section is present
&& (bss_shdr.sh_addr == 0) // not absolute address
)
{
bss_offset = (bss_shdr.sh_offset - data_shdr.sh_offset) &
~(uint64_t(bss_shdr.sh_addralign) - 1);
}
if (Verbose)
ui->output(".bss addr offset = 0x%llx \n", bss_offset);
// find .dynsym and .symtab sections
scn = 0;
while ((scn = elf_nextscn(elf, scn)) != 0)
{
uint_t symcnt;
uint_t ndx;
Elf_Data *symdata;
Elf_Data *shndxdata;
if (gelf_getshdr(scn, &shdr) == 0)
{
ui->error("%s: elf_getshdr() failed: %s\n",
file, elf_errmsg(0));
return;
}
if ((shdr.sh_type != SHT_SYMTAB) &&
(shdr.sh_type != SHT_DYNSYM))
continue;
// Get the data associated with the Symbol
// section.
if ((symdata = elf_getdata(scn, 0)) == 0)
{
ui->error("%s: elf_getdata() failed: %s\n",
file, elf_errmsg(0));
return;
}
#ifdef STANDALONG
// Print symbol table title and header for symbol display
ui->output("\nSymTab: %s:%s\n", file,
elf_strptr(elf, shstrndx, shdr.sh_name));
ui->output(" index value size type "
"bind oth shndx name\n");
#endif
// iterate over the symbol table
shndxdata = 0;
uint_t nosymshndx = 0;
symcnt = shdr.sh_size / shdr.sh_entsize;
for (ndx = 0; ndx < symcnt; ndx++)
{
GElf_Sym sym;
Elf32_Word shndx;
uint_t type;
uint_t bind;
uint_t specshndx;
// Get a symbol entry
if (gelf_getsymshndx(symdata, shndxdata, ndx,
&sym, &shndx) == NULL)
{
(void) ui->error(
"%s: gelf_getsymshndx() failed: %s\n",
file, elf_errmsg(0));
return;
}
// Check to see if this symbol's st_shndx
// is using the 'Extended SHNDX table' for
// a SYMTAB.
// If it is - and we havn't searched before,
// go find the associated SHT_SYMTAB_SHNDX
// section.
if ((sym.st_shndx == SHN_XINDEX) &&
(shndxdata == 0) && (nosymshndx == 0))
{
Elf_Scn *_scn;
GElf_Shdr _shdr;
GElf_Word symscnndx;
_scn = 0;
specshndx = 0;
symscnndx = elf_ndxscn(scn);
while ((_scn = elf_nextscn(elf, _scn)) != 0)
{
if (gelf_getshdr(_scn, &_shdr) == 0)
break;
// We've found the Symtab SHNDX table
// if it's of type SHT_SYMTAB_SHNDX
// and it's shdr.sh_link points to the
// section index for the current symbol
// table.
if ((_shdr.sh_type ==
SHT_SYMTAB_SHNDX) &&
(_shdr.sh_link == symscnndx))
{
if ((shndxdata =
elf_getdata(_scn, 0)) != 0)
break;
}
}
// Get a symbol entry
if (shndxdata &&
(gelf_getsymshndx(symdata, shndxdata, ndx,
&sym, &shndx) == NULL))
{
(void) ui->error(
"%s: gelf_getsymshndx() "
"failed: %s\n",
file, elf_errmsg(0));
return;
}
// No Symtab SHNDX table was found. We could
// give a fatal error here - instead we'll
// just mark that fact and display as much of
// the symbol table as we can. Any symbol
// displayed with a XINDX section index has
// a bogus value - skip it
if (shndxdata == 0)
nosymshndx = 1;
}
// Decode the type & binding information
type = GELF_ST_TYPE(sym.st_info);
bind = GELF_ST_BIND(sym.st_info);
// section index to which this symbol refer to
specshndx = 0;
if (sym.st_shndx < SHN_LORESERVE)
shndx = sym.st_shndx;
else if ((sym.st_shndx != SHN_XINDEX) ||
(shndxdata == NULL))
{
shndx = sym.st_shndx;
specshndx = 1;
}
// skip zero size symbols
if (sym.st_size == 0)
continue;
uint64_t va = sym.st_value;
// check if it is in text section
if ((text_shdr.sh_type != SHT_NULL) && (shndx == text_idx))
{
// text symbol
va += text_base;
}
else if ( type == STT_OBJECT ) //data symbol
{
if ((data_shdr.sh_type != SHT_NULL) && (shndx == data_idx))
{
// initialized data
va += data_base;
}
else if ((bss_shdr.sh_type != SHT_NULL) && (shndx == bss_idx))
{
// uninitialized data
if ( bind == STB_GLOBAL )
{
va += data_base;
}
else if (bind == STB_LOCAL)
{
// local uninitialized data is located at the end
// of data segment
va += (data_base + bss_offset);
}
else
continue;
}
else
continue;
}
else
continue;
// append symbol
char *sym_name = elf_strptr(elf, shdr.sh_link, sym.st_name);
#ifndef STANDALONG
// check if symbol is already there
Symbol *s = g_sym_table->find(sym_name);
if (s == NULL || s->vaddr() != va || s->size() != sym.st_size)
{
g_sym_table->add(module_name, sym_name, va, sym.st_size, context);
}
#else // STANDALONG
// prepare sym type info
const int INTSTRLEN = 32;
char bindbuf[INTSTRLEN];
char typebuf[INTSTRLEN];
char shndxbuf[INTSTRLEN];
const char *bindstr;
const char *typestr;
const char *shndxstr;
static const char *symbind[STB_NUM] = {
/* STB_LOCL */ "LOCL",
/* STB_GLOBAL */ "GLOB",
/* STB_WEAK */ "WEAK"};
static const char *symtype[STT_NUM] = {
/* STT_NOTYPE */ "NOTY",
/* STT_OBJECT */ "OBJT",
/* STT_FUNC */ "FUNC",
/* STT_SECTION */ "SECT",
/* STT_FILE */ "FILE",
/* STT_COMMON */ "COMM",
/* STT_TLS */ "TLS"};
if (type < STT_NUM)
typestr = symtype[type];
else
{
snprintf(typebuf, INTSTRLEN, "%d", type);
typestr = typebuf;
}
if (bind < STB_NUM)
bindstr = symbind[bind];
else
{
snprintf(bindbuf, INTSTRLEN, "%d", bind);
bindstr = bindbuf;
}
if (shndx == SHN_UNDEF)
{
shndxstr = (const char *)"UNDEF";
}
else if (specshndx)
{
if (shndx == SHN_ABS)
shndxstr = (const char *)"ABS";
else if (shndx == SHN_COMMON)
shndxstr = (const char *)"COMM";
else if (shndx == SHN_XINDEX)
shndxstr = (const char *)"XIND";
else
{
snprintf(shndxbuf, INTSTRLEN, "%ld", shndx);
shndxstr = shndxbuf;
}
}
else
{
(void) snprintf(shndxbuf, INTSTRLEN,
"%ld", shndx);
shndxstr = shndxbuf;
}
//char *this_sh_name = elf_strptr(elf, shstrndx, shndx);
// display the symbol entry
ui->otput("[%3d] 0x%08llx 0x%08llx %-4s %-6s %2d %5s %s \n",
ndx, va /* sym.st_value */, sym.st_size,
typestr, bindstr, sym.st_other, shndxstr,
sym_name);
#endif // STANDALONG
}
}
}
//////////////////////////////////////////////////////////////////
//
// open an ELF file using
// elf_begin(ELF_C_READ) and examine search the ELF file
// for either a SHT_SYMTAB or SHT_DYNSYM symbol table.
// If it finds either - it will extract the contents of
// the symbol tables.
static void process_elf
(
Elf *elf,
char *file,
int fd,
int member,
uint64_t text_base,
uint64_t data_base,
uint32_t context
)
{
Elf_Cmd cmd;
Elf *_elf;
switch (elf_kind(elf))
{
case ELF_K_ELF:
// This is an ELF file
get_symbols(elf, file, text_base, data_base,context);
break;
case ELF_K_AR:
// Archives contain multiple ELF files, which can each
// in turn be examined with libelf.
// Iterate over each member of the
// archive and recursivly call process_elf() for processing.
cmd = ELF_C_READ;
while ((_elf = elf_begin(fd, cmd, elf)) != 0)
{
Elf_Arhdr *arhdr;
char buffer[1024];
arhdr = elf_getarhdr(_elf);
// Build up file names based off of
// 'archivename(membername)'.
snprintf(buffer, 1024, "%s(%s)", file, arhdr->ar_name);
// recursivly process the ELF members.
process_elf(_elf, buffer, fd, 1, text_base, data_base, context);
cmd = elf_next(_elf);
elf_end(_elf);
}
break;
default:
if (!member)
ui->error("%s: unexpected elf_kind(): 0x%x\n",
file, elf_kind(elf));
return;
}
}
////////////////////////////////////////////////////////////////////
int load_symbols
(
char* elf_fname,
uint64_t text_base,
uint64_t data_base,
uint32_t context
)
{
/*
* Initialize the elf library, must be called before elf_begin()
* can be called.
*/
if (elf_version(EV_CURRENT) == EV_NONE)
{
ui->error( "elf_version() failed: %s\n", elf_errmsg(0));
return(1);
}
int fd;
Elf *elf;
if ((fd = open(elf_fname, O_RDONLY)) == -1)
{
ui->perror("open");
return 1;
}
// open an Elf descriptor Read-Only
if ((elf = elf_begin(fd, ELF_C_READ, 0)) == NULL)
{
ui->error( "elf_begin() failed: %s\n", elf_errmsg(0));
close(fd);
return 1;
}
// Process elf descriptor
process_elf(elf, elf_fname, fd, 0, text_base, data_base, context);
elf_end(elf);
close(fd);
return 0;
}