| |
| /*--------------------------------------------------------------------*/ |
| /*--- 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. |
| */ |
| |
| /* 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; |
| 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", |
| 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)", |
| di->filename, di->memname, di->text_avma); |
| } else { |
| VG_(message)(Vg_DebugMsg, "Reading syms from %s (%#lx)", |
| 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; |
| } |
| |
| /*--------------------------------------------------------------------*/ |
| /*--- end ---*/ |
| /*--------------------------------------------------------------------*/ |