blob: 3ce209a768ae56f06aa3daead4564da018c64ff6 [file] [log] [blame]
/*--------------------------------------------------------------------*/
/*--- Read XCOFF debug info. readxcoff.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Valgrind, a dynamic binary instrumentation
framework.
Copyright (C) 2006-2009 OpenWorks LLP
info@open-works.co.uk
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This 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 program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
Neither the names of the U.S. Department of Energy nor the
University of California nor the names of its contributors may be
used to endorse or promote products derived from this software
without prior written permission.
*/
#if defined(VGO_aix5)
/* This file reads XCOFF symbol tables and debug info.
Known limitations:
* only one text section per object file is handled
* C_BINCL/C_EINCL handling is wrong, so functions defined in files
included from other files will end up with the wrong file name
and possibly line numbers. Fixable.
* The line number reader leans heavily on the fact that the generic
line number canonicaliser in storage.c truncates overlapping
ranges.
*/
#include "pub_core_basics.h"
#include "pub_core_vki.h" /* struct vki_stat et al */
#include "pub_core_libcbase.h"
#include "pub_core_libcassert.h"
#include "pub_core_libcprint.h"
#include "pub_core_libcfile.h" /* stat, open, close */
#include "pub_core_aspacemgr.h" /* for mmaping debuginfo files */
#include "pub_core_options.h" /* VG_(clo_trace_symtab) */
#include "pub_core_xarray.h"
#include "priv_misc.h"
#include "priv_tytypes.h"
#include "pub_tool_debuginfo.h"
#include "priv_d3basics.h"
#include "priv_storage.h"
#include "priv_readxcoff.h" /* self */
/* --- !!! --- EXTERNAL HEADERS start --- !!! --- */
#if defined(VGP_ppc32_aix5)
# define __XCOFF32__ 1
# undef __XCOFF64__
#elif defined(VGP_ppc64_aix5)
# define __XCOFF64__ 1
# undef __XCOFF32__
#else
# error "This file should only be compiled on AIX"
#endif
#include <xcoff.h>
#undef __AR_SMALL__
#define __AR_BIG__ 1
#include <ar.h>
/* --- !!! --- EXTERNAL HEADERS end --- !!! --- */
/* Debug stuff */
#define SHOW_LD_STRTAB 1 /* loader string tables */
#define SHOW_LD_SYMTAB 1 /* loader symbol table */
#define SHOW_LD_RELTAB 1 /* loader reloc table */
#define SHOW_STRTAB 1 /* main string table */
#define SHOW_SYMS_P1 1 /* P1: find text sym starts */
#define SHOW_SYMS_P2 1 /* P2: find text sym ends */
#define SHOW_SYMS_P3 1 /* P3: src filenames & fn start/end line #s */
#define SHOW_SYMS_P4 1 /* P4: line numbers */
#define SHOW_SYMS_P5 1 /* P5: find TOC pointers */
#define SHOW_SYMS_P6 1 /* P6: finalise symbol info */
#define SHOW_AR_DETAILS 0 /* show details of .a file internals */
#define SHOW di->trace_symtab
/* A small stack of filenames is maintained for dealing
with BINCL/EINCL symbol table entries. */
#define N_FILENAME_STACK 16
/* Phase 5 (find TOC pointers) has two implementations, the official
version, which involves reading the data segment symbols, and the
kludgey version, which basically scans the (actual loaded) data
segment to find structs which look like function descriptors. */
#if 1
# undef OFFICIAL_PHASE5
#else
# define OFFICIAL_PHASE5 1
#endif
/*------------------------------------------------------------*/
/*--- Read XCOFF format debug info. ---*/
/*------------------------------------------------------------*/
/* COFF uses a strange way to represent symbol names. A symbol is an
eight-byte field.
In 32-bit mode: if the first four bytes are zero, then the second
four bytes give the offset into the string table where the string
really is. Otherwise, the whole 8-byte thing is itself the name.
In 64-bit mode: a four-byte field at offset 8 is always interpreted
as an offset into the string table.
For a symbol of length 8, in 32-bit mode, there is no obvious way
to zero-terminate it. One solution is to copy the name into
dynamically allocated memory, but that complicates storage
management.
An alternative solution, used here, is to represent a name as a
(data, length) pair instead of the traditional zero-terminated
string. Such a pair can be constructed for any XCOFF symbol name,
and has the advantages that (1) no dynamic memory is required, and
(2) the name is guaranteed to be accessible as long as the object
image is mapped in.
What the .vec points at must not be modified; if you want to do
that, copy it elsewhere first.
*/
typedef
struct {
UChar* vec; /* the text of the name */
UInt len; /* length of the text */
}
Name;
static Name maybeDerefStrTab( SYMENT* sym,
UChar* oi_strtab, UWord oi_n_strtab)
{
Name res;
static UChar* bogus
= (UChar*)"**_Error_Dereferencing_COFF_String_Table_**";
UChar* bytes = (UChar*)sym;
# if defined(VGP_ppc32_aix5)
if (bytes[0]==0 && bytes[1]==0 && bytes[2]==0 && bytes[3]==0) {
UInt off = *(UInt*)&bytes[4];
if (oi_strtab && oi_n_strtab > 0 && off < oi_n_strtab) {
res.vec = &oi_strtab[off];
res.len = VG_(strlen)(res.vec);
return res;
} else
goto bad;
} else {
Int i;
res.vec = bytes;
res.len = 8;
for (i = 0; i < 8; i++)
if (bytes[i] == 0)
res.len--;
return res;
}
# elif defined(VGP_ppc64_aix5)
ULong off = (ULong)( *(UInt*)&bytes[8] );
if (oi_strtab && oi_n_strtab > 0 && off < oi_n_strtab) {
res.vec = &oi_strtab[off];
res.len = VG_(strlen)(res.vec);
return res;
} else
goto bad;
# else
# error "Unknown platform"
# endif
bad:
res.vec = bogus;
res.len = VG_(strlen)(bogus);
return res;
}
/* Similar scheme for extracting names from C_FILE auxiliary entries,
except that the 32-bit scheme appears to be always used, even for
XCOFF64. */
static Name maybeDerefStrTab_fname ( UChar* bytes,
UChar* oi_strtab, UWord oi_n_strtab)
{
Name res;
static UChar* bogus
= (UChar*)"**_Error_Dereferencing_COFF_String_Table_**";
if (bytes[0]==0 && bytes[1]==0 && bytes[2]==0 && bytes[3]==0) {
UInt off = *(UInt*)&bytes[4];
if (oi_strtab && oi_n_strtab > 0 && off < oi_n_strtab) {
res.vec = &oi_strtab[off];
res.len = VG_(strlen)(res.vec);
return res;
} else
goto bad;
} else {
Int i;
res.vec = bytes;
res.len = 8;
for (i = 0; i < 8; i++)
if (bytes[i] == 0)
res.len--;
return res;
}
bad:
res.vec = bogus;
res.len = VG_(strlen)(bogus);
return res;
}
static Name mk_const_Name ( HChar* str )
{
Name res;
res.vec = str;
res.len = VG_(strlen)(res.vec);
return res;
}
static Name mk_empty_Name ( void )
{
Name res;
res.vec = "";
res.len = 0;
return res;
}
static Bool is_empty_Name ( Name name )
{
return name.len == 0;
}
static Bool eq_string_Name ( Name name, UChar* str )
{
UInt i;
for (i = 0; i < name.len; i++) {
if (str[i] == 0)
return False;
if (str[i] != name.vec[i])
return False;
}
if (str[name.len] == 0)
return True;
else
return False;
}
static Int cmp_Names ( Name n1, Name n2 )
{
UInt i = 0;
while (1) {
vg_assert(i >= 0 && i <= n1.len);
vg_assert(i >= 0 && i <= n2.len);
if (i == n1.len && i == n2.len)
return 0;
if (i == n1.len && i < n2.len)
return -1;
if (i < n1.len && i == n2.len)
return 1;
if (n1.vec[i] < n2.vec[i])
return -1;
if (n1.vec[i] > n2.vec[i])
return 1;
i++;
}
}
static void print_Name ( Name name )
{
UInt i;
for (i = 0; i < name.len; i++)
VG_(printf)("%c", name.vec[i]);
}
static UChar sanitiseChar ( UChar c )
{
if (c < 32 || c > 127)
c = '?';
return c;
}
static HChar* name_of_filhdr_f_magic ( Int magic )
{
switch (magic) {
case 0x01DF: return "xcoff32";
case 0x01EF: return "xcoff64-upto-aix43";
case 0x01F7: return "xcoff64-from-aix51";
default: return "unknown-xcoff-header-magic";
}
}
static HChar* name_of_scnhdr_s_flags ( Int flags )
{
switch (flags & 0xFFFF) {
case STYP_REG: return "\"regular\"";
case STYP_PAD: return "\"padding\"";
case STYP_TEXT: return "text only";
case STYP_DATA: return "data only";
case STYP_BSS: return "bss only";
case STYP_EXCEPT: return "Exception";
case STYP_INFO: return "Comment";
case STYP_LOADER: return "Loader";
case STYP_DEBUG: return "Debug";
case STYP_TYPCHK: return "Typecheck";
case STYP_OVRFLO: return "Overflow";
default: return "unknown-section-header-name";
}
}
static HChar* name_of_syment_n_sclass ( Int sclass )
{
static HChar buf[10];
switch (sclass) {
/* dbx ones (>= 0x80) */
case C_GSYM: return "gsym";
case C_LSYM: return "lsym";
case C_PSYM: return "psym";
case C_RSYM: return "rsym";
case C_RPSYM: return "rpsym";
case C_STSYM: return "stsym";
case C_DECL: return "decl";
case C_FUN: return "fun";
case C_BSTAT: return "bstat";
case C_ESTAT: return "estat";
/* non-dbx ones (< 0x80) */
case C_STAT: return "STAT";
case C_FILE: return "FILE";
case C_HIDEXT: return "HIDEXT";
case C_EXT: return "EXT";
case C_FCN: return "FCN";
case C_BINCL: return "BINCL";
case C_EINCL: return "EINCL";
case C_BLOCK: return "BLOCK";
case C_WEAKEXT: return "WEAKEXT";
default:
VG_(sprintf)(buf, "??%d??", sclass);
return buf;
}
}
typedef
struct {
Name name; /* symbol's name */
Addr first; /* first address; always known */
Addr last; /* last address; may be an overestimate */
Name fname; /* source file name, if known */
Int slnno; /* starting line #, or 0 if unknown */
Int elnno; /* ending line #, or 0 if unknown */
UWord r2value; /* what r2 should be for this fn (tocptr) */
Bool r2known; /* do we have a r2 value? */
}
XCoffSym;
static void init_XCoffSym( XCoffSym* sym )
{
sym->name = mk_empty_Name();
sym->first = 0;
sym->last = 0;
sym->fname = mk_empty_Name();
sym->slnno = 0;
sym->elnno = 0;
sym->r2known = False;
sym->r2value = False;
}
/* Compare XCoffSyms by their start address. */
static Int cmp_XCoffSym_by_start ( void* v1, void* v2 )
{
XCoffSym* s1 = (XCoffSym*)v1;
XCoffSym* s2 = (XCoffSym*)v2;
if (s1->first < s2->first) return -1;
if (s1->first > s2->first) return 1;
return 0;
}
/* Compare XCoffSyms by a slightly weaker ordering, returning zero
(equivalence) for any overlap, and -1 or 1 otherwise. */
static Int cmp_XCoffSym_by_overlap ( void* v1, void* v2 )
{
XCoffSym* s1 = (XCoffSym*)v1;
XCoffSym* s2 = (XCoffSym*)v2;
if (s1->last < s2->first) return -1;
if (s2->last < s1->first) return 1;
return 0;
}
/* Compare XCoffSyms by their start address, and for equal addresses,
use the name as a secondary sort key. */
static Int cmp_XCoffSym_by_start_then_name ( void* v1, void* v2 )
{
XCoffSym* s1 = (XCoffSym*)v1;
XCoffSym* s2 = (XCoffSym*)v2;
if (s1->first < s2->first) return -1;
if (s1->first > s2->first) return 1;
return cmp_Names(s1->name, s2->name);
}
/* csect_idx is an index in the symbol table (start, n_entries) to a
symbol defining a csect. If possible, find the bounds of the csect
and assign them to *first and *last, and return True; else return
False. sntext_1based_if_known is the 1-based number of the text
section. Note: computes stated VMAs, not actual VMAs. */
#if defined(VGP_ppc32_aix5)
# define SMTYP_SMTYP(x) ((x) & 0x7) /* symbol type */
# define CSECT(PP) (((AUXENT*)(PP))->x_csect)
# define CSECT_LEN(PP) (CSECT(PP).x_scnlen)
# define CSECT_ALIGN(PP) (SMTYP_ALIGN(CSECT(PP).x_smtyp))
# define CSECT_SMTYP(PP) (SMTYP_SMTYP(CSECT(PP).x_smtyp))
# define CSECT_SCLAS(PP) (CSECT(PP).x_smclas)
#elif defined(VGP_ppc64_aix5)
# define SMTYP_SMTYP(x) ((x) & 0x7) /* symbol type */
# define CSECT(PP) (((AUXENT*)(PP))->x_csect)
# define CSECT_LEN(PP) ((((ULong)(CSECT(PP).x_scnlen_hi)) << 32) \
| ((ULong)(CSECT(PP).x_scnlen_lo)))
# define CSECT_ALIGN(PP) (SMTYP_ALIGN(CSECT(PP).x_smtyp))
# define CSECT_SMTYP(PP) (SMTYP_SMTYP(CSECT(PP).x_smtyp))
# define CSECT_SCLAS(PP) (CSECT(PP).x_smclas)
#else
# error "Unknown platform"
#endif
#define SYM_IX(_tab,_n) ((SYMENT*)(((UChar*)(_tab)) + SYMESZ * (_n)))
static
Bool get_csect_bounds ( UChar* start, UWord n_entries,
UWord csect_idx,
Int sntext_1based_if_known,
/*OUT*/UChar** first, /*OUT*/UChar** last )
{
Bool is_text;
SYMENT* cssym;
AUXENT* csaux;
vg_assert(SYMESZ == 18); /* both for XCOFF32 and XCOFF64 */
if (n_entries < 2)
return False;
if (csect_idx+1 >= n_entries)
return False;
cssym = (SYMENT*)SYM_IX(start, csect_idx);
csaux = (AUXENT*)SYM_IX(start, csect_idx+1);
is_text = sntext_1based_if_known != -1
&& (Int)cssym->n_scnum == sntext_1based_if_known;
if (!is_text)
return False;
if (cssym->n_sclass == C_EXT || cssym->n_sclass == C_HIDEXT) {
if (cssym->n_numaux == 1) {
if (CSECT_SMTYP(csaux) == XTY_SD) {
if (0) VG_(printf)("GCB: SD: len is %lld\n", (Long)CSECT_LEN(csaux));
*first = (UChar*)(cssym->n_value);
*last = *first + CSECT_LEN(csaux)-1;
return True;
}
} else {
/* Possibly complain or take evasive action here. In fact
I've yet to see a case where a csect definition symbol has
n_numaux != 1. */
}
}
return False;
}
/* Read symbol and line number info for the given text section. (This
is the central routine for XCOFF reading.) Returns NULL on
success, or the text of an error message otherwise. */
static
HChar* read_symbol_table (
/*MOD*/struct _DebugInfo* di,
/* location of symbol table */
UChar* oi_symtab, UWord oi_nent_symtab,
/* location of string table */
UChar* oi_strtab, UWord oi_n_strtab,
/* location of debug section (stabs strings, if any) */
UChar* oi_debug, UWord oi_n_debug,
/* location of line number info, if any */
UChar* oi_lnos, UWord oi_nent_lnos,
/* section indices */
Int sntext_1based_if_known,
Int sndata_1based_if_known,
/* where the mapped data section is */
/* Now in di->data_avma: Addr data_avma, */
/* Now in di->data_size: UWord data_alen, */
UWord data_alen_from_auxhdr,
/* where the mapped toc is (in the data section,
presumably), if known */
Addr toc_avma,
/* stated-to-actual VMA offsets */
Word text_bias,
Word data_bias
)
{
SYMENT* sym;
SYMENT* aux;
UInt i, j, nsyms, k, m;
Name name;
Bool is_text, is_data;
XArray* syms = NULL; /* XArray of XCoffSyms */
/* If the TOC avma is obviously bogus, get rid of it */
{
UWord data_maxlen = di->data_size;
if (data_maxlen < data_alen_from_auxhdr)
data_maxlen = data_alen_from_auxhdr;
//VG_(printf)(" toc_avma %p\n", toc_avma);
//VG_(printf)("data_avma %p\n", data_avma);
//VG_(printf)("dxxx_avma %p\n", data_avma + data_maxlen);
if (toc_avma != 0
&& (toc_avma < di->data_avma
|| toc_avma >= di->data_avma + data_maxlen))
toc_avma = 0;
//VG_(printf)("2toc_avma %p\n", toc_avma);
}
/* We can't just treat this as an array of SYMENTs, because C
thinks they have size 20 whereas the spec says they have size 18
(alignment padding) so doing the obvious thing screws up. Hence
we have to calculate the offset of each entry manually. */
if (0) VG_(printf)("size of SYMENT = %ld\n", sizeof(SYMENT));
/* ----------------------------------------------------------
Phase 1: first make a pass through the symbols, looking for
stuff in the text segment. Calculate their actual VMAs,
dump any outside the text segment actual VMA bounds, and
add the rest to 'syms'.
---------------------------------------------------------- */
syms = VG_(newXA)( ML_(dinfo_zalloc), "di.readxcoff.rst.1",
ML_(dinfo_free), sizeof(XCoffSym) );
if (SHOW && SHOW_SYMS_P1) {
VG_(printf)("--- BEGIN Phase1 (find text symbol starts) ---\n");
VG_(printf)("--- note: shown addresses are STATED VMAs ---\n");
}
i = 0;
while (1) {
if (i >= oi_nent_symtab)
break;
sym = SYM_IX(oi_symtab, i);
is_text = sntext_1based_if_known != -1
&& (Int)sym->n_scnum == sntext_1based_if_known;
is_data = sndata_1based_if_known != -1
&& (Int)sym->n_scnum == sndata_1based_if_known;
if (SHOW && SHOW_SYMS_P1)
VG_(printf)("Phase1: %5d+%d ", i, (Int)sym->n_numaux);
name = mk_const_Name("(unknown)");
if (sym->n_scnum == N_DEBUG && sym->n_sclass == C_FUN)
name = maybeDerefStrTab( sym, oi_debug, oi_n_debug );
else
if (sym->n_sclass & DBXMASK)
name = mk_const_Name("(dbxstr)");
else
name = maybeDerefStrTab( sym, oi_strtab, oi_n_strtab);
if (SHOW && SHOW_SYMS_P1) {
VG_(printf)("%5s(%2d) %6s 0x%016llx ",
is_text ? "text" : is_data ? "data" : "other",
(Int)sym->n_scnum,
name_of_syment_n_sclass(sym->n_sclass),
(ULong)sym->n_value);
print_Name(name);
VG_(printf)("\n");
}
i++;
i += sym->n_numaux;
if (!is_text)
continue;
/* --- BEGIN regular(ish) symbol --- */
if ((sym->n_sclass == C_EXT || sym->n_sclass == C_HIDEXT)
&& (sym->n_numaux == 1 || sym->n_numaux == 2)) {
/* Dealing with a symbol with a csect entry. By convention
(according to IBM docs) the csect entry is the last
auxiliary for this symbol, if there is more than one
auxiliary present; hence "SYM_IX(oi_symtab, i-1)" below. */
aux = SYM_IX(oi_symtab, i-1);
if (0) VG_(printf)("symtype is %d\n", CSECT_SMTYP(aux));
if (CSECT_SMTYP(aux) == XTY_SD) {
/* Aux is a csect definition. This is relatively rare,
but at least it is simple: the CSECT_LEN(aux) field
contains it's length, so we just heave that into the
pot for phase 2. */
XCoffSym cand;
if (0) VG_(printf)("SD: len is %d\n", (Int)CSECT_LEN(aux));
if (0) VG_(printf)("SD: proposed %#llx\n", (ULong)sym->n_value);
init_XCoffSym(&cand);
cand.first = sym->n_value;
cand.last = cand.first + (UWord)CSECT_LEN(aux) - 1;
cand.first += text_bias;
cand.last += text_bias;
cand.name = name;
if (cand.last < di->text_avma
|| cand.first >= di->text_avma + di->text_size)
continue;
if (cand.last < cand.first)
continue;
if (is_empty_Name(name))
continue;
(void)VG_(addToXA)(syms, &cand);
}
if (CSECT_SMTYP(aux) == XTY_LD) {
/* Aux is a label definition. This is the common case. */
XCoffSym cand;
Bool ok;
UChar *csect_first, *csect_last;
/* x_scnlen contains the symbol table entry of the
containing csect. Use the symbol's stated vma and csect
end as the initial approximation of this symbol's start
and length. The length will get revised downwards in
Phase 2. */
init_XCoffSym(&cand);
ok = get_csect_bounds( oi_symtab, oi_nent_symtab,
CSECT_LEN(aux),
sntext_1based_if_known,
&csect_first, &csect_last );
if (0 && ok)
VG_(printf)("new csect svma %p %p\n", csect_first, csect_last);
if (ok && ((UWord)csect_first) <= ((UWord)sym->n_value)
&& ((UWord)sym->n_value) <= ((UWord)csect_last)) {
if (0) {
VG_(printf)("LD: in a csect %p %p\n",
csect_first, csect_last);
VG_(printf)("CAND: %p .. %p %s\n",
(void*)sym->n_value, (void*)csect_last,
"fixme-Name-printing(1)" /*name*/);
}
cand.first = sym->n_value;
cand.last = (Addr)csect_last;
} else {
if (0) {
VG_(printf)("LD: can't compute csect bounds?!\n");
VG_(printf)("CAND: %p .. %p %s\n",
(HChar*)sym->n_value,
(HChar*)sym->n_value+1,
"fixme-Name-printing(2)" /*name*/);
}
cand.first = sym->n_value;
cand.last = cand.first + 1;
}
/* cand.first is a stated VMA; turn it into an actual VMA
and ignore it if not in the actual text segment. */
cand.first += text_bias;
cand.last += text_bias;
cand.name = name;
if (cand.last < di->text_avma
|| cand.first >= di->text_avma + di->text_size)
continue;
if (cand.last < cand.first)
continue;
if (is_empty_Name(name))
continue;
(void)VG_(addToXA)(syms, &cand);
}
}
/* --- END regular(ish) symbol --- */
}
/* ----------------------------------------------------------
Phase 2: suitable text symbols have been put into 'syms'. Their
start addresses are correct, but end addresses are those of the
containing csect, which is in general way too long. This phase
clips the ends so that the ranges no longer overlap, and thereby
constrains each symbol's range to something which, for the most
part, is correct.
---------------------------------------------------------- */
nsyms = VG_(sizeXA)(syms);
if (SHOW && SHOW_SYMS_P1)
VG_(printf)("Phase1 acquired %d text symbols\n", nsyms);
if (SHOW && SHOW_SYMS_P2) {
VG_(printf)("--- BEGIN Phase2 (find text symbol ends) ---\n");
VG_(printf)("--- note: shown addresses are ACTUAL VMAs ---\n");
}
VG_(setCmpFnXA)(syms, cmp_XCoffSym_by_start_then_name);
VG_(sortXA)(syms);
/* We only know for sure the start addresses (actual VMAs) of
symbols, and an overestimation of their end addresses. So sort
by start address, then clip each symbol so that its end address
does not overlap with the next one along.
There is a small refinement: if a group of symbols have the same
address, treat them as a group: find the next symbol along that
has a higher start address, and clip all of the group
accordingly. This clips the group as a whole so as not to
overlap following symbols. This leaves prefersym() in
storage.c, which is not XCOFF-specific, to later decide which of
the symbols in the group to keep.
Another refinement is that we need to get rid of symbols which,
after clipping, have identical starts, ends, and names. So the
sorting uses the name as a secondary key.
*/
for (i = 0; i < nsyms; i++) {
for (k = i+1;
k < nsyms
&& ((XCoffSym*)VG_(indexXA)(syms,i))->first
== ((XCoffSym*)VG_(indexXA)(syms,k))->first;
k++)
;
/* So now [i .. k-1] is a group all with the same start address.
Clip their ending addresses so they don't overlap [k]. In
the normal case (no overlaps), k == i+1. */
if (k < nsyms) {
XCoffSym* next = (XCoffSym*)VG_(indexXA)(syms,k);
for (m = i; m < k; m++) {
XCoffSym* here = (XCoffSym*)VG_(indexXA)(syms,m);
vg_assert(here->first < next->first);
if (here->last >= next->first)
here->last = next->first-1;
}
}
i = k-1;
vg_assert(i <= nsyms);
}
j = 0;
if (nsyms > 0) {
j = 1;
for (i = 1; i < nsyms; i++) {
vg_assert(j <= i);
XCoffSym* s_j1 = (XCoffSym*)VG_(indexXA)(syms, j-1);
XCoffSym* s_j = (XCoffSym*)VG_(indexXA)(syms, j);
XCoffSym* s_i = (XCoffSym*)VG_(indexXA)(syms, i);
if (s_i->first != s_j1->first
|| s_i->last != s_j1->last
|| 0 != cmp_Names(s_i->name, s_j1->name)) {
*s_j = *s_i;
j++;
} else {
if (SHOW && SHOW_SYMS_P2) {
VG_(printf)("Phase2: dump duplicate ");
print_Name(s_i->name);
VG_(printf)("\n");
}
}
}
}
vg_assert(j >= 0 && j <= nsyms);
VG_(dropTailXA)(syms, nsyms - j);
nsyms = j;
if (1) {
for (i = 0; i < nsyms; i++) {
XCoffSym* s = (XCoffSym*)VG_(indexXA)(syms, i);
if (SHOW && SHOW_SYMS_P2) {
VG_(printf)("Phase2: %d 0x%lx 0x%lx ",
i, s->first, s->last);
print_Name(s->name);
VG_(printf)("\n");
}
}
}
/* ----------------------------------------------------------
Phase 3: rescan the symbol table, looking for info on function
start/end line numbers and source file names. Generally
this will be absent for sources compiled without -g.
---------------------------------------------------------- */
if (SHOW && SHOW_SYMS_P3) {
VG_(printf)("--- BEGIN Phase3 (find src filenames "
"& fn start/end line #s) ---\n");
VG_(printf)("--- note: shown addresses are STATED VMAs ---\n");
}
/* The lookupXAs in the C_FUN(.bf) part have to operate by
inclusion. Hence: */
VG_(setCmpFnXA)(syms, cmp_XCoffSym_by_overlap);
VG_(sortXA)(syms);
/* In this loop, p3currsym is maintained as a pointer to the most
recent XCoffSym identified as FCN(.bf) (function start).
Subsequent FCN(.ef) (function end) indications are compared
against said symbol. This assumes that function start/end
indications are not nested. */
XCoffSym* p3currsym = NULL;
/* Maintain a stack of filenames. We allow the stack pointer to go
beyond the end, but obviously nothing is stored in this
imaginary part of the stack. */
Name filenames[N_FILENAME_STACK];
Int filenames_used = 1;
Name name_unknown = mk_empty_Name();
Name name_overflow = mk_const_Name("(filename_stack_overflow)");
for (i = 0; i < N_FILENAME_STACK; i++)
filenames[i] = name_unknown;
# define FNAME_PUSH(_fname) \
do { \
vg_assert(filenames_used >= 1);\
if (filenames_used < N_FILENAME_STACK)\
filenames[filenames_used] = (_fname);\
filenames_used++;\
} while (0)
# define FNAME_POP \
do {\
vg_assert(filenames_used >= 1);\
if (filenames_used > 1 && filenames_used <= N_FILENAME_STACK) \
filenames[filenames_used-1] = name_unknown; \
if (filenames_used > 1)\
filenames_used--;\
} while (0)
# define FNAME_GET_TOP \
(filenames_used > N_FILENAME_STACK \
? name_overflow \
: filenames[filenames_used-1])
# define FNAME_SET_TOP(_fname) \
do {\
vg_assert(filenames_used >= 1);\
filenames[filenames_used-1] = (_fname);\
} while (0)
i = 0;
while (1) {
if (i >= oi_nent_symtab)
break;
sym = SYM_IX(oi_symtab, i);
is_text = sntext_1based_if_known != -1
&& (Int)sym->n_scnum == sntext_1based_if_known;
is_data = sndata_1based_if_known != -1
&& (Int)sym->n_scnum == sndata_1based_if_known;
if (0 && SHOW && SHOW_SYMS_P3)
VG_(printf)("Phase3: %5d+%d ", i, (Int)sym->n_numaux);
name = mk_const_Name("(unknown)");
if (sym->n_scnum == N_DEBUG && sym->n_sclass == C_FUN)
name = maybeDerefStrTab( sym, oi_debug, oi_n_debug );
else
if (sym->n_sclass & DBXMASK)
name = mk_const_Name("(dbxstr)");
else
name = maybeDerefStrTab( sym, oi_strtab, oi_n_strtab);
if (0 && SHOW && SHOW_SYMS_P3) {
VG_(printf)("%5s(%2d) %6s 0x%016llx ",
is_text ? "text" : is_data ? "data" : "other",
(Int)sym->n_scnum,
name_of_syment_n_sclass(sym->n_sclass),
(ULong)sym->n_value);
print_Name(name);
VG_(printf)("\n");
}
i++;
i += sym->n_numaux;
/* --- BEGIN C_FILE [source file] --- */
/* There are two variants of C_FILE: a simple one with n_numaux
== 0, where the primary name is what we're after, and another
variant with n_numaux == 3, in which we have to hunt around
in the auxiliary entries to find the file name. gcc produces
exclusively the first kind, and xlc a mixture of both. */
if (sym->n_sclass == C_FILE && sym->n_numaux == 0) {
if (!is_empty_Name(name))
FNAME_SET_TOP(name);
if (SHOW && SHOW_SYMS_P3) {
VG_(printf)("Phase3: %5d+%d FILE ",
i-1-sym->n_numaux, (Int)sym->n_numaux );
print_Name(name);
VG_(printf)("\n");
}
continue;
}
if (sym->n_sclass == C_FILE && sym->n_numaux > 1
&& sym->n_numaux <= 5 /*stay sane*/) {
for (k = 0; k < sym->n_numaux; k++) {
aux = SYM_IX(oi_symtab, i - sym->n_numaux + k);
Name fname
= maybeDerefStrTab_fname(
(UChar*)&((AUXENT*)aux)->x_file.x_fname,
oi_strtab, oi_n_strtab);
if (((AUXENT*)aux)->x_file._x.x_ftype == XFT_FN) {
if (!is_empty_Name(fname))
FNAME_SET_TOP(fname);
if (SHOW && SHOW_SYMS_P3) {
VG_(printf)("Phase3: %5d+%d FILE ",
i-1-sym->n_numaux, (Int)sym->n_numaux );
print_Name(fname);
VG_(printf)("\n");
}
break;
}
}
continue;
}
/* --- END C_FILE [source file] --- */
/* --- BEGIN C_BINCL [beginning of include] --- */
if (sym->n_sclass == C_BINCL && sym->n_numaux == 0) {
FNAME_PUSH(name);
if (SHOW && SHOW_SYMS_P3)
VG_(printf)("Phase3: %5d+%d BINCL %s\n",
i-1-sym->n_numaux, (Int)sym->n_numaux,
"fixme-Name-printing(3)" /*name*/ );
continue;
}
/* --- END C_BINCL [beginning of include] --- */
/* --- BEGIN C_EINCL [end of include] --- */
if (sym->n_sclass == C_EINCL && sym->n_numaux == 0) {
FNAME_POP;
if (SHOW && SHOW_SYMS_P3)
VG_(printf)("Phase3: %5d+%d EINCL %s\n",
i-1-sym->n_numaux, (Int)sym->n_numaux,
"fixme-Name-printing(4)" /*name*/ );
continue;
}
/* --- END C_EINCL [end of include] --- */
/* everything else that is interesting is in the text
section. */
if (!is_text)
continue;
/* --- BEGIN C_FCN(.bf) [function begin mark] --- */
if (sym->n_sclass == C_FCN
&& sym->n_numaux == 1
&& eq_string_Name(name, ".bf")) {
/* aux is BLOCK */
aux = SYM_IX(oi_symtab, i-1);
Addr fn_start_avma = ((Addr)sym->n_value) + text_bias;
Int fn_start_lnno = ((AUXENT*)aux)->x_sym.x_misc.x_lnsz.x_lnno;
/* Look in 'syms' to see if we have anything for address
fn_avma. */
XCoffSym key;
VG_(memset)(&key, 0, sizeof(key));
key.first = fn_start_avma;
key.last = fn_start_avma;
Word ix_lo, ix_hi;
/* Search for all symbols intersecting fn_start_avma. */
Bool found = VG_(lookupXA)(syms, &key, &ix_lo, &ix_hi);
if (found) {
/* All the 'syms' entries from ix_lo to ix_hi match. */
for (k = ix_lo; k <= ix_hi; k++) {
XCoffSym* tsym = (XCoffSym*)VG_(indexXA)(syms,k);
/* note the start line number */
if (tsym->slnno == 0 && fn_start_lnno > 0)
tsym->slnno = fn_start_lnno;
/* also the current filename, if we know it */
if (is_empty_Name(tsym->fname)
&& !is_empty_Name(FNAME_GET_TOP))
tsym->fname = FNAME_GET_TOP;
/* remember the first in the range as the new current
(I've never seen a range with > 1) */
if (k == ix_lo)
p3currsym = tsym;
if (SHOW && SHOW_SYMS_P3) {
VG_(printf)("Phase3: %5d+%d FCN(.bf) 0x%016llx "
"lnno=%-4d ",
i-1-sym->n_numaux, (Int)sym->n_numaux,
(ULong)sym->n_value,
fn_start_lnno );
print_Name(tsym->name);
VG_(printf)("\n");
if (!is_empty_Name(tsym->fname)) {
VG_(printf)("Phase3: ");
print_Name(tsym->fname);
VG_(printf)("\n");
}
}
}
}
continue;
}
/* --- END C_FCN(.bf) [function begin mark] --- */
/* --- BEGIN C_FCN(.ef) [function end mark] --- */
if (sym->n_sclass == C_FCN
&& sym->n_numaux == 1
&& eq_string_Name(name, ".ef")) {
/* aux is BLOCK */
aux = SYM_IX(oi_symtab, i-1);
/* In this case the n_value field appears to give the address
of the first insn following the end of the function.
Hence the - 1. */
Addr fn_end_avma = ((Addr)sym->n_value) + text_bias - 1;
Int fn_end_lnno = ((AUXENT*)aux)->x_sym.x_misc.x_lnsz.x_lnno;
if (p3currsym
&& fn_end_avma >= p3currsym->first
&& fn_end_avma <= p3currsym->last) {
if (p3currsym->elnno == 0 && fn_end_lnno > 0)
p3currsym->elnno = fn_end_lnno;
if (SHOW && SHOW_SYMS_P3) {
VG_(printf)("Phase3: %5d+%d FCN(.ef) 0x%016llx "
"lnno=%-4d ",
i-1-sym->n_numaux, (Int)sym->n_numaux,
(ULong)sym->n_value,
fn_end_lnno );
print_Name(p3currsym->name);
VG_(printf)("\n");
}
if (fn_end_avma < p3currsym->last) {
/* also take the opportunity to trim the symbol's
length to something less than established by the
initial estimation done by Phases 1 and 2. */
if (0) VG_(printf)("trim end from %#lx to %#lx\n",
p3currsym->last, fn_end_avma);
p3currsym->last = fn_end_avma;
}
}
continue;
}
/* --- END C_FCN(.ef) [function end mark] --- */
}
/* ----------------------------------------------------------
Phase 4: read and enumerate the line number entries, if
there are any. This depends on knowing the function start/end
line numbers established in Phase 3.
---------------------------------------------------------- */
if (SHOW && SHOW_SYMS_P4) {
VG_(printf)("--- BEGIN Phase4 (read line number info) ---\n");
VG_(printf)("--- note: shown addresses are ACTUAL VMAs ---\n");
}
/* Re-sort 'syms' using the compare-start-addresses ordering, so we
can use that in subsequent searches. */
VG_(setCmpFnXA)(syms, cmp_XCoffSym_by_start);
VG_(sortXA)(syms);
if (oi_lnos && oi_nent_lnos > 0) {
# if defined(VGP_ppc32_aix5)
vg_assert(LINESZ == 6); /* XCOFF32 */
# elif defined(VGP_ppc64_aix5)
vg_assert(LINESZ == 12); /* XCOFF64 */
# else
# error "Unknown plat"
# endif
# define LNO_IX(_tab,_n) \
((LINENO*)(((UChar*)(_tab)) + LINESZ * (_n)))
/* Current fn that we are processing line numbers for */
XCoffSym* p4currsym = NULL;
/* SegInfo's string table pointer for p4currsym's file name.
Allocated on demand, so as not to waste space in the
SegInfo's string table. */
UChar* si_fname_str = NULL;
/* Ditto the directory name, if we can manage it. */
UChar* si_dname_str = NULL;
for (i = 0; i < oi_nent_lnos; i++) {
LINENO* lno = LNO_IX(oi_lnos,i);
if (lno->l_lnno == 0) {
/* New fn. We get given the index in the symbol table of
the relevant function. It should be a C_EXT, C_WEAKEXT
or C_HIDEXT flavour, according to the IBM docs. */
Int sym_ix = (Int)lno->l_addr.l_symndx;
sym = SYM_IX(oi_symtab, sym_ix);
if (!(sym->n_sclass == C_EXT
|| sym->n_sclass == C_WEAKEXT
|| sym->n_sclass == C_HIDEXT))
return "readxcoff.c: invalid symbol reference"
" in line number info";
/* For these 3 symbol kinds, the n_value field is the
symbol's stated VMA. Convert this to an actual VMA and
use that to find the associated XCoffSym. */
Addr sym_avma = ((Addr)sym->n_value) + text_bias;
XCoffSym key;
VG_(memset)(&key, 0, sizeof(key));
key.first = sym_avma;
Word ix_lo, ix_hi;
Bool found = VG_(lookupXA)(syms, &key, &ix_lo, &ix_hi);
if (found) {
/* All the 'syms' entries from ix_lo to ix_hi match.
Just use the lowest (sigh ..) */
p4currsym = (XCoffSym*)VG_(indexXA)(syms, ix_lo);
} else {
/* We can't find the relevant sym, but we still have to
wade through the line number info for this function
until we get to the starting record for the next
one. */
p4currsym = NULL;
}
/* If we decide to add any line info for this fn to the
SegInfo, we'll allocate this. Otherwise don't
bother. */
si_fname_str = NULL;
si_dname_str = NULL;
if (SHOW && SHOW_SYMS_P4) {
VG_(printf)("Phase4: new fn (%d found), avma 0x%016llx ",
(Int)(ix_hi-ix_lo+1),
(ULong)sym_avma );
if (p4currsym)
print_Name(p4currsym->name);
else
VG_(printf)("UNKNOWN");
VG_(printf)("\n");
}
} else {
/* Line number entry for the current fn. */
if (!p4currsym)
continue;
Int line_no = (Int)(UInt)lno->l_lnno;
line_no += (p4currsym->slnno - 1);
Addr line_first_avma = ((Addr)lno->l_addr.l_paddr) + text_bias;
if (line_first_avma < p4currsym->first
|| line_first_avma > p4currsym->last)
continue;
Addr line_last_avma = p4currsym->last;
/* Try to refine the last_avma by looking at the next
line's entry. */
/* XXX: TODO. What we have currently works only because
the generic line number canonicaliser truncates
overlapping address ranges in the way which we happen
to need anyway. */
if (SHOW && SHOW_SYMS_P4)
VG_(printf)("Phase4: line %d 0x%016llx - 0x%016llx\n",
line_no, (ULong)line_first_avma,
(ULong)line_last_avma);
/* This now has to be allocated. Try and figure out the
dir name at the same time. This is a bit ugly in that
it involves messing with the string after it's been
copied into the SegInfo's string table, but seems
harmless enough. */
if ((!si_fname_str) && !is_empty_Name(p4currsym->fname)) {
si_dname_str = NULL;
si_fname_str = ML_(addStr)(di, p4currsym->fname.vec,
p4currsym->fname.len);
UChar* lastslash = VG_(strrchr)(si_fname_str, '/');
if (lastslash)
vg_assert(lastslash[0] == '/');
if (lastslash[1] != 0) {
si_dname_str = si_fname_str;
lastslash[0] = 0; /* replace the / with a NUL
terminator */
si_fname_str = lastslash+1;
if (0) VG_(printf)("XXX %s %s\n", si_dname_str,
si_fname_str);
}
}
/* finally .. */
if (line_no >= 0)
ML_(addLineInfo)(di, si_fname_str, si_dname_str,
line_first_avma, line_last_avma+1,
line_no, i/*debugging only*/);
}
}
# undef LNO_IX
}
#if defined(OFFICIAL_PHASE5)
/* ----------------------------------------------------------
Phase 5: Do another trawl of the XCOFF symbol table, looking
for TOC entries for the entries we've already placed in 'syms'.
---------------------------------------------------------- */
if (SHOW && SHOW_SYMS_P5)
VG_(printf)("--- BEGIN official Phase5 (find TOC pointers) ---\n");
Bool is_cfun;
i = 0;
while (1) {
if (i >= oi_nent_symtab)
break;
sym = SYM_IX(oi_symtab, i);
is_text = sntext_1based_if_known != -1
&& (Int)sym->n_scnum == sntext_1based_if_known;
is_data = sndata_1based_if_known != -1
&& (Int)sym->n_scnum == sndata_1based_if_known;
is_cfun = sym->n_scnum == N_DEBUG
&& sym->n_sclass == C_FUN;
i++;
i += sym->n_numaux;
if (!is_cfun && !is_data)
continue;
if (SHOW && SHOW_SYMS_P5)
VG_(printf)("Phase5o: %5d+%d ", i-1-sym->n_numaux,
(Int)sym->n_numaux);
name = mk_const_Name("(unknown)");
if (is_cfun)
name = maybeDerefStrTab( sym, oi_debug, oi_n_debug );
else
if (sym->n_sclass & DBXMASK)
name = mk_const_Name("(dbxstr)");
else
name = maybeDerefStrTab( sym, oi_strtab, oi_n_strtab);
if (SHOW && SHOW_SYMS_P5) {
VG_(printf)("%5s(%2d) %6s svma 0x%016llx ",
is_text ? "text" : is_data ? "data" : "other",
(Int)sym->n_scnum,
name_of_syment_n_sclass(sym->n_sclass),
(ULong)sym->n_value);
print_Name(name);
VG_(printf)("\n");
}
Addr avma = (Addr)sym->n_value + data_bias;
if (0) VG_(printf)("data sym: avma %p, limits %p-%p\n",
avma, data_avma,data_avma + data_alen);
/* Does avma point to 3 valid words inside the actual data
segment? iow, can it possibly be a valid function
descriptor? If not, move on. */
if (! (avma >= data_avma
&& avma + 3 * sizeof(Word) <= data_avma + data_alen) )
continue;
UWord* fndescr = (UWord*)avma;
if (SHOW && SHOW_SYMS_P5)
VG_(printf)(" fndescr = {0x%lx,0x%lx}\n",
fndescr[0], fndescr[1]);
/* Another check: fndescr[0], the entry point, must point inside
the actual text segment. Discard any that don't. */
Addr fndescr_0 = (Addr)fndescr[0];
if (fndescr_0 < si->text_avma
|| fndescr_0 >= si->text_avma+si->text_size)
continue;
/* Let's suppose that fndescr is the descriptor for a
function with name NAME. If that's so, then 'syms'
acquired by stage 2 should have an entry of name '.NAME'
whose address is fndescr[0]. If so, then fndescr[1] must
be the relevant r2 value for it. */
/* Look in 'syms' to see if we have anything for address
fndescr[0]. */
XCoffSym key;
VG_(memset)(&key, 0, sizeof(key));
key.first = fndescr_0;
Word ix_lo, ix_hi;
Bool found = VG_(lookupXA)(syms, &key, &ix_lo, &ix_hi);
if (found) {
/* So all the 'syms' entries from ix_lo to ix_hi have an
address which matches the entry point address stated in
this descriptor. For each one, as a final sanity
check, see if the 'syms' entry has a name .NAME where
NAME is that of the data symbol currently under
consideration. If so, it's a pretty good bet that this
descriptor matches the text symbol we already have, and
so we have a valid tocptr value from fndescr[1]. */
for (k = ix_lo; k <= ix_hi; k++) {
XCoffSym* tsym = (XCoffSym*)VG_(indexXA)(syms,k);
vg_assert(!is_empty_Name(tsym->name));
/* VG_(printf)("cmp %s %s\n", name, tsym->name); */
/* VG_(printf)("found matching %d %s\n", k, tsym->name); */
if (tsym->name.len == 1 + name.len
&& tsym->name.vec[0] == '.'
&& 0 == VG_(memcmp)(&tsym->name.vec[1],
&name.vec[0], name.len)) {
Addr r2val = fndescr[1];
if (tsym->r2known) {
if (tsym->r2value != r2val)
/* COMPLAIN - conflicting r2 values*/ ;
} else {
tsym->r2known = True;
tsym->r2value = r2val;
}
}
}
}
}
#else /* !defined(OFFICIAL_PHASE5) */
/* ----------------------------------------------------------
Alternative kludgey Phase 5: find TOC entries for 'syms' by the
blunt-instrument approach of scanning the actual data section
and noting anything that looks like a function descriptor.
This is dangerous in the sense that if there are any 3 word
structs which are not real function descriptors but just happen
to look like them, then those will be included too.
Seems unlikely though.
---------------------------------------------------------- */
if (SHOW && SHOW_SYMS_P5)
VG_(printf)("--- BEGIN kludged Phase5 (find TOC pointers) ---\n");
if (SHOW)
VG_(printf)("Phase5: actual data segment: %#lx %#lx\n",
di->data_avma, di->data_avma + di->data_size);
/* Skip obviously-missing data sections. */
if (di->data_avma != 0 && di->data_size >= sizeof(UWord)) {
/* set up for inspecting all the aligned words in the actual
data section. */
Addr tmp = di->data_avma;
while (tmp & (sizeof(UWord)-1))
tmp++;
UWord* first_data_word = (UWord*)tmp;
tmp = di->data_avma + di->data_size - sizeof(UWord);
while (tmp & (sizeof(UWord)-1))
tmp--;
UWord* last_data_word = (UWord*)tmp;
if (SHOW)
VG_(printf)("Phase5: data segment conservatively aligned %p %p\n",
first_data_word, last_data_word);
UWord* wP = first_data_word;
UWord w;
while (True) {
XCoffSym key;
Word ix_lo, ix_hi;
Bool found;
if (& wP[2] > last_data_word)
break; /* no space left for a 3-word descriptor */
w = wP[0];
if (!(w >= di->text_avma
&& w < di->text_avma + di->text_size)) {
wP++;
continue; /* entry pointer is not to text segment */
}
w = wP[1];
if (!(w >= di->data_avma && w < di->data_avma + di->data_size)) {
wP++;
if (SHOW && SHOW_SYMS_P5) {
VG_(memset)(&key, 0, sizeof(key));
key.first = wP[0];
found = VG_(lookupXA)(syms, &key, &ix_lo, &ix_hi);
if (found) {
vg_assert(ix_lo <= ix_hi);
XCoffSym* tsym = (XCoffSym*)VG_(indexXA)(syms,ix_lo);
VG_(printf)("Phase5: bad tocptc at 0x%016llx={",
(ULong)(UWord)(wP-1));
print_Name(tsym->name);
VG_(printf)(",%p}\n", (void*)w);
}
}
continue; /* r2 value does not point to data segment */
}
/* ok, so wP might be a valid fn descr. But does it point to
a text symbol we know about? Look in 'syms' to see if we
have anything for wP[0]. */
VG_(memset)(&key, 0, sizeof(key));
key.first = wP[0];
found = VG_(lookupXA)(syms, &key, &ix_lo, &ix_hi);
if (found) {
for (k = ix_lo; k <= ix_hi; k++) {
XCoffSym* tsym = (XCoffSym*)VG_(indexXA)(syms,k);
Addr r2val = wP[1];
if (tsym->r2known) {
if (tsym->r2value != r2val)
/* COMPLAIN - conflicting r2 values*/ ;
} else {
tsym->r2known = True;
tsym->r2value = r2val;
if (SHOW && SHOW_SYMS_P5) {
VG_(printf)("Phase5: found tocptr 0x%016llx for ",
(ULong)r2val);
print_Name(tsym->name);
VG_(printf)("\n");
}
}
}
}
wP++;
}
}
#endif /* defined(OFFICIAL_PHASE5) */
/* ----------------------------------------------------------
Phase 6: trivial: copy the syms out of 'syms' into the
generic debuginfo tables, and free up 'syms'.
---------------------------------------------------------- */
if (SHOW && SHOW_SYMS_P6) {
VG_(printf)("--- BEGIN Phase6 (finalise symbol info) ---\n");
VG_(printf)("--- note: shown addresses are ACTUAL VMAs ---\n");
}
for (i = 0; i < nsyms; i++) {
DiSym dis;
XCoffSym* s = (XCoffSym*)VG_(indexXA)(syms, i);
Addr addr = s->first;
UWord size = s->last + 1 - s->first;
Bool guessed_toc = False;
/* If everything worked right, the symbol should fall within the
mapped text segment. Hence .. */
Bool sane = addr >= di->text_avma
&& addr+size <= di->text_avma + di->text_size;
if (SHOW && SHOW_SYMS_P6) {
VG_(printf)("Phase6: %s %3d 0x%08lx-0x%08lx 0x%08lx ",
sane ? " " : "BAD",
i,
addr,
addr + size - 1,
s->r2known ? s->r2value : 0 );
print_Name(s->name);
VG_(printf)("\n");
}
# if defined(VGP_ppc64_aix5)
/* 64-bit kludge: if we can't find a plausible toc ptr just use
the one specified in the XCOFF auxiliary header. */
if ((!s->r2known)
&& toc_avma != 0
&& s->name.len > 8
&& 0==VG_(strncmp)(&s->name.vec[0], "._vgwZU_", 8)) {
s->r2known = True;
s->r2value = toc_avma;
guessed_toc = True;
if (SHOW && SHOW_SYMS_P6)
VG_(printf)("Phase6: assuming toc 0x%08lx for above sym\n",
s->r2value);
}
# endif
/* Actually add the symbol (finallyatlast) */
if (sane) {
UInt nlen;
dis.addr = addr;
dis.size = size;
dis.tocptr = s->r2known ? s->r2value : 0;
dis.isText = True;
dis.isIFunc = False;
vg_assert(!is_empty_Name(s->name));
nlen = s->name.len;
vg_assert(nlen > 0);
if (s->name.vec[0] == '.')
dis.name = ML_(addStr)(di, &s->name.vec[1], nlen-1 );
else
dis.name = ML_(addStr)(di, &s->name.vec[0], nlen-0 );
ML_(addSym)( di, &dis );
if (0 && s->r2known)
VG_(printf)("r2 known for %s\n",
"fixme-Name-printing(5)" /*s->name*/ );
if (guessed_toc)
VG_(message)(Vg_DebugMsg, "WARNING: assuming toc 0x%lx for %s\n",
s->r2value, dis.name);
}
}
/* Free up the XA */
VG_(deleteXA)(syms);
# undef SYM_IX
return NULL; /*success*/
}
static void show_loader_section ( struct _DebugInfo* di,
UChar* oi_start, UWord size )
{
Int i, j;
LDHDR* hdr = (LDHDR*)oi_start;
UChar* strtab_import = NULL;
UChar* strtab_other = NULL;
if (SHOW) {
VG_(printf)(" l_version %llu\n", (ULong)hdr->l_version);
VG_(printf)(" l_nsyms %lld\n", (Long)hdr->l_nsyms);
VG_(printf)(" l_nreloc %lld\n", (Long)hdr->l_nreloc);
VG_(printf)(" l_istlen (i st len) %lld\n", (Long)hdr->l_istlen);
VG_(printf)(" l_impoff (i st off) %llu\n", (ULong)hdr->l_impoff);
VG_(printf)(" l_nimpid (# imps) %llu\n", (ULong)hdr->l_nimpid);
VG_(printf)(" l_stlen (st len) %llu\n", (ULong)hdr->l_stlen);
VG_(printf)(" l_stoff (st off) %llu\n", (ULong)hdr->l_stoff);
}
if (hdr->l_istlen > 0)
strtab_import = oi_start + hdr->l_impoff;
if (hdr->l_stlen > 0)
strtab_other = oi_start + hdr->l_stoff;
if (strtab_import) {
if (SHOW)
VG_(printf)(" Loader Import String Table: %llu bytes\n",
(ULong)hdr->l_istlen);
i = 0;
j = 0;
while (1) {
if (i >= hdr->l_istlen)
break;
if (SHOW && SHOW_LD_STRTAB)
VG_(printf)(" %3d%s ", i, (j%3)==0 ? "::" : " ");
j++;
while (i < hdr->l_istlen && strtab_import[i]) {
if (SHOW && SHOW_LD_STRTAB)
VG_(printf)("%c", sanitiseChar(strtab_import[i]));
i++;
}
i++;
if (SHOW && SHOW_LD_STRTAB)
VG_(printf)("\n");
}
}
if (strtab_other) {
if (SHOW)
VG_(printf)(" Loader Other String Table: %llu bytes\n",
(ULong)hdr->l_stlen);
i = 0;
while (1) {
int len = 0;
if (i+1 >= hdr->l_stlen)
break;
len = (unsigned char)strtab_other[i];
len <<= 8;
len |= (unsigned char)strtab_other[i+1];
i += 2;
if (i >= hdr->l_stlen)
break;
if (SHOW && SHOW_LD_STRTAB)
VG_(printf)(" %2d len %2d ", i, len);
while (len >= 0 && i < hdr->l_stlen && strtab_other[i]) {
if (SHOW && SHOW_LD_STRTAB)
VG_(printf)("%c", sanitiseChar(strtab_other[i]));
i++;
len--;
}
i++;
if (SHOW && SHOW_LD_STRTAB)
VG_(printf)("\n");
}
}
if (SHOW)
VG_(printf)(" Loader Symbol Table: %lld entries\n", (Long)hdr->l_nsyms);
LDSYM* sym = (LDSYM*)(oi_start + sizeof(LDHDR));
for (i = 0; i < hdr->l_nsyms; i++) {
Name name = maybeDerefStrTab( (SYMENT*)&sym[i],
strtab_other, hdr->l_stlen );
if (SHOW && SHOW_LD_SYMTAB) {
VG_(printf)(" %2d: %016llx sec %d ty 0x%02x "
"scla 0x%02x itab %d ",
i, (ULong)sym[i].l_value, (Int)sym[i].l_scnum,
(Int)sym[i].l_smtype, (Int)sym[i].l_smclas,
(Int)sym[i].l_ifile);
print_Name(name);
VG_(printf)("\n");
}
}
# if defined(VGP_ppc32_aix5)
vg_assert(sizeof(LDREL) == 12);
# elif defined(VGP_ppc64_aix5)
vg_assert(sizeof(LDREL) == 16);
# else
# error Unknown platform
# endif
LDREL* rel = (LDREL*)(&sym[hdr->l_nsyms]);
if (SHOW)
VG_(printf)(" Loader Relocation Table: %lld entries\n",
(Long)hdr->l_nreloc);
for (i = 0; i < hdr->l_nreloc; i++) {
if (SHOW && SHOW_LD_RELTAB)
VG_(printf)(" %3d: va %016llx sym %2lld rty 0x%4x sec %2d\n",
i, (ULong)rel[i].l_vaddr, (Long)rel[i].l_symndx,
(Int)rel[i].l_rtype, (Int)rel[i].l_rsecnm);
}
if (SHOW)
VG_(printf)("\n");
}
/* Returns True on success, False on any kind of error.
The object file from which to read symbols is mapped temporarily at
[oimage .. oimage + n_oimage).
The VMA of where the relevant text section really got loaded (the
"actual VMA", _avma) is [si->text_avma .. si->text_avma
+ si->text_size).
The VMA of the associated data section really got loaded
(the "actual VMA", _avma) is [data_avma .. data_avma + data_alen).
We will need to peer at the loaded data section in order to make
sense of TOC entries, hence we need to be assured it is mapped and
readable. m_aspacemgr should have given us that assurance, in the
sense that data_avma/data_alen will be save to read in by the time
we get here.
*/
static
Bool read_xcoff_mapped_object ( struct _DebugInfo* di,
UChar* oimage, UWord n_oimage )
{
#define BAD(_msg) do { ML_(symerr)(di, True/*serious*/,_msg); \
return False; } while (0)
Int i, j;
/* The first byte after the oimage - we can't go here */
UChar* oimage_after = oimage + n_oimage;
UChar* cursor = oimage;
/* ------------ File Header ------------ */
# if defined(VGP_ppc32_aix5)
if (sizeof(FILHDR) != 20)
BAD("readxcoff.c: invalid FILHDR size (32-bit)");
# elif defined(VGP_ppc64_aix5)
if (sizeof(FILHDR) != 24)
BAD("readxcoff.c: invalid FILHDR size (64-bit)");
# else
# error "Invalid platform"
# endif
if (n_oimage < sizeof(FILHDR))
BAD("readxcoff.c: XCOFF object file header is implausibly small (2)");
FILHDR* t_filehdr = (FILHDR*)cursor;
cursor += sizeof(FILHDR);
if (SHOW) {
VG_(printf)("\nFile Header:\n");
VG_(printf)(" magic 0x%04x (%s)\n",
(UInt)t_filehdr->f_magic,
name_of_filhdr_f_magic(t_filehdr->f_magic));
}
# if defined(VGP_ppc32_aix5)
if (t_filehdr->f_magic != 0x01DF /* XCOFF32 */)
BAD("readxcoff.c: XCOFF32 object file header has invalid magic");
# elif defined(VGP_ppc64_aix5)
if (t_filehdr->f_magic != 0x01F7 /* XCOFF64 */)
BAD("readxcoff.c: XCOFF64 object file header has invalid magic");
# else
# error "Invalid platform"
# endif
if (SHOW) {
VG_(printf)(" # of sections %u\n", (UInt)t_filehdr->f_nscns);
VG_(printf)(" time/date 0x%08llx\n", (ULong)t_filehdr->f_timdat);
VG_(printf)(" symtab foffset %llu\n", (ULong)t_filehdr->f_symptr);
VG_(printf)(" # symtab entries %llu\n", (ULong)t_filehdr->f_nsyms);
VG_(printf)(" size of aux hdr %llu\n", (ULong)t_filehdr->f_opthdr);
VG_(printf)(" flags 0x%04x\n", (UInt)t_filehdr->f_flags);
if (t_filehdr->f_flags) {
VG_(printf)(" ");
if (t_filehdr->f_flags & F_RELFLG) VG_(printf)("NoRelocInfo ");
if (t_filehdr->f_flags & F_EXEC) VG_(printf)("IsExec ");
if (t_filehdr->f_flags & F_LNNO) VG_(printf)("NoLineInfo ");
if (t_filehdr->f_flags & F_LSYMS) VG_(printf)("LSYMS ");
if (t_filehdr->f_flags & F_FDPR_PROF) VG_(printf)("FDPR_PROF ");
if (t_filehdr->f_flags & F_FDPR_OPTI) VG_(printf)("FDPR_OPTI ");
if (t_filehdr->f_flags & F_DSA) VG_(printf)("LargeProc ");
# if defined(F_DEP_1)
if (t_filehdr->f_flags & F_DEP_1) VG_(printf)("DEP_1 ");
# endif
# if defined(F_VARPG)
if (t_filehdr->f_flags & F_VARPG) VG_(printf)("VARPG ");
# endif
if (t_filehdr->f_flags & F_LPTEXT) VG_(printf)("LPTEXT ");
if (t_filehdr->f_flags & F_LPDATA) VG_(printf)("LPDATA ");
if (t_filehdr->f_flags & F_DYNLOAD) VG_(printf)("Dynamic ");
if (t_filehdr->f_flags & F_SHROBJ) VG_(printf)("SharedObj ");
if (t_filehdr->f_flags & F_LOADONLY) VG_(printf)("LOADONLY ");
# if defined(F_DEP_2)
if (t_filehdr->f_flags & F_DEP_2) VG_(printf)("DEP_2 ");
# endif
VG_(printf)("\n");
}
}
/* ------------ Auxiliary Header ------------ */
# if defined(VGP_ppc32_aix5)
if (sizeof(AOUTHDR) != 72)
BAD("readxcoff.c: invalid AOUTHDR size (32-bit)");
# elif defined(VGP_ppc64_aix5)
if (sizeof(AOUTHDR) != 120)
BAD("readxcoff.c: invalid AOUTHDR size (64-bit)");
# else
# error "Invalid platform"
# endif
Int sntext_1based_if_known = -1;
Int sndata_1based_if_known = -1;
Addr data_svma = 0; /* stated VMA of data section, if known */
Bool data_svma_known = False;
Word data_bias = 0;
UWord data_alen_from_auxhdr = 0;
Addr text_svma = 0; /* stated VMA of text section, if known */
Bool text_svma_known = False;
Word text_bias = 0;
Addr toc_avma = 0; /* actual VMA of toc, if known */
Addr toc_svma = 0; /* stated VMA of toc, if known */
Addr toc_svma_known = False;
AOUTHDR* t_auxhdr = NULL;
if (t_filehdr->f_opthdr > 0) {
t_auxhdr = (AOUTHDR*)cursor;
cursor += sizeof(AOUTHDR);
sntext_1based_if_known = (Int)t_auxhdr->o_sntext;
sndata_1based_if_known = (Int)t_auxhdr->o_sndata;
if (SHOW) {
VG_(printf)("\nAuxiliary Header\n");
VG_(printf)(" magic 0x%04x (should be 0x010b)\n",
(UInt)t_auxhdr->magic);
VG_(printf)(" vstamp 0x%04x\n", (UInt)t_auxhdr->vstamp);
VG_(printf)(" tsize %lld\n", (Long)t_auxhdr->tsize);
VG_(printf)(" dsize %lld\n", (Long)t_auxhdr->dsize);
VG_(printf)(" bsize %lld\n", (Long)t_auxhdr->bsize);
VG_(printf)(" entry 0x%llx\n", (ULong)t_auxhdr->entry);
VG_(printf)(" text_start 0x%llx (stated)\n",
(ULong)t_auxhdr->text_start);
VG_(printf)(" data_start 0x%llx (stated)\n",
(ULong)t_auxhdr->data_start);
VG_(printf)(" o_toc 0x%llx\n", (ULong)t_auxhdr->o_toc);
VG_(printf)(" o_snentry %d\n", (Int)t_auxhdr->o_snentry);
VG_(printf)(" o_sntext %d\n", (Int)t_auxhdr->o_sntext);
VG_(printf)(" o_sndata %d\n", (Int)t_auxhdr->o_sndata);
VG_(printf)(" o_sntoc %d\n", (Int)t_auxhdr->o_sntoc);
VG_(printf)(" o_snloader %d\n", (Int)t_auxhdr->o_snloader);
VG_(printf)(" o_snbss %d\n", (Int)t_auxhdr->o_snbss);
VG_(printf)(" o_algntext %d\n", (Int)t_auxhdr->o_algntext);
VG_(printf)(" o_algndata %d\n", (Int)t_auxhdr->o_algndata);
VG_(printf)(" o_modtype \"%c%c\"\n",
(UChar)t_auxhdr->o_modtype[0],
(UChar)t_auxhdr->o_modtype[1] );
VG_(printf)(" o_cpuflag 0x%02x\n", (UInt)t_auxhdr->o_cpuflag);
VG_(printf)(" o_cputype 0x%02x\n", (UInt)t_auxhdr->o_cputype);
VG_(printf)(" o_maxstack %llu\n", (ULong)t_auxhdr->o_maxstack);
VG_(printf)(" o_maxdata %llu\n", (ULong)t_auxhdr->o_maxdata);
VG_(printf)(" o_debugger %u\n", t_auxhdr->o_debugger);
/* printf(" o_textpsize %u\n", (UInt)t_auxhdr->o_textpsize); */
/* printf(" o_stackpsize %u\n", (UInt)t_auxhdr->o_stackpsize); */
}
text_svma = t_auxhdr->text_start;
text_svma_known = True;
data_svma = t_auxhdr->data_start;
data_svma_known = True;
/* The auxhdr may claim the data section is longer than
data_alen, so note the auxhdr-claimed size too. */
data_alen_from_auxhdr = (UWord)t_auxhdr->dsize;
if (t_auxhdr->o_sntoc == t_auxhdr->o_sndata) {
toc_svma = (Addr)t_auxhdr->o_toc;
toc_svma_known = True;
}
}
/* ------------ Section Headers ------------ */
# if defined(VGP_ppc32_aix5)
if (sizeof(SCNHDR) != 40)
BAD("readxcoff.c: invalid SCNHDR size (32-bit)");
# elif defined(VGP_ppc64_aix5)
if (sizeof(SCNHDR) != 72)
BAD("readxcoff.c: invalid SCNHDR size (64-bit)");
# else
# error "Invalid platform"
# endif
SCNHDR* t_scnhdr = (SCNHDR*)cursor;
if (SHOW)
VG_(printf)("\nSection Headers: %d entries\n", t_filehdr->f_nscns);
/* Where the stabs strings are in the oimage */
UChar* oi_debug = NULL;
UWord oi_n_debug = 0;
/* Where the line number entries for the text section are
in the oimage */
UChar* oi_lnos = NULL;
UWord oi_nent_lnos = 0; /* number of records */
for (i = 0; i < t_filehdr->f_nscns; i++) {
UChar sname_safe[9];
for (j = 0; j < 8; j++)
sname_safe[j] = t_scnhdr[i].s_name[j];
sname_safe[8] = 0;
if (SHOW) {
VG_(printf)(" --- #%d ---\n", i);
VG_(printf)(" s_name %s\n", sname_safe);
VG_(printf)(" s_paddr 0x%llx\n", (ULong)t_scnhdr[i].s_paddr);
VG_(printf)(" s_vaddr 0x%llx\n", (ULong)t_scnhdr[i].s_vaddr);
VG_(printf)(" s_size %lld\n", (Long)t_scnhdr[i].s_size);
VG_(printf)(" s_scnptr %lld\n", (Long)t_scnhdr[i].s_scnptr);
VG_(printf)(" s_relptr %lld\n", (Long)t_scnhdr[i].s_relptr);
VG_(printf)(" s_lnnoptr %lld\n", (Long)t_scnhdr[i].s_lnnoptr);
VG_(printf)(" s_nreloc %llu\n", (ULong)t_scnhdr[i].s_nreloc);
VG_(printf)(" s_nlnno %llu\n", (ULong)t_scnhdr[i].s_nlnno);
VG_(printf)(" s_flags 0x%llx (%s)\n",
(ULong)t_scnhdr[i].s_flags,
name_of_scnhdr_s_flags(t_scnhdr[i].s_flags));
}
/* find the stabs strings */
if (t_scnhdr[i].s_flags == STYP_DEBUG) {
oi_debug = oimage;
oi_debug += (UWord)t_scnhdr[i].s_scnptr;
oi_n_debug = (UWord)t_scnhdr[i].s_size;
}
/* find the line number entries for the text section */
if (t_scnhdr[i].s_flags == STYP_TEXT && t_scnhdr[i].s_lnnoptr > 0) {
oi_lnos = oimage;
oi_lnos += (UWord)t_scnhdr[i].s_lnnoptr;
oi_nent_lnos = (UWord)t_scnhdr[i].s_nlnno;
/* XCOFF is clearly the result of years of kludgery, and
here's one place it shows. .s_nlnno is a 16-bit field, so
if there are 65535 or more entries, they can't be
represented here. In that case, the real number is stored
in a 32-bit field of a an "overflow section header" - a
dummy section header which has no purpose other than to
hold the correct count. And then this kludge applies to
XCOFF32, not XCOFF64. */
if (t_scnhdr[i].s_nlnno == 0xFFFF
|| t_scnhdr[i].s_nreloc == 0xFFFF) {
/* have to test both fields, according to the docs */
/* find the relevant overflow header */
for (j = 0; j < t_filehdr->f_nscns; j++)
if (t_scnhdr[j].s_flags == STYP_OVRFLO
&& t_scnhdr[j].s_nlnno == i+1 /* ref to correct scn? */
&& t_scnhdr[j].s_nreloc == i+1 /* also must check this */)
break;
vg_assert(j >= 0 && j <= t_filehdr->f_nscns);
if (j == t_filehdr->f_nscns)
/* Hmm. We're hosed. Give up. */
BAD("readxcoff.c: can't find a required "
"overflow section header");
/* finally, we have the real count. */
oi_nent_lnos = (UWord)t_scnhdr[j].s_vaddr;
}
}
cursor += sizeof(SCNHDR);
}
if (SHOW) {
VG_(printf)("\n debug image (stabs strings) at %p size %ld bytes\n",
oi_debug, oi_n_debug);
VG_(printf)(" line number info at %p with %ld entries\n",
oi_lnos, oi_nent_lnos);
}
/* ------------ establish Text/data biases ------------ */
/* Calculate, into text_bias, the offset that has to be added to
symbol table values (stated VMAs) so as to convert them to correct
addresses in the running image (actual VMAs). I can't find any
documentation for this, so the following is determined empirically.
There appear to be two classes of loaded object:
.o files. These have a stated text VMA of zero, and so their
symbols start from zero and work upwards. In that case the
bias is precisely the offset where the text section is
loaded (si->text_avma), that is, the actual text VMA.
Except -- cryptically -- /usr/include/sys/ldr.h says that the
ld_info.ldinfo_textorg field is "start of loaded program
image (includes the XCOFF headers)". And so to get the
correct text bias it is necessary (determined empirically) to
add on the file offset for the text section. I guess this
means that (1) it is assumed the text section is always the
first in the file, and (2) in this case the stated text VMA
is where the start of the file is mapped, not the start of
the text section.
Last verified 24 May 06.
.so files, and executables. These have a non-zero stated text
VMA, for example 0x10000150. They appear to get loaded at some
arbitrary address (actual VMA) which is always a whole number
of pages, eg 0x20002000, and in such a way that the offset is
a whole number of pages. So in this example the offset (bias)
would be 0x20002000 - round_to_page_base(0x10000150).
*/
if (text_svma_known) {
#if 0
if (text_svma == 0) {
text_bias = di->text_avma;
if (sntext_1based_if_known >= 1
&& sntext_1based_if_known <= t_filehdr->f_nscns)
text_bias += t_scnhdr[sntext_1based_if_known - 1].s_scnptr;
} else {
text_bias = di->text_avma - VG_PGROUNDDN(text_svma);
}
#else
text_bias = di->text_avma - text_svma;
if (sntext_1based_if_known >= 1
&& sntext_1based_if_known <= t_filehdr->f_nscns)
text_bias += t_scnhdr[sntext_1based_if_known - 1].s_scnptr;
#endif
if (SHOW)
VG_(printf)(" text section: stated vma 0x%lx, "
"actual vma 0x%lx, bias 0x%lx\n",
text_svma, di->text_avma, text_bias);
} else {
text_bias = 0;
if (SHOW)
VG_(printf)(" text section: svma UNKNOWN, bias UNKNOWN\n");
}
if (data_svma_known) {
data_bias = di->data_avma - data_svma;
if (SHOW)
VG_(printf)(" data section: stated vma 0x%lx, "
"actual vma 0x%lx, bias 0x%lx\n",
data_svma, di->data_avma, data_bias);
} else {
data_bias = 0;
if (SHOW)
VG_(printf)(" data section: svma UNKNOWN, bias UNKNOWN\n");
}
if (toc_svma_known) {
toc_avma = toc_svma + data_bias;
if (SHOW)
VG_(printf)(" toc: stated vma 0x%lx, actual vma 0x%lx\n",
toc_svma, toc_avma);
} else {
if (SHOW)
VG_(printf)(" toc: svma UNKNOWN\n");
toc_avma = 0;
}
/* ------------ Section Data ------------ */
for (i = 0; i < t_filehdr->f_nscns; i++) {
if (SHOW)
VG_(printf)("\nSection Data (sec %d, \"%s\")\n",
i, name_of_scnhdr_s_flags(t_scnhdr[i].s_flags) );
switch (t_scnhdr[i].s_flags & 0xFFFF) {
case STYP_LOADER:
show_loader_section( di, oimage + t_scnhdr[i].s_scnptr,
t_scnhdr[i].s_size );
break;
default:
if (SHOW)
VG_(printf)(" Not handled yet\n");
break;
}
}
/* ------------ establish String Table ------------ */
/* This is after the symbol table, if it exists at all. */
/* This is a bit of a hack. The easy way to find the string table
is assume it immediately follows the symbol table. That doesn't
work if there is no symbol table; but on the other hand if there
is no symbol table then there isn't much point in carrying on.
Hence, if there is no symbol table we just give up here and
claim to have successfully loaded zero symbols. */
if (t_filehdr->f_nsyms == 0) {
if (SHOW)
VG_(printf)("Object contains no symbols. Stopping here.\n");
return True;
}
cursor = oimage;
cursor += t_filehdr->f_symptr; /* symtab start */
cursor += SYMESZ * t_filehdr->f_nsyms; /* strtab start */
/* Does this fall inside the file image? The first 4 bytes is the
string table size, so we need to be able to see at least
them. */
UChar* oi_strtab = NULL;
UWord oi_n_strtab = 0;
if (cursor + 4 <= oimage_after) {
oi_strtab = cursor;
oi_n_strtab = (UWord)( *(UInt*)oi_strtab );
if (0) {
VG_(printf)("oimage %p\n", oimage);
VG_(printf)("oimage_after %p\n", oimage_after);
VG_(printf)("cursor %p\n", cursor);
}
if (oi_strtab + oi_n_strtab > oimage_after)
BAD("readxcoff.c: string table exceeds image end");
}
/* ------------ Symbol Table ------------ */
if (SHOW)
VG_(printf)("\nSymbol Table: %llu entries\n", (ULong)t_filehdr->f_nsyms);
cursor = oimage;
cursor += t_filehdr->f_symptr;
HChar* badness = read_symbol_table(
di,
cursor, t_filehdr->f_nsyms,
oi_strtab, oi_n_strtab,
oi_debug, oi_n_debug,
oi_lnos, oi_nent_lnos,
sntext_1based_if_known, sndata_1based_if_known,
data_alen_from_auxhdr,
toc_avma,
text_bias, data_bias
);
if (badness)
BAD(badness);
/* cursor not used after this point */
/* ------------ String Table ------------ */
if (oi_strtab) {
if (SHOW)
VG_(printf)("\nString Table: %lu bytes\n", oi_n_strtab);
i = 4;
while (1) {
if (i >= oi_n_strtab)
break;
if (SHOW && SHOW_STRTAB)
VG_(printf)(" %5d ", i);
while (i < oi_n_strtab && oi_strtab[i]) {
if (SHOW && SHOW_STRTAB)
VG_(printf)("%c", sanitiseChar(oi_strtab[i]));
i++;
}
i++;
if (SHOW && SHOW_STRTAB)
VG_(printf)("\n");
}
}
if (SHOW)
VG_(printf)("\n");
return True;
#undef BAD
}
static ULong ascii_to_ULong ( void* vbuf, Int nbuf )
{
Int i;
UChar c;
UChar* buf = (UChar*)vbuf;
ULong n = 0;
for (i = 0; i < nbuf; i++) {
c = buf[i];
if (c >= '0' && c <= '9')
n = 10ULL * n + (ULong)(c - '0');
}
return n;
}
/* Returns True on success, False if any kind of problem. */
static
Bool read_xcoff_o_or_a ( /*MOD*/struct _DebugInfo* di,
HChar* a_name, HChar* o_name )
{
UChar* image = NULL;
Word n_image = 0;
Bool ok;
Int i;
SysRes sr, fd;
struct vg_stat stat_buf;
vg_assert(o_name);
if (a_name == NULL) {
/* This is just a plain XCOFF object file. */
sr = VG_(stat)( o_name, &stat_buf );
if (sr.isError) {
ML_(symerr)(di, True, "can't stat XCOFF object file");
return False;
}
n_image = stat_buf.st_size;
if (SHOW && SHOW_AR_DETAILS)
VG_(printf)("XCOFF object file size %ld\n", n_image);
if (n_image <= 0) {
ML_(symerr)(di, True, "implausible XCOFF object file size");
return False;
}
fd = VG_(open)( o_name, VKI_O_RDONLY, 0 );
if (fd.isError) {
ML_(symerr)(di, True, "can't open XCOFF object file");
return False;
}
sr = VG_(am_mmap_file_float_valgrind)(n_image, VKI_PROT_READ,
fd.res, 0);
VG_(close)(fd.res);
if (sr.isError) {
ML_(symerr)(di, True, "can't mmap XCOFF object file");
return False;
}
image = (UChar*)sr.res;
ok = read_xcoff_mapped_object( di, image, n_image );
VG_(am_munmap_valgrind)( (Addr)image, n_image);
/* assert OK */
return ok;
} else {
/* It's an XCOFF .a file ("ar file format, large"). Map the
whole thing in, find the member specified by O_NAME, and read
symbols from that. */
sr = VG_(stat)( a_name, &stat_buf );
if (sr.isError) {
ML_(symerr)(di, True, "can't stat XCOFF archive file");
return False;
}
n_image = stat_buf.st_size;
if (SHOW && SHOW_AR_DETAILS)
VG_(printf)("XCOFF archive file size %ld\n", n_image);
if (n_image <= 0) {
ML_(symerr)(di, True, "implausible XCOFF archive file size");
return False;
}
fd = VG_(open)( a_name, VKI_O_RDONLY, 0 );
if (fd.isError) {
ML_(symerr)(di, True, "can't open XCOFF archive file");
return False;
}
sr = VG_(am_mmap_file_float_valgrind)(n_image, VKI_PROT_READ,
fd.res, 0);
VG_(close)(fd.res);
if (sr.isError) {
ML_(symerr)(di, True, "can't mmap XCOFF archive file");
return False;
}
image = (UChar*)sr.res;
ok = False;
/* Right. Let's go looking for the requested object. First,
peer at the archive's fixed header. */
if (n_image < sizeof(FL_HDR)) {
ML_(symerr)(di, True, "XCOFF archive too small for fixed header");
goto done;
}
FL_HDR* fl_hdr = (FL_HDR*)image;
if (SHOW && SHOW_AR_DETAILS) {
VG_(printf)("magic: %s\n", fl_hdr->fl_magic);
VG_(printf)("memoff: %s\n", fl_hdr->fl_memoff);
VG_(printf)("gstoff: %s\n", fl_hdr->fl_gstoff);
VG_(printf)("gst64off: %s\n", fl_hdr->fl_gst64off);
}
{ UChar* s = (UChar*)&fl_hdr->fl_magic;
if (s[0] == '<' && s[1] == 'b' && s[2] == 'i'
&& s[3] == 'g' && s[4] == 'a' && s[5] == 'f'
&& s[6] == '>' && s[7] == '\n') {
/* ok */
} else {
ML_(symerr)(di, True,
"Is not XCOFF 'big'-variant .a format archive");
goto done;
}
}
/* Get a pointer to the member table entry. */
UChar* mtabC = image + ascii_to_ULong(&fl_hdr->fl_memoff,
sizeof(fl_hdr->fl_memoff));
AR_HDR* mt_hdr = (AR_HDR*)mtabC;
if (mtabC < image || mtabC + sizeof(AR_HDR) > image + n_image) {
ML_(symerr)(di, True,
"XCOFF archive member table header exceeds image");
goto done;
}
/* should be: backquote newline */
if (mt_hdr->_ar_name.ar_name[0] != 0x60 /* backquote */
|| mt_hdr->_ar_name.ar_name[1] != 0x0A /* \n */) {
ML_(symerr)(di, True,
"XCOFF archive member table header is invalid");
goto done;
}
if (SHOW) {
VG_(printf)("member table ar_size = %lld\n",
ascii_to_ULong(&mt_hdr->ar_size,20));
VG_(printf)("member table ar_namlen = %lld\n",
ascii_to_ULong(&mt_hdr->ar_namlen,4));
}
if (mtabC < image
|| mtabC + sizeof(AR_HDR)
+ ascii_to_ULong(&mt_hdr->ar_size, 20)
> image + n_image) {
ML_(symerr)(di, True, "XCOFF archive member table exceeds image");
goto done;
}
UChar* data = mtabC + sizeof(AR_HDR)
+ ascii_to_ULong(&mt_hdr->ar_namlen,4);
/* ALIGN */
if ( ((UWord)data) & 1 ) data++;
if (SHOW)
VG_(printf)("member table data = %p\n", data);
UInt nmembers = ascii_to_ULong(data, 20);
if (SHOW)
VG_(printf)("member table contains %d entries\n", nmembers);
for (i = 0; i < nmembers; i++) {
if (SHOW && SHOW_AR_DETAILS)
VG_(printf)(" %d has off %d\n",
i, (Int)ascii_to_ULong(data + 20 + 20*i, 20));
}
UChar* p = data + 20 + 20*nmembers;
for (i = 0; i < nmembers; i++) {
if (0 != VG_(strcmp)(p, o_name))
goto move_on;
UInt objoff = ascii_to_ULong(data + 20 + 20*i, 20);
if (SHOW && SHOW_AR_DETAILS)
VG_(printf)("got offset = %u\n", objoff);
vg_assert(ok == False);
/* Sanity check the selected member */
UChar* o_hdrC = image + objoff;
if (o_hdrC + sizeof(AR_HDR) >= image + n_image) {
ML_(symerr)(di, True,
"XCOFF archive member header exceeds image");
goto done;
}
AR_HDR* o_hdr = (AR_HDR*)o_hdrC;
UWord o_size = (UWord)ascii_to_ULong(&o_hdr->ar_size, 20);
UChar* o_data = o_hdrC + sizeof(AR_HDR)
+ (UWord)ascii_to_ULong(&o_hdr->ar_namlen,4);
/* ALIGN */
if ( ((UWord)o_data) & 1 ) o_data++;
if (SHOW)
VG_(printf)("member data = %p, size = %ld\n", o_data, o_size);
if (!(o_data >= image && o_data + o_size <= image + n_image)) {
ML_(symerr)(di, True,
"XCOFF archive member exceeds image");
goto done;
}
if (o_size < sizeof(FILHDR)) {
ML_(symerr)(di, True,
"XCOFF object file header is implausibly small (1)");
goto done;
}
/* It's the right name, but need to also check the magic
number, since some archives contain both a 32-bit and
64-bit version of the same object. */
FILHDR* t_filhdr = (FILHDR*)o_data;
# if defined(VGP_ppc32_aix5)
if (t_filhdr->f_magic == 0x01F7 /* XCOFF64 */) {
if (0)
VG_(printf)("Skipping 64-bit archive on 32-bit platform\n");
goto move_on;
}
# elif defined(VGP_ppc64_aix5)
if (t_filhdr->f_magic == 0x01DF /* XCOFF32 */) {
if (0)
VG_(printf)("Skipping 32-bit archive on 64-bit platform\n");
goto move_on;
}
# endif
if (SHOW && SHOW_AR_DETAILS)
VG_(printf)("\nimage: %p-%p object: %p-%p\n\n",
image, image+n_image-1, o_data, o_data+o_size-1);
ok = read_xcoff_mapped_object( di, o_data, o_size );
goto done;
vg_assert(0);
/* NOTREACHED */
move_on:
while (*p) {
if (SHOW && SHOW_AR_DETAILS)
VG_(printf)("%c", *p);
p++;
}
if (SHOW && SHOW_AR_DETAILS)
VG_(printf)("\n");
p++;
}
vg_assert(i == nmembers);
ML_(symerr)(di, True, "can't find object in XCOFF archive file");
done:
if (image) {
VG_(am_munmap_valgrind)( (Addr)image, n_image );
/* assert munmap succeeded */
}
return ok;
}
}
/* Main entry point for XCOFF reading. The following di fields must
be filled in by the caller:
filename
memname (optional)
text_avma, text_size
data_avma, data_size
and all other fields should be zeroed.
*/
Bool ML_(read_xcoff_debug_info) ( struct _DebugInfo* di,
Bool is_mainexe )
{
Bool ok;
if (VG_(clo_verbosity) > 1 || VG_(clo_trace_redir)) {
if (di->memname) {
VG_(message)(Vg_DebugMsg, "Reading syms from %s(%s) (%#lx)\n",
di->filename, di->memname, di->text_avma);
} else {
VG_(message)(Vg_DebugMsg, "Reading syms from %s (%#lx)\n",
di->filename, di->text_avma);
}
}
if (SHOW) {
VG_(printf)("------------------- BEGIN read xcoff ------------------\n");
VG_(printf)("--- file: %s\n", di->filename);
VG_(printf)("--- mem: %s\n", di->memname ? di->memname
: (UChar*)"(none)" );
VG_(printf)("--- t actual vma: %#lx\n", di->text_avma);
VG_(printf)("--- t actual len: %ld\n", di->text_size);
VG_(printf)("--- d actual vma: %#lx\n", di->data_avma);
VG_(printf)("--- d actual len: %ld\n", di->data_size);
}
if (di->memname) {
/* XCOFF .a file. di->filename is its name, di->memname is the
name of the required .o within it. */
ok = read_xcoff_o_or_a( di, di->filename, di->memname );
} else {
/* no archive member name, so di->filename is an XCOFF object */
ok = read_xcoff_o_or_a( di, NULL, di->filename );
}
di->soname = NULL;
if (ok) {
if (is_mainexe) {
di->soname = "NONE";
} else {
UChar* p = VG_(strrchr)(di->filename, '/');
p = p ? p+1 : di->filename;
/* p points at the main filename */
if (di->memname) {
/* set the soname to "archive.a(member.o)" */
Int nbytes = VG_(strlen)(p) + 1 + VG_(strlen)(di->memname) + 1 + 1;
UChar* so = ML_(dinfo_zalloc)("di.readxcoff.rxdi.1", nbytes);
vg_assert(so);
VG_(sprintf)(so, "%s(%s)", p, di->memname);
vg_assert(VG_(strlen)(so) == nbytes-1);
di->soname = so;
} else {
/* no member name, hence soname = "archive.a" */
di->soname = ML_(dinfo_strdup)("di.readxcoff.rxdi.2", p);
}
}
if (SHOW)
VG_(printf)("Setting soname to %s\n", di->soname);
}
if (SHOW)
VG_(printf)("------------------- END read xcoff ------------------\n\n");
return ok;
}
#endif // defined(VGO_aix5)
/*--------------------------------------------------------------------*/
/*--- end ---*/
/*--------------------------------------------------------------------*/