sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1 | |
| 2 | /*--------------------------------------------------------------------*/ |
| 3 | /*--- Top level management of symbols and debugging information. ---*/ |
| 4 | /*--- debuginfo.c ---*/ |
| 5 | /*--------------------------------------------------------------------*/ |
| 6 | |
| 7 | /* |
| 8 | This file is part of Valgrind, a dynamic binary instrumentation |
| 9 | framework. |
| 10 | |
sewardj | 0f157dd | 2013-10-18 14:27:36 +0000 | [diff] [blame] | 11 | Copyright (C) 2000-2013 Julian Seward |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 12 | jseward@acm.org |
| 13 | |
| 14 | This program is free software; you can redistribute it and/or |
| 15 | modify it under the terms of the GNU General Public License as |
| 16 | published by the Free Software Foundation; either version 2 of the |
| 17 | License, or (at your option) any later version. |
| 18 | |
| 19 | This program is distributed in the hope that it will be useful, but |
| 20 | WITHOUT ANY WARRANTY; without even the implied warranty of |
| 21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 22 | General Public License for more details. |
| 23 | |
| 24 | You should have received a copy of the GNU General Public License |
| 25 | along with this program; if not, write to the Free Software |
| 26 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 27 | 02111-1307, USA. |
| 28 | |
| 29 | The GNU General Public License is contained in the file COPYING. |
| 30 | */ |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 31 | |
| 32 | #include "pub_core_basics.h" |
sewardj | 4cfea4f | 2006-10-14 19:26:10 +0000 | [diff] [blame] | 33 | #include "pub_core_vki.h" |
sewardj | 6c591e1 | 2011-04-11 16:17:51 +0000 | [diff] [blame] | 34 | #include "pub_core_libcsetjmp.h" // to keep _threadstate.h happy |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 35 | #include "pub_core_threadstate.h" |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 36 | #include "pub_core_debuginfo.h" /* self */ |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 37 | #include "pub_core_demangle.h" |
| 38 | #include "pub_core_libcbase.h" |
| 39 | #include "pub_core_libcassert.h" |
| 40 | #include "pub_core_libcprint.h" |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 41 | #include "pub_core_libcfile.h" |
sewardj | 13ac96d | 2010-02-12 12:12:39 +0000 | [diff] [blame] | 42 | #include "pub_core_libcproc.h" // VG_(getenv) |
sewardj | d7a02db | 2008-12-12 08:07:49 +0000 | [diff] [blame] | 43 | #include "pub_core_seqmatch.h" |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 44 | #include "pub_core_options.h" |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 45 | #include "pub_core_redir.h" // VG_(redir_notify_{new,delete}_SegInfo) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 46 | #include "pub_core_aspacemgr.h" |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 47 | #include "pub_core_machine.h" // VG_PLAT_USES_PPCTOC |
sewardj | 72427fa | 2007-02-27 16:52:23 +0000 | [diff] [blame] | 48 | #include "pub_core_xarray.h" |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 49 | #include "pub_core_oset.h" |
njn | 6882443 | 2009-02-10 06:48:00 +0000 | [diff] [blame] | 50 | #include "pub_core_stacktrace.h" // VG_(get_StackTrace) XXX: circular dependency |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 51 | #include "pub_core_ume.h" |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 52 | |
| 53 | #include "priv_misc.h" /* dinfo_zalloc/free */ |
sewardj | 5d616df | 2013-07-02 08:07:15 +0000 | [diff] [blame] | 54 | #include "priv_image.h" |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 55 | #include "priv_d3basics.h" /* ML_(pp_GX) */ |
| 56 | #include "priv_tytypes.h" |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 57 | #include "priv_storage.h" |
| 58 | #include "priv_readdwarf.h" |
| 59 | #include "priv_readstabs.h" |
sewardj | 4ee4f98 | 2006-10-17 01:37:10 +0000 | [diff] [blame] | 60 | #if defined(VGO_linux) |
| 61 | # include "priv_readelf.h" |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 62 | # include "priv_readdwarf3.h" |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 63 | # include "priv_readpdb.h" |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 64 | #elif defined(VGO_darwin) |
| 65 | # include "priv_readmacho.h" |
| 66 | # include "priv_readpdb.h" |
sewardj | 4ee4f98 | 2006-10-17 01:37:10 +0000 | [diff] [blame] | 67 | #endif |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 68 | |
sewardj | c6d3f6f | 2007-01-11 19:42:11 +0000 | [diff] [blame] | 69 | |
| 70 | /*------------------------------------------------------------*/ |
| 71 | /*--- The _svma / _avma / _image / _bias naming scheme ---*/ |
| 72 | /*------------------------------------------------------------*/ |
| 73 | |
| 74 | /* JRS 11 Jan 07: I find the different kinds of addresses involved in |
| 75 | debuginfo reading confusing. Recently I arrived at some |
| 76 | terminology which makes it clearer (to me, at least). There are 3 |
| 77 | kinds of address used in the debuginfo reading process: |
| 78 | |
| 79 | stated VMAs - the address where (eg) a .so says a symbol is, that |
| 80 | is, what it tells you if you consider the .so in |
| 81 | isolation |
| 82 | |
| 83 | actual VMAs - the address where (eg) said symbol really wound up |
| 84 | after the .so was mapped into memory |
| 85 | |
| 86 | image addresses - pointers into the copy of the .so (etc) |
| 87 | transiently mmaped aboard whilst we read its info |
| 88 | |
| 89 | Additionally I use the term 'bias' to denote the difference |
| 90 | between stated and actual VMAs for a given entity. |
| 91 | |
| 92 | This terminology is not used consistently, but a start has been |
| 93 | made. readelf.c and the call-frame info reader in readdwarf.c now |
| 94 | use it. Specifically, various variables and structure fields have |
sewardj | f767d96 | 2007-02-12 17:47:14 +0000 | [diff] [blame] | 95 | been annotated with _avma / _svma / _image / _bias. In places _img |
| 96 | is used instead of _image for the sake of brevity. |
sewardj | c6d3f6f | 2007-01-11 19:42:11 +0000 | [diff] [blame] | 97 | */ |
| 98 | |
| 99 | |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 100 | /*------------------------------------------------------------*/ |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 101 | /*--- fwdses ---*/ |
| 102 | /*------------------------------------------------------------*/ |
| 103 | |
philippe | 20ede3a | 2013-01-30 23:18:11 +0000 | [diff] [blame] | 104 | static UInt CF_info_generation = 0; |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 105 | static void cfsi_cache__invalidate ( void ); |
| 106 | |
| 107 | |
| 108 | /*------------------------------------------------------------*/ |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 109 | /*--- Root structure ---*/ |
| 110 | /*------------------------------------------------------------*/ |
| 111 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 112 | /* The root structure for the entire debug info system. It is a |
| 113 | linked list of DebugInfos. */ |
| 114 | static DebugInfo* debugInfo_list = NULL; |
| 115 | |
| 116 | |
| 117 | /* Find 'di' in the debugInfo_list and move it one step closer the the |
| 118 | front of the list, so as to make subsequent searches for it |
| 119 | cheaper. When used in a controlled way, makes a major improvement |
| 120 | in some DebugInfo-search-intensive situations, most notably stack |
| 121 | unwinding on amd64-linux. */ |
| 122 | static void move_DebugInfo_one_step_forward ( DebugInfo* di ) |
| 123 | { |
| 124 | DebugInfo *di0, *di1, *di2; |
| 125 | if (di == debugInfo_list) |
| 126 | return; /* already at head of list */ |
| 127 | vg_assert(di != NULL); |
| 128 | di0 = debugInfo_list; |
| 129 | di1 = NULL; |
| 130 | di2 = NULL; |
| 131 | while (True) { |
| 132 | if (di0 == NULL || di0 == di) break; |
| 133 | di2 = di1; |
| 134 | di1 = di0; |
| 135 | di0 = di0->next; |
| 136 | } |
| 137 | vg_assert(di0 == di); |
| 138 | if (di0 != NULL && di1 != NULL && di2 != NULL) { |
| 139 | DebugInfo* tmp; |
| 140 | /* di0 points to di, di1 to its predecessor, and di2 to di1's |
| 141 | predecessor. Swap di0 and di1, that is, move di0 one step |
| 142 | closer to the start of the list. */ |
| 143 | vg_assert(di2->next == di1); |
| 144 | vg_assert(di1->next == di0); |
| 145 | tmp = di0->next; |
| 146 | di2->next = di0; |
| 147 | di0->next = di1; |
| 148 | di1->next = tmp; |
| 149 | } |
| 150 | else |
| 151 | if (di0 != NULL && di1 != NULL && di2 == NULL) { |
| 152 | /* it's second in the list. */ |
| 153 | vg_assert(debugInfo_list == di1); |
| 154 | vg_assert(di1->next == di0); |
| 155 | di1->next = di0->next; |
| 156 | di0->next = di1; |
| 157 | debugInfo_list = di0; |
| 158 | } |
| 159 | } |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 160 | |
| 161 | |
| 162 | /*------------------------------------------------------------*/ |
| 163 | /*--- Notification (acquire/discard) helpers ---*/ |
| 164 | /*------------------------------------------------------------*/ |
| 165 | |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 166 | /* Gives out unique abstract handles for allocated DebugInfos. See |
| 167 | comment in priv_storage.h, declaration of struct _DebugInfo, for |
| 168 | details. */ |
| 169 | static ULong handle_counter = 1; |
| 170 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 171 | /* Allocate and zero out a new DebugInfo record. */ |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 172 | static |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 173 | DebugInfo* alloc_DebugInfo( const HChar* filename ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 174 | { |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 175 | Bool traceme; |
| 176 | DebugInfo* di; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 177 | |
sewardj | f767d96 | 2007-02-12 17:47:14 +0000 | [diff] [blame] | 178 | vg_assert(filename); |
| 179 | |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 180 | di = ML_(dinfo_zalloc)("di.debuginfo.aDI.1", sizeof(DebugInfo)); |
sewardj | a5acac3 | 2011-09-20 21:59:50 +0000 | [diff] [blame] | 181 | di->handle = handle_counter++; |
| 182 | di->fsm.filename = ML_(dinfo_strdup)("di.debuginfo.aDI.2", filename); |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 183 | di->fsm.maps = VG_(newXA)( |
| 184 | ML_(dinfo_zalloc), "di.debuginfo.aDI.3", |
| 185 | ML_(dinfo_free), sizeof(struct _DebugInfoMapping)); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 186 | |
sewardj | 452e89a | 2011-05-30 10:18:59 +0000 | [diff] [blame] | 187 | /* Everything else -- pointers, sizes, arrays -- is zeroed by |
| 188 | ML_(dinfo_zalloc). Now set up the debugging-output flags. */ |
sewardj | f767d96 | 2007-02-12 17:47:14 +0000 | [diff] [blame] | 189 | traceme |
sewardj | 0f4126c | 2011-09-20 16:10:59 +0000 | [diff] [blame] | 190 | = VG_(string_match)( VG_(clo_trace_symtab_patt), filename ); |
sewardj | f767d96 | 2007-02-12 17:47:14 +0000 | [diff] [blame] | 191 | if (traceme) { |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 192 | di->trace_symtab = VG_(clo_trace_symtab); |
| 193 | di->trace_cfi = VG_(clo_trace_cfi); |
| 194 | di->ddump_syms = VG_(clo_debug_dump_syms); |
| 195 | di->ddump_line = VG_(clo_debug_dump_line); |
| 196 | di->ddump_frames = VG_(clo_debug_dump_frames); |
sewardj | f767d96 | 2007-02-12 17:47:14 +0000 | [diff] [blame] | 197 | } |
| 198 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 199 | return di; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 200 | } |
| 201 | |
| 202 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 203 | /* Free a DebugInfo, and also all the stuff hanging off it. */ |
| 204 | static void free_DebugInfo ( DebugInfo* di ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 205 | { |
sewardj | 59a2d18 | 2008-08-22 23:18:02 +0000 | [diff] [blame] | 206 | Word i, j, n; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 207 | struct strchunk *chunk, *next; |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 208 | TyEnt* ent; |
sewardj | 59a2d18 | 2008-08-22 23:18:02 +0000 | [diff] [blame] | 209 | GExpr* gexpr; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 210 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 211 | vg_assert(di != NULL); |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 212 | if (di->fsm.maps) VG_(deleteXA)(di->fsm.maps); |
sewardj | a5acac3 | 2011-09-20 21:59:50 +0000 | [diff] [blame] | 213 | if (di->fsm.filename) ML_(dinfo_free)(di->fsm.filename); |
philippe | f1e1aa6 | 2012-03-12 22:06:57 +0000 | [diff] [blame] | 214 | if (di->soname) ML_(dinfo_free)(di->soname); |
sewardj | a5acac3 | 2011-09-20 21:59:50 +0000 | [diff] [blame] | 215 | if (di->loctab) ML_(dinfo_free)(di->loctab); |
| 216 | if (di->cfsi) ML_(dinfo_free)(di->cfsi); |
| 217 | if (di->cfsi_exprs) VG_(deleteXA)(di->cfsi_exprs); |
| 218 | if (di->fpo) ML_(dinfo_free)(di->fpo); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 219 | |
sewardj | a5cace0 | 2011-08-15 09:42:34 +0000 | [diff] [blame] | 220 | if (di->symtab) { |
| 221 | /* We have to visit all the entries so as to free up any |
| 222 | sec_names arrays that might exist. */ |
| 223 | n = di->symtab_used; |
| 224 | for (i = 0; i < n; i++) { |
| 225 | DiSym* sym = &di->symtab[i]; |
| 226 | if (sym->sec_names) |
| 227 | ML_(dinfo_free)(sym->sec_names); |
| 228 | } |
| 229 | /* and finally .. */ |
| 230 | ML_(dinfo_free)(di->symtab); |
| 231 | } |
| 232 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 233 | for (chunk = di->strchunks; chunk != NULL; chunk = next) { |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 234 | next = chunk->next; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 235 | ML_(dinfo_free)(chunk); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 236 | } |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 237 | |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 238 | /* Delete the two admin arrays. These lists exist primarily so |
| 239 | that we can visit each object exactly once when we need to |
| 240 | delete them. */ |
| 241 | if (di->admin_tyents) { |
| 242 | n = VG_(sizeXA)(di->admin_tyents); |
sewardj | 59a2d18 | 2008-08-22 23:18:02 +0000 | [diff] [blame] | 243 | for (i = 0; i < n; i++) { |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 244 | ent = (TyEnt*)VG_(indexXA)(di->admin_tyents, i); |
| 245 | /* Dump anything hanging off this ent */ |
| 246 | ML_(TyEnt__make_EMPTY)(ent); |
sewardj | 59a2d18 | 2008-08-22 23:18:02 +0000 | [diff] [blame] | 247 | } |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 248 | VG_(deleteXA)(di->admin_tyents); |
| 249 | di->admin_tyents = NULL; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 250 | } |
sewardj | 59a2d18 | 2008-08-22 23:18:02 +0000 | [diff] [blame] | 251 | |
| 252 | if (di->admin_gexprs) { |
| 253 | n = VG_(sizeXA)(di->admin_gexprs); |
| 254 | for (i = 0; i < n; i++) { |
| 255 | gexpr = *(GExpr**)VG_(indexXA)(di->admin_gexprs, i); |
| 256 | ML_(dinfo_free)(gexpr); |
| 257 | } |
| 258 | VG_(deleteXA)(di->admin_gexprs); |
| 259 | di->admin_gexprs = NULL; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 260 | } |
| 261 | |
| 262 | /* Dump the variable info. This is kinda complex: we must take |
| 263 | care not to free items which reside in either the admin lists |
| 264 | (as we have just freed them) or which reside in the DebugInfo's |
| 265 | string table. */ |
| 266 | if (di->varinfo) { |
| 267 | for (i = 0; i < VG_(sizeXA)(di->varinfo); i++) { |
| 268 | OSet* scope = *(OSet**)VG_(indexXA)(di->varinfo, i); |
| 269 | if (!scope) continue; |
| 270 | /* iterate over all entries in 'scope' */ |
| 271 | VG_(OSetGen_ResetIter)(scope); |
| 272 | while (True) { |
| 273 | DiAddrRange* arange = VG_(OSetGen_Next)(scope); |
| 274 | if (!arange) break; |
| 275 | /* for each var in 'arange' */ |
| 276 | vg_assert(arange->vars); |
| 277 | for (j = 0; j < VG_(sizeXA)( arange->vars ); j++) { |
| 278 | DiVariable* var = (DiVariable*)VG_(indexXA)(arange->vars,j); |
| 279 | vg_assert(var); |
| 280 | /* Nothing to free in var: all the pointer fields refer |
| 281 | to stuff either on an admin list, or in |
| 282 | .strchunks */ |
| 283 | } |
| 284 | VG_(deleteXA)(arange->vars); |
| 285 | /* Don't free arange itself, as OSetGen_Destroy does |
| 286 | that */ |
| 287 | } |
| 288 | VG_(OSetGen_Destroy)(scope); |
| 289 | } |
| 290 | VG_(deleteXA)(di->varinfo); |
| 291 | } |
| 292 | |
| 293 | ML_(dinfo_free)(di); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 294 | } |
| 295 | |
| 296 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 297 | /* 'si' is a member of debugInfo_list. Find it, remove it from the |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 298 | list, notify m_redir that this has happened, and free all storage |
| 299 | reachable from it. |
| 300 | */ |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 301 | static void discard_DebugInfo ( DebugInfo* di ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 302 | { |
florian | 6bd9dc1 | 2012-11-23 16:17:43 +0000 | [diff] [blame] | 303 | const HChar* reason = "munmap"; |
sewardj | 4ee4f98 | 2006-10-17 01:37:10 +0000 | [diff] [blame] | 304 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 305 | DebugInfo** prev_next_ptr = &debugInfo_list; |
| 306 | DebugInfo* curr = debugInfo_list; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 307 | |
| 308 | while (curr) { |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 309 | if (curr == di) { |
| 310 | /* Found it; remove from list and free it. */ |
sewardj | 33e4e7e | 2008-03-06 18:31:42 +0000 | [diff] [blame] | 311 | if (curr->have_dinfo |
| 312 | && (VG_(clo_verbosity) > 1 || VG_(clo_trace_redir))) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 313 | VG_(message)(Vg_DebugMsg, |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 314 | "Discarding syms at %#lx-%#lx in %s due to %s()\n", |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 315 | di->text_avma, |
| 316 | di->text_avma + di->text_size, |
sewardj | a5acac3 | 2011-09-20 21:59:50 +0000 | [diff] [blame] | 317 | curr->fsm.filename ? curr->fsm.filename |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 318 | : "???", |
sewardj | 4ee4f98 | 2006-10-17 01:37:10 +0000 | [diff] [blame] | 319 | reason); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 320 | vg_assert(*prev_next_ptr == curr); |
| 321 | *prev_next_ptr = curr->next; |
sewardj | 33e4e7e | 2008-03-06 18:31:42 +0000 | [diff] [blame] | 322 | if (curr->have_dinfo) |
| 323 | VG_(redir_notify_delete_DebugInfo)( curr ); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 324 | free_DebugInfo(curr); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 325 | return; |
| 326 | } |
| 327 | prev_next_ptr = &curr->next; |
| 328 | curr = curr->next; |
| 329 | } |
| 330 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 331 | /* Not found. */ |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 332 | } |
| 333 | |
| 334 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 335 | /* Repeatedly scan debugInfo_list, looking for DebugInfos with text |
| 336 | AVMAs intersecting [start,start+length), and call discard_DebugInfo |
| 337 | to get rid of them. This modifies the list, hence the multiple |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 338 | iterations. Returns True iff any such DebugInfos were found. |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 339 | */ |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 340 | static Bool discard_syms_in_range ( Addr start, SizeT length ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 341 | { |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 342 | Bool anyFound = False; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 343 | Bool found; |
| 344 | DebugInfo* curr; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 345 | |
| 346 | while (True) { |
| 347 | found = False; |
| 348 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 349 | curr = debugInfo_list; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 350 | while (True) { |
| 351 | if (curr == NULL) |
| 352 | break; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 353 | if (curr->text_present |
| 354 | && curr->text_size > 0 |
| 355 | && (start+length - 1 < curr->text_avma |
| 356 | || curr->text_avma + curr->text_size - 1 < start)) { |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 357 | /* no overlap */ |
| 358 | } else { |
| 359 | found = True; |
| 360 | break; |
| 361 | } |
| 362 | curr = curr->next; |
| 363 | } |
| 364 | |
| 365 | if (!found) break; |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 366 | anyFound = True; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 367 | discard_DebugInfo( curr ); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 368 | } |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 369 | |
| 370 | return anyFound; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 371 | } |
| 372 | |
| 373 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 374 | /* Does [s1,+len1) overlap [s2,+len2) ? Note: does not handle |
| 375 | wraparound at the end of the address space -- just asserts in that |
| 376 | case. */ |
| 377 | static Bool ranges_overlap (Addr s1, SizeT len1, Addr s2, SizeT len2 ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 378 | { |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 379 | Addr e1, e2; |
| 380 | if (len1 == 0 || len2 == 0) |
| 381 | return False; |
| 382 | e1 = s1 + len1 - 1; |
| 383 | e2 = s2 + len2 - 1; |
| 384 | /* Assert that we don't have wraparound. If we do it would imply |
| 385 | that file sections are getting mapped around the end of the |
| 386 | address space, which sounds unlikely. */ |
| 387 | vg_assert(s1 <= e1); |
| 388 | vg_assert(s2 <= e2); |
| 389 | if (e1 < s2 || e2 < s1) return False; |
| 390 | return True; |
| 391 | } |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 392 | |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 393 | |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 394 | /* Do the basic mappings of the two DebugInfos overlap in any way? */ |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 395 | static Bool do_DebugInfos_overlap ( DebugInfo* di1, DebugInfo* di2 ) |
| 396 | { |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 397 | Word i, j; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 398 | vg_assert(di1); |
| 399 | vg_assert(di2); |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 400 | for (i = 0; i < VG_(sizeXA)(di1->fsm.maps); i++) { |
| 401 | struct _DebugInfoMapping* map1 = VG_(indexXA)(di1->fsm.maps, i); |
| 402 | for (j = 0; j < VG_(sizeXA)(di2->fsm.maps); j++) { |
| 403 | struct _DebugInfoMapping* map2 = VG_(indexXA)(di2->fsm.maps, j); |
| 404 | if (ranges_overlap(map1->avma, map1->size, map2->avma, map2->size)) |
| 405 | return True; |
| 406 | } |
| 407 | } |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 408 | |
| 409 | return False; |
| 410 | } |
| 411 | |
| 412 | |
| 413 | /* Discard all elements of debugInfo_list whose .mark bit is set. |
| 414 | */ |
| 415 | static void discard_marked_DebugInfos ( void ) |
| 416 | { |
| 417 | DebugInfo* curr; |
| 418 | |
| 419 | while (True) { |
| 420 | |
| 421 | curr = debugInfo_list; |
| 422 | while (True) { |
| 423 | if (!curr) |
| 424 | break; |
| 425 | if (curr->mark) |
| 426 | break; |
| 427 | curr = curr->next; |
| 428 | } |
| 429 | |
| 430 | if (!curr) break; |
| 431 | discard_DebugInfo( curr ); |
| 432 | |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 433 | } |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 434 | } |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 435 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 436 | |
| 437 | /* Discard any elements of debugInfo_list which overlap with diRef. |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 438 | Clearly diRef must have its mapping information set to something sane. */ |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 439 | static void discard_DebugInfos_which_overlap_with ( DebugInfo* diRef ) |
| 440 | { |
| 441 | DebugInfo* di; |
| 442 | /* Mark all the DebugInfos in debugInfo_list that need to be |
| 443 | deleted. First, clear all the mark bits; then set them if they |
| 444 | overlap with siRef. Since siRef itself is in this list we at |
| 445 | least expect its own mark bit to be set. */ |
| 446 | for (di = debugInfo_list; di; di = di->next) { |
| 447 | di->mark = do_DebugInfos_overlap( di, diRef ); |
| 448 | if (di == diRef) { |
| 449 | vg_assert(di->mark); |
| 450 | di->mark = False; |
| 451 | } |
| 452 | } |
| 453 | discard_marked_DebugInfos(); |
| 454 | } |
| 455 | |
| 456 | |
sewardj | 0f4126c | 2011-09-20 16:10:59 +0000 | [diff] [blame] | 457 | /* Find the existing DebugInfo for |filename| or if not found, create |
| 458 | one. In the latter case |filename| is strdup'd into VG_AR_DINFO, |
| 459 | and the new DebugInfo is added to debugInfo_list. */ |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 460 | static DebugInfo* find_or_create_DebugInfo_for ( HChar* filename ) |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 461 | { |
| 462 | DebugInfo* di; |
| 463 | vg_assert(filename); |
| 464 | for (di = debugInfo_list; di; di = di->next) { |
sewardj | a5acac3 | 2011-09-20 21:59:50 +0000 | [diff] [blame] | 465 | vg_assert(di->fsm.filename); |
| 466 | if (0==VG_(strcmp)(di->fsm.filename, filename)) |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 467 | break; |
| 468 | } |
| 469 | if (!di) { |
sewardj | 0f4126c | 2011-09-20 16:10:59 +0000 | [diff] [blame] | 470 | di = alloc_DebugInfo(filename); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 471 | vg_assert(di); |
| 472 | di->next = debugInfo_list; |
| 473 | debugInfo_list = di; |
| 474 | } |
| 475 | return di; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 476 | } |
| 477 | |
| 478 | |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 479 | /* Debuginfo reading for 'di' has just been successfully completed. |
| 480 | Check that the invariants stated in |
| 481 | "Comment_on_IMPORTANT_CFSI_REPRESENTATIONAL_INVARIANTS" in |
| 482 | priv_storage.h are observed. */ |
| 483 | static void check_CFSI_related_invariants ( DebugInfo* di ) |
| 484 | { |
| 485 | DebugInfo* di2 = NULL; |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 486 | Bool has_nonempty_rx = False; |
| 487 | Bool cfsi_fits = False; |
| 488 | Word i, j; |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 489 | vg_assert(di); |
| 490 | /* This fn isn't called until after debuginfo for this object has |
| 491 | been successfully read. And that shouldn't happen until we have |
| 492 | both a r-x and rw- mapping for the object. Hence: */ |
sewardj | a5acac3 | 2011-09-20 21:59:50 +0000 | [diff] [blame] | 493 | vg_assert(di->fsm.have_rx_map); |
| 494 | vg_assert(di->fsm.have_rw_map); |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 495 | for (i = 0; i < VG_(sizeXA)(di->fsm.maps); i++) { |
| 496 | struct _DebugInfoMapping* map = VG_(indexXA)(di->fsm.maps, i); |
| 497 | /* We are interested in r-x mappings only */ |
| 498 | if (!map->rx) |
| 499 | continue; |
| 500 | |
| 501 | /* degenerate case: r-x section is empty */ |
| 502 | if (map->size == 0) |
| 503 | continue; |
| 504 | has_nonempty_rx = True; |
| 505 | |
| 506 | /* normal case: r-x section is nonempty */ |
| 507 | /* invariant (0) */ |
| 508 | vg_assert(map->size > 0); |
| 509 | |
| 510 | /* invariant (1) */ |
| 511 | for (di2 = debugInfo_list; di2; di2 = di2->next) { |
| 512 | if (di2 == di) |
| 513 | continue; |
| 514 | for (j = 0; j < VG_(sizeXA)(di2->fsm.maps); j++) { |
| 515 | struct _DebugInfoMapping* map2 = VG_(indexXA)(di2->fsm.maps, j); |
| 516 | if (!map2->rx || map2->size == 0) |
| 517 | continue; |
| 518 | vg_assert(!ranges_overlap(map->avma, map->size, |
| 519 | map2->avma, map2->size)); |
| 520 | } |
| 521 | } |
| 522 | di2 = NULL; |
| 523 | |
| 524 | /* invariant (2) */ |
| 525 | if (di->cfsi) { |
| 526 | vg_assert(di->cfsi_minavma <= di->cfsi_maxavma); /* duh! */ |
| 527 | /* Assume the csfi fits completely into one individual mapping |
| 528 | for now. This might need to be improved/reworked later. */ |
| 529 | if (di->cfsi_minavma >= map->avma && |
| 530 | di->cfsi_maxavma < map->avma + map->size) |
| 531 | cfsi_fits = True; |
| 532 | } |
| 533 | } |
| 534 | |
| 535 | /* degenerate case: all r-x sections are empty */ |
| 536 | if (!has_nonempty_rx) { |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 537 | vg_assert(di->cfsi == NULL); |
| 538 | return; |
| 539 | } |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 540 | |
| 541 | /* invariant (2) - cont. */ |
| 542 | if (di->cfsi) |
| 543 | vg_assert(cfsi_fits); |
| 544 | |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 545 | /* invariants (3) and (4) */ |
| 546 | if (di->cfsi) { |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 547 | vg_assert(di->cfsi_used > 0); |
| 548 | vg_assert(di->cfsi_size > 0); |
| 549 | for (i = 0; i < di->cfsi_used; i++) { |
| 550 | DiCfSI* cfsi = &di->cfsi[i]; |
| 551 | vg_assert(cfsi->len > 0); |
| 552 | vg_assert(cfsi->base >= di->cfsi_minavma); |
| 553 | vg_assert(cfsi->base + cfsi->len - 1 <= di->cfsi_maxavma); |
| 554 | if (i > 0) { |
| 555 | DiCfSI* cfsip = &di->cfsi[i-1]; |
| 556 | vg_assert(cfsip->base + cfsip->len <= cfsi->base); |
| 557 | } |
| 558 | } |
| 559 | } else { |
| 560 | vg_assert(di->cfsi_used == 0); |
| 561 | vg_assert(di->cfsi_size == 0); |
| 562 | } |
| 563 | } |
| 564 | |
| 565 | |
| 566 | /*--------------------------------------------------------------*/ |
| 567 | /*--- ---*/ |
| 568 | /*--- TOP LEVEL: INITIALISE THE DEBUGINFO SYSTEM ---*/ |
| 569 | /*--- ---*/ |
| 570 | /*--------------------------------------------------------------*/ |
| 571 | |
| 572 | void VG_(di_initialise) ( void ) |
| 573 | { |
| 574 | /* There's actually very little to do here, since everything |
| 575 | centers around the DebugInfos in debugInfo_list, they are |
| 576 | created and destroyed on demand, and each one is treated more or |
| 577 | less independently. */ |
| 578 | vg_assert(debugInfo_list == NULL); |
| 579 | |
| 580 | /* flush the CFI fast query cache. */ |
| 581 | cfsi_cache__invalidate(); |
| 582 | } |
| 583 | |
| 584 | |
sewardj | 4ee4f98 | 2006-10-17 01:37:10 +0000 | [diff] [blame] | 585 | /*--------------------------------------------------------------*/ |
| 586 | /*--- ---*/ |
| 587 | /*--- TOP LEVEL: NOTIFICATION (ACQUIRE/DISCARD INFO) (LINUX) ---*/ |
| 588 | /*--- ---*/ |
| 589 | /*--------------------------------------------------------------*/ |
| 590 | |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 591 | #if defined(VGO_linux) || defined(VGO_darwin) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 592 | |
| 593 | /* The debug info system is driven by notifications that a text |
sewardj | 731f9cf | 2011-09-21 08:43:08 +0000 | [diff] [blame] | 594 | segment has been mapped in, or unmapped, or when sections change |
| 595 | permission. It's all a bit kludgey and basically means watching |
| 596 | syscalls, trying to second-guess when the system's dynamic linker |
| 597 | is done with mapping in a new object for execution. This is all |
| 598 | tracked using the DebugInfoFSM struct for the object. Anyway, once |
| 599 | we finally decide we've got to an accept state, this section then |
| 600 | will acquire whatever info is available for the corresponding |
| 601 | object. This section contains the notification handlers, which |
| 602 | update the FSM and determine when an accept state has been reached. |
| 603 | */ |
| 604 | |
| 605 | /* When the sequence of observations causes a DebugInfoFSM to move |
| 606 | into the accept state, call here to actually get the debuginfo read |
| 607 | in. Returns a ULong whose purpose is described in comments |
| 608 | preceding VG_(di_notify_mmap) just below. |
| 609 | */ |
| 610 | static ULong di_notify_ACHIEVE_ACCEPT_STATE ( struct _DebugInfo* di ) |
| 611 | { |
| 612 | ULong di_handle; |
| 613 | Bool ok; |
| 614 | |
| 615 | vg_assert(di->fsm.filename); |
| 616 | TRACE_SYMTAB("\n"); |
| 617 | TRACE_SYMTAB("------ start ELF OBJECT " |
| 618 | "------------------------------\n"); |
| 619 | TRACE_SYMTAB("------ name = %s\n", di->fsm.filename); |
| 620 | TRACE_SYMTAB("\n"); |
| 621 | |
| 622 | /* We're going to read symbols and debug info for the avma |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 623 | ranges specified in the _DebugInfoFsm mapping array. First |
| 624 | get rid of any other DebugInfos which overlap any of those |
| 625 | ranges (to avoid total confusion). */ |
sewardj | 731f9cf | 2011-09-21 08:43:08 +0000 | [diff] [blame] | 626 | discard_DebugInfos_which_overlap_with( di ); |
| 627 | |
| 628 | /* .. and acquire new info. */ |
| 629 | # if defined(VGO_linux) |
| 630 | ok = ML_(read_elf_debug_info)( di ); |
| 631 | # elif defined(VGO_darwin) |
| 632 | ok = ML_(read_macho_debug_info)( di ); |
| 633 | # else |
| 634 | # error "unknown OS" |
| 635 | # endif |
| 636 | |
| 637 | if (ok) { |
| 638 | |
| 639 | TRACE_SYMTAB("\n------ Canonicalising the " |
| 640 | "acquired info ------\n"); |
| 641 | /* invalidate the CFI unwind cache. */ |
| 642 | cfsi_cache__invalidate(); |
| 643 | /* prepare read data for use */ |
| 644 | ML_(canonicaliseTables)( di ); |
| 645 | /* notify m_redir about it */ |
| 646 | TRACE_SYMTAB("\n------ Notifying m_redir ------\n"); |
| 647 | VG_(redir_notify_new_DebugInfo)( di ); |
| 648 | /* Note that we succeeded */ |
| 649 | di->have_dinfo = True; |
| 650 | tl_assert(di->handle > 0); |
| 651 | di_handle = di->handle; |
| 652 | /* Check invariants listed in |
| 653 | Comment_on_IMPORTANT_REPRESENTATIONAL_INVARIANTS in |
| 654 | priv_storage.h. */ |
| 655 | check_CFSI_related_invariants(di); |
| 656 | |
| 657 | } else { |
| 658 | TRACE_SYMTAB("\n------ ELF reading failed ------\n"); |
| 659 | /* Something went wrong (eg. bad ELF file). Should we delete |
| 660 | this DebugInfo? No - it contains info on the rw/rx |
| 661 | mappings, at least. */ |
| 662 | di_handle = 0; |
| 663 | vg_assert(di->have_dinfo == False); |
| 664 | } |
| 665 | |
| 666 | TRACE_SYMTAB("\n"); |
| 667 | TRACE_SYMTAB("------ name = %s\n", di->fsm.filename); |
| 668 | TRACE_SYMTAB("------ end ELF OBJECT " |
| 669 | "------------------------------\n"); |
| 670 | TRACE_SYMTAB("\n"); |
| 671 | |
| 672 | return di_handle; |
| 673 | } |
| 674 | |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 675 | |
| 676 | /* Notify the debuginfo system about a new mapping. This is the way |
| 677 | new debug information gets loaded. If allow_SkFileV is True, it |
| 678 | will try load debug info if the mapping at 'a' belongs to Valgrind; |
| 679 | whereas normally (False) it will not do that. This allows us to |
| 680 | carefully control when the thing will read symbols from the |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 681 | Valgrind executable itself. |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 682 | |
sewardj | 5f2dcad | 2011-10-24 08:53:03 +0000 | [diff] [blame] | 683 | If use_fd is not -1, that is used instead of the filename; this |
| 684 | avoids perturbing fcntl locks, which are released by simply |
| 685 | re-opening and closing the same file (even via different fd!). |
| 686 | |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 687 | If a call to VG_(di_notify_mmap) causes debug info to be read, then |
| 688 | the returned ULong is an abstract handle which can later be used to |
| 689 | refer to the debuginfo read as a result of this specific mapping, |
| 690 | in later queries to m_debuginfo. In this case the handle value |
| 691 | will be one or above. If the returned value is zero, no debug info |
| 692 | was read. */ |
| 693 | |
sewardj | 5f2dcad | 2011-10-24 08:53:03 +0000 | [diff] [blame] | 694 | ULong VG_(di_notify_mmap)( Addr a, Bool allow_SkFileV, Int use_fd ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 695 | { |
sewardj | 4ee4f98 | 2006-10-17 01:37:10 +0000 | [diff] [blame] | 696 | NSegment const * seg; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 697 | HChar* filename; |
sewardj | 731f9cf | 2011-09-21 08:43:08 +0000 | [diff] [blame] | 698 | Bool is_rx_map, is_rw_map, is_ro_map; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 699 | DebugInfo* di; |
sewardj | 5f2dcad | 2011-10-24 08:53:03 +0000 | [diff] [blame] | 700 | Int actual_fd, oflags; |
| 701 | SysRes preadres; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 702 | HChar buf1k[1024]; |
| 703 | Bool debug = False; |
sewardj | ec61b65 | 2008-08-19 07:03:04 +0000 | [diff] [blame] | 704 | SysRes statres; |
| 705 | struct vg_stat statbuf; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 706 | |
sewardj | 5f2dcad | 2011-10-24 08:53:03 +0000 | [diff] [blame] | 707 | vg_assert(use_fd >= -1); |
| 708 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 709 | /* In short, figure out if this mapping is of interest to us, and |
| 710 | if so, try to guess what ld.so is doing and when/if we should |
| 711 | read debug info. */ |
| 712 | seg = VG_(am_find_nsegment)(a); |
| 713 | vg_assert(seg); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 714 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 715 | if (debug) |
bart | a0b6b2c | 2008-07-07 06:49:24 +0000 | [diff] [blame] | 716 | VG_(printf)("di_notify_mmap-1: %#lx-%#lx %c%c%c\n", |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 717 | seg->start, seg->end, |
| 718 | seg->hasR ? 'r' : '-', |
| 719 | seg->hasW ? 'w' : '-',seg->hasX ? 'x' : '-' ); |
| 720 | |
| 721 | /* guaranteed by aspacemgr-linux.c, sane_NSegment() */ |
| 722 | vg_assert(seg->end > seg->start); |
| 723 | |
| 724 | /* Ignore non-file mappings */ |
| 725 | if ( ! (seg->kind == SkFileC |
| 726 | || (seg->kind == SkFileV && allow_SkFileV)) ) |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 727 | return 0; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 728 | |
| 729 | /* If the file doesn't have a name, we're hosed. Give up. */ |
florian | 3e79863 | 2012-11-24 19:41:54 +0000 | [diff] [blame] | 730 | filename = VG_(am_get_filename)( seg ); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 731 | if (!filename) |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 732 | return 0; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 733 | |
bart | 0ab84fe | 2012-09-09 18:30:17 +0000 | [diff] [blame] | 734 | /* |
| 735 | * Cannot read from these magic files: |
| 736 | * --20208-- WARNING: Serious error when reading debug info |
| 737 | * --20208-- When reading debug info from /proc/xen/privcmd: |
| 738 | * --20208-- can't read file to inspect ELF header |
| 739 | */ |
| 740 | if (VG_(strncmp)(filename, "/proc/xen/", 10) == 0) |
| 741 | return 0; |
| 742 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 743 | if (debug) |
| 744 | VG_(printf)("di_notify_mmap-2: %s\n", filename); |
| 745 | |
sewardj | ec61b65 | 2008-08-19 07:03:04 +0000 | [diff] [blame] | 746 | /* Only try to read debug information from regular files. */ |
bart | 15728ab | 2008-04-09 16:21:34 +0000 | [diff] [blame] | 747 | statres = VG_(stat)(filename, &statbuf); |
sewardj | ec61b65 | 2008-08-19 07:03:04 +0000 | [diff] [blame] | 748 | |
| 749 | /* stat dereferences symlinks, so we don't expect it to succeed and |
| 750 | yet produce something that is a symlink. */ |
njn | 9c20ece | 2009-05-20 02:02:30 +0000 | [diff] [blame] | 751 | vg_assert(sr_isError(statres) || ! VKI_S_ISLNK(statbuf.mode)); |
sewardj | ec61b65 | 2008-08-19 07:03:04 +0000 | [diff] [blame] | 752 | |
| 753 | /* Don't let the stat call fail silently. Filter out some known |
| 754 | sources of noise before complaining, though. */ |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 755 | if (sr_isError(statres)) { |
sewardj | ec61b65 | 2008-08-19 07:03:04 +0000 | [diff] [blame] | 756 | DebugInfo fake_di; |
| 757 | Bool quiet = VG_(strstr)(filename, "/var/run/nscd/") != NULL; |
sewardj | e025eca | 2008-08-25 12:10:14 +0000 | [diff] [blame] | 758 | if (!quiet && VG_(clo_verbosity) > 1) { |
sewardj | ec61b65 | 2008-08-19 07:03:04 +0000 | [diff] [blame] | 759 | VG_(memset)(&fake_di, 0, sizeof(fake_di)); |
sewardj | a5acac3 | 2011-09-20 21:59:50 +0000 | [diff] [blame] | 760 | fake_di.fsm.filename = filename; |
sewardj | ec61b65 | 2008-08-19 07:03:04 +0000 | [diff] [blame] | 761 | ML_(symerr)(&fake_di, True, "failed to stat64/stat this file"); |
| 762 | } |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 763 | return 0; |
sewardj | 2ac79d3 | 2008-03-17 16:23:54 +0000 | [diff] [blame] | 764 | } |
sewardj | 2ac79d3 | 2008-03-17 16:23:54 +0000 | [diff] [blame] | 765 | |
sewardj | ec61b65 | 2008-08-19 07:03:04 +0000 | [diff] [blame] | 766 | /* Finally, the point of all this stattery: if it's not a regular file, |
| 767 | don't try to read debug info from it. */ |
njn | 9c20ece | 2009-05-20 02:02:30 +0000 | [diff] [blame] | 768 | if (! VKI_S_ISREG(statbuf.mode)) |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 769 | return 0; |
sewardj | ec61b65 | 2008-08-19 07:03:04 +0000 | [diff] [blame] | 770 | |
| 771 | /* no uses of statbuf below here. */ |
bart | 15728ab | 2008-04-09 16:21:34 +0000 | [diff] [blame] | 772 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 773 | /* Now we have to guess if this is a text-like mapping, a data-like |
| 774 | mapping, neither or both. The rules are: |
| 775 | |
| 776 | text if: x86-linux r and x |
| 777 | other-linux r and x and not w |
| 778 | |
| 779 | data if: x86-linux r and w |
| 780 | other-linux r and w and not x |
| 781 | |
| 782 | Background: On x86-linux, objects are typically mapped twice: |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 783 | |
| 784 | 1b8fb000-1b8ff000 r-xp 00000000 08:02 4471477 vgpreload_memcheck.so |
| 785 | 1b8ff000-1b900000 rw-p 00004000 08:02 4471477 vgpreload_memcheck.so |
| 786 | |
| 787 | whereas ppc32-linux mysteriously does this: |
| 788 | |
| 789 | 118a6000-118ad000 r-xp 00000000 08:05 14209428 vgpreload_memcheck.so |
| 790 | 118ad000-118b6000 ---p 00007000 08:05 14209428 vgpreload_memcheck.so |
| 791 | 118b6000-118bd000 rwxp 00000000 08:05 14209428 vgpreload_memcheck.so |
| 792 | |
| 793 | The third mapping should not be considered to have executable |
| 794 | code in. Therefore a test which works for both is: r and x and |
| 795 | NOT w. Reading symbols from the rwx segment -- which overlaps |
| 796 | the r-x segment in the file -- causes the redirection mechanism |
| 797 | to redirect to addresses in that third segment, which is wrong |
| 798 | and causes crashes. |
| 799 | |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 800 | JRS 28 Dec 05: unfortunately icc 8.1 on x86 has been seen to |
| 801 | produce executables with a single rwx segment rather than a |
| 802 | (r-x,rw-) pair. That means the rules have to be modified thusly: |
| 803 | |
| 804 | x86-linux: consider if r and x |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 805 | all others: consider if r and x and not w |
sewardj | f56d255 | 2009-08-16 01:48:35 +0000 | [diff] [blame] | 806 | |
| 807 | 2009 Aug 16: apply similar kludge to ppc32-linux. |
| 808 | See http://bugs.kde.org/show_bug.cgi?id=190820 |
sewardj | b5b8740 | 2011-03-07 16:05:35 +0000 | [diff] [blame] | 809 | |
| 810 | There are two modes on s390x: with and without the noexec kernel |
| 811 | parameter. Together with some older kernels, this leads to several |
| 812 | variants: |
| 813 | executable: r and x |
| 814 | data: r and w and x |
| 815 | or |
| 816 | executable: r and x |
| 817 | data: r and w |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 818 | */ |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 819 | is_rx_map = False; |
| 820 | is_rw_map = False; |
sewardj | 731f9cf | 2011-09-21 08:43:08 +0000 | [diff] [blame] | 821 | is_ro_map = False; |
| 822 | |
petarj | 4df0bfc | 2013-02-27 23:17:33 +0000 | [diff] [blame] | 823 | # if defined(VGA_x86) || defined(VGA_ppc32) || defined(VGA_mips32) \ |
| 824 | || defined(VGA_mips64) |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 825 | is_rx_map = seg->hasR && seg->hasX; |
| 826 | is_rw_map = seg->hasR && seg->hasW; |
sewardj | f0c1250 | 2014-01-12 12:54:00 +0000 | [diff] [blame] | 827 | # elif defined(VGA_amd64) || defined(VGA_ppc64) || defined(VGA_arm) \ |
| 828 | || defined(VGA_arm64) |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 829 | is_rx_map = seg->hasR && seg->hasX && !seg->hasW; |
| 830 | is_rw_map = seg->hasR && seg->hasW && !seg->hasX; |
sewardj | b5b8740 | 2011-03-07 16:05:35 +0000 | [diff] [blame] | 831 | # elif defined(VGP_s390x_linux) |
| 832 | is_rx_map = seg->hasR && seg->hasX && !seg->hasW; |
| 833 | is_rw_map = seg->hasR && seg->hasW; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 834 | # else |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 835 | # error "Unknown platform" |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 836 | # endif |
| 837 | |
sewardj | 731f9cf | 2011-09-21 08:43:08 +0000 | [diff] [blame] | 838 | # if defined(VGP_x86_darwin) && DARWIN_VERS == DARWIN_10_7 |
| 839 | is_ro_map = seg->hasR && !seg->hasW && !seg->hasX; |
| 840 | # endif |
| 841 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 842 | if (debug) |
| 843 | VG_(printf)("di_notify_mmap-3: is_rx_map %d, is_rw_map %d\n", |
| 844 | (Int)is_rx_map, (Int)is_rw_map); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 845 | |
sewardj | 731f9cf | 2011-09-21 08:43:08 +0000 | [diff] [blame] | 846 | /* Ignore mappings with permissions we can't possibly be interested in. */ |
| 847 | if (!(is_rx_map || is_rw_map || is_ro_map)) |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 848 | return 0; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 849 | |
sewardj | 5a5eec0 | 2008-11-08 15:22:19 +0000 | [diff] [blame] | 850 | /* Peer at the first few bytes of the file, to see if it is an ELF */ |
| 851 | /* object file. Ignore the file if we do not have read permission. */ |
| 852 | VG_(memset)(buf1k, 0, sizeof(buf1k)); |
sewardj | cec083d | 2010-10-07 09:56:19 +0000 | [diff] [blame] | 853 | oflags = VKI_O_RDONLY; |
| 854 | # if defined(VKI_O_LARGEFILE) |
| 855 | oflags |= VKI_O_LARGEFILE; |
| 856 | # endif |
sewardj | 5a5eec0 | 2008-11-08 15:22:19 +0000 | [diff] [blame] | 857 | |
sewardj | 5f2dcad | 2011-10-24 08:53:03 +0000 | [diff] [blame] | 858 | if (use_fd == -1) { |
| 859 | SysRes fd = VG_(open)( filename, oflags, 0 ); |
| 860 | if (sr_isError(fd)) { |
| 861 | if (sr_Err(fd) != VKI_EACCES) { |
| 862 | DebugInfo fake_di; |
| 863 | VG_(memset)(&fake_di, 0, sizeof(fake_di)); |
| 864 | fake_di.fsm.filename = filename; |
| 865 | ML_(symerr)(&fake_di, True, |
| 866 | "can't open file to inspect ELF header"); |
| 867 | } |
| 868 | return 0; |
| 869 | } |
| 870 | actual_fd = sr_Res(fd); |
| 871 | } else { |
| 872 | actual_fd = use_fd; |
| 873 | } |
| 874 | |
| 875 | preadres = VG_(pread)( actual_fd, buf1k, sizeof(buf1k), 0 ); |
| 876 | if (use_fd == -1) { |
| 877 | VG_(close)( actual_fd ); |
| 878 | } |
| 879 | |
| 880 | if (sr_isError(preadres)) { |
sewardj | 5a5eec0 | 2008-11-08 15:22:19 +0000 | [diff] [blame] | 881 | DebugInfo fake_di; |
| 882 | VG_(memset)(&fake_di, 0, sizeof(fake_di)); |
sewardj | a5acac3 | 2011-09-20 21:59:50 +0000 | [diff] [blame] | 883 | fake_di.fsm.filename = filename; |
sewardj | 5a5eec0 | 2008-11-08 15:22:19 +0000 | [diff] [blame] | 884 | ML_(symerr)(&fake_di, True, "can't read file to inspect ELF header"); |
| 885 | return 0; |
| 886 | } |
sewardj | 5f2dcad | 2011-10-24 08:53:03 +0000 | [diff] [blame] | 887 | if (sr_Res(preadres) == 0) |
| 888 | return 0; |
| 889 | vg_assert(sr_Res(preadres) > 0 && sr_Res(preadres) <= sizeof(buf1k) ); |
sewardj | 5a5eec0 | 2008-11-08 15:22:19 +0000 | [diff] [blame] | 890 | |
njn | 8b68b64 | 2009-06-24 00:37:09 +0000 | [diff] [blame] | 891 | /* We're only interested in mappings of object files. */ |
sewardj | 6e9de46 | 2011-06-28 07:25:29 +0000 | [diff] [blame] | 892 | # if defined(VGO_linux) |
sewardj | f7c9714 | 2012-07-14 09:59:01 +0000 | [diff] [blame] | 893 | if (!ML_(is_elf_object_file)( buf1k, (SizeT)sr_Res(preadres), False )) |
sewardj | 5a5eec0 | 2008-11-08 15:22:19 +0000 | [diff] [blame] | 894 | return 0; |
sewardj | 6e9de46 | 2011-06-28 07:25:29 +0000 | [diff] [blame] | 895 | # elif defined(VGO_darwin) |
sewardj | 5f2dcad | 2011-10-24 08:53:03 +0000 | [diff] [blame] | 896 | if (!ML_(is_macho_object_file)( buf1k, (SizeT)sr_Res(preadres) )) |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 897 | return 0; |
sewardj | 6e9de46 | 2011-06-28 07:25:29 +0000 | [diff] [blame] | 898 | # else |
| 899 | # error "unknown OS" |
| 900 | # endif |
sewardj | 5a5eec0 | 2008-11-08 15:22:19 +0000 | [diff] [blame] | 901 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 902 | /* See if we have a DebugInfo for this filename. If not, |
| 903 | create one. */ |
sewardj | 0f4126c | 2011-09-20 16:10:59 +0000 | [diff] [blame] | 904 | di = find_or_create_DebugInfo_for( filename ); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 905 | vg_assert(di); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 906 | |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 907 | /* Note the details about the mapping. */ |
| 908 | struct _DebugInfoMapping map; |
| 909 | map.avma = a; |
| 910 | map.size = seg->end + 1 - seg->start; |
| 911 | map.foff = seg->offset; |
| 912 | map.rx = is_rx_map; |
| 913 | map.rw = is_rw_map; |
| 914 | map.ro = is_ro_map; |
| 915 | VG_(addToXA)(di->fsm.maps, &map); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 916 | |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 917 | /* Update flags about what kind of mappings we've already seen. */ |
| 918 | di->fsm.have_rx_map |= is_rx_map; |
| 919 | di->fsm.have_rw_map |= is_rw_map; |
| 920 | di->fsm.have_ro_map |= is_ro_map; |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 921 | |
sewardj | 731f9cf | 2011-09-21 08:43:08 +0000 | [diff] [blame] | 922 | /* So, finally, are we in an accept state? */ |
| 923 | if (di->fsm.have_rx_map && di->fsm.have_rw_map && !di->have_dinfo) { |
| 924 | /* Ok, so, finally, we found what we need, and we haven't |
| 925 | already read debuginfo for this object. So let's do so now. |
| 926 | Yee-ha! */ |
| 927 | return di_notify_ACHIEVE_ACCEPT_STATE ( di ); |
| 928 | } else { |
| 929 | /* If we don't have an rx and rw mapping, or if we already have |
| 930 | debuginfo for this mapping for whatever reason, go no |
| 931 | further. */ |
| 932 | return 0; |
| 933 | } |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 934 | } |
| 935 | |
| 936 | |
| 937 | /* Unmap is simpler - throw away any SegInfos intersecting |
| 938 | [a, a+len). */ |
| 939 | void VG_(di_notify_munmap)( Addr a, SizeT len ) |
| 940 | { |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 941 | Bool anyFound; |
bart | a0b6b2c | 2008-07-07 06:49:24 +0000 | [diff] [blame] | 942 | if (0) VG_(printf)("DISCARD %#lx %#lx\n", a, a+len); |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 943 | anyFound = discard_syms_in_range(a, len); |
| 944 | if (anyFound) |
| 945 | cfsi_cache__invalidate(); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 946 | } |
| 947 | |
| 948 | |
| 949 | /* Uh, this doesn't do anything at all. IIRC glibc (or ld.so, I don't |
| 950 | remember) does a bunch of mprotects on itself, and if we follow |
| 951 | through here, it causes the debug info for that object to get |
| 952 | discarded. */ |
| 953 | void VG_(di_notify_mprotect)( Addr a, SizeT len, UInt prot ) |
| 954 | { |
| 955 | Bool exe_ok = toBool(prot & VKI_PROT_EXEC); |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 956 | # if defined(VGA_x86) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 957 | exe_ok = exe_ok || toBool(prot & VKI_PROT_READ); |
| 958 | # endif |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 959 | if (0 && !exe_ok) { |
| 960 | Bool anyFound = discard_syms_in_range(a, len); |
| 961 | if (anyFound) |
| 962 | cfsi_cache__invalidate(); |
| 963 | } |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 964 | } |
| 965 | |
sewardj | 731f9cf | 2011-09-21 08:43:08 +0000 | [diff] [blame] | 966 | |
| 967 | /* This is a MacOSX 10.7 32-bit only special. See comments on the |
| 968 | declaration of struct _DebugInfoFSM for details. */ |
| 969 | void VG_(di_notify_vm_protect)( Addr a, SizeT len, UInt prot ) |
| 970 | { |
| 971 | Bool do_nothing = True; |
sewardj | ae284e5 | 2012-08-02 18:25:04 +0000 | [diff] [blame] | 972 | # if defined(VGP_x86_darwin) && (DARWIN_VERS == DARWIN_10_7 || DARWIN_VERS == DARWIN_10_8) |
sewardj | 731f9cf | 2011-09-21 08:43:08 +0000 | [diff] [blame] | 973 | do_nothing = False; |
| 974 | # endif |
| 975 | if (do_nothing /* wrong platform */) |
| 976 | return; |
| 977 | |
| 978 | Bool r_ok = toBool(prot & VKI_PROT_READ); |
| 979 | Bool w_ok = toBool(prot & VKI_PROT_WRITE); |
| 980 | Bool x_ok = toBool(prot & VKI_PROT_EXEC); |
| 981 | if (! (r_ok && !w_ok && x_ok)) |
| 982 | return; /* not an upgrade to r-x */ |
| 983 | |
| 984 | /* Find a DebugInfo containing a FSM that has [a, +len) previously |
| 985 | observed as a r-- mapping, plus some other rw- mapping. If such |
| 986 | is found, conclude we're in an accept state and read debuginfo |
| 987 | accordingly. */ |
| 988 | DebugInfo* di; |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 989 | struct _DebugInfoMapping *map = NULL; |
| 990 | Word i; |
sewardj | 731f9cf | 2011-09-21 08:43:08 +0000 | [diff] [blame] | 991 | for (di = debugInfo_list; di; di = di->next) { |
| 992 | vg_assert(di->fsm.filename); |
| 993 | if (di->have_dinfo) |
| 994 | continue; /* already have debuginfo for this object */ |
| 995 | if (!di->fsm.have_ro_map) |
| 996 | continue; /* need to have a r-- mapping for this object */ |
| 997 | if (di->fsm.have_rx_map) |
| 998 | continue; /* rx- mapping already exists */ |
| 999 | if (!di->fsm.have_rw_map) |
| 1000 | continue; /* need to have a rw- mapping */ |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 1001 | /* Try to find a mapping matching the memory area. */ |
| 1002 | for (i = 0; i < VG_(sizeXA)(di->fsm.maps); i++) { |
| 1003 | map = (struct _DebugInfoMapping*)VG_(indexXA)(di->fsm.maps, i); |
| 1004 | if (map->ro && map->avma == a && map->size == len) |
| 1005 | break; |
| 1006 | map = NULL; |
| 1007 | } |
| 1008 | if (!map) |
| 1009 | continue; /* this isn't an upgrade of an r-- mapping */ |
sewardj | 731f9cf | 2011-09-21 08:43:08 +0000 | [diff] [blame] | 1010 | /* looks like we're in luck! */ |
| 1011 | break; |
| 1012 | } |
| 1013 | if (di == NULL) |
| 1014 | return; /* didn't find anything */ |
| 1015 | |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 1016 | /* Do the upgrade. Simply update the flags of the mapping |
| 1017 | and pretend we never saw the RO map at all. */ |
sewardj | 731f9cf | 2011-09-21 08:43:08 +0000 | [diff] [blame] | 1018 | vg_assert(di->fsm.have_ro_map); |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 1019 | map->rx = True; |
| 1020 | map->ro = False; |
sewardj | 731f9cf | 2011-09-21 08:43:08 +0000 | [diff] [blame] | 1021 | di->fsm.have_rx_map = True; |
sewardj | 731f9cf | 2011-09-21 08:43:08 +0000 | [diff] [blame] | 1022 | di->fsm.have_ro_map = False; |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 1023 | /* See if there are any more ro mappings */ |
| 1024 | for (i = 0; i < VG_(sizeXA)(di->fsm.maps); i++) { |
| 1025 | map = (struct _DebugInfoMapping*)VG_(indexXA)(di->fsm.maps, i); |
| 1026 | if (map->ro) { |
| 1027 | di->fsm.have_ro_map = True; |
| 1028 | break; |
| 1029 | } |
| 1030 | } |
sewardj | 731f9cf | 2011-09-21 08:43:08 +0000 | [diff] [blame] | 1031 | |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 1032 | /* Check if we're now in an accept state and read debuginfo. Finally. */ |
| 1033 | if (di->fsm.have_rx_map && di->fsm.have_rw_map && !di->have_dinfo) { |
| 1034 | ULong di_handle __attribute__((unused)) |
| 1035 | = di_notify_ACHIEVE_ACCEPT_STATE( di ); |
| 1036 | /* di_handle is ignored. That's not a problem per se -- it just |
| 1037 | means nobody will ever be able to refer to this debuginfo by |
| 1038 | handle since nobody will know what the handle value is. */ |
| 1039 | } |
sewardj | 731f9cf | 2011-09-21 08:43:08 +0000 | [diff] [blame] | 1040 | } |
| 1041 | |
| 1042 | |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1043 | /*--------- PDB (windows debug info) reading --------- */ |
| 1044 | |
| 1045 | /* this should really return ULong, as per VG_(di_notify_mmap). */ |
| 1046 | void VG_(di_notify_pdb_debuginfo)( Int fd_obj, Addr avma_obj, |
sewardj | 54c45db | 2012-07-13 12:58:55 +0000 | [diff] [blame] | 1047 | SizeT total_size, PtrdiffT bias_obj ) |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1048 | { |
sewardj | 13ac96d | 2010-02-12 12:12:39 +0000 | [diff] [blame] | 1049 | Int i, r, sz_exename; |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1050 | ULong obj_mtime, pdb_mtime; |
florian | 54fe202 | 2012-10-27 23:07:42 +0000 | [diff] [blame] | 1051 | HChar exename[VKI_PATH_MAX]; |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1052 | HChar* pdbname = NULL; |
| 1053 | HChar* dot; |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1054 | SysRes sres; |
| 1055 | Int fd_pdbimage; |
| 1056 | SizeT n_pdbimage; |
| 1057 | struct vg_stat stat_buf; |
| 1058 | |
| 1059 | if (VG_(clo_verbosity) > 0) { |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 1060 | VG_(message)(Vg_UserMsg, "\n"); |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1061 | VG_(message)(Vg_UserMsg, |
sewardj | cd458d2 | 2010-12-06 11:11:29 +0000 | [diff] [blame] | 1062 | "LOAD_PDB_DEBUGINFO: clreq: fd=%d, avma=%#lx, total_size=%lu, " |
sewardj | 54c45db | 2012-07-13 12:58:55 +0000 | [diff] [blame] | 1063 | "bias=%#lx\n", |
| 1064 | fd_obj, avma_obj, total_size, bias_obj |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1065 | ); |
| 1066 | } |
| 1067 | |
| 1068 | /* 'fd' refers to the .exe/.dll we're dealing with. Get its modification |
| 1069 | time into obj_mtime. */ |
| 1070 | r = VG_(fstat)(fd_obj, &stat_buf); |
| 1071 | if (r == -1) |
| 1072 | goto out; /* stat failed ?! */ |
| 1073 | vg_assert(r == 0); |
njn | 9c20ece | 2009-05-20 02:02:30 +0000 | [diff] [blame] | 1074 | obj_mtime = stat_buf.mtime; |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1075 | |
| 1076 | /* and get its name into exename[]. */ |
| 1077 | vg_assert(VKI_PATH_MAX > 100); /* to ensure /proc/self/fd/%d is safe */ |
| 1078 | VG_(memset)(exename, 0, sizeof(exename)); |
| 1079 | VG_(sprintf)(exename, "/proc/self/fd/%d", fd_obj); |
| 1080 | /* convert exename from a symlink to real name .. overwrites the |
| 1081 | old contents of the buffer. Ick. */ |
| 1082 | sz_exename = VG_(readlink)(exename, exename, sizeof(exename)-2 ); |
| 1083 | if (sz_exename == -1) |
| 1084 | goto out; /* readlink failed ?! */ |
| 1085 | vg_assert(sz_exename >= 0 && sz_exename < sizeof(exename)); |
| 1086 | vg_assert(exename[sizeof(exename)-1] == 0); |
| 1087 | |
| 1088 | if (VG_(clo_verbosity) > 0) { |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 1089 | VG_(message)(Vg_UserMsg, "LOAD_PDB_DEBUGINFO: objname: %s\n", exename); |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1090 | } |
| 1091 | |
sewardj | 13ac96d | 2010-02-12 12:12:39 +0000 | [diff] [blame] | 1092 | /* Try to get the PDB file name from the executable. */ |
| 1093 | pdbname = ML_(find_name_of_pdb_file)(exename); |
| 1094 | if (pdbname) { |
| 1095 | vg_assert(VG_(strlen)(pdbname) >= 5); /* 5 = strlen("X.pdb") */ |
| 1096 | /* So we successfully extracted a name from the PE file. But it's |
| 1097 | likely to be of the form |
| 1098 | e:\foo\bar\xyzzy\wibble.pdb |
| 1099 | and we need to change it into something we can actually open |
| 1100 | in Wine-world, which basically means turning it into |
| 1101 | $HOME/.wine/drive_e/foo/bar/xyzzy/wibble.pdb |
| 1102 | We also take into account $WINEPREFIX, if it is set. |
| 1103 | For the moment, if the name isn't fully qualified, just forget it |
| 1104 | (we'd have to root around to find where the pdb actually is) |
| 1105 | */ |
| 1106 | /* Change all the backslashes to forward slashes */ |
| 1107 | for (i = 0; pdbname[i]; i++) { |
| 1108 | if (pdbname[i] == '\\') |
| 1109 | pdbname[i] = '/'; |
| 1110 | } |
| 1111 | Bool is_quald |
| 1112 | = ('a' <= VG_(tolower)(pdbname[0]) && VG_(tolower)(pdbname[0]) <= 'z') |
| 1113 | && pdbname[1] == ':' |
| 1114 | && pdbname[2] == '/'; |
| 1115 | HChar* home = VG_(getenv)("HOME"); |
| 1116 | HChar* wpfx = VG_(getenv)("WINEPREFIX"); |
| 1117 | if (is_quald && wpfx) { |
| 1118 | /* Change e:/foo/bar/xyzzy/wibble.pdb |
| 1119 | to $WINEPREFIX/drive_e/foo/bar/xyzzy/wibble.pdb |
| 1120 | */ |
| 1121 | Int mashedSzB = VG_(strlen)(pdbname) + VG_(strlen)(wpfx) + 50/*misc*/; |
| 1122 | HChar* mashed = ML_(dinfo_zalloc)("di.debuginfo.dnpdi.1", mashedSzB); |
bart | 98500e2 | 2011-10-28 15:05:50 +0000 | [diff] [blame] | 1123 | VG_(snprintf)(mashed, mashedSzB, "%s/drive_%c%s", |
| 1124 | wpfx, pdbname[0], &pdbname[2]); |
sewardj | 13ac96d | 2010-02-12 12:12:39 +0000 | [diff] [blame] | 1125 | vg_assert(mashed[mashedSzB-1] == 0); |
| 1126 | ML_(dinfo_free)(pdbname); |
| 1127 | pdbname = mashed; |
| 1128 | } |
| 1129 | else if (is_quald && home && !wpfx) { |
| 1130 | /* Change e:/foo/bar/xyzzy/wibble.pdb |
| 1131 | to $HOME/.wine/drive_e/foo/bar/xyzzy/wibble.pdb |
| 1132 | */ |
| 1133 | Int mashedSzB = VG_(strlen)(pdbname) + VG_(strlen)(home) + 50/*misc*/; |
| 1134 | HChar* mashed = ML_(dinfo_zalloc)("di.debuginfo.dnpdi.2", mashedSzB); |
bart | 98500e2 | 2011-10-28 15:05:50 +0000 | [diff] [blame] | 1135 | VG_(snprintf)(mashed, mashedSzB, "%s/.wine/drive_%c%s", |
| 1136 | home, pdbname[0], &pdbname[2]); |
sewardj | 13ac96d | 2010-02-12 12:12:39 +0000 | [diff] [blame] | 1137 | vg_assert(mashed[mashedSzB-1] == 0); |
| 1138 | ML_(dinfo_free)(pdbname); |
| 1139 | pdbname = mashed; |
| 1140 | } else { |
| 1141 | /* It's not a fully qualified path, or neither $HOME nor $WINE |
| 1142 | are set (strange). Give up. */ |
| 1143 | ML_(dinfo_free)(pdbname); |
| 1144 | pdbname = NULL; |
| 1145 | } |
| 1146 | } |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1147 | |
sewardj | 13ac96d | 2010-02-12 12:12:39 +0000 | [diff] [blame] | 1148 | /* Try s/exe/pdb/ if we don't have a valid pdbname. */ |
| 1149 | if (!pdbname) { |
| 1150 | /* Try to find a matching PDB file from which to read debuginfo. |
| 1151 | Windows PE files have symbol tables and line number information, |
| 1152 | but MSVC doesn't seem to use them. */ |
| 1153 | /* Why +5 ? Because in the worst case, we could find a dot as the |
| 1154 | last character of pdbname, and we'd then put "pdb" right after |
| 1155 | it, hence extending it a bit. */ |
| 1156 | pdbname = ML_(dinfo_zalloc)("di.debuginfo.lpd1", sz_exename+5); |
| 1157 | VG_(strcpy)(pdbname, exename); |
| 1158 | vg_assert(pdbname[sz_exename+5-1] == 0); |
| 1159 | dot = VG_(strrchr)(pdbname, '.'); |
| 1160 | if (!dot) |
| 1161 | goto out; /* there's no dot in the exe's name ?! */ |
| 1162 | if (dot[1] == 0) |
| 1163 | goto out; /* hmm, path ends in "." */ |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1164 | |
sewardj | 13ac96d | 2010-02-12 12:12:39 +0000 | [diff] [blame] | 1165 | if ('A' <= dot[1] && dot[1] <= 'Z') |
| 1166 | VG_(strcpy)(dot, ".PDB"); |
| 1167 | else |
| 1168 | VG_(strcpy)(dot, ".pdb"); |
| 1169 | |
| 1170 | vg_assert(pdbname[sz_exename+5-1] == 0); |
| 1171 | } |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1172 | |
| 1173 | /* See if we can find it, and check it's in-dateness. */ |
| 1174 | sres = VG_(stat)(pdbname, &stat_buf); |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 1175 | if (sr_isError(sres)) { |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 1176 | VG_(message)(Vg_UserMsg, "Warning: Missing or un-stat-able %s\n", |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1177 | pdbname); |
| 1178 | if (VG_(clo_verbosity) > 0) |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 1179 | VG_(message)(Vg_UserMsg, "LOAD_PDB_DEBUGINFO: missing: %s\n", pdbname); |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1180 | goto out; |
| 1181 | } |
njn | 9c20ece | 2009-05-20 02:02:30 +0000 | [diff] [blame] | 1182 | pdb_mtime = stat_buf.mtime; |
sewardj | 7138ef0 | 2010-01-29 22:37:02 +0000 | [diff] [blame] | 1183 | |
sewardj | ced8506 | 2010-02-10 13:37:37 +0000 | [diff] [blame] | 1184 | if (obj_mtime > pdb_mtime + 60ULL) { |
sewardj | 7138ef0 | 2010-01-29 22:37:02 +0000 | [diff] [blame] | 1185 | /* PDB file is older than PE file. Really, the PDB should be |
| 1186 | newer than the PE, but that doesn't always seem to be the |
| 1187 | case. Allow the PDB to be up to one minute older. |
| 1188 | Otherwise, it's probably out of date, in which case ignore it |
| 1189 | or we will either (a) print wrong stack traces or more likely |
| 1190 | (b) crash. |
| 1191 | */ |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 1192 | VG_(message)(Vg_UserMsg, |
sewardj | ced8506 | 2010-02-10 13:37:37 +0000 | [diff] [blame] | 1193 | "Warning: %s (mtime = %llu)\n" |
| 1194 | " is older than %s (mtime = %llu)\n", |
| 1195 | pdbname, pdb_mtime, exename, obj_mtime); |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1196 | } |
| 1197 | |
| 1198 | sres = VG_(open)(pdbname, VKI_O_RDONLY, 0); |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 1199 | if (sr_isError(sres)) { |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 1200 | VG_(message)(Vg_UserMsg, "Warning: Can't open %s\n", pdbname); |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1201 | goto out; |
| 1202 | } |
| 1203 | |
sewardj | cd458d2 | 2010-12-06 11:11:29 +0000 | [diff] [blame] | 1204 | /* Looks promising; go on to try and read stuff from it. But don't |
| 1205 | mmap the file. Instead mmap free space and read the file into |
| 1206 | it. This is because files on CIFS filesystems that are mounted |
| 1207 | '-o directio' can't be mmap'd, and that mount option is needed |
| 1208 | to make CIFS work reliably. (See |
| 1209 | http://www.nabble.com/Corrupted-data-on-write-to- |
| 1210 | Windows-2003-Server-t2782623.html) |
| 1211 | This is slower, but at least it works reliably. */ |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 1212 | fd_pdbimage = sr_Res(sres); |
njn | 9c20ece | 2009-05-20 02:02:30 +0000 | [diff] [blame] | 1213 | n_pdbimage = stat_buf.size; |
sewardj | cd458d2 | 2010-12-06 11:11:29 +0000 | [diff] [blame] | 1214 | if (n_pdbimage == 0 || n_pdbimage > 0x7FFFFFFF) { |
| 1215 | // 0x7FFFFFFF: why? Because the VG_(read) just below only |
| 1216 | // can deal with a signed int as the size of data to read, |
| 1217 | // so we can't reliably check for read failure for files |
| 1218 | // greater than that size. Hence just skip them; we're |
| 1219 | // unlikely to encounter a PDB that large anyway. |
| 1220 | VG_(close)(fd_pdbimage); |
| 1221 | goto out; |
| 1222 | } |
| 1223 | sres = VG_(am_mmap_anon_float_valgrind)( n_pdbimage ); |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 1224 | if (sr_isError(sres)) { |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1225 | VG_(close)(fd_pdbimage); |
| 1226 | goto out; |
| 1227 | } |
| 1228 | |
sewardj | cd458d2 | 2010-12-06 11:11:29 +0000 | [diff] [blame] | 1229 | void* pdbimage = (void*)sr_Res(sres); |
| 1230 | r = VG_(read)( fd_pdbimage, pdbimage, (Int)n_pdbimage ); |
| 1231 | if (r < 0 || r != (Int)n_pdbimage) { |
| 1232 | VG_(am_munmap_valgrind)( (Addr)pdbimage, n_pdbimage ); |
| 1233 | VG_(close)(fd_pdbimage); |
| 1234 | goto out; |
| 1235 | } |
| 1236 | |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1237 | if (VG_(clo_verbosity) > 0) |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 1238 | VG_(message)(Vg_UserMsg, "LOAD_PDB_DEBUGINFO: pdbname: %s\n", pdbname); |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1239 | |
| 1240 | /* play safe; always invalidate the CFI cache. I don't know if |
| 1241 | this is necessary, but anyway .. */ |
| 1242 | cfsi_cache__invalidate(); |
| 1243 | /* dump old info for this range, if any */ |
| 1244 | discard_syms_in_range( avma_obj, total_size ); |
| 1245 | |
sewardj | 0f4126c | 2011-09-20 16:10:59 +0000 | [diff] [blame] | 1246 | { DebugInfo* di = find_or_create_DebugInfo_for(exename); |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1247 | |
| 1248 | /* this di must be new, since we just nuked any old stuff in the range */ |
sewardj | a5acac3 | 2011-09-20 21:59:50 +0000 | [diff] [blame] | 1249 | vg_assert(di && !di->fsm.have_rx_map && !di->fsm.have_rw_map); |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1250 | vg_assert(!di->have_dinfo); |
| 1251 | |
| 1252 | /* don't set up any of the di-> fields; let |
| 1253 | ML_(read_pdb_debug_info) do it. */ |
sewardj | 54c45db | 2012-07-13 12:58:55 +0000 | [diff] [blame] | 1254 | ML_(read_pdb_debug_info)( di, avma_obj, bias_obj, |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1255 | pdbimage, n_pdbimage, pdbname, pdb_mtime ); |
| 1256 | // JRS fixme: take notice of return value from read_pdb_debug_info, |
| 1257 | // and handle failure |
| 1258 | vg_assert(di->have_dinfo); // fails if PDB read failed |
| 1259 | VG_(am_munmap_valgrind)( (Addr)pdbimage, n_pdbimage ); |
| 1260 | VG_(close)(fd_pdbimage); |
sewardj | cd458d2 | 2010-12-06 11:11:29 +0000 | [diff] [blame] | 1261 | |
| 1262 | if (VG_(clo_verbosity) > 0) { |
| 1263 | VG_(message)(Vg_UserMsg, "LOAD_PDB_DEBUGINFO: done: " |
| 1264 | "%lu syms, %lu src locs, %lu fpo recs\n", |
| 1265 | di->symtab_used, di->loctab_used, di->fpo_size); |
| 1266 | } |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 1267 | } |
| 1268 | |
| 1269 | out: |
| 1270 | if (pdbname) ML_(dinfo_free)(pdbname); |
| 1271 | } |
| 1272 | |
njn | 8b68b64 | 2009-06-24 00:37:09 +0000 | [diff] [blame] | 1273 | #endif /* defined(VGO_linux) || defined(VGO_darwin) */ |
sewardj | 4ee4f98 | 2006-10-17 01:37:10 +0000 | [diff] [blame] | 1274 | |
| 1275 | |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1276 | /*------------------------------------------------------------*/ |
| 1277 | /*--- ---*/ |
| 1278 | /*--- TOP LEVEL: QUERYING EXISTING DEBUG INFO ---*/ |
| 1279 | /*--- ---*/ |
| 1280 | /*------------------------------------------------------------*/ |
| 1281 | |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 1282 | void VG_(di_discard_ALL_debuginfo)( void ) |
| 1283 | { |
| 1284 | DebugInfo *di, *di2; |
| 1285 | di = debugInfo_list; |
| 1286 | while (di) { |
| 1287 | di2 = di->next; |
| 1288 | VG_(printf)("XXX rm %p\n", di); |
| 1289 | free_DebugInfo( di ); |
| 1290 | di = di2; |
| 1291 | } |
| 1292 | } |
| 1293 | |
| 1294 | |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 1295 | struct _DebugInfoMapping* ML_(find_rx_mapping) ( struct _DebugInfo* di, |
| 1296 | Addr lo, Addr hi ) |
| 1297 | { |
| 1298 | Word i; |
| 1299 | vg_assert(lo <= hi); |
| 1300 | |
| 1301 | /* Optimization: Try to use the last matched rx mapping first */ |
| 1302 | if ( di->last_rx_map |
| 1303 | && lo >= di->last_rx_map->avma |
| 1304 | && hi < di->last_rx_map->avma + di->last_rx_map->size) |
| 1305 | return di->last_rx_map; |
| 1306 | |
| 1307 | for (i = 0; i < VG_(sizeXA)(di->fsm.maps); i++) { |
| 1308 | struct _DebugInfoMapping* map = VG_(indexXA)(di->fsm.maps, i); |
| 1309 | if ( map->rx && map->size > 0 |
| 1310 | && lo >= map->avma && hi < map->avma + map->size) { |
| 1311 | di->last_rx_map = map; |
| 1312 | return map; |
| 1313 | } |
| 1314 | } |
| 1315 | |
| 1316 | return NULL; |
| 1317 | } |
| 1318 | |
| 1319 | |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1320 | /*------------------------------------------------------------*/ |
| 1321 | /*--- Use of symbol table & location info to create ---*/ |
| 1322 | /*--- plausible-looking stack dumps. ---*/ |
| 1323 | /*------------------------------------------------------------*/ |
| 1324 | |
| 1325 | /* Search all symtabs that we know about to locate ptr. If found, set |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1326 | *pdi to the relevant DebugInfo, and *symno to the symtab entry |
| 1327 | *number within that. If not found, *psi is set to NULL. |
| 1328 | If findText==True, only text symbols are searched for. |
| 1329 | If findText==False, only data symbols are searched for. |
| 1330 | */ |
| 1331 | static void search_all_symtabs ( Addr ptr, /*OUT*/DebugInfo** pdi, |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 1332 | /*OUT*/Word* symno, |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1333 | Bool match_anywhere_in_sym, |
| 1334 | Bool findText ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1335 | { |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 1336 | Word sno; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1337 | DebugInfo* di; |
| 1338 | Bool inRange; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1339 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1340 | for (di = debugInfo_list; di != NULL; di = di->next) { |
| 1341 | |
| 1342 | if (findText) { |
sewardj | 51c9d37 | 2010-04-12 20:56:56 +0000 | [diff] [blame] | 1343 | /* Consider any symbol in the r-x mapped area to be text. |
| 1344 | See Comment_Regarding_Text_Range_Checks in storage.c for |
| 1345 | details. */ |
sewardj | a5acac3 | 2011-09-20 21:59:50 +0000 | [diff] [blame] | 1346 | inRange = di->fsm.have_rx_map |
sewardj | 6b5625b | 2012-07-13 11:24:05 +0000 | [diff] [blame] | 1347 | && (ML_(find_rx_mapping)(di, ptr, ptr) != NULL); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1348 | } else { |
| 1349 | inRange = (di->data_present |
| 1350 | && di->data_size > 0 |
| 1351 | && di->data_avma <= ptr |
| 1352 | && ptr < di->data_avma + di->data_size) |
| 1353 | || |
| 1354 | (di->sdata_present |
| 1355 | && di->sdata_size > 0 |
| 1356 | && di->sdata_avma <= ptr |
| 1357 | && ptr < di->sdata_avma + di->sdata_size) |
| 1358 | || |
| 1359 | (di->bss_present |
| 1360 | && di->bss_size > 0 |
| 1361 | && di->bss_avma <= ptr |
sewardj | 5706ca9 | 2009-01-22 21:18:15 +0000 | [diff] [blame] | 1362 | && ptr < di->bss_avma + di->bss_size) |
| 1363 | || |
| 1364 | (di->sbss_present |
| 1365 | && di->sbss_size > 0 |
| 1366 | && di->sbss_avma <= ptr |
| 1367 | && ptr < di->sbss_avma + di->sbss_size) |
| 1368 | || |
| 1369 | (di->rodata_present |
| 1370 | && di->rodata_size > 0 |
| 1371 | && di->rodata_avma <= ptr |
| 1372 | && ptr < di->rodata_avma + di->rodata_size); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1373 | } |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1374 | |
| 1375 | if (!inRange) continue; |
| 1376 | |
| 1377 | sno = ML_(search_one_symtab) ( |
| 1378 | di, ptr, match_anywhere_in_sym, findText ); |
| 1379 | if (sno == -1) goto not_found; |
| 1380 | *symno = sno; |
| 1381 | *pdi = di; |
| 1382 | return; |
| 1383 | |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1384 | } |
| 1385 | not_found: |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1386 | *pdi = NULL; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1387 | } |
| 1388 | |
| 1389 | |
| 1390 | /* Search all loctabs that we know about to locate ptr. If found, set |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1391 | *pdi to the relevant DebugInfo, and *locno to the loctab entry |
| 1392 | *number within that. If not found, *pdi is set to NULL. */ |
| 1393 | static void search_all_loctabs ( Addr ptr, /*OUT*/DebugInfo** pdi, |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 1394 | /*OUT*/Word* locno ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1395 | { |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 1396 | Word lno; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1397 | DebugInfo* di; |
| 1398 | for (di = debugInfo_list; di != NULL; di = di->next) { |
| 1399 | if (di->text_present |
sewardj | 5706ca9 | 2009-01-22 21:18:15 +0000 | [diff] [blame] | 1400 | && di->text_size > 0 |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1401 | && di->text_avma <= ptr |
| 1402 | && ptr < di->text_avma + di->text_size) { |
| 1403 | lno = ML_(search_one_loctab) ( di, ptr ); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1404 | if (lno == -1) goto not_found; |
| 1405 | *locno = lno; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1406 | *pdi = di; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1407 | return; |
| 1408 | } |
| 1409 | } |
| 1410 | not_found: |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1411 | *pdi = NULL; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1412 | } |
| 1413 | |
| 1414 | |
| 1415 | /* The whole point of this whole big deal: map a code address to a |
| 1416 | plausible symbol name. Returns False if no idea; otherwise True. |
njn | 6b7611b | 2009-02-11 06:06:10 +0000 | [diff] [blame] | 1417 | Caller supplies buf and nbuf. If do_cxx_demangling is False, don't do |
| 1418 | C++ demangling, regardless of VG_(clo_demangle) -- probably because the |
| 1419 | call has come from VG_(get_fnname_raw)(). findText |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1420 | indicates whether we're looking for a text symbol or a data symbol |
| 1421 | -- caller must choose one kind or the other. */ |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1422 | static |
njn | 6b7611b | 2009-02-11 06:06:10 +0000 | [diff] [blame] | 1423 | Bool get_sym_name ( Bool do_cxx_demangling, Bool do_z_demangling, |
| 1424 | Bool do_below_main_renaming, |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1425 | Addr a, HChar* buf, Int nbuf, |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1426 | Bool match_anywhere_in_sym, Bool show_offset, |
njn | c4431bf | 2009-01-15 21:29:24 +0000 | [diff] [blame] | 1427 | Bool findText, /*OUT*/PtrdiffT* offsetP ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1428 | { |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1429 | DebugInfo* di; |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 1430 | Word sno; |
njn | c4431bf | 2009-01-15 21:29:24 +0000 | [diff] [blame] | 1431 | PtrdiffT offset; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1432 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1433 | search_all_symtabs ( a, &di, &sno, match_anywhere_in_sym, findText ); |
| 1434 | if (di == NULL) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1435 | return False; |
njn | 6882443 | 2009-02-10 06:48:00 +0000 | [diff] [blame] | 1436 | |
sewardj | a5cace0 | 2011-08-15 09:42:34 +0000 | [diff] [blame] | 1437 | vg_assert(di->symtab[sno].pri_name); |
njn | 6b7611b | 2009-02-11 06:06:10 +0000 | [diff] [blame] | 1438 | VG_(demangle) ( do_cxx_demangling, do_z_demangling, |
sewardj | a5cace0 | 2011-08-15 09:42:34 +0000 | [diff] [blame] | 1439 | di->symtab[sno].pri_name, buf, nbuf ); |
njn | 6b7611b | 2009-02-11 06:06:10 +0000 | [diff] [blame] | 1440 | |
| 1441 | /* Do the below-main hack */ |
| 1442 | // To reduce the endless nuisance of multiple different names |
| 1443 | // for "the frame below main()" screwing up the testsuite, change all |
| 1444 | // known incarnations of said into a single name, "(below main)", if |
| 1445 | // --show-below-main=yes. |
| 1446 | if ( do_below_main_renaming && ! VG_(clo_show_below_main) && |
| 1447 | Vg_FnNameBelowMain == VG_(get_fnname_kind)(buf) ) |
| 1448 | { |
| 1449 | VG_(strncpy_safely)(buf, "(below main)", nbuf); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1450 | } |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1451 | offset = a - di->symtab[sno].addr; |
njn | c4431bf | 2009-01-15 21:29:24 +0000 | [diff] [blame] | 1452 | if (offsetP) *offsetP = offset; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1453 | |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1454 | if (show_offset && offset != 0) { |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1455 | HChar buf2[12]; |
| 1456 | HChar* symend = buf + VG_(strlen)(buf); |
| 1457 | HChar* end = buf + nbuf; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1458 | Int len; |
| 1459 | |
njn | c4431bf | 2009-01-15 21:29:24 +0000 | [diff] [blame] | 1460 | len = VG_(sprintf)(buf2, "%c%ld", |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1461 | offset < 0 ? '-' : '+', |
| 1462 | offset < 0 ? -offset : offset); |
| 1463 | vg_assert(len < (Int)sizeof(buf2)); |
| 1464 | |
| 1465 | if (len < (end - symend)) { |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1466 | HChar *cp = buf2; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1467 | VG_(memcpy)(symend, cp, len+1); |
| 1468 | } |
| 1469 | } |
| 1470 | |
njn | 6b7611b | 2009-02-11 06:06:10 +0000 | [diff] [blame] | 1471 | buf[nbuf-1] = 0; /* paranoia */ |
| 1472 | |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1473 | return True; |
| 1474 | } |
| 1475 | |
| 1476 | /* ppc64-linux only: find the TOC pointer (R2 value) that should be in |
| 1477 | force at the entry point address of the function containing |
| 1478 | guest_code_addr. Returns 0 if not known. */ |
| 1479 | Addr VG_(get_tocptr) ( Addr guest_code_addr ) |
| 1480 | { |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1481 | DebugInfo* si; |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 1482 | Word sno; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1483 | search_all_symtabs ( guest_code_addr, |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1484 | &si, &sno, |
| 1485 | True/*match_anywhere_in_fun*/, |
| 1486 | True/*consider text symbols only*/ ); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1487 | if (si == NULL) |
| 1488 | return 0; |
| 1489 | else |
| 1490 | return si->symtab[sno].tocptr; |
| 1491 | } |
| 1492 | |
| 1493 | /* This is available to tools... always demangle C++ names, |
| 1494 | match anywhere in function, but don't show offsets. */ |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1495 | Bool VG_(get_fnname) ( Addr a, HChar* buf, Int nbuf ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1496 | { |
njn | 6b7611b | 2009-02-11 06:06:10 +0000 | [diff] [blame] | 1497 | return get_sym_name ( /*C++-demangle*/True, /*Z-demangle*/True, |
| 1498 | /*below-main-renaming*/True, |
| 1499 | a, buf, nbuf, |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1500 | /*match_anywhere_in_fun*/True, |
| 1501 | /*show offset?*/False, |
| 1502 | /*text syms only*/True, |
| 1503 | /*offsetP*/NULL ); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1504 | } |
| 1505 | |
| 1506 | /* This is available to tools... always demangle C++ names, |
| 1507 | match anywhere in function, and show offset if nonzero. */ |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1508 | Bool VG_(get_fnname_w_offset) ( Addr a, HChar* buf, Int nbuf ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1509 | { |
njn | 6b7611b | 2009-02-11 06:06:10 +0000 | [diff] [blame] | 1510 | return get_sym_name ( /*C++-demangle*/True, /*Z-demangle*/True, |
| 1511 | /*below-main-renaming*/True, |
| 1512 | a, buf, nbuf, |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1513 | /*match_anywhere_in_fun*/True, |
| 1514 | /*show offset?*/True, |
| 1515 | /*text syms only*/True, |
| 1516 | /*offsetP*/NULL ); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1517 | } |
| 1518 | |
| 1519 | /* This is available to tools... always demangle C++ names, |
| 1520 | only succeed if 'a' matches first instruction of function, |
| 1521 | and don't show offsets. */ |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1522 | Bool VG_(get_fnname_if_entry) ( Addr a, HChar* buf, Int nbuf ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1523 | { |
njn | 6b7611b | 2009-02-11 06:06:10 +0000 | [diff] [blame] | 1524 | return get_sym_name ( /*C++-demangle*/True, /*Z-demangle*/True, |
| 1525 | /*below-main-renaming*/True, |
| 1526 | a, buf, nbuf, |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1527 | /*match_anywhere_in_fun*/False, |
| 1528 | /*show offset?*/False, |
| 1529 | /*text syms only*/True, |
| 1530 | /*offsetP*/NULL ); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1531 | } |
| 1532 | |
njn | 6b7611b | 2009-02-11 06:06:10 +0000 | [diff] [blame] | 1533 | /* This is only available to core... don't C++-demangle, don't Z-demangle, |
| 1534 | don't rename below-main, match anywhere in function, and don't show |
| 1535 | offsets. */ |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1536 | Bool VG_(get_fnname_raw) ( Addr a, HChar* buf, Int nbuf ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1537 | { |
njn | 6b7611b | 2009-02-11 06:06:10 +0000 | [diff] [blame] | 1538 | return get_sym_name ( /*C++-demangle*/False, /*Z-demangle*/False, |
| 1539 | /*below-main-renaming*/False, |
| 1540 | a, buf, nbuf, |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1541 | /*match_anywhere_in_fun*/True, |
| 1542 | /*show offset?*/False, |
| 1543 | /*text syms only*/True, |
| 1544 | /*offsetP*/NULL ); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1545 | } |
| 1546 | |
| 1547 | /* This is only available to core... don't demangle C++ names, but do |
njn | 6b7611b | 2009-02-11 06:06:10 +0000 | [diff] [blame] | 1548 | do Z-demangling and below-main-renaming, match anywhere in function, and |
| 1549 | don't show offsets. */ |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1550 | Bool VG_(get_fnname_no_cxx_demangle) ( Addr a, HChar* buf, Int nbuf ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1551 | { |
njn | 6b7611b | 2009-02-11 06:06:10 +0000 | [diff] [blame] | 1552 | return get_sym_name ( /*C++-demangle*/False, /*Z-demangle*/True, |
| 1553 | /*below-main-renaming*/True, |
| 1554 | a, buf, nbuf, |
| 1555 | /*match_anywhere_in_fun*/True, |
| 1556 | /*show offset?*/False, |
| 1557 | /*text syms only*/True, |
| 1558 | /*offsetP*/NULL ); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1559 | } |
| 1560 | |
sewardj | 5db1540 | 2012-06-07 09:13:21 +0000 | [diff] [blame] | 1561 | /* mips-linux only: find the offset of current address. This is needed for |
| 1562 | stack unwinding for MIPS. |
| 1563 | */ |
| 1564 | Bool VG_(get_inst_offset_in_function)( Addr a, |
| 1565 | /*OUT*/PtrdiffT* offset ) |
| 1566 | { |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1567 | HChar fnname[64]; |
sewardj | 5db1540 | 2012-06-07 09:13:21 +0000 | [diff] [blame] | 1568 | return get_sym_name ( /*C++-demangle*/False, /*Z-demangle*/False, |
| 1569 | /*below-main-renaming*/False, |
| 1570 | a, fnname, 64, |
| 1571 | /*match_anywhere_in_sym*/True, |
| 1572 | /*show offset?*/True, |
| 1573 | /*data syms only please*/True, |
| 1574 | offset ); |
| 1575 | } |
| 1576 | |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1577 | Vg_FnNameKind VG_(get_fnname_kind) ( HChar* name ) |
njn | 6882443 | 2009-02-10 06:48:00 +0000 | [diff] [blame] | 1578 | { |
| 1579 | if (VG_STREQ("main", name)) { |
| 1580 | return Vg_FnNameMain; |
| 1581 | |
| 1582 | } else if ( |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 1583 | # if defined(VGO_linux) |
njn | 6882443 | 2009-02-10 06:48:00 +0000 | [diff] [blame] | 1584 | VG_STREQ("__libc_start_main", name) || // glibc glibness |
| 1585 | VG_STREQ("generic_start_main", name) || // Yellow Dog doggedness |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 1586 | # elif defined(VGO_darwin) |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 1587 | // See readmacho.c for an explanation of this. |
| 1588 | VG_STREQ("start_according_to_valgrind", name) || // Darwin, darling |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 1589 | # else |
| 1590 | # error "Unknown OS" |
| 1591 | # endif |
njn | 6882443 | 2009-02-10 06:48:00 +0000 | [diff] [blame] | 1592 | 0) { |
| 1593 | return Vg_FnNameBelowMain; |
| 1594 | |
| 1595 | } else { |
| 1596 | return Vg_FnNameNormal; |
| 1597 | } |
| 1598 | } |
| 1599 | |
| 1600 | Vg_FnNameKind VG_(get_fnname_kind_from_IP) ( Addr ip ) |
| 1601 | { |
| 1602 | // We don't need a big buffer; all the special names are small. |
| 1603 | #define BUFLEN 50 |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1604 | HChar buf[50]; |
njn | 6882443 | 2009-02-10 06:48:00 +0000 | [diff] [blame] | 1605 | |
| 1606 | // We don't demangle, because it's faster not to, and the special names |
| 1607 | // we're looking for won't be demangled. |
njn | 6b7611b | 2009-02-11 06:06:10 +0000 | [diff] [blame] | 1608 | if (VG_(get_fnname_raw) ( ip, buf, BUFLEN )) { |
njn | 6882443 | 2009-02-10 06:48:00 +0000 | [diff] [blame] | 1609 | buf[BUFLEN-1] = '\0'; // paranoia |
| 1610 | return VG_(get_fnname_kind)(buf); |
| 1611 | } else { |
| 1612 | return Vg_FnNameNormal; // Don't know the name, treat it as normal. |
| 1613 | } |
| 1614 | } |
| 1615 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1616 | /* Looks up data_addr in the collection of data symbols, and if found |
| 1617 | puts its name (or as much as will fit) into dname[0 .. n_dname-1], |
| 1618 | which is guaranteed to be zero terminated. Also data_addr's offset |
| 1619 | from the symbol start is put into *offset. */ |
| 1620 | Bool VG_(get_datasym_and_offset)( Addr data_addr, |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1621 | /*OUT*/HChar* dname, Int n_dname, |
njn | c4431bf | 2009-01-15 21:29:24 +0000 | [diff] [blame] | 1622 | /*OUT*/PtrdiffT* offset ) |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1623 | { |
| 1624 | Bool ok; |
| 1625 | vg_assert(n_dname > 1); |
njn | 6b7611b | 2009-02-11 06:06:10 +0000 | [diff] [blame] | 1626 | ok = get_sym_name ( /*C++-demangle*/False, /*Z-demangle*/False, |
| 1627 | /*below-main-renaming*/False, |
| 1628 | data_addr, dname, n_dname, |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1629 | /*match_anywhere_in_sym*/True, |
| 1630 | /*show offset?*/False, |
| 1631 | /*data syms only please*/False, |
| 1632 | offset ); |
| 1633 | if (!ok) |
| 1634 | return False; |
| 1635 | dname[n_dname-1] = 0; |
| 1636 | return True; |
| 1637 | } |
| 1638 | |
| 1639 | /* Map a code address to the name of a shared object file or the |
| 1640 | executable. Returns False if no idea; otherwise True. Doesn't |
| 1641 | require debug info. Caller supplies buf and nbuf. */ |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1642 | Bool VG_(get_objname) ( Addr a, HChar* buf, Int nbuf ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1643 | { |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1644 | DebugInfo* di; |
tom | f32ec7f | 2008-01-08 17:44:04 +0000 | [diff] [blame] | 1645 | const NSegment *seg; |
| 1646 | HChar* filename; |
sewardj | 4ee4f98 | 2006-10-17 01:37:10 +0000 | [diff] [blame] | 1647 | vg_assert(nbuf > 0); |
sewardj | 7cf4e6b | 2008-05-01 20:24:26 +0000 | [diff] [blame] | 1648 | /* Look in the debugInfo_list to find the name. In most cases we |
| 1649 | expect this to produce a result. */ |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1650 | for (di = debugInfo_list; di != NULL; di = di->next) { |
| 1651 | if (di->text_present |
sewardj | 5706ca9 | 2009-01-22 21:18:15 +0000 | [diff] [blame] | 1652 | && di->text_size > 0 |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1653 | && di->text_avma <= a |
| 1654 | && a < di->text_avma + di->text_size) { |
sewardj | a5acac3 | 2011-09-20 21:59:50 +0000 | [diff] [blame] | 1655 | VG_(strncpy_safely)(buf, di->fsm.filename, nbuf); |
sewardj | 4ee4f98 | 2006-10-17 01:37:10 +0000 | [diff] [blame] | 1656 | buf[nbuf-1] = 0; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1657 | return True; |
| 1658 | } |
| 1659 | } |
sewardj | 7cf4e6b | 2008-05-01 20:24:26 +0000 | [diff] [blame] | 1660 | /* Last-ditch fallback position: if we don't find the address in |
| 1661 | the debugInfo_list, ask the address space manager whether it |
| 1662 | knows the name of the file associated with this mapping. This |
| 1663 | allows us to print the names of exe/dll files in the stack trace |
| 1664 | when running programs under wine. */ |
| 1665 | if ( (seg = VG_(am_find_nsegment(a))) != NULL |
| 1666 | && (filename = VG_(am_get_filename)(seg)) != NULL ) { |
tom | f32ec7f | 2008-01-08 17:44:04 +0000 | [diff] [blame] | 1667 | VG_(strncpy_safely)(buf, filename, nbuf); |
| 1668 | return True; |
| 1669 | } |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1670 | return False; |
| 1671 | } |
| 1672 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1673 | /* Map a code address to its DebugInfo. Returns NULL if not found. Doesn't |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1674 | require debug info. */ |
sewardj | e3f1e59 | 2009-07-31 09:41:29 +0000 | [diff] [blame] | 1675 | DebugInfo* VG_(find_DebugInfo) ( Addr a ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1676 | { |
sewardj | e3f1e59 | 2009-07-31 09:41:29 +0000 | [diff] [blame] | 1677 | static UWord n_search = 0; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1678 | DebugInfo* di; |
sewardj | e3f1e59 | 2009-07-31 09:41:29 +0000 | [diff] [blame] | 1679 | n_search++; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1680 | for (di = debugInfo_list; di != NULL; di = di->next) { |
| 1681 | if (di->text_present |
sewardj | 5706ca9 | 2009-01-22 21:18:15 +0000 | [diff] [blame] | 1682 | && di->text_size > 0 |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1683 | && di->text_avma <= a |
| 1684 | && a < di->text_avma + di->text_size) { |
sewardj | e3f1e59 | 2009-07-31 09:41:29 +0000 | [diff] [blame] | 1685 | if (0 == (n_search & 0xF)) |
| 1686 | move_DebugInfo_one_step_forward( di ); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1687 | return di; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1688 | } |
| 1689 | } |
| 1690 | return NULL; |
| 1691 | } |
| 1692 | |
| 1693 | /* Map a code address to a filename. Returns True if successful. */ |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1694 | Bool VG_(get_filename)( Addr a, HChar* filename, Int n_filename ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1695 | { |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1696 | DebugInfo* si; |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 1697 | Word locno; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1698 | search_all_loctabs ( a, &si, &locno ); |
| 1699 | if (si == NULL) |
| 1700 | return False; |
| 1701 | VG_(strncpy_safely)(filename, si->loctab[locno].filename, n_filename); |
| 1702 | return True; |
| 1703 | } |
| 1704 | |
| 1705 | /* Map a code address to a line number. Returns True if successful. */ |
| 1706 | Bool VG_(get_linenum)( Addr a, UInt* lineno ) |
| 1707 | { |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1708 | DebugInfo* si; |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 1709 | Word locno; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1710 | search_all_loctabs ( a, &si, &locno ); |
| 1711 | if (si == NULL) |
| 1712 | return False; |
| 1713 | *lineno = si->loctab[locno].lineno; |
| 1714 | |
| 1715 | return True; |
| 1716 | } |
| 1717 | |
| 1718 | /* Map a code address to a filename/line number/dir name info. |
| 1719 | See prototype for detailed description of behaviour. |
| 1720 | */ |
| 1721 | Bool VG_(get_filename_linenum) ( Addr a, |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1722 | /*OUT*/HChar* filename, Int n_filename, |
| 1723 | /*OUT*/HChar* dirname, Int n_dirname, |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1724 | /*OUT*/Bool* dirname_available, |
| 1725 | /*OUT*/UInt* lineno ) |
| 1726 | { |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1727 | DebugInfo* si; |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 1728 | Word locno; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1729 | |
| 1730 | vg_assert( (dirname == NULL && dirname_available == NULL) |
| 1731 | || |
| 1732 | (dirname != NULL && dirname_available != NULL) ); |
| 1733 | |
| 1734 | search_all_loctabs ( a, &si, &locno ); |
njn | c1b1d42 | 2007-04-19 23:35:42 +0000 | [diff] [blame] | 1735 | if (si == NULL) { |
njn | db5c657 | 2007-04-20 02:15:28 +0000 | [diff] [blame] | 1736 | if (dirname_available) { |
| 1737 | *dirname_available = False; |
| 1738 | *dirname = 0; |
| 1739 | } |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1740 | return False; |
njn | c1b1d42 | 2007-04-19 23:35:42 +0000 | [diff] [blame] | 1741 | } |
| 1742 | |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1743 | VG_(strncpy_safely)(filename, si->loctab[locno].filename, n_filename); |
| 1744 | *lineno = si->loctab[locno].lineno; |
| 1745 | |
| 1746 | if (dirname) { |
| 1747 | /* caller wants directory info too .. */ |
| 1748 | vg_assert(n_dirname > 0); |
| 1749 | if (si->loctab[locno].dirname) { |
| 1750 | /* .. and we have some */ |
| 1751 | *dirname_available = True; |
| 1752 | VG_(strncpy_safely)(dirname, si->loctab[locno].dirname, |
| 1753 | n_dirname); |
| 1754 | } else { |
| 1755 | /* .. but we don't have any */ |
| 1756 | *dirname_available = False; |
| 1757 | *dirname = 0; |
| 1758 | } |
| 1759 | } |
| 1760 | |
| 1761 | return True; |
| 1762 | } |
| 1763 | |
| 1764 | |
sewardj | 4ee4f98 | 2006-10-17 01:37:10 +0000 | [diff] [blame] | 1765 | /* Map a function name to its entry point and toc pointer. Is done by |
| 1766 | sequential search of all symbol tables, so is very slow. To |
| 1767 | mitigate the worst performance effects, you may specify a soname |
| 1768 | pattern, and only objects matching that pattern are searched. |
| 1769 | Therefore specify "*" to search all the objects. On TOC-afflicted |
| 1770 | platforms, a symbol is deemed to be found only if it has a nonzero |
| 1771 | TOC pointer. */ |
florian | 6bd9dc1 | 2012-11-23 16:17:43 +0000 | [diff] [blame] | 1772 | Bool VG_(lookup_symbol_SLOW)(const HChar* sopatt, HChar* name, |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1773 | Addr* pEnt, Addr* pToc) |
sewardj | 4ee4f98 | 2006-10-17 01:37:10 +0000 | [diff] [blame] | 1774 | { |
| 1775 | Bool require_pToc = False; |
| 1776 | Int i; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1777 | DebugInfo* si; |
sewardj | 4ee4f98 | 2006-10-17 01:37:10 +0000 | [diff] [blame] | 1778 | Bool debug = False; |
| 1779 | # if defined(VG_PLAT_USES_PPCTOC) |
| 1780 | require_pToc = True; |
| 1781 | # endif |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 1782 | for (si = debugInfo_list; si; si = si->next) { |
sewardj | 4ee4f98 | 2006-10-17 01:37:10 +0000 | [diff] [blame] | 1783 | if (debug) |
| 1784 | VG_(printf)("lookup_symbol_SLOW: considering %s\n", si->soname); |
| 1785 | if (!VG_(string_match)(sopatt, si->soname)) { |
| 1786 | if (debug) |
| 1787 | VG_(printf)(" ... skip\n"); |
| 1788 | continue; |
| 1789 | } |
| 1790 | for (i = 0; i < si->symtab_used; i++) { |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1791 | HChar* pri_name = si->symtab[i].pri_name; |
sewardj | a5cace0 | 2011-08-15 09:42:34 +0000 | [diff] [blame] | 1792 | tl_assert(pri_name); |
| 1793 | if (0==VG_(strcmp)(name, pri_name) |
sewardj | 4ee4f98 | 2006-10-17 01:37:10 +0000 | [diff] [blame] | 1794 | && (require_pToc ? si->symtab[i].tocptr : True)) { |
| 1795 | *pEnt = si->symtab[i].addr; |
| 1796 | *pToc = si->symtab[i].tocptr; |
| 1797 | return True; |
| 1798 | } |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1799 | HChar** sec_names = si->symtab[i].sec_names; |
sewardj | a5cace0 | 2011-08-15 09:42:34 +0000 | [diff] [blame] | 1800 | if (sec_names) { |
| 1801 | tl_assert(sec_names[0]); |
| 1802 | while (*sec_names) { |
| 1803 | if (0==VG_(strcmp)(name, *sec_names) |
| 1804 | && (require_pToc ? si->symtab[i].tocptr : True)) { |
| 1805 | *pEnt = si->symtab[i].addr; |
| 1806 | *pToc = si->symtab[i].tocptr; |
| 1807 | return True; |
| 1808 | } |
| 1809 | sec_names++; |
| 1810 | } |
| 1811 | } |
sewardj | 4ee4f98 | 2006-10-17 01:37:10 +0000 | [diff] [blame] | 1812 | } |
| 1813 | } |
| 1814 | return False; |
| 1815 | } |
| 1816 | |
| 1817 | |
sewardj | e872fec | 2007-01-10 15:42:15 +0000 | [diff] [blame] | 1818 | /* VG_(describe_IP): print into buf info on code address, function |
| 1819 | name and filename. */ |
| 1820 | |
| 1821 | /* Copy str into buf starting at n, but not going past buf[n_buf-1] |
| 1822 | and always ensuring that buf is zero-terminated. */ |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1823 | |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1824 | static Int putStr ( Int n, Int n_buf, HChar* buf, const HChar* str ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1825 | { |
sewardj | e872fec | 2007-01-10 15:42:15 +0000 | [diff] [blame] | 1826 | vg_assert(n_buf > 0); |
| 1827 | vg_assert(n >= 0 && n < n_buf); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1828 | for (; n < n_buf-1 && *str != 0; n++,str++) |
| 1829 | buf[n] = *str; |
sewardj | e872fec | 2007-01-10 15:42:15 +0000 | [diff] [blame] | 1830 | vg_assert(n >= 0 && n < n_buf); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1831 | buf[n] = '\0'; |
| 1832 | return n; |
| 1833 | } |
sewardj | e872fec | 2007-01-10 15:42:15 +0000 | [diff] [blame] | 1834 | |
| 1835 | /* Same as putStr, but escaping chars for XML output, and |
| 1836 | also not adding more than count chars to n_buf. */ |
| 1837 | |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1838 | static Int putStrEsc ( Int n, Int n_buf, Int count, HChar* buf, HChar* str ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1839 | { |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1840 | HChar alt[2]; |
sewardj | e872fec | 2007-01-10 15:42:15 +0000 | [diff] [blame] | 1841 | vg_assert(n_buf > 0); |
| 1842 | vg_assert(count >= 0 && count < n_buf); |
| 1843 | vg_assert(n >= 0 && n < n_buf); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1844 | for (; *str != 0; str++) { |
sewardj | e872fec | 2007-01-10 15:42:15 +0000 | [diff] [blame] | 1845 | vg_assert(count >= 0); |
| 1846 | if (count <= 0) |
| 1847 | goto done; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1848 | switch (*str) { |
sewardj | e872fec | 2007-01-10 15:42:15 +0000 | [diff] [blame] | 1849 | case '&': |
| 1850 | if (count < 5) goto done; |
| 1851 | n = putStr( n, n_buf, buf, "&"); |
| 1852 | count -= 5; |
| 1853 | break; |
| 1854 | case '<': |
| 1855 | if (count < 4) goto done; |
| 1856 | n = putStr( n, n_buf, buf, "<"); |
| 1857 | count -= 4; |
| 1858 | break; |
| 1859 | case '>': |
| 1860 | if (count < 4) goto done; |
| 1861 | n = putStr( n, n_buf, buf, ">"); |
| 1862 | count -= 4; |
| 1863 | break; |
| 1864 | default: |
| 1865 | if (count < 1) goto done; |
| 1866 | alt[0] = *str; |
| 1867 | alt[1] = 0; |
| 1868 | n = putStr( n, n_buf, buf, alt ); |
| 1869 | count -= 1; |
| 1870 | break; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1871 | } |
| 1872 | } |
sewardj | e872fec | 2007-01-10 15:42:15 +0000 | [diff] [blame] | 1873 | done: |
| 1874 | vg_assert(count >= 0); /* should not go -ve in loop */ |
| 1875 | vg_assert(n >= 0 && n < n_buf); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1876 | return n; |
| 1877 | } |
| 1878 | |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1879 | HChar* VG_(describe_IP)(Addr eip, HChar* buf, Int n_buf) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1880 | { |
| 1881 | # define APPEND(_str) \ |
sewardj | e872fec | 2007-01-10 15:42:15 +0000 | [diff] [blame] | 1882 | n = putStr(n, n_buf, buf, _str) |
| 1883 | # define APPEND_ESC(_count,_str) \ |
| 1884 | n = putStrEsc(n, n_buf, (_count), buf, (_str)) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1885 | # define BUF_LEN 4096 |
| 1886 | |
| 1887 | UInt lineno; |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1888 | HChar ibuf[50]; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1889 | Int n = 0; |
sewardj | 14cdbf8 | 2010-10-12 00:44:05 +0000 | [diff] [blame] | 1890 | |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1891 | static HChar buf_fn[BUF_LEN]; |
| 1892 | static HChar buf_obj[BUF_LEN]; |
| 1893 | static HChar buf_srcloc[BUF_LEN]; |
| 1894 | static HChar buf_dirname[BUF_LEN]; |
sewardj | 14cdbf8 | 2010-10-12 00:44:05 +0000 | [diff] [blame] | 1895 | buf_fn[0] = buf_obj[0] = buf_srcloc[0] = buf_dirname[0] = 0; |
| 1896 | |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1897 | Bool know_dirinfo = False; |
sewardj | 4ee4f98 | 2006-10-17 01:37:10 +0000 | [diff] [blame] | 1898 | Bool know_fnname = VG_(clo_sym_offsets) |
| 1899 | ? VG_(get_fnname_w_offset) (eip, buf_fn, BUF_LEN) |
| 1900 | : VG_(get_fnname) (eip, buf_fn, BUF_LEN); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1901 | Bool know_objname = VG_(get_objname)(eip, buf_obj, BUF_LEN); |
| 1902 | Bool know_srcloc = VG_(get_filename_linenum)( |
| 1903 | eip, |
| 1904 | buf_srcloc, BUF_LEN, |
| 1905 | buf_dirname, BUF_LEN, &know_dirinfo, |
| 1906 | &lineno |
| 1907 | ); |
sewardj | 14cdbf8 | 2010-10-12 00:44:05 +0000 | [diff] [blame] | 1908 | buf_fn [ sizeof(buf_fn)-1 ] = 0; |
| 1909 | buf_obj [ sizeof(buf_obj)-1 ] = 0; |
| 1910 | buf_srcloc [ sizeof(buf_srcloc)-1 ] = 0; |
| 1911 | buf_dirname[ sizeof(buf_dirname)-1 ] = 0; |
| 1912 | |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1913 | if (VG_(clo_xml)) { |
| 1914 | |
| 1915 | Bool human_readable = True; |
florian | 6bd9dc1 | 2012-11-23 16:17:43 +0000 | [diff] [blame] | 1916 | const HChar* maybe_newline = human_readable ? "\n " : ""; |
| 1917 | const HChar* maybe_newline2 = human_readable ? "\n " : ""; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1918 | |
sewardj | e872fec | 2007-01-10 15:42:15 +0000 | [diff] [blame] | 1919 | /* Print in XML format, dumping in as much info as we know. |
| 1920 | Ensure all tags are balanced even if the individual strings |
| 1921 | are too long. Allocate 1/10 of BUF_LEN to the object name, |
| 1922 | 6/10s to the function name, 1/10 to the directory name and |
| 1923 | 1/10 to the file name, leaving 1/10 for all the fixed-length |
| 1924 | stuff. */ |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1925 | APPEND("<frame>"); |
sewardj | a44b15f | 2007-02-16 14:10:24 +0000 | [diff] [blame] | 1926 | VG_(sprintf)(ibuf,"<ip>0x%llX</ip>", (ULong)eip); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1927 | APPEND(maybe_newline); |
| 1928 | APPEND(ibuf); |
| 1929 | if (know_objname) { |
| 1930 | APPEND(maybe_newline); |
| 1931 | APPEND("<obj>"); |
sewardj | e872fec | 2007-01-10 15:42:15 +0000 | [diff] [blame] | 1932 | APPEND_ESC(1*BUF_LEN/10, buf_obj); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1933 | APPEND("</obj>"); |
| 1934 | } |
| 1935 | if (know_fnname) { |
| 1936 | APPEND(maybe_newline); |
| 1937 | APPEND("<fn>"); |
sewardj | e872fec | 2007-01-10 15:42:15 +0000 | [diff] [blame] | 1938 | APPEND_ESC(6*BUF_LEN/10, buf_fn); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1939 | APPEND("</fn>"); |
| 1940 | } |
| 1941 | if (know_srcloc) { |
| 1942 | if (know_dirinfo) { |
| 1943 | APPEND(maybe_newline); |
| 1944 | APPEND("<dir>"); |
sewardj | e872fec | 2007-01-10 15:42:15 +0000 | [diff] [blame] | 1945 | APPEND_ESC(1*BUF_LEN/10, buf_dirname); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1946 | APPEND("</dir>"); |
| 1947 | } |
| 1948 | APPEND(maybe_newline); |
| 1949 | APPEND("<file>"); |
sewardj | e872fec | 2007-01-10 15:42:15 +0000 | [diff] [blame] | 1950 | APPEND_ESC(1*BUF_LEN/10, buf_srcloc); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1951 | APPEND("</file>"); |
| 1952 | APPEND(maybe_newline); |
| 1953 | APPEND("<line>"); |
| 1954 | VG_(sprintf)(ibuf,"%d",lineno); |
| 1955 | APPEND(ibuf); |
| 1956 | APPEND("</line>"); |
| 1957 | } |
| 1958 | APPEND(maybe_newline2); |
| 1959 | APPEND("</frame>"); |
| 1960 | |
| 1961 | } else { |
| 1962 | |
| 1963 | /* Print for humans to read */ |
njn | 5e40aba | 2009-03-16 22:11:31 +0000 | [diff] [blame] | 1964 | // |
| 1965 | // Possible forms: |
| 1966 | // |
| 1967 | // 0x80483BF: really (a.c:20) |
| 1968 | // 0x80483BF: really (in /foo/a.out) |
| 1969 | // 0x80483BF: really (in ???) |
| 1970 | // 0x80483BF: ??? (in /foo/a.out) |
| 1971 | // 0x80483BF: ??? (a.c:20) |
| 1972 | // 0x80483BF: ??? |
| 1973 | // |
sewardj | a44b15f | 2007-02-16 14:10:24 +0000 | [diff] [blame] | 1974 | VG_(sprintf)(ibuf,"0x%llX: ", (ULong)eip); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1975 | APPEND(ibuf); |
njn | 5e40aba | 2009-03-16 22:11:31 +0000 | [diff] [blame] | 1976 | if (know_fnname) { |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1977 | APPEND(buf_fn); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 1978 | } else { |
| 1979 | APPEND("???"); |
| 1980 | } |
| 1981 | if (know_srcloc) { |
| 1982 | APPEND(" ("); |
sewardj | 14cdbf8 | 2010-10-12 00:44:05 +0000 | [diff] [blame] | 1983 | // Get the directory name, if any, possibly pruned, into dirname. |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1984 | HChar* dirname = NULL; |
sewardj | 14cdbf8 | 2010-10-12 00:44:05 +0000 | [diff] [blame] | 1985 | if (VG_(clo_n_fullpath_after) > 0) { |
| 1986 | Int i; |
| 1987 | dirname = buf_dirname; |
| 1988 | // Remove leading prefixes from the dirname. |
| 1989 | // If user supplied --fullpath-after=foo, this will remove |
| 1990 | // a leading string which matches '.*foo' (not greedy). |
| 1991 | for (i = 0; i < VG_(clo_n_fullpath_after); i++) { |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 1992 | const HChar* prefix = VG_(clo_fullpath_after)[i]; |
| 1993 | HChar* str = VG_(strstr)(dirname, prefix); |
sewardj | 14cdbf8 | 2010-10-12 00:44:05 +0000 | [diff] [blame] | 1994 | if (str) { |
| 1995 | dirname = str + VG_(strlen)(prefix); |
| 1996 | break; |
| 1997 | } |
| 1998 | } |
| 1999 | /* remove leading "./" */ |
| 2000 | if (dirname[0] == '.' && dirname[1] == '/') |
| 2001 | dirname += 2; |
| 2002 | } |
| 2003 | // do we have any interesting directory name to show? If so |
| 2004 | // add it in. |
| 2005 | if (dirname && dirname[0] != 0) { |
| 2006 | APPEND(dirname); |
| 2007 | APPEND("/"); |
bart | 5dd0190 | 2010-08-31 15:18:32 +0000 | [diff] [blame] | 2008 | } |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 2009 | APPEND(buf_srcloc); |
| 2010 | APPEND(":"); |
| 2011 | VG_(sprintf)(ibuf,"%d",lineno); |
| 2012 | APPEND(ibuf); |
| 2013 | APPEND(")"); |
njn | 5e40aba | 2009-03-16 22:11:31 +0000 | [diff] [blame] | 2014 | } else if (know_objname) { |
| 2015 | APPEND(" (in "); |
| 2016 | APPEND(buf_obj); |
| 2017 | APPEND(")"); |
| 2018 | } else if (know_fnname) { |
| 2019 | // Nb: do this in two steps because "??)" is a trigraph! |
| 2020 | APPEND(" (in ???"); |
| 2021 | APPEND(")"); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 2022 | } |
| 2023 | |
| 2024 | } |
| 2025 | return buf; |
| 2026 | |
| 2027 | # undef APPEND |
| 2028 | # undef APPEND_ESC |
| 2029 | # undef BUF_LEN |
| 2030 | } |
| 2031 | |
sewardj | 72427fa | 2007-02-27 16:52:23 +0000 | [diff] [blame] | 2032 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2033 | /*--------------------------------------------------------------*/ |
| 2034 | /*--- ---*/ |
| 2035 | /*--- TOP LEVEL: FOR UNWINDING THE STACK USING ---*/ |
| 2036 | /*--- DWARF3 .eh_frame INFO ---*/ |
| 2037 | /*--- ---*/ |
| 2038 | /*--------------------------------------------------------------*/ |
sewardj | 72427fa | 2007-02-27 16:52:23 +0000 | [diff] [blame] | 2039 | |
| 2040 | /* Gather up all the constant pieces of info needed to evaluate |
| 2041 | a CfiExpr into one convenient struct. */ |
| 2042 | typedef |
| 2043 | struct { |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2044 | D3UnwindRegs* uregs; |
| 2045 | Addr min_accessible; |
| 2046 | Addr max_accessible; |
sewardj | 72427fa | 2007-02-27 16:52:23 +0000 | [diff] [blame] | 2047 | } |
| 2048 | CfiExprEvalContext; |
| 2049 | |
| 2050 | /* Evaluate the CfiExpr rooted at ix in exprs given the context eec. |
| 2051 | *ok is set to False on failure, but not to True on success. The |
| 2052 | caller must set it to True before calling. */ |
sewardj | f7183e3 | 2010-03-14 17:19:02 +0000 | [diff] [blame] | 2053 | __attribute__((noinline)) |
| 2054 | static |
sewardj | 72427fa | 2007-02-27 16:52:23 +0000 | [diff] [blame] | 2055 | UWord evalCfiExpr ( XArray* exprs, Int ix, |
| 2056 | CfiExprEvalContext* eec, Bool* ok ) |
| 2057 | { |
tom | 40628fa | 2012-09-21 09:12:30 +0000 | [diff] [blame] | 2058 | UWord w, wL, wR; |
sewardj | 19dc88f | 2007-02-28 01:46:30 +0000 | [diff] [blame] | 2059 | Addr a; |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2060 | CfiExpr* e; |
| 2061 | vg_assert(sizeof(Addr) == sizeof(UWord)); |
| 2062 | e = VG_(indexXA)( exprs, ix ); |
sewardj | 72427fa | 2007-02-27 16:52:23 +0000 | [diff] [blame] | 2063 | switch (e->tag) { |
tom | 40628fa | 2012-09-21 09:12:30 +0000 | [diff] [blame] | 2064 | case Cex_Unop: |
| 2065 | w = evalCfiExpr( exprs, e->Cex.Unop.ix, eec, ok ); |
| 2066 | if (!(*ok)) return 0; |
| 2067 | switch (e->Cex.Unop.op) { |
| 2068 | case Cunop_Abs: return (Word) w < 0 ? - w : w; |
| 2069 | case Cunop_Neg: return - (Word) w; |
| 2070 | case Cunop_Not: return ~ w; |
| 2071 | default: goto unhandled; |
| 2072 | } |
| 2073 | /*NOTREACHED*/ |
sewardj | 72427fa | 2007-02-27 16:52:23 +0000 | [diff] [blame] | 2074 | case Cex_Binop: |
| 2075 | wL = evalCfiExpr( exprs, e->Cex.Binop.ixL, eec, ok ); |
| 2076 | if (!(*ok)) return 0; |
| 2077 | wR = evalCfiExpr( exprs, e->Cex.Binop.ixR, eec, ok ); |
| 2078 | if (!(*ok)) return 0; |
| 2079 | switch (e->Cex.Binop.op) { |
tom | f6716dd | 2012-09-21 09:04:27 +0000 | [diff] [blame] | 2080 | case Cbinop_Add: return wL + wR; |
| 2081 | case Cbinop_Sub: return wL - wR; |
| 2082 | case Cbinop_And: return wL & wR; |
| 2083 | case Cbinop_Mul: return wL * wR; |
| 2084 | case Cbinop_Shl: return wL << wR; |
| 2085 | case Cbinop_Shr: return wL >> wR; |
| 2086 | case Cbinop_Eq: return wL == wR ? 1 : 0; |
| 2087 | case Cbinop_Ge: return (Word) wL >= (Word) wR ? 1 : 0; |
| 2088 | case Cbinop_Gt: return (Word) wL > (Word) wR ? 1 : 0; |
| 2089 | case Cbinop_Le: return (Word) wL <= (Word) wR ? 1 : 0; |
| 2090 | case Cbinop_Lt: return (Word) wL < (Word) wR ? 1 : 0; |
| 2091 | case Cbinop_Ne: return wL != wR ? 1 : 0; |
sewardj | 72427fa | 2007-02-27 16:52:23 +0000 | [diff] [blame] | 2092 | default: goto unhandled; |
| 2093 | } |
| 2094 | /*NOTREACHED*/ |
| 2095 | case Cex_CfiReg: |
| 2096 | switch (e->Cex.CfiReg.reg) { |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2097 | # if defined(VGA_x86) || defined(VGA_amd64) |
| 2098 | case Creg_IA_IP: return eec->uregs->xip; |
| 2099 | case Creg_IA_SP: return eec->uregs->xsp; |
| 2100 | case Creg_IA_BP: return eec->uregs->xbp; |
| 2101 | # elif defined(VGA_arm) |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2102 | case Creg_ARM_R15: return eec->uregs->r15; |
| 2103 | case Creg_ARM_R14: return eec->uregs->r14; |
sewardj | fa5ce56 | 2010-09-23 22:05:59 +0000 | [diff] [blame] | 2104 | case Creg_ARM_R13: return eec->uregs->r13; |
| 2105 | case Creg_ARM_R12: return eec->uregs->r12; |
sewardj | b5b8740 | 2011-03-07 16:05:35 +0000 | [diff] [blame] | 2106 | # elif defined(VGA_s390x) |
| 2107 | case Creg_IA_IP: return eec->uregs->ia; |
| 2108 | case Creg_IA_SP: return eec->uregs->sp; |
| 2109 | case Creg_IA_BP: return eec->uregs->fp; |
| 2110 | case Creg_S390_R14: return eec->uregs->lr; |
petarj | 4df0bfc | 2013-02-27 23:17:33 +0000 | [diff] [blame] | 2111 | # elif defined(VGA_mips32) || defined(VGA_mips64) |
sewardj | 5db1540 | 2012-06-07 09:13:21 +0000 | [diff] [blame] | 2112 | case Creg_IA_IP: return eec->uregs->pc; |
| 2113 | case Creg_IA_SP: return eec->uregs->sp; |
| 2114 | case Creg_IA_BP: return eec->uregs->fp; |
| 2115 | case Creg_MIPS_RA: return eec->uregs->ra; |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2116 | # elif defined(VGA_ppc32) || defined(VGA_ppc64) |
sewardj | f0c1250 | 2014-01-12 12:54:00 +0000 | [diff] [blame] | 2117 | # elif defined(VGP_arm64_linux) |
sewardj | 821283b | 2014-01-13 00:21:09 +0000 | [diff] [blame] | 2118 | case Creg_ARM64_X30: return eec->uregs->x30; |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2119 | # else |
| 2120 | # error "Unsupported arch" |
| 2121 | # endif |
sewardj | 72427fa | 2007-02-27 16:52:23 +0000 | [diff] [blame] | 2122 | default: goto unhandled; |
| 2123 | } |
| 2124 | /*NOTREACHED*/ |
| 2125 | case Cex_Const: |
| 2126 | return e->Cex.Const.con; |
sewardj | 19dc88f | 2007-02-28 01:46:30 +0000 | [diff] [blame] | 2127 | case Cex_Deref: |
| 2128 | a = evalCfiExpr( exprs, e->Cex.Deref.ixAddr, eec, ok ); |
| 2129 | if (!(*ok)) return 0; |
| 2130 | if (a < eec->min_accessible |
sewardj | 720e6b7 | 2011-10-20 08:09:39 +0000 | [diff] [blame] | 2131 | || a > eec->max_accessible - sizeof(UWord) + 1) { |
sewardj | 19dc88f | 2007-02-28 01:46:30 +0000 | [diff] [blame] | 2132 | *ok = False; |
| 2133 | return 0; |
| 2134 | } |
| 2135 | /* let's hope it doesn't trap! */ |
tom | 86781fa | 2011-10-05 08:48:07 +0000 | [diff] [blame] | 2136 | return ML_(read_UWord)((void *)a); |
sewardj | 72427fa | 2007-02-27 16:52:23 +0000 | [diff] [blame] | 2137 | default: |
| 2138 | goto unhandled; |
| 2139 | } |
| 2140 | /*NOTREACHED*/ |
| 2141 | unhandled: |
| 2142 | VG_(printf)("\n\nevalCfiExpr: unhandled\n"); |
| 2143 | ML_(ppCfiExpr)( exprs, ix ); |
| 2144 | VG_(printf)("\n"); |
| 2145 | vg_assert(0); |
| 2146 | /*NOTREACHED*/ |
| 2147 | return 0; |
| 2148 | } |
| 2149 | |
| 2150 | |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 2151 | /* Search all the DebugInfos in the entire system, to find the DiCfSI |
| 2152 | that pertains to 'ip'. |
| 2153 | |
| 2154 | If found, set *diP to the DebugInfo in which it resides, and |
| 2155 | *ixP to the index in that DebugInfo's cfsi array. |
| 2156 | |
| 2157 | If not found, set *diP to (DebugInfo*)1 and *ixP to zero. |
| 2158 | */ |
| 2159 | __attribute__((noinline)) |
| 2160 | static void find_DiCfSI ( /*OUT*/DebugInfo** diP, |
| 2161 | /*OUT*/Word* ixP, |
| 2162 | Addr ip ) |
| 2163 | { |
| 2164 | DebugInfo* di; |
| 2165 | Word i = -1; |
| 2166 | |
| 2167 | static UWord n_search = 0; |
| 2168 | static UWord n_steps = 0; |
| 2169 | n_search++; |
| 2170 | |
| 2171 | if (0) VG_(printf)("search for %#lx\n", ip); |
| 2172 | |
| 2173 | for (di = debugInfo_list; di != NULL; di = di->next) { |
| 2174 | Word j; |
| 2175 | n_steps++; |
| 2176 | |
| 2177 | /* Use the per-DebugInfo summary address ranges to skip |
| 2178 | inapplicable DebugInfos quickly. */ |
| 2179 | if (di->cfsi_used == 0) |
| 2180 | continue; |
| 2181 | if (ip < di->cfsi_minavma || ip > di->cfsi_maxavma) |
| 2182 | continue; |
| 2183 | |
| 2184 | /* It might be in this DebugInfo. Search it. */ |
| 2185 | j = ML_(search_one_cfitab)( di, ip ); |
| 2186 | vg_assert(j >= -1 && j < (Word)di->cfsi_used); |
| 2187 | |
| 2188 | if (j != -1) { |
| 2189 | i = j; |
| 2190 | break; /* found it */ |
| 2191 | } |
| 2192 | } |
| 2193 | |
| 2194 | if (i == -1) { |
| 2195 | |
| 2196 | /* we didn't find it. */ |
| 2197 | *diP = (DebugInfo*)1; |
| 2198 | *ixP = 0; |
| 2199 | |
| 2200 | } else { |
| 2201 | |
| 2202 | /* found it. */ |
| 2203 | /* ensure that di is 4-aligned (at least), so it can't possibly |
| 2204 | be equal to (DebugInfo*)1. */ |
| 2205 | vg_assert(di && VG_IS_4_ALIGNED(di)); |
| 2206 | vg_assert(i >= 0 && i < di->cfsi_used); |
| 2207 | *diP = di; |
| 2208 | *ixP = i; |
| 2209 | |
| 2210 | /* Start of performance-enhancing hack: once every 64 (chosen |
| 2211 | hackily after profiling) successful searches, move the found |
| 2212 | DebugInfo one step closer to the start of the list. This |
| 2213 | makes future searches cheaper. For starting konqueror on |
| 2214 | amd64, this in fact reduces the total amount of searching |
| 2215 | done by the above find-the-right-DebugInfo loop by more than |
| 2216 | a factor of 20. */ |
| 2217 | if ((n_search & 0xF) == 0) { |
| 2218 | /* Move di one step closer to the start of the list. */ |
| 2219 | move_DebugInfo_one_step_forward( di ); |
| 2220 | } |
| 2221 | /* End of performance-enhancing hack. */ |
| 2222 | |
| 2223 | if (0 && ((n_search & 0x7FFFF) == 0)) |
| 2224 | VG_(printf)("find_DiCfSI: %lu searches, " |
| 2225 | "%lu DebugInfos looked at\n", |
| 2226 | n_search, n_steps); |
| 2227 | |
| 2228 | } |
| 2229 | |
| 2230 | } |
| 2231 | |
| 2232 | |
| 2233 | /* Now follows a mechanism for caching queries to find_DiCfSI, since |
| 2234 | they are extremely frequent on amd64-linux, during stack unwinding. |
| 2235 | |
| 2236 | Each cache entry binds an ip value to a (di, ix) pair. Possible |
| 2237 | values: |
| 2238 | |
| 2239 | di is non-null, ix >= 0 ==> cache slot in use, "di->cfsi[ix]" |
| 2240 | di is (DebugInfo*)1 ==> cache slot in use, no associated di |
| 2241 | di is NULL ==> cache slot not in use |
| 2242 | |
| 2243 | Hence simply zeroing out the entire cache invalidates all |
| 2244 | entries. |
| 2245 | |
| 2246 | Why not map ip values directly to DiCfSI*'s? Because this would |
| 2247 | cause problems if/when the cfsi array is moved due to resizing. |
| 2248 | Instead we cache .cfsi array index value, which should be invariant |
| 2249 | across resizing. (That said, I don't think the current |
| 2250 | implementation will resize whilst during queries, since the DiCfSI |
| 2251 | records are added all at once, when the debuginfo for an object is |
| 2252 | read, and is not changed ever thereafter. */ |
| 2253 | |
philippe | 50863bb | 2013-01-17 23:57:35 +0000 | [diff] [blame] | 2254 | // Prime number, giving about 3K cache on 32 bits, 6K cache on 64 bits. |
| 2255 | #define N_CFSI_CACHE 509 |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 2256 | |
| 2257 | typedef |
| 2258 | struct { Addr ip; DebugInfo* di; Word ix; } |
| 2259 | CFSICacheEnt; |
| 2260 | |
| 2261 | static CFSICacheEnt cfsi_cache[N_CFSI_CACHE]; |
| 2262 | |
| 2263 | static void cfsi_cache__invalidate ( void ) { |
| 2264 | VG_(memset)(&cfsi_cache, 0, sizeof(cfsi_cache)); |
philippe | 20ede3a | 2013-01-30 23:18:11 +0000 | [diff] [blame] | 2265 | CF_info_generation++; |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 2266 | } |
| 2267 | |
philippe | 20ede3a | 2013-01-30 23:18:11 +0000 | [diff] [blame] | 2268 | UInt VG_(CF_info_generation) (void) |
| 2269 | { |
| 2270 | return CF_info_generation; |
| 2271 | } |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 2272 | |
sewardj | f7183e3 | 2010-03-14 17:19:02 +0000 | [diff] [blame] | 2273 | static inline CFSICacheEnt* cfsi_cache__find ( Addr ip ) |
tom | 3c9cf34 | 2009-11-12 13:28:34 +0000 | [diff] [blame] | 2274 | { |
| 2275 | UWord hash = ip % N_CFSI_CACHE; |
| 2276 | CFSICacheEnt* ce = &cfsi_cache[hash]; |
| 2277 | static UWord n_q = 0, n_m = 0; |
| 2278 | |
| 2279 | n_q++; |
| 2280 | if (0 && 0 == (n_q & 0x1FFFFF)) |
| 2281 | VG_(printf)("QQQ %lu %lu\n", n_q, n_m); |
| 2282 | |
| 2283 | if (LIKELY(ce->ip == ip) && LIKELY(ce->di != NULL)) { |
| 2284 | /* found an entry in the cache .. */ |
| 2285 | } else { |
| 2286 | /* not found in cache. Search and update. */ |
| 2287 | n_m++; |
| 2288 | ce->ip = ip; |
| 2289 | find_DiCfSI( &ce->di, &ce->ix, ip ); |
| 2290 | } |
| 2291 | |
| 2292 | if (UNLIKELY(ce->di == (DebugInfo*)1)) { |
| 2293 | /* no DiCfSI for this address */ |
| 2294 | return NULL; |
| 2295 | } else { |
| 2296 | /* found a DiCfSI for this address */ |
| 2297 | return ce; |
| 2298 | } |
| 2299 | } |
| 2300 | |
| 2301 | |
sewardj | f7183e3 | 2010-03-14 17:19:02 +0000 | [diff] [blame] | 2302 | inline |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2303 | static Addr compute_cfa ( D3UnwindRegs* uregs, |
tom | 3c9cf34 | 2009-11-12 13:28:34 +0000 | [diff] [blame] | 2304 | Addr min_accessible, Addr max_accessible, |
| 2305 | DebugInfo* di, DiCfSI* cfsi ) |
| 2306 | { |
| 2307 | CfiExprEvalContext eec; |
| 2308 | Addr cfa; |
| 2309 | Bool ok; |
| 2310 | |
| 2311 | /* Compute the CFA. */ |
| 2312 | cfa = 0; |
| 2313 | switch (cfsi->cfa_how) { |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2314 | # if defined(VGA_x86) || defined(VGA_amd64) |
| 2315 | case CFIC_IA_SPREL: |
| 2316 | cfa = cfsi->cfa_off + uregs->xsp; |
tom | 3c9cf34 | 2009-11-12 13:28:34 +0000 | [diff] [blame] | 2317 | break; |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2318 | case CFIC_IA_BPREL: |
| 2319 | cfa = cfsi->cfa_off + uregs->xbp; |
tom | 3c9cf34 | 2009-11-12 13:28:34 +0000 | [diff] [blame] | 2320 | break; |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2321 | # elif defined(VGA_arm) |
| 2322 | case CFIC_ARM_R13REL: |
| 2323 | cfa = cfsi->cfa_off + uregs->r13; |
| 2324 | break; |
| 2325 | case CFIC_ARM_R12REL: |
| 2326 | cfa = cfsi->cfa_off + uregs->r12; |
| 2327 | break; |
| 2328 | case CFIC_ARM_R11REL: |
| 2329 | cfa = cfsi->cfa_off + uregs->r11; |
| 2330 | break; |
sewardj | fa5ce56 | 2010-09-23 22:05:59 +0000 | [diff] [blame] | 2331 | case CFIC_ARM_R7REL: |
| 2332 | cfa = cfsi->cfa_off + uregs->r7; |
| 2333 | break; |
sewardj | b5b8740 | 2011-03-07 16:05:35 +0000 | [diff] [blame] | 2334 | # elif defined(VGA_s390x) |
| 2335 | case CFIC_IA_SPREL: |
| 2336 | cfa = cfsi->cfa_off + uregs->sp; |
| 2337 | break; |
| 2338 | case CFIR_MEMCFAREL: |
| 2339 | { |
| 2340 | Addr a = uregs->sp + cfsi->cfa_off; |
| 2341 | if (a < min_accessible || a > max_accessible-sizeof(Addr)) |
| 2342 | break; |
tom | 86781fa | 2011-10-05 08:48:07 +0000 | [diff] [blame] | 2343 | cfa = ML_(read_Addr)((void *)a); |
sewardj | b5b8740 | 2011-03-07 16:05:35 +0000 | [diff] [blame] | 2344 | break; |
| 2345 | } |
| 2346 | case CFIR_SAME: |
| 2347 | cfa = uregs->fp; |
| 2348 | break; |
| 2349 | case CFIC_IA_BPREL: |
| 2350 | cfa = cfsi->cfa_off + uregs->fp; |
| 2351 | break; |
petarj | 4df0bfc | 2013-02-27 23:17:33 +0000 | [diff] [blame] | 2352 | # elif defined(VGA_mips32) || defined(VGA_mips64) |
sewardj | 5db1540 | 2012-06-07 09:13:21 +0000 | [diff] [blame] | 2353 | case CFIC_IA_SPREL: |
| 2354 | cfa = cfsi->cfa_off + uregs->sp; |
| 2355 | break; |
| 2356 | case CFIR_SAME: |
| 2357 | cfa = uregs->fp; |
| 2358 | break; |
| 2359 | case CFIC_IA_BPREL: |
| 2360 | cfa = cfsi->cfa_off + uregs->fp; |
| 2361 | break; |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2362 | # elif defined(VGA_ppc32) || defined(VGA_ppc64) |
sewardj | f0c1250 | 2014-01-12 12:54:00 +0000 | [diff] [blame] | 2363 | # elif defined(VGP_arm64_linux) |
sewardj | 821283b | 2014-01-13 00:21:09 +0000 | [diff] [blame] | 2364 | case CFIC_ARM64_SPREL: |
| 2365 | cfa = cfsi->cfa_off + uregs->sp; |
| 2366 | break; |
| 2367 | case CFIC_ARM64_X29REL: |
| 2368 | cfa = cfsi->cfa_off + uregs->x29; |
| 2369 | break; |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2370 | # else |
| 2371 | # error "Unsupported arch" |
| 2372 | # endif |
| 2373 | case CFIC_EXPR: /* available on all archs */ |
tom | 3c9cf34 | 2009-11-12 13:28:34 +0000 | [diff] [blame] | 2374 | if (0) { |
| 2375 | VG_(printf)("CFIC_EXPR: "); |
| 2376 | ML_(ppCfiExpr)(di->cfsi_exprs, cfsi->cfa_off); |
| 2377 | VG_(printf)("\n"); |
| 2378 | } |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2379 | eec.uregs = uregs; |
tom | 3c9cf34 | 2009-11-12 13:28:34 +0000 | [diff] [blame] | 2380 | eec.min_accessible = min_accessible; |
| 2381 | eec.max_accessible = max_accessible; |
| 2382 | ok = True; |
| 2383 | cfa = evalCfiExpr(di->cfsi_exprs, cfsi->cfa_off, &eec, &ok ); |
| 2384 | if (!ok) return 0; |
| 2385 | break; |
| 2386 | default: |
| 2387 | vg_assert(0); |
| 2388 | } |
| 2389 | return cfa; |
| 2390 | } |
| 2391 | |
| 2392 | |
| 2393 | /* Get the call frame address (CFA) given an IP/SP/FP triple. */ |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2394 | /* NOTE: This function may rearrange the order of entries in the |
| 2395 | DebugInfo list. */ |
tom | 3c9cf34 | 2009-11-12 13:28:34 +0000 | [diff] [blame] | 2396 | Addr ML_(get_CFA) ( Addr ip, Addr sp, Addr fp, |
| 2397 | Addr min_accessible, Addr max_accessible ) |
| 2398 | { |
| 2399 | CFSICacheEnt* ce; |
| 2400 | DebugInfo* di; |
sewardj | d2be8cc | 2011-03-28 20:33:52 +0000 | [diff] [blame] | 2401 | DiCfSI* cfsi __attribute__((unused)); |
tom | 3c9cf34 | 2009-11-12 13:28:34 +0000 | [diff] [blame] | 2402 | |
| 2403 | ce = cfsi_cache__find(ip); |
| 2404 | |
| 2405 | if (UNLIKELY(ce == NULL)) |
| 2406 | return 0; /* no info. Nothing we can do. */ |
| 2407 | |
| 2408 | di = ce->di; |
| 2409 | cfsi = &di->cfsi[ ce->ix ]; |
| 2410 | |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2411 | /* Temporary impedance-matching kludge so that this keeps working |
| 2412 | on x86-linux and amd64-linux. */ |
| 2413 | # if defined(VGA_x86) || defined(VGA_amd64) |
| 2414 | { D3UnwindRegs uregs; |
| 2415 | uregs.xip = ip; |
| 2416 | uregs.xsp = sp; |
| 2417 | uregs.xbp = fp; |
| 2418 | return compute_cfa(&uregs, |
| 2419 | min_accessible, max_accessible, di, cfsi); |
| 2420 | } |
sewardj | b5b8740 | 2011-03-07 16:05:35 +0000 | [diff] [blame] | 2421 | #elif defined(VGA_s390x) |
| 2422 | { D3UnwindRegs uregs; |
| 2423 | uregs.ia = ip; |
| 2424 | uregs.sp = sp; |
| 2425 | uregs.fp = fp; |
| 2426 | return compute_cfa(&uregs, |
| 2427 | min_accessible, max_accessible, di, cfsi); |
| 2428 | } |
dejanj | f03d0b7 | 2014-04-14 11:54:36 +0000 | [diff] [blame] | 2429 | #elif defined(VGA_mips32) || defined(VGA_mips64) |
dejanj | fe0b433 | 2014-04-04 10:02:03 +0000 | [diff] [blame] | 2430 | { D3UnwindRegs uregs; |
| 2431 | uregs.pc = ip; |
| 2432 | uregs.sp = sp; |
| 2433 | uregs.fp = fp; |
| 2434 | return compute_cfa(&uregs, |
| 2435 | min_accessible, max_accessible, di, cfsi); |
| 2436 | } |
sewardj | b5b8740 | 2011-03-07 16:05:35 +0000 | [diff] [blame] | 2437 | |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2438 | # else |
| 2439 | return 0; /* indicates failure */ |
| 2440 | # endif |
tom | 3c9cf34 | 2009-11-12 13:28:34 +0000 | [diff] [blame] | 2441 | } |
| 2442 | |
| 2443 | |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2444 | /* The main function for DWARF2/3 CFI-based stack unwinding. Given a |
| 2445 | set of registers in UREGS, modify it to hold the register values |
| 2446 | for the previous frame, if possible. Returns True if successful. |
| 2447 | If not successful, *UREGS is not changed. |
| 2448 | |
| 2449 | For x86 and amd64, the unwound registers are: {E,R}IP, |
| 2450 | {E,R}SP, {E,R}BP. |
| 2451 | |
sewardj | fa5ce56 | 2010-09-23 22:05:59 +0000 | [diff] [blame] | 2452 | For arm, the unwound registers are: R7 R11 R12 R13 R14 R15. |
sewardj | 821283b | 2014-01-13 00:21:09 +0000 | [diff] [blame] | 2453 | |
| 2454 | For arm64, the unwound registers are: X29(FP) X30(LR) SP PC. |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2455 | */ |
| 2456 | Bool VG_(use_CF_info) ( /*MOD*/D3UnwindRegs* uregsHere, |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 2457 | Addr min_accessible, |
| 2458 | Addr max_accessible ) |
| 2459 | { |
tom | 3c9cf34 | 2009-11-12 13:28:34 +0000 | [diff] [blame] | 2460 | DebugInfo* di; |
| 2461 | DiCfSI* cfsi = NULL; |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2462 | Addr cfa, ipHere = 0; |
tom | 3c9cf34 | 2009-11-12 13:28:34 +0000 | [diff] [blame] | 2463 | CFSICacheEnt* ce; |
sewardj | d2be8cc | 2011-03-28 20:33:52 +0000 | [diff] [blame] | 2464 | CfiExprEvalContext eec __attribute__((unused)); |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2465 | D3UnwindRegs uregsPrev; |
sewardj | 72427fa | 2007-02-27 16:52:23 +0000 | [diff] [blame] | 2466 | |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2467 | # if defined(VGA_x86) || defined(VGA_amd64) |
| 2468 | ipHere = uregsHere->xip; |
| 2469 | # elif defined(VGA_arm) |
| 2470 | ipHere = uregsHere->r15; |
sewardj | b5b8740 | 2011-03-07 16:05:35 +0000 | [diff] [blame] | 2471 | # elif defined(VGA_s390x) |
| 2472 | ipHere = uregsHere->ia; |
petarj | 4df0bfc | 2013-02-27 23:17:33 +0000 | [diff] [blame] | 2473 | # elif defined(VGA_mips32) || defined(VGA_mips64) |
sewardj | 5db1540 | 2012-06-07 09:13:21 +0000 | [diff] [blame] | 2474 | ipHere = uregsHere->pc; |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2475 | # elif defined(VGA_ppc32) || defined(VGA_ppc64) |
sewardj | f0c1250 | 2014-01-12 12:54:00 +0000 | [diff] [blame] | 2476 | # elif defined(VGP_arm64_linux) |
sewardj | 821283b | 2014-01-13 00:21:09 +0000 | [diff] [blame] | 2477 | ipHere = uregsHere->pc; |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2478 | # else |
| 2479 | # error "Unknown arch" |
| 2480 | # endif |
| 2481 | ce = cfsi_cache__find(ipHere); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 2482 | |
tom | 3c9cf34 | 2009-11-12 13:28:34 +0000 | [diff] [blame] | 2483 | if (UNLIKELY(ce == NULL)) |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 2484 | return False; /* no info. Nothing we can do. */ |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 2485 | |
tom | 3c9cf34 | 2009-11-12 13:28:34 +0000 | [diff] [blame] | 2486 | di = ce->di; |
| 2487 | cfsi = &di->cfsi[ ce->ix ]; |
| 2488 | |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 2489 | if (0) { |
| 2490 | VG_(printf)("found cfisi: "); |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 2491 | ML_(ppDiCfSI)(di->cfsi_exprs, cfsi); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 2492 | } |
| 2493 | |
sewardj | f7183e3 | 2010-03-14 17:19:02 +0000 | [diff] [blame] | 2494 | VG_(bzero_inline)(&uregsPrev, sizeof(uregsPrev)); |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 2495 | |
sewardj | 72427fa | 2007-02-27 16:52:23 +0000 | [diff] [blame] | 2496 | /* First compute the CFA. */ |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2497 | cfa = compute_cfa(uregsHere, |
| 2498 | min_accessible, max_accessible, di, cfsi); |
tom | 3c9cf34 | 2009-11-12 13:28:34 +0000 | [diff] [blame] | 2499 | if (UNLIKELY(cfa == 0)) |
| 2500 | return False; |
sewardj | 72427fa | 2007-02-27 16:52:23 +0000 | [diff] [blame] | 2501 | |
| 2502 | /* Now we know the CFA, use it to roll back the registers we're |
| 2503 | interested in. */ |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 2504 | |
| 2505 | # define COMPUTE(_prev, _here, _how, _off) \ |
| 2506 | do { \ |
| 2507 | switch (_how) { \ |
| 2508 | case CFIR_UNKNOWN: \ |
| 2509 | return False; \ |
| 2510 | case CFIR_SAME: \ |
| 2511 | _prev = _here; break; \ |
| 2512 | case CFIR_MEMCFAREL: { \ |
| 2513 | Addr a = cfa + (Word)_off; \ |
| 2514 | if (a < min_accessible \ |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 2515 | || a > max_accessible-sizeof(Addr)) \ |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 2516 | return False; \ |
tom | 86781fa | 2011-10-05 08:48:07 +0000 | [diff] [blame] | 2517 | _prev = ML_(read_Addr)((void *)a); \ |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 2518 | break; \ |
| 2519 | } \ |
| 2520 | case CFIR_CFAREL: \ |
| 2521 | _prev = cfa + (Word)_off; \ |
| 2522 | break; \ |
sewardj | 72427fa | 2007-02-27 16:52:23 +0000 | [diff] [blame] | 2523 | case CFIR_EXPR: \ |
| 2524 | if (0) \ |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 2525 | ML_(ppCfiExpr)(di->cfsi_exprs,_off); \ |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2526 | eec.uregs = uregsHere; \ |
sewardj | 72427fa | 2007-02-27 16:52:23 +0000 | [diff] [blame] | 2527 | eec.min_accessible = min_accessible; \ |
| 2528 | eec.max_accessible = max_accessible; \ |
sewardj | d2be8cc | 2011-03-28 20:33:52 +0000 | [diff] [blame] | 2529 | Bool ok = True; \ |
sewardj | f98e1c0 | 2008-10-25 16:22:41 +0000 | [diff] [blame] | 2530 | _prev = evalCfiExpr(di->cfsi_exprs, _off, &eec, &ok ); \ |
sewardj | 72427fa | 2007-02-27 16:52:23 +0000 | [diff] [blame] | 2531 | if (!ok) return False; \ |
| 2532 | break; \ |
| 2533 | default: \ |
| 2534 | vg_assert(0); \ |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 2535 | } \ |
| 2536 | } while (0) |
| 2537 | |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2538 | # if defined(VGA_x86) || defined(VGA_amd64) |
| 2539 | COMPUTE(uregsPrev.xip, uregsHere->xip, cfsi->ra_how, cfsi->ra_off); |
sewardj | 9365e3f | 2010-01-01 19:55:17 +0000 | [diff] [blame] | 2540 | COMPUTE(uregsPrev.xsp, uregsHere->xsp, cfsi->sp_how, cfsi->sp_off); |
| 2541 | COMPUTE(uregsPrev.xbp, uregsHere->xbp, cfsi->bp_how, cfsi->bp_off); |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2542 | # elif defined(VGA_arm) |
| 2543 | COMPUTE(uregsPrev.r15, uregsHere->r15, cfsi->ra_how, cfsi->ra_off); |
| 2544 | COMPUTE(uregsPrev.r14, uregsHere->r14, cfsi->r14_how, cfsi->r14_off); |
| 2545 | COMPUTE(uregsPrev.r13, uregsHere->r13, cfsi->r13_how, cfsi->r13_off); |
| 2546 | COMPUTE(uregsPrev.r12, uregsHere->r12, cfsi->r12_how, cfsi->r12_off); |
| 2547 | COMPUTE(uregsPrev.r11, uregsHere->r11, cfsi->r11_how, cfsi->r11_off); |
sewardj | fa5ce56 | 2010-09-23 22:05:59 +0000 | [diff] [blame] | 2548 | COMPUTE(uregsPrev.r7, uregsHere->r7, cfsi->r7_how, cfsi->r7_off); |
sewardj | b5b8740 | 2011-03-07 16:05:35 +0000 | [diff] [blame] | 2549 | # elif defined(VGA_s390x) |
| 2550 | COMPUTE(uregsPrev.ia, uregsHere->ia, cfsi->ra_how, cfsi->ra_off); |
| 2551 | COMPUTE(uregsPrev.sp, uregsHere->sp, cfsi->sp_how, cfsi->sp_off); |
| 2552 | COMPUTE(uregsPrev.fp, uregsHere->fp, cfsi->fp_how, cfsi->fp_off); |
petarj | 4df0bfc | 2013-02-27 23:17:33 +0000 | [diff] [blame] | 2553 | # elif defined(VGA_mips32) || defined(VGA_mips64) |
sewardj | 5db1540 | 2012-06-07 09:13:21 +0000 | [diff] [blame] | 2554 | COMPUTE(uregsPrev.pc, uregsHere->pc, cfsi->ra_how, cfsi->ra_off); |
| 2555 | COMPUTE(uregsPrev.sp, uregsHere->sp, cfsi->sp_how, cfsi->sp_off); |
| 2556 | COMPUTE(uregsPrev.fp, uregsHere->fp, cfsi->fp_how, cfsi->fp_off); |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2557 | # elif defined(VGA_ppc32) || defined(VGA_ppc64) |
sewardj | f0c1250 | 2014-01-12 12:54:00 +0000 | [diff] [blame] | 2558 | # elif defined(VGP_arm64_linux) |
sewardj | 821283b | 2014-01-13 00:21:09 +0000 | [diff] [blame] | 2559 | COMPUTE(uregsPrev.pc, uregsHere->pc, cfsi->ra_how, cfsi->ra_off); |
| 2560 | COMPUTE(uregsPrev.sp, uregsHere->sp, cfsi->sp_how, cfsi->sp_off); |
| 2561 | COMPUTE(uregsPrev.x30, uregsHere->x30, cfsi->x30_how, cfsi->x30_off); |
| 2562 | COMPUTE(uregsPrev.x29, uregsHere->x29, cfsi->x29_how, cfsi->x29_off); |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2563 | # else |
| 2564 | # error "Unknown arch" |
| 2565 | # endif |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 2566 | |
| 2567 | # undef COMPUTE |
| 2568 | |
sewardj | 3026f71 | 2010-01-01 18:46:41 +0000 | [diff] [blame] | 2569 | *uregsHere = uregsPrev; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 2570 | return True; |
| 2571 | } |
| 2572 | |
| 2573 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2574 | /*--------------------------------------------------------------*/ |
| 2575 | /*--- ---*/ |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 2576 | /*--- TOP LEVEL: FOR UNWINDING THE STACK USING ---*/ |
| 2577 | /*--- MSVC FPO INFO ---*/ |
| 2578 | /*--- ---*/ |
| 2579 | /*--------------------------------------------------------------*/ |
| 2580 | |
| 2581 | Bool VG_(use_FPO_info) ( /*MOD*/Addr* ipP, |
| 2582 | /*MOD*/Addr* spP, |
| 2583 | /*MOD*/Addr* fpP, |
| 2584 | Addr min_accessible, |
| 2585 | Addr max_accessible ) |
| 2586 | { |
| 2587 | Word i; |
| 2588 | DebugInfo* di; |
| 2589 | FPO_DATA* fpo = NULL; |
| 2590 | Addr spHere; |
| 2591 | |
| 2592 | static UWord n_search = 0; |
| 2593 | static UWord n_steps = 0; |
| 2594 | n_search++; |
| 2595 | |
| 2596 | if (0) VG_(printf)("search FPO for %#lx\n", *ipP); |
| 2597 | |
| 2598 | for (di = debugInfo_list; di != NULL; di = di->next) { |
| 2599 | n_steps++; |
| 2600 | |
| 2601 | /* Use the per-DebugInfo summary address ranges to skip |
| 2602 | inapplicable DebugInfos quickly. */ |
| 2603 | if (di->fpo == NULL) |
| 2604 | continue; |
| 2605 | if (*ipP < di->fpo_minavma || *ipP > di->fpo_maxavma) |
| 2606 | continue; |
| 2607 | |
| 2608 | i = ML_(search_one_fpotab)( di, *ipP ); |
| 2609 | if (i != -1) { |
| 2610 | Word j; |
| 2611 | if (0) { |
| 2612 | /* debug printing only */ |
| 2613 | VG_(printf)("look for %#lx size %ld i %ld\n", |
| 2614 | *ipP, di->fpo_size, i); |
| 2615 | for (j = 0; j < di->fpo_size; j++) |
| 2616 | VG_(printf)("[%02ld] %#x %d\n", |
| 2617 | j, di->fpo[j].ulOffStart, di->fpo[j].cbProcSize); |
| 2618 | } |
| 2619 | vg_assert(i >= 0 && i < di->fpo_size); |
| 2620 | fpo = &di->fpo[i]; |
| 2621 | break; |
| 2622 | } |
| 2623 | } |
| 2624 | |
| 2625 | if (fpo == NULL) |
| 2626 | return False; |
| 2627 | |
| 2628 | if (0 && ((n_search & 0x7FFFF) == 0)) |
| 2629 | VG_(printf)("VG_(use_FPO_info): %lu searches, " |
| 2630 | "%lu DebugInfos looked at\n", |
| 2631 | n_search, n_steps); |
| 2632 | |
| 2633 | |
| 2634 | /* Start of performance-enhancing hack: once every 64 (chosen |
| 2635 | hackily after profiling) successful searches, move the found |
| 2636 | DebugInfo one step closer to the start of the list. This makes |
| 2637 | future searches cheaper. For starting konqueror on amd64, this |
| 2638 | in fact reduces the total amount of searching done by the above |
| 2639 | find-the-right-DebugInfo loop by more than a factor of 20. */ |
| 2640 | if ((n_search & 0x3F) == 0) { |
| 2641 | /* Move si one step closer to the start of the list. */ |
| 2642 | //move_DebugInfo_one_step_forward( di ); |
| 2643 | } |
| 2644 | /* End of performance-enhancing hack. */ |
| 2645 | |
| 2646 | if (0) { |
| 2647 | VG_(printf)("found fpo: "); |
| 2648 | //ML_(ppFPO)(fpo); |
| 2649 | } |
| 2650 | |
| 2651 | /* |
| 2652 | Stack layout is: |
| 2653 | %esp-> |
| 2654 | 4*.cbRegs {%edi, %esi, %ebp, %ebx} |
| 2655 | 4*.cdwLocals |
| 2656 | return_pc |
| 2657 | 4*.cdwParams |
| 2658 | prior_%esp-> |
| 2659 | |
| 2660 | Typical code looks like: |
| 2661 | sub $4*.cdwLocals,%esp |
| 2662 | Alternative to above for >=4KB (and sometimes for smaller): |
| 2663 | mov $size,%eax |
| 2664 | call __chkstk # WinNT performs page-by-page probe! |
| 2665 | __chkstk is much like alloc(), except that on return |
| 2666 | %eax= 5+ &CALL. Thus it could be used as part of |
| 2667 | Position Independent Code to locate the Global Offset Table. |
| 2668 | push %ebx |
| 2669 | push %ebp |
| 2670 | push %esi |
| 2671 | Other once-only instructions often scheduled >here<. |
| 2672 | push %edi |
| 2673 | |
| 2674 | If the pc is within the first .cbProlog bytes of the function, |
| 2675 | then you must disassemble to see how many registers have been pushed, |
| 2676 | because instructions in the prolog may be scheduled for performance. |
| 2677 | The order of PUSH is always %ebx, %ebp, %esi, %edi, with trailing |
| 2678 | registers not pushed when .cbRegs < 4. This seems somewhat strange |
| 2679 | because %ebp is the register whose usage you want to minimize, |
| 2680 | yet it is in the first half of the PUSH list. |
| 2681 | |
| 2682 | I don't know what happens when the compiler constructs an outgoing CALL. |
| 2683 | %esp could move if outgoing parameters are PUSHed, and this affects |
| 2684 | traceback for errors during the PUSHes. */ |
| 2685 | |
| 2686 | spHere = *spP; |
| 2687 | |
tom | 86781fa | 2011-10-05 08:48:07 +0000 | [diff] [blame] | 2688 | *ipP = ML_(read_Addr)((void *)(spHere + 4*(fpo->cbRegs + fpo->cdwLocals))); |
| 2689 | *spP = spHere + 4*(fpo->cbRegs + fpo->cdwLocals + 1 |
| 2690 | + fpo->cdwParams); |
| 2691 | *fpP = ML_(read_Addr)((void *)(spHere + 4*2)); |
sewardj | c8259b8 | 2009-04-22 22:42:10 +0000 | [diff] [blame] | 2692 | return True; |
| 2693 | } |
| 2694 | |
| 2695 | |
| 2696 | /*--------------------------------------------------------------*/ |
| 2697 | /*--- ---*/ |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2698 | /*--- TOP LEVEL: GENERATE DESCRIPTION OF DATA ADDRESSES ---*/ |
| 2699 | /*--- FROM DWARF3 DEBUG INFO ---*/ |
| 2700 | /*--- ---*/ |
| 2701 | /*--------------------------------------------------------------*/ |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 2702 | |
sewardj | 588adef | 2009-08-15 22:41:51 +0000 | [diff] [blame] | 2703 | /* Try to make p2XA(dst, fmt, args..) turn into |
bart | b3af9cf | 2011-10-06 19:08:37 +0000 | [diff] [blame] | 2704 | VG_(xaprintf)(dst, fmt, args) without having to resort to |
sewardj | 588adef | 2009-08-15 22:41:51 +0000 | [diff] [blame] | 2705 | vararg macros. As usual with everything to do with varargs, it's |
| 2706 | an ugly hack. |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2707 | |
sewardj | 588adef | 2009-08-15 22:41:51 +0000 | [diff] [blame] | 2708 | //#define p2XA(dstxa, format, args...) |
bart | b3af9cf | 2011-10-06 19:08:37 +0000 | [diff] [blame] | 2709 | // VG_(xaprintf)(dstxa, format, ##args) |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2710 | */ |
bart | b3af9cf | 2011-10-06 19:08:37 +0000 | [diff] [blame] | 2711 | #define p2XA VG_(xaprintf) |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2712 | |
sewardj | 588adef | 2009-08-15 22:41:51 +0000 | [diff] [blame] | 2713 | /* Add a zero-terminating byte to DST, which must be an XArray* of |
| 2714 | HChar. */ |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2715 | static void zterm_XA ( XArray* dst ) |
| 2716 | { |
| 2717 | HChar zero = 0; |
| 2718 | (void) VG_(addBytesToXA)( dst, &zero, 1 ); |
| 2719 | } |
| 2720 | |
| 2721 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2722 | /* Evaluate the location expression/list for var, to see whether or |
| 2723 | not data_addr falls within the variable. If so also return the |
| 2724 | offset of data_addr from the start of the variable. Note that |
| 2725 | regs, which supplies ip,sp,fp values, will be NULL for global |
| 2726 | variables, and non-NULL for local variables. */ |
njn | c4431bf | 2009-01-15 21:29:24 +0000 | [diff] [blame] | 2727 | static Bool data_address_is_in_var ( /*OUT*/PtrdiffT* offset, |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 2728 | XArray* /* TyEnt */ tyents, |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2729 | DiVariable* var, |
| 2730 | RegSummary* regs, |
| 2731 | Addr data_addr, |
tom | 588658b | 2009-01-22 13:40:12 +0000 | [diff] [blame] | 2732 | const DebugInfo* di ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 2733 | { |
sewardj | 50fde23 | 2008-10-20 16:08:55 +0000 | [diff] [blame] | 2734 | MaybeULong mul; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2735 | SizeT var_szB; |
| 2736 | GXResult res; |
| 2737 | Bool show = False; |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 2738 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2739 | vg_assert(var->name); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2740 | vg_assert(var->gexpr); |
| 2741 | |
| 2742 | /* Figure out how big the variable is. */ |
sewardj | 50fde23 | 2008-10-20 16:08:55 +0000 | [diff] [blame] | 2743 | mul = ML_(sizeOfType)(tyents, var->typeR); |
| 2744 | /* If this var has a type whose size is unknown, zero, or |
| 2745 | impossibly large, it should never have been added. ML_(addVar) |
| 2746 | should have rejected it. */ |
| 2747 | vg_assert(mul.b == True); |
| 2748 | vg_assert(mul.ul > 0); |
| 2749 | if (sizeof(void*) == 4) vg_assert(mul.ul < (1ULL << 32)); |
| 2750 | /* After this point, we assume we can truncate mul.ul to a host word |
| 2751 | safely (without loss of info). */ |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2752 | |
sewardj | 50fde23 | 2008-10-20 16:08:55 +0000 | [diff] [blame] | 2753 | var_szB = (SizeT)mul.ul; /* NB: truncate to host word */ |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2754 | |
| 2755 | if (show) { |
bart | a0b6b2c | 2008-07-07 06:49:24 +0000 | [diff] [blame] | 2756 | VG_(printf)("VVVV: data_address_%#lx_is_in_var: %s :: ", |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2757 | data_addr, var->name ); |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 2758 | ML_(pp_TyEnt_C_ishly)( tyents, var->typeR ); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2759 | VG_(printf)("\n"); |
| 2760 | } |
| 2761 | |
| 2762 | /* ignore zero-sized vars; they can never match anything. */ |
| 2763 | if (var_szB == 0) { |
| 2764 | if (show) |
| 2765 | VG_(printf)("VVVV: -> Fail (variable is zero sized)\n"); |
| 2766 | return False; |
| 2767 | } |
| 2768 | |
tom | 588658b | 2009-01-22 13:40:12 +0000 | [diff] [blame] | 2769 | res = ML_(evaluate_GX)( var->gexpr, var->fbGX, regs, di ); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2770 | |
| 2771 | if (show) { |
| 2772 | VG_(printf)("VVVV: -> "); |
| 2773 | ML_(pp_GXResult)( res ); |
| 2774 | VG_(printf)("\n"); |
| 2775 | } |
| 2776 | |
tom | 3c9cf34 | 2009-11-12 13:28:34 +0000 | [diff] [blame] | 2777 | if (res.kind == GXR_Addr |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2778 | && res.word <= data_addr |
| 2779 | && data_addr < res.word + var_szB) { |
| 2780 | *offset = data_addr - res.word; |
| 2781 | return True; |
| 2782 | } else { |
| 2783 | return False; |
| 2784 | } |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 2785 | } |
| 2786 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2787 | |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2788 | /* Format the acquired information into DN(AME)1 and DN(AME)2, which |
| 2789 | are XArray*s of HChar, that have been initialised by the caller. |
| 2790 | Resulting strings will be zero terminated. Information is |
| 2791 | formatted in an understandable way. Not so easy. If frameNo is |
| 2792 | -1, this is assumed to be a global variable; else a local |
| 2793 | variable. */ |
| 2794 | static void format_message ( /*MOD*/XArray* /* of HChar */ dn1, |
| 2795 | /*MOD*/XArray* /* of HChar */ dn2, |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2796 | Addr data_addr, |
| 2797 | DiVariable* var, |
njn | c4431bf | 2009-01-15 21:29:24 +0000 | [diff] [blame] | 2798 | PtrdiffT var_offset, |
| 2799 | PtrdiffT residual_offset, |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2800 | XArray* /*UChar*/ described, |
| 2801 | Int frameNo, |
| 2802 | ThreadId tid ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 2803 | { |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2804 | Bool have_descr, have_srcloc; |
| 2805 | Bool xml = VG_(clo_xml); |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 2806 | const HChar* vo_plural = var_offset == 1 ? "" : "s"; |
| 2807 | const HChar* ro_plural = residual_offset == 1 ? "" : "s"; |
| 2808 | const HChar* basetag = "auxwhat"; /* a constant */ |
| 2809 | HChar tagL[32], tagR[32], xagL[32], xagR[32]; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2810 | |
sewardj | d7adca7 | 2011-05-04 09:06:17 +0000 | [diff] [blame] | 2811 | if (frameNo < -1) { |
| 2812 | vg_assert(0); /* Not allowed */ |
| 2813 | } |
| 2814 | else if (frameNo == -1) { |
| 2815 | vg_assert(tid == VG_INVALID_THREADID); |
| 2816 | } |
| 2817 | else /* (frameNo >= 0) */ { |
| 2818 | vg_assert(tid != VG_INVALID_THREADID); |
| 2819 | } |
| 2820 | |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2821 | vg_assert(dn1 && dn2); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2822 | vg_assert(described); |
| 2823 | vg_assert(var && var->name); |
| 2824 | have_descr = VG_(sizeXA)(described) > 0 |
| 2825 | && *(UChar*)VG_(indexXA)(described,0) != '\0'; |
| 2826 | have_srcloc = var->fileName && var->lineNo > 0; |
| 2827 | |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2828 | tagL[0] = tagR[0] = xagL[0] = xagR[0] = 0; |
| 2829 | if (xml) { |
| 2830 | VG_(sprintf)(tagL, "<%s>", basetag); // <auxwhat> |
| 2831 | VG_(sprintf)(tagR, "</%s>", basetag); // </auxwhat> |
| 2832 | VG_(sprintf)(xagL, "<x%s>", basetag); // <xauxwhat> |
| 2833 | VG_(sprintf)(xagR, "</x%s>", basetag); // </xauxwhat> |
| 2834 | } |
| 2835 | |
| 2836 | # define TAGL(_xa) p2XA(_xa, "%s", tagL) |
| 2837 | # define TAGR(_xa) p2XA(_xa, "%s", tagR) |
| 2838 | # define XAGL(_xa) p2XA(_xa, "%s", xagL) |
| 2839 | # define XAGR(_xa) p2XA(_xa, "%s", xagR) |
| 2840 | # define TXTL(_xa) p2XA(_xa, "%s", "<text>") |
| 2841 | # define TXTR(_xa) p2XA(_xa, "%s", "</text>") |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2842 | |
| 2843 | /* ------ local cases ------ */ |
| 2844 | |
| 2845 | if ( frameNo >= 0 && (!have_srcloc) && (!have_descr) ) { |
| 2846 | /* no srcloc, no description: |
| 2847 | Location 0x7fefff6cf is 543 bytes inside local var "a", |
| 2848 | in frame #1 of thread 1 |
| 2849 | */ |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2850 | if (xml) { |
| 2851 | TAGL( dn1 ); |
| 2852 | p2XA( dn1, |
bart | b3af9cf | 2011-10-06 19:08:37 +0000 | [diff] [blame] | 2853 | "Location 0x%lx is %lu byte%s inside local var \"%pS\",", |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2854 | data_addr, var_offset, vo_plural, var->name ); |
| 2855 | TAGR( dn1 ); |
| 2856 | TAGL( dn2 ); |
| 2857 | p2XA( dn2, |
| 2858 | "in frame #%d of thread %d", frameNo, (Int)tid ); |
| 2859 | TAGR( dn2 ); |
| 2860 | } else { |
| 2861 | p2XA( dn1, |
| 2862 | "Location 0x%lx is %lu byte%s inside local var \"%s\",", |
| 2863 | data_addr, var_offset, vo_plural, var->name ); |
| 2864 | p2XA( dn2, |
| 2865 | "in frame #%d of thread %d", frameNo, (Int)tid ); |
| 2866 | } |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2867 | } |
| 2868 | else |
| 2869 | if ( frameNo >= 0 && have_srcloc && (!have_descr) ) { |
| 2870 | /* no description: |
| 2871 | Location 0x7fefff6cf is 543 bytes inside local var "a" |
| 2872 | declared at dsyms7.c:17, in frame #1 of thread 1 |
| 2873 | */ |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2874 | if (xml) { |
| 2875 | TAGL( dn1 ); |
| 2876 | p2XA( dn1, |
bart | b3af9cf | 2011-10-06 19:08:37 +0000 | [diff] [blame] | 2877 | "Location 0x%lx is %lu byte%s inside local var \"%pS\"", |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2878 | data_addr, var_offset, vo_plural, var->name ); |
| 2879 | TAGR( dn1 ); |
| 2880 | XAGL( dn2 ); |
| 2881 | TXTL( dn2 ); |
| 2882 | p2XA( dn2, |
bart | b3af9cf | 2011-10-06 19:08:37 +0000 | [diff] [blame] | 2883 | "declared at %pS:%d, in frame #%d of thread %d", |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2884 | var->fileName, var->lineNo, frameNo, (Int)tid ); |
| 2885 | TXTR( dn2 ); |
| 2886 | // FIXME: also do <dir> |
| 2887 | p2XA( dn2, |
bart | b3af9cf | 2011-10-06 19:08:37 +0000 | [diff] [blame] | 2888 | " <file>%pS</file> <line>%d</line> ", |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2889 | var->fileName, var->lineNo ); |
| 2890 | XAGR( dn2 ); |
| 2891 | } else { |
| 2892 | p2XA( dn1, |
| 2893 | "Location 0x%lx is %lu byte%s inside local var \"%s\"", |
| 2894 | data_addr, var_offset, vo_plural, var->name ); |
| 2895 | p2XA( dn2, |
| 2896 | "declared at %s:%d, in frame #%d of thread %d", |
| 2897 | var->fileName, var->lineNo, frameNo, (Int)tid ); |
| 2898 | } |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2899 | } |
| 2900 | else |
| 2901 | if ( frameNo >= 0 && (!have_srcloc) && have_descr ) { |
| 2902 | /* no srcloc: |
| 2903 | Location 0x7fefff6cf is 2 bytes inside a[3].xyzzy[21].c2 |
| 2904 | in frame #1 of thread 1 |
| 2905 | */ |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2906 | if (xml) { |
| 2907 | TAGL( dn1 ); |
| 2908 | p2XA( dn1, |
bart | b3af9cf | 2011-10-06 19:08:37 +0000 | [diff] [blame] | 2909 | "Location 0x%lx is %lu byte%s inside %pS%pS", |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2910 | data_addr, residual_offset, ro_plural, var->name, |
| 2911 | (HChar*)(VG_(indexXA)(described,0)) ); |
| 2912 | TAGR( dn1 ); |
| 2913 | TAGL( dn2 ); |
| 2914 | p2XA( dn2, |
| 2915 | "in frame #%d of thread %d", frameNo, (Int)tid ); |
| 2916 | TAGR( dn2 ); |
| 2917 | } else { |
| 2918 | p2XA( dn1, |
| 2919 | "Location 0x%lx is %lu byte%s inside %s%s", |
| 2920 | data_addr, residual_offset, ro_plural, var->name, |
| 2921 | (HChar*)(VG_(indexXA)(described,0)) ); |
| 2922 | p2XA( dn2, |
| 2923 | "in frame #%d of thread %d", frameNo, (Int)tid ); |
| 2924 | } |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2925 | } |
| 2926 | else |
| 2927 | if ( frameNo >= 0 && have_srcloc && have_descr ) { |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2928 | /* Location 0x7fefff6cf is 2 bytes inside a[3].xyzzy[21].c2, |
| 2929 | declared at dsyms7.c:17, in frame #1 of thread 1 */ |
| 2930 | if (xml) { |
| 2931 | TAGL( dn1 ); |
| 2932 | p2XA( dn1, |
bart | b3af9cf | 2011-10-06 19:08:37 +0000 | [diff] [blame] | 2933 | "Location 0x%lx is %lu byte%s inside %pS%pS,", |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2934 | data_addr, residual_offset, ro_plural, var->name, |
| 2935 | (HChar*)(VG_(indexXA)(described,0)) ); |
| 2936 | TAGR( dn1 ); |
| 2937 | XAGL( dn2 ); |
| 2938 | TXTL( dn2 ); |
| 2939 | p2XA( dn2, |
bart | b3af9cf | 2011-10-06 19:08:37 +0000 | [diff] [blame] | 2940 | "declared at %pS:%d, in frame #%d of thread %d", |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2941 | var->fileName, var->lineNo, frameNo, (Int)tid ); |
| 2942 | TXTR( dn2 ); |
| 2943 | // FIXME: also do <dir> |
| 2944 | p2XA( dn2, |
bart | b3af9cf | 2011-10-06 19:08:37 +0000 | [diff] [blame] | 2945 | " <file>%pS</file> <line>%d</line> ", |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2946 | var->fileName, var->lineNo ); |
| 2947 | XAGR( dn2 ); |
| 2948 | } else { |
| 2949 | p2XA( dn1, |
| 2950 | "Location 0x%lx is %lu byte%s inside %s%s,", |
| 2951 | data_addr, residual_offset, ro_plural, var->name, |
| 2952 | (HChar*)(VG_(indexXA)(described,0)) ); |
| 2953 | p2XA( dn2, |
| 2954 | "declared at %s:%d, in frame #%d of thread %d", |
| 2955 | var->fileName, var->lineNo, frameNo, (Int)tid ); |
| 2956 | } |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2957 | } |
| 2958 | else |
| 2959 | /* ------ global cases ------ */ |
| 2960 | if ( frameNo >= -1 && (!have_srcloc) && (!have_descr) ) { |
| 2961 | /* no srcloc, no description: |
| 2962 | Location 0x7fefff6cf is 543 bytes inside global var "a" |
| 2963 | */ |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2964 | if (xml) { |
| 2965 | TAGL( dn1 ); |
| 2966 | p2XA( dn1, |
bart | b3af9cf | 2011-10-06 19:08:37 +0000 | [diff] [blame] | 2967 | "Location 0x%lx is %lu byte%s inside global var \"%pS\"", |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2968 | data_addr, var_offset, vo_plural, var->name ); |
| 2969 | TAGR( dn1 ); |
| 2970 | } else { |
| 2971 | p2XA( dn1, |
| 2972 | "Location 0x%lx is %lu byte%s inside global var \"%s\"", |
| 2973 | data_addr, var_offset, vo_plural, var->name ); |
| 2974 | } |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 2975 | } |
| 2976 | else |
| 2977 | if ( frameNo >= -1 && have_srcloc && (!have_descr) ) { |
| 2978 | /* no description: |
| 2979 | Location 0x7fefff6cf is 543 bytes inside global var "a" |
| 2980 | declared at dsyms7.c:17 |
| 2981 | */ |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2982 | if (xml) { |
| 2983 | TAGL( dn1 ); |
| 2984 | p2XA( dn1, |
bart | b3af9cf | 2011-10-06 19:08:37 +0000 | [diff] [blame] | 2985 | "Location 0x%lx is %lu byte%s inside global var \"%pS\"", |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2986 | data_addr, var_offset, vo_plural, var->name ); |
| 2987 | TAGR( dn1 ); |
| 2988 | XAGL( dn2 ); |
| 2989 | TXTL( dn2 ); |
| 2990 | p2XA( dn2, |
bart | b3af9cf | 2011-10-06 19:08:37 +0000 | [diff] [blame] | 2991 | "declared at %pS:%d", |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2992 | var->fileName, var->lineNo); |
| 2993 | TXTR( dn2 ); |
| 2994 | // FIXME: also do <dir> |
| 2995 | p2XA( dn2, |
bart | b3af9cf | 2011-10-06 19:08:37 +0000 | [diff] [blame] | 2996 | " <file>%pS</file> <line>%d</line> ", |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 2997 | var->fileName, var->lineNo ); |
| 2998 | XAGR( dn2 ); |
| 2999 | } else { |
| 3000 | p2XA( dn1, |
| 3001 | "Location 0x%lx is %lu byte%s inside global var \"%s\"", |
| 3002 | data_addr, var_offset, vo_plural, var->name ); |
| 3003 | p2XA( dn2, |
| 3004 | "declared at %s:%d", |
| 3005 | var->fileName, var->lineNo); |
| 3006 | } |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3007 | } |
| 3008 | else |
| 3009 | if ( frameNo >= -1 && (!have_srcloc) && have_descr ) { |
| 3010 | /* no srcloc: |
| 3011 | Location 0x7fefff6cf is 2 bytes inside a[3].xyzzy[21].c2, |
| 3012 | a global variable |
| 3013 | */ |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3014 | if (xml) { |
| 3015 | TAGL( dn1 ); |
| 3016 | p2XA( dn1, |
bart | b3af9cf | 2011-10-06 19:08:37 +0000 | [diff] [blame] | 3017 | "Location 0x%lx is %lu byte%s inside %pS%pS,", |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3018 | data_addr, residual_offset, ro_plural, var->name, |
| 3019 | (HChar*)(VG_(indexXA)(described,0)) ); |
| 3020 | TAGR( dn1 ); |
| 3021 | TAGL( dn2 ); |
| 3022 | p2XA( dn2, |
| 3023 | "a global variable"); |
| 3024 | TAGR( dn2 ); |
| 3025 | } else { |
| 3026 | p2XA( dn1, |
| 3027 | "Location 0x%lx is %lu byte%s inside %s%s,", |
| 3028 | data_addr, residual_offset, ro_plural, var->name, |
| 3029 | (char*)(VG_(indexXA)(described,0)) ); |
| 3030 | p2XA( dn2, |
| 3031 | "a global variable"); |
| 3032 | } |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3033 | } |
| 3034 | else |
| 3035 | if ( frameNo >= -1 && have_srcloc && have_descr ) { |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3036 | /* Location 0x7fefff6cf is 2 bytes inside a[3].xyzzy[21].c2, |
| 3037 | a global variable declared at dsyms7.c:17 */ |
| 3038 | if (xml) { |
| 3039 | TAGL( dn1 ); |
| 3040 | p2XA( dn1, |
bart | b3af9cf | 2011-10-06 19:08:37 +0000 | [diff] [blame] | 3041 | "Location 0x%lx is %lu byte%s inside %pS%pS,", |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3042 | data_addr, residual_offset, ro_plural, var->name, |
| 3043 | (HChar*)(VG_(indexXA)(described,0)) ); |
| 3044 | TAGR( dn1 ); |
| 3045 | XAGL( dn2 ); |
| 3046 | TXTL( dn2 ); |
| 3047 | p2XA( dn2, |
bart | b3af9cf | 2011-10-06 19:08:37 +0000 | [diff] [blame] | 3048 | "a global variable declared at %pS:%d", |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3049 | var->fileName, var->lineNo); |
| 3050 | TXTR( dn2 ); |
| 3051 | // FIXME: also do <dir> |
| 3052 | p2XA( dn2, |
bart | b3af9cf | 2011-10-06 19:08:37 +0000 | [diff] [blame] | 3053 | " <file>%pS</file> <line>%d</line> ", |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3054 | var->fileName, var->lineNo ); |
| 3055 | XAGR( dn2 ); |
| 3056 | } else { |
| 3057 | p2XA( dn1, |
| 3058 | "Location 0x%lx is %lu byte%s inside %s%s,", |
| 3059 | data_addr, residual_offset, ro_plural, var->name, |
| 3060 | (HChar*)(VG_(indexXA)(described,0)) ); |
| 3061 | p2XA( dn2, |
| 3062 | "a global variable declared at %s:%d", |
| 3063 | var->fileName, var->lineNo); |
| 3064 | } |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3065 | } |
| 3066 | else |
| 3067 | vg_assert(0); |
| 3068 | |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3069 | /* Zero terminate both strings */ |
| 3070 | zterm_XA( dn1 ); |
| 3071 | zterm_XA( dn2 ); |
| 3072 | |
| 3073 | # undef TAGL |
| 3074 | # undef TAGR |
| 3075 | # undef XAGL |
| 3076 | # undef XAGR |
| 3077 | # undef TXTL |
| 3078 | # undef TXTR |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 3079 | } |
| 3080 | |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3081 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3082 | /* Determine if data_addr is a local variable in the frame |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3083 | characterised by (ip,sp,fp), and if so write its description at the |
| 3084 | ends of DNAME{1,2}, which are XArray*s of HChar, that have been |
| 3085 | initialised by the caller, zero terminate both, and return True. |
| 3086 | If it's not a local variable in said frame, return False. */ |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3087 | static |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3088 | Bool consider_vars_in_frame ( /*MOD*/XArray* /* of HChar */ dname1, |
| 3089 | /*MOD*/XArray* /* of HChar */ dname2, |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3090 | Addr data_addr, |
| 3091 | Addr ip, Addr sp, Addr fp, |
| 3092 | /* shown to user: */ |
| 3093 | ThreadId tid, Int frameNo ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 3094 | { |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3095 | Word i; |
| 3096 | DebugInfo* di; |
| 3097 | RegSummary regs; |
| 3098 | Bool debug = False; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 3099 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3100 | static UInt n_search = 0; |
| 3101 | static UInt n_steps = 0; |
| 3102 | n_search++; |
| 3103 | if (debug) |
bart | a0b6b2c | 2008-07-07 06:49:24 +0000 | [diff] [blame] | 3104 | VG_(printf)("QQQQ: cvif: ip,sp,fp %#lx,%#lx,%#lx\n", ip,sp,fp); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3105 | /* first, find the DebugInfo that pertains to 'ip'. */ |
| 3106 | for (di = debugInfo_list; di; di = di->next) { |
| 3107 | n_steps++; |
| 3108 | /* text segment missing? unlikely, but handle it .. */ |
| 3109 | if (!di->text_present || di->text_size == 0) |
| 3110 | continue; |
| 3111 | /* Ok. So does this text mapping bracket the ip? */ |
| 3112 | if (di->text_avma <= ip && ip < di->text_avma + di->text_size) |
| 3113 | break; |
| 3114 | } |
| 3115 | |
| 3116 | /* Didn't find it. Strange -- means ip is a code address outside |
| 3117 | of any mapped text segment. Unlikely but not impossible -- app |
| 3118 | could be generating code to run. */ |
| 3119 | if (!di) |
| 3120 | return False; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 3121 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3122 | if (0 && ((n_search & 0x1) == 0)) |
| 3123 | VG_(printf)("consider_vars_in_frame: %u searches, " |
| 3124 | "%u DebugInfos looked at\n", |
| 3125 | n_search, n_steps); |
| 3126 | /* Start of performance-enhancing hack: once every ??? (chosen |
| 3127 | hackily after profiling) successful searches, move the found |
| 3128 | DebugInfo one step closer to the start of the list. This makes |
| 3129 | future searches cheaper. */ |
| 3130 | if ((n_search & 0xFFFF) == 0) { |
| 3131 | /* Move si one step closer to the start of the list. */ |
| 3132 | move_DebugInfo_one_step_forward( di ); |
| 3133 | } |
| 3134 | /* End of performance-enhancing hack. */ |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 3135 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3136 | /* any var info at all? */ |
| 3137 | if (!di->varinfo) |
| 3138 | return False; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 3139 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3140 | /* Work through the scopes from most deeply nested outwards, |
| 3141 | looking for code address ranges that bracket 'ip'. The |
| 3142 | variables on each such address range found are in scope right |
| 3143 | now. Don't descend to level zero as that is the global |
| 3144 | scope. */ |
| 3145 | regs.ip = ip; |
| 3146 | regs.sp = sp; |
| 3147 | regs.fp = fp; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 3148 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3149 | /* "for each scope, working outwards ..." */ |
| 3150 | for (i = VG_(sizeXA)(di->varinfo) - 1; i >= 1; i--) { |
| 3151 | XArray* vars; |
| 3152 | Word j; |
| 3153 | DiAddrRange* arange; |
| 3154 | OSet* this_scope |
| 3155 | = *(OSet**)VG_(indexXA)( di->varinfo, i ); |
| 3156 | if (debug) |
| 3157 | VG_(printf)("QQQQ: considering scope %ld\n", (Word)i); |
| 3158 | if (!this_scope) |
| 3159 | continue; |
| 3160 | /* Find the set of variables in this scope that |
| 3161 | bracket the program counter. */ |
| 3162 | arange = VG_(OSetGen_LookupWithCmp)( |
| 3163 | this_scope, &ip, |
| 3164 | ML_(cmp_for_DiAddrRange_range) |
| 3165 | ); |
| 3166 | if (!arange) |
| 3167 | continue; |
| 3168 | /* stay sane */ |
| 3169 | vg_assert(arange->aMin <= arange->aMax); |
| 3170 | /* It must bracket the ip we asked for, else |
| 3171 | ML_(cmp_for_DiAddrRange_range) is somehow broken. */ |
| 3172 | vg_assert(arange->aMin <= ip && ip <= arange->aMax); |
| 3173 | /* It must have an attached XArray of DiVariables. */ |
| 3174 | vars = arange->vars; |
| 3175 | vg_assert(vars); |
| 3176 | /* But it mustn't cover the entire address range. We only |
| 3177 | expect that to happen for the global scope (level 0), which |
| 3178 | we're not looking at here. Except, it may cover the entire |
| 3179 | address range, but in that case the vars array must be |
| 3180 | empty. */ |
| 3181 | vg_assert(! (arange->aMin == (Addr)0 |
| 3182 | && arange->aMax == ~(Addr)0 |
| 3183 | && VG_(sizeXA)(vars) > 0) ); |
| 3184 | for (j = 0; j < VG_(sizeXA)( vars ); j++) { |
| 3185 | DiVariable* var = (DiVariable*)VG_(indexXA)( vars, j ); |
njn | c4431bf | 2009-01-15 21:29:24 +0000 | [diff] [blame] | 3186 | PtrdiffT offset; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3187 | if (debug) |
bart | a0b6b2c | 2008-07-07 06:49:24 +0000 | [diff] [blame] | 3188 | VG_(printf)("QQQQ: var:name=%s %#lx-%#lx %#lx\n", |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3189 | var->name,arange->aMin,arange->aMax,ip); |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3190 | if (data_address_is_in_var( &offset, di->admin_tyents, |
| 3191 | var, ®s, |
tom | 588658b | 2009-01-22 13:40:12 +0000 | [diff] [blame] | 3192 | data_addr, di )) { |
njn | c4431bf | 2009-01-15 21:29:24 +0000 | [diff] [blame] | 3193 | PtrdiffT residual_offset = 0; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3194 | XArray* described = ML_(describe_type)( &residual_offset, |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3195 | di->admin_tyents, |
| 3196 | var->typeR, offset ); |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3197 | format_message( dname1, dname2, |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3198 | data_addr, var, offset, residual_offset, |
| 3199 | described, frameNo, tid ); |
| 3200 | VG_(deleteXA)( described ); |
| 3201 | return True; |
| 3202 | } |
sewardj | ab3698a | 2007-12-02 22:03:43 +0000 | [diff] [blame] | 3203 | } |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 3204 | } |
| 3205 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3206 | return False; |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 3207 | } |
| 3208 | |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3209 | /* Try to form some description of DATA_ADDR by looking at the DWARF3 |
philippe | 2596337 | 2013-01-16 22:07:02 +0000 | [diff] [blame] | 3210 | debug info we have. This considers all global variables, and 8 |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3211 | frames in the stacks of all threads. Result is written at the ends |
| 3212 | of DNAME{1,2}V, which are XArray*s of HChar, that have been |
| 3213 | initialised by the caller, and True is returned. If no description |
| 3214 | is created, False is returned. Regardless of the return value, |
| 3215 | DNAME{1,2}V are guaranteed to be zero terminated after the call. |
| 3216 | |
| 3217 | Note that after the call, DNAME{1,2} may have more than one |
| 3218 | trailing zero, so callers should establish the useful text length |
| 3219 | using VG_(strlen) on the contents, rather than VG_(sizeXA) on the |
| 3220 | XArray itself. |
| 3221 | */ |
| 3222 | Bool VG_(get_data_description)( |
| 3223 | /*MOD*/ void* /* really, XArray* of HChar */ dname1v, |
| 3224 | /*MOD*/ void* /* really, XArray* of HChar */ dname2v, |
| 3225 | Addr data_addr |
| 3226 | ) |
sewardj | bbec772 | 2007-11-25 14:08:53 +0000 | [diff] [blame] | 3227 | { |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3228 | # define N_FRAMES 8 |
| 3229 | Addr ips[N_FRAMES], sps[N_FRAMES], fps[N_FRAMES]; |
| 3230 | UInt n_frames; |
| 3231 | |
| 3232 | Addr stack_min, stack_max; |
| 3233 | ThreadId tid; |
| 3234 | Bool found; |
| 3235 | DebugInfo* di; |
| 3236 | Word j; |
| 3237 | |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3238 | XArray* dname1 = (XArray*)dname1v; |
| 3239 | XArray* dname2 = (XArray*)dname2v; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3240 | |
bart | a0b6b2c | 2008-07-07 06:49:24 +0000 | [diff] [blame] | 3241 | if (0) VG_(printf)("get_data_description: dataaddr %#lx\n", data_addr); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3242 | /* First, see if data_addr is (or is part of) a global variable. |
| 3243 | Loop over the DebugInfos we have. Check data_addr against the |
| 3244 | outermost scope of all of them, as that should be a global |
| 3245 | scope. */ |
| 3246 | for (di = debugInfo_list; di != NULL; di = di->next) { |
| 3247 | OSet* global_scope; |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3248 | Word gs_size; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3249 | Addr zero; |
| 3250 | DiAddrRange* global_arange; |
| 3251 | Word i; |
| 3252 | XArray* vars; |
| 3253 | |
| 3254 | /* text segment missing? unlikely, but handle it .. */ |
| 3255 | if (!di->text_present || di->text_size == 0) |
| 3256 | continue; |
| 3257 | /* any var info at all? */ |
| 3258 | if (!di->varinfo) |
| 3259 | continue; |
| 3260 | /* perhaps this object didn't contribute any vars at all? */ |
| 3261 | if (VG_(sizeXA)( di->varinfo ) == 0) |
| 3262 | continue; |
| 3263 | global_scope = *(OSet**)VG_(indexXA)( di->varinfo, 0 ); |
| 3264 | vg_assert(global_scope); |
| 3265 | gs_size = VG_(OSetGen_Size)( global_scope ); |
| 3266 | /* The global scope might be completely empty if this |
| 3267 | compilation unit declared locals but nothing global. */ |
| 3268 | if (gs_size == 0) |
| 3269 | continue; |
| 3270 | /* But if it isn't empty, then it must contain exactly one |
| 3271 | element, which covers the entire address range. */ |
| 3272 | vg_assert(gs_size == 1); |
| 3273 | /* Fish out the global scope and check it is as expected. */ |
| 3274 | zero = 0; |
| 3275 | global_arange |
| 3276 | = VG_(OSetGen_Lookup)( global_scope, &zero ); |
| 3277 | /* The global range from (Addr)0 to ~(Addr)0 must exist */ |
| 3278 | vg_assert(global_arange); |
| 3279 | vg_assert(global_arange->aMin == (Addr)0 |
| 3280 | && global_arange->aMax == ~(Addr)0); |
| 3281 | /* Any vars in this range? */ |
| 3282 | if (!global_arange->vars) |
| 3283 | continue; |
| 3284 | /* Ok, there are some vars in the global scope of this |
| 3285 | DebugInfo. Wade through them and see if the data addresses |
| 3286 | of any of them bracket data_addr. */ |
| 3287 | vars = global_arange->vars; |
| 3288 | for (i = 0; i < VG_(sizeXA)( vars ); i++) { |
njn | c4431bf | 2009-01-15 21:29:24 +0000 | [diff] [blame] | 3289 | PtrdiffT offset; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3290 | DiVariable* var = (DiVariable*)VG_(indexXA)( vars, i ); |
| 3291 | vg_assert(var->name); |
| 3292 | /* Note we use a NULL RegSummary* here. It can't make any |
| 3293 | sense for a global variable to have a location expression |
| 3294 | which depends on a SP/FP/IP value. So don't supply any. |
| 3295 | This means, if the evaluation of the location |
| 3296 | expression/list requires a register, we have to let it |
| 3297 | fail. */ |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3298 | if (data_address_is_in_var( &offset, di->admin_tyents, var, |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3299 | NULL/* RegSummary* */, |
tom | 588658b | 2009-01-22 13:40:12 +0000 | [diff] [blame] | 3300 | data_addr, di )) { |
njn | c4431bf | 2009-01-15 21:29:24 +0000 | [diff] [blame] | 3301 | PtrdiffT residual_offset = 0; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3302 | XArray* described = ML_(describe_type)( &residual_offset, |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3303 | di->admin_tyents, |
| 3304 | var->typeR, offset ); |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3305 | format_message( dname1, dname2, |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3306 | data_addr, var, offset, residual_offset, |
sewardj | d7adca7 | 2011-05-04 09:06:17 +0000 | [diff] [blame] | 3307 | described, -1/*frameNo*/, |
| 3308 | VG_INVALID_THREADID ); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3309 | VG_(deleteXA)( described ); |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3310 | zterm_XA( dname1 ); |
| 3311 | zterm_XA( dname2 ); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3312 | return True; |
| 3313 | } |
| 3314 | } |
sewardj | bbec772 | 2007-11-25 14:08:53 +0000 | [diff] [blame] | 3315 | } |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3316 | |
| 3317 | /* Ok, well it's not a global variable. So now let's snoop around |
| 3318 | in the stacks of all the threads. First try to figure out which |
| 3319 | thread's stack data_addr is in. */ |
| 3320 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3321 | /* Perhaps it's on a thread's stack? */ |
| 3322 | found = False; |
| 3323 | VG_(thread_stack_reset_iter)(&tid); |
| 3324 | while ( VG_(thread_stack_next)(&tid, &stack_min, &stack_max) ) { |
| 3325 | if (stack_min >= stack_max) |
| 3326 | continue; /* ignore obviously stupid cases */ |
| 3327 | if (stack_min - VG_STACK_REDZONE_SZB <= data_addr |
| 3328 | && data_addr <= stack_max) { |
| 3329 | found = True; |
| 3330 | break; |
| 3331 | } |
| 3332 | } |
| 3333 | if (!found) { |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3334 | zterm_XA( dname1 ); |
| 3335 | zterm_XA( dname2 ); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3336 | return False; |
| 3337 | } |
| 3338 | |
| 3339 | /* We conclude data_addr is in thread tid's stack. Unwind the |
| 3340 | stack to get a bunch of (ip,sp,fp) triples describing the |
| 3341 | frames, and for each frame, consider the local variables. */ |
| 3342 | n_frames = VG_(get_StackTrace)( tid, ips, N_FRAMES, |
| 3343 | sps, fps, 0/*first_ip_delta*/ ); |
sewardj | b1ae15d | 2008-12-12 13:23:03 +0000 | [diff] [blame] | 3344 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3345 | vg_assert(n_frames >= 0 && n_frames <= N_FRAMES); |
| 3346 | for (j = 0; j < n_frames; j++) { |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3347 | if (consider_vars_in_frame( dname1, dname2, |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3348 | data_addr, |
sewardj | b1ae15d | 2008-12-12 13:23:03 +0000 | [diff] [blame] | 3349 | ips[j], |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3350 | sps[j], fps[j], tid, j )) { |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3351 | zterm_XA( dname1 ); |
| 3352 | zterm_XA( dname2 ); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3353 | return True; |
| 3354 | } |
| 3355 | /* Now, it appears that gcc sometimes appears to produce |
| 3356 | location lists whose ranges don't actually cover the call |
| 3357 | instruction, even though the address of the variable in |
| 3358 | question is passed as a parameter in the call. AFAICS this |
| 3359 | is simply a bug in gcc - how can the variable be claimed not |
| 3360 | exist in memory (on the stack) for the duration of a call in |
| 3361 | which its address is passed? But anyway, in the particular |
| 3362 | case I investigated (memcheck/tests/varinfo6.c, call to croak |
| 3363 | on line 2999, local var budget declared at line 3115 |
| 3364 | appearing not to exist across the call to mainSort on line |
| 3365 | 3143, "gcc.orig (GCC) 3.4.4 20050721 (Red Hat 3.4.4-2)" on |
| 3366 | amd64), the variable's location list does claim it exists |
| 3367 | starting at the first byte of the first instruction after the |
| 3368 | call instruction. So, call consider_vars_in_frame a second |
sewardj | b1ae15d | 2008-12-12 13:23:03 +0000 | [diff] [blame] | 3369 | time, but this time add 1 to the IP. GDB handles this |
| 3370 | example with no difficulty, which leads me to believe that |
| 3371 | either (1) I misunderstood something, or (2) GDB has an |
| 3372 | equivalent kludge. */ |
| 3373 | if (j > 0 /* this is a non-innermost frame */ |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3374 | && consider_vars_in_frame( dname1, dname2, |
sewardj | b1ae15d | 2008-12-12 13:23:03 +0000 | [diff] [blame] | 3375 | data_addr, |
| 3376 | ips[j] + 1, |
| 3377 | sps[j], fps[j], tid, j )) { |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3378 | zterm_XA( dname1 ); |
| 3379 | zterm_XA( dname2 ); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3380 | return True; |
| 3381 | } |
| 3382 | } |
| 3383 | |
| 3384 | /* We didn't find anything useful. */ |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 3385 | zterm_XA( dname1 ); |
| 3386 | zterm_XA( dname2 ); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3387 | return False; |
| 3388 | # undef N_FRAMES |
sewardj | bbec772 | 2007-11-25 14:08:53 +0000 | [diff] [blame] | 3389 | } |
| 3390 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3391 | |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3392 | ////////////////////////////////////////////////////////////////// |
| 3393 | // // |
| 3394 | // Support for other kinds of queries to the Dwarf3 var info // |
| 3395 | // // |
| 3396 | ////////////////////////////////////////////////////////////////// |
| 3397 | |
| 3398 | /* Figure out if the variable 'var' has a location that is linearly |
| 3399 | dependent on a stack pointer value, or a frame pointer value, and |
| 3400 | if it is, add a description of it to 'blocks'. Otherwise ignore |
| 3401 | it. If 'arrays_only' is True, also ignore it unless it has an |
| 3402 | array type. */ |
| 3403 | |
| 3404 | static |
| 3405 | void analyse_deps ( /*MOD*/XArray* /* of FrameBlock */ blocks, |
| 3406 | XArray* /* TyEnt */ tyents, |
tom | 588658b | 2009-01-22 13:40:12 +0000 | [diff] [blame] | 3407 | Addr ip, const DebugInfo* di, DiVariable* var, |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3408 | Bool arrays_only ) |
| 3409 | { |
| 3410 | GXResult res_sp_6k, res_sp_7k, res_fp_6k, res_fp_7k; |
| 3411 | RegSummary regs; |
sewardj | 50fde23 | 2008-10-20 16:08:55 +0000 | [diff] [blame] | 3412 | MaybeULong mul; |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3413 | Bool isVec; |
| 3414 | TyEnt* ty; |
| 3415 | |
| 3416 | Bool debug = False; |
| 3417 | if (0&&debug) |
| 3418 | VG_(printf)("adeps: var %s\n", var->name ); |
| 3419 | |
| 3420 | /* Figure out how big the variable is. */ |
sewardj | 50fde23 | 2008-10-20 16:08:55 +0000 | [diff] [blame] | 3421 | mul = ML_(sizeOfType)(tyents, var->typeR); |
| 3422 | /* If this var has a type whose size is unknown, zero, or |
| 3423 | impossibly large, it should never have been added. ML_(addVar) |
| 3424 | should have rejected it. */ |
| 3425 | vg_assert(mul.b == True); |
| 3426 | vg_assert(mul.ul > 0); |
| 3427 | if (sizeof(void*) == 4) vg_assert(mul.ul < (1ULL << 32)); |
| 3428 | /* After this point, we assume we can truncate mul.ul to a host word |
| 3429 | safely (without loss of info). */ |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3430 | |
| 3431 | /* skip if non-array and we're only interested in arrays */ |
| 3432 | ty = ML_(TyEnts__index_by_cuOff)( tyents, NULL, var->typeR ); |
| 3433 | vg_assert(ty); |
| 3434 | vg_assert(ty->tag == Te_UNKNOWN || ML_(TyEnt__is_type)(ty)); |
| 3435 | if (ty->tag == Te_UNKNOWN) |
| 3436 | return; /* perhaps we should complain in this case? */ |
| 3437 | isVec = ty->tag == Te_TyArray; |
| 3438 | if (arrays_only && !isVec) |
| 3439 | return; |
| 3440 | |
| 3441 | if (0) {ML_(pp_TyEnt_C_ishly)(tyents, var->typeR); |
| 3442 | VG_(printf)(" %s\n", var->name);} |
| 3443 | |
| 3444 | /* Do some test evaluations of the variable's location expression, |
| 3445 | in order to guess whether it is sp-relative, fp-relative, or |
| 3446 | none. A crude hack, which can be interpreted roughly as finding |
| 3447 | the first derivative of the location expression w.r.t. the |
| 3448 | supplied frame and stack pointer values. */ |
| 3449 | regs.fp = 0; |
| 3450 | regs.ip = ip; |
| 3451 | regs.sp = 6 * 1024; |
tom | 588658b | 2009-01-22 13:40:12 +0000 | [diff] [blame] | 3452 | res_sp_6k = ML_(evaluate_GX)( var->gexpr, var->fbGX, ®s, di ); |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3453 | |
| 3454 | regs.fp = 0; |
| 3455 | regs.ip = ip; |
| 3456 | regs.sp = 7 * 1024; |
tom | 588658b | 2009-01-22 13:40:12 +0000 | [diff] [blame] | 3457 | res_sp_7k = ML_(evaluate_GX)( var->gexpr, var->fbGX, ®s, di ); |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3458 | |
| 3459 | regs.fp = 6 * 1024; |
| 3460 | regs.ip = ip; |
| 3461 | regs.sp = 0; |
tom | 588658b | 2009-01-22 13:40:12 +0000 | [diff] [blame] | 3462 | res_fp_6k = ML_(evaluate_GX)( var->gexpr, var->fbGX, ®s, di ); |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3463 | |
| 3464 | regs.fp = 7 * 1024; |
| 3465 | regs.ip = ip; |
| 3466 | regs.sp = 0; |
tom | 588658b | 2009-01-22 13:40:12 +0000 | [diff] [blame] | 3467 | res_fp_7k = ML_(evaluate_GX)( var->gexpr, var->fbGX, ®s, di ); |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3468 | |
| 3469 | vg_assert(res_sp_6k.kind == res_sp_7k.kind); |
| 3470 | vg_assert(res_sp_6k.kind == res_fp_6k.kind); |
| 3471 | vg_assert(res_sp_6k.kind == res_fp_7k.kind); |
| 3472 | |
tom | 3c9cf34 | 2009-11-12 13:28:34 +0000 | [diff] [blame] | 3473 | if (res_sp_6k.kind == GXR_Addr) { |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3474 | StackBlock block; |
| 3475 | GXResult res; |
| 3476 | UWord sp_delta = res_sp_7k.word - res_sp_6k.word; |
| 3477 | UWord fp_delta = res_fp_7k.word - res_fp_6k.word; |
| 3478 | tl_assert(sp_delta == 0 || sp_delta == 1024); |
| 3479 | tl_assert(fp_delta == 0 || fp_delta == 1024); |
| 3480 | |
| 3481 | if (sp_delta == 0 && fp_delta == 0) { |
| 3482 | /* depends neither on sp nor fp, so it can't be a stack |
| 3483 | local. Ignore it. */ |
| 3484 | } |
| 3485 | else |
| 3486 | if (sp_delta == 1024 && fp_delta == 0) { |
| 3487 | regs.sp = regs.fp = 0; |
| 3488 | regs.ip = ip; |
tom | 588658b | 2009-01-22 13:40:12 +0000 | [diff] [blame] | 3489 | res = ML_(evaluate_GX)( var->gexpr, var->fbGX, ®s, di ); |
tom | 3c9cf34 | 2009-11-12 13:28:34 +0000 | [diff] [blame] | 3490 | tl_assert(res.kind == GXR_Addr); |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3491 | if (debug) |
| 3492 | VG_(printf)(" %5ld .. %5ld (sp) %s\n", |
sewardj | 50fde23 | 2008-10-20 16:08:55 +0000 | [diff] [blame] | 3493 | res.word, res.word + ((UWord)mul.ul) - 1, var->name); |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3494 | block.base = res.word; |
sewardj | 50fde23 | 2008-10-20 16:08:55 +0000 | [diff] [blame] | 3495 | block.szB = (SizeT)mul.ul; |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3496 | block.spRel = True; |
| 3497 | block.isVec = isVec; |
| 3498 | VG_(memset)( &block.name[0], 0, sizeof(block.name) ); |
| 3499 | if (var->name) |
| 3500 | VG_(strncpy)( &block.name[0], var->name, sizeof(block.name)-1 ); |
| 3501 | block.name[ sizeof(block.name)-1 ] = 0; |
| 3502 | VG_(addToXA)( blocks, &block ); |
| 3503 | } |
| 3504 | else |
| 3505 | if (sp_delta == 0 && fp_delta == 1024) { |
| 3506 | regs.sp = regs.fp = 0; |
| 3507 | regs.ip = ip; |
tom | 588658b | 2009-01-22 13:40:12 +0000 | [diff] [blame] | 3508 | res = ML_(evaluate_GX)( var->gexpr, var->fbGX, ®s, di ); |
tom | 3c9cf34 | 2009-11-12 13:28:34 +0000 | [diff] [blame] | 3509 | tl_assert(res.kind == GXR_Addr); |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3510 | if (debug) |
| 3511 | VG_(printf)(" %5ld .. %5ld (FP) %s\n", |
sewardj | 50fde23 | 2008-10-20 16:08:55 +0000 | [diff] [blame] | 3512 | res.word, res.word + ((UWord)mul.ul) - 1, var->name); |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3513 | block.base = res.word; |
sewardj | 50fde23 | 2008-10-20 16:08:55 +0000 | [diff] [blame] | 3514 | block.szB = (SizeT)mul.ul; |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3515 | block.spRel = False; |
| 3516 | block.isVec = isVec; |
| 3517 | VG_(memset)( &block.name[0], 0, sizeof(block.name) ); |
| 3518 | if (var->name) |
| 3519 | VG_(strncpy)( &block.name[0], var->name, sizeof(block.name)-1 ); |
| 3520 | block.name[ sizeof(block.name)-1 ] = 0; |
| 3521 | VG_(addToXA)( blocks, &block ); |
| 3522 | } |
| 3523 | else { |
| 3524 | vg_assert(0); |
| 3525 | } |
| 3526 | } |
| 3527 | } |
| 3528 | |
| 3529 | |
| 3530 | /* Get an XArray of StackBlock which describe the stack (auto) blocks |
| 3531 | for this ip. The caller is expected to free the XArray at some |
| 3532 | point. If 'arrays_only' is True, only array-typed blocks are |
| 3533 | returned; otherwise blocks of all types are returned. */ |
| 3534 | |
| 3535 | void* /* really, XArray* of StackBlock */ |
| 3536 | VG_(di_get_stack_blocks_at_ip)( Addr ip, Bool arrays_only ) |
| 3537 | { |
| 3538 | /* This is a derivation of consider_vars_in_frame() above. */ |
| 3539 | Word i; |
| 3540 | DebugInfo* di; |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3541 | Bool debug = False; |
| 3542 | |
| 3543 | XArray* res = VG_(newXA)( ML_(dinfo_zalloc), "di.debuginfo.dgsbai.1", |
| 3544 | ML_(dinfo_free), |
| 3545 | sizeof(StackBlock) ); |
| 3546 | |
| 3547 | static UInt n_search = 0; |
| 3548 | static UInt n_steps = 0; |
| 3549 | n_search++; |
| 3550 | if (debug) |
| 3551 | VG_(printf)("QQQQ: dgsbai: ip %#lx\n", ip); |
| 3552 | /* first, find the DebugInfo that pertains to 'ip'. */ |
| 3553 | for (di = debugInfo_list; di; di = di->next) { |
| 3554 | n_steps++; |
| 3555 | /* text segment missing? unlikely, but handle it .. */ |
| 3556 | if (!di->text_present || di->text_size == 0) |
| 3557 | continue; |
| 3558 | /* Ok. So does this text mapping bracket the ip? */ |
| 3559 | if (di->text_avma <= ip && ip < di->text_avma + di->text_size) |
| 3560 | break; |
| 3561 | } |
| 3562 | |
| 3563 | /* Didn't find it. Strange -- means ip is a code address outside |
| 3564 | of any mapped text segment. Unlikely but not impossible -- app |
| 3565 | could be generating code to run. */ |
| 3566 | if (!di) |
| 3567 | return res; /* currently empty */ |
| 3568 | |
| 3569 | if (0 && ((n_search & 0x1) == 0)) |
| 3570 | VG_(printf)("VG_(di_get_stack_blocks_at_ip): %u searches, " |
| 3571 | "%u DebugInfos looked at\n", |
| 3572 | n_search, n_steps); |
| 3573 | /* Start of performance-enhancing hack: once every ??? (chosen |
| 3574 | hackily after profiling) successful searches, move the found |
| 3575 | DebugInfo one step closer to the start of the list. This makes |
| 3576 | future searches cheaper. */ |
| 3577 | if ((n_search & 0xFFFF) == 0) { |
| 3578 | /* Move si one step closer to the start of the list. */ |
| 3579 | move_DebugInfo_one_step_forward( di ); |
| 3580 | } |
| 3581 | /* End of performance-enhancing hack. */ |
| 3582 | |
| 3583 | /* any var info at all? */ |
| 3584 | if (!di->varinfo) |
| 3585 | return res; /* currently empty */ |
| 3586 | |
| 3587 | /* Work through the scopes from most deeply nested outwards, |
| 3588 | looking for code address ranges that bracket 'ip'. The |
| 3589 | variables on each such address range found are in scope right |
| 3590 | now. Don't descend to level zero as that is the global |
| 3591 | scope. */ |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3592 | |
| 3593 | /* "for each scope, working outwards ..." */ |
| 3594 | for (i = VG_(sizeXA)(di->varinfo) - 1; i >= 1; i--) { |
| 3595 | XArray* vars; |
| 3596 | Word j; |
| 3597 | DiAddrRange* arange; |
| 3598 | OSet* this_scope |
| 3599 | = *(OSet**)VG_(indexXA)( di->varinfo, i ); |
| 3600 | if (debug) |
| 3601 | VG_(printf)("QQQQ: considering scope %ld\n", (Word)i); |
| 3602 | if (!this_scope) |
| 3603 | continue; |
| 3604 | /* Find the set of variables in this scope that |
| 3605 | bracket the program counter. */ |
| 3606 | arange = VG_(OSetGen_LookupWithCmp)( |
| 3607 | this_scope, &ip, |
| 3608 | ML_(cmp_for_DiAddrRange_range) |
| 3609 | ); |
| 3610 | if (!arange) |
| 3611 | continue; |
| 3612 | /* stay sane */ |
| 3613 | vg_assert(arange->aMin <= arange->aMax); |
| 3614 | /* It must bracket the ip we asked for, else |
| 3615 | ML_(cmp_for_DiAddrRange_range) is somehow broken. */ |
| 3616 | vg_assert(arange->aMin <= ip && ip <= arange->aMax); |
| 3617 | /* It must have an attached XArray of DiVariables. */ |
| 3618 | vars = arange->vars; |
| 3619 | vg_assert(vars); |
| 3620 | /* But it mustn't cover the entire address range. We only |
| 3621 | expect that to happen for the global scope (level 0), which |
| 3622 | we're not looking at here. Except, it may cover the entire |
| 3623 | address range, but in that case the vars array must be |
| 3624 | empty. */ |
| 3625 | vg_assert(! (arange->aMin == (Addr)0 |
| 3626 | && arange->aMax == ~(Addr)0 |
| 3627 | && VG_(sizeXA)(vars) > 0) ); |
| 3628 | for (j = 0; j < VG_(sizeXA)( vars ); j++) { |
| 3629 | DiVariable* var = (DiVariable*)VG_(indexXA)( vars, j ); |
| 3630 | if (debug) |
| 3631 | VG_(printf)("QQQQ: var:name=%s %#lx-%#lx %#lx\n", |
| 3632 | var->name,arange->aMin,arange->aMax,ip); |
| 3633 | analyse_deps( res, di->admin_tyents, ip, |
tom | 588658b | 2009-01-22 13:40:12 +0000 | [diff] [blame] | 3634 | di, var, arrays_only ); |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3635 | } |
| 3636 | } |
| 3637 | |
| 3638 | return res; |
| 3639 | } |
| 3640 | |
| 3641 | |
| 3642 | /* Get an array of GlobalBlock which describe the global blocks owned |
| 3643 | by the shared object characterised by the given di_handle. Asserts |
| 3644 | if the handle is invalid. The caller is responsible for freeing |
| 3645 | the array at some point. If 'arrays_only' is True, only |
| 3646 | array-typed blocks are returned; otherwise blocks of all types are |
| 3647 | returned. */ |
| 3648 | |
| 3649 | void* /* really, XArray* of GlobalBlock */ |
| 3650 | VG_(di_get_global_blocks_from_dihandle) ( ULong di_handle, |
| 3651 | Bool arrays_only ) |
| 3652 | { |
| 3653 | /* This is a derivation of consider_vars_in_frame() above. */ |
| 3654 | |
| 3655 | DebugInfo* di; |
| 3656 | XArray* gvars; /* XArray* of GlobalBlock */ |
| 3657 | Word nScopes, scopeIx; |
| 3658 | |
| 3659 | /* The first thing to do is find the DebugInfo that |
| 3660 | pertains to 'di_handle'. */ |
| 3661 | tl_assert(di_handle > 0); |
| 3662 | for (di = debugInfo_list; di; di = di->next) { |
| 3663 | if (di->handle == di_handle) |
| 3664 | break; |
| 3665 | } |
| 3666 | |
| 3667 | /* If this fails, we were unable to find any DebugInfo with the |
| 3668 | given handle. This is considered an error on the part of the |
| 3669 | caller. */ |
| 3670 | tl_assert(di != NULL); |
| 3671 | |
| 3672 | /* we'll put the collected variables in here. */ |
| 3673 | gvars = VG_(newXA)( ML_(dinfo_zalloc), "di.debuginfo.dggbfd.1", |
| 3674 | ML_(dinfo_free), sizeof(GlobalBlock) ); |
| 3675 | tl_assert(gvars); |
| 3676 | |
| 3677 | /* any var info at all? */ |
| 3678 | if (!di->varinfo) |
| 3679 | return gvars; |
| 3680 | |
| 3681 | /* we'll iterate over all the variables we can find, even if |
| 3682 | it seems senseless to visit stack-allocated variables */ |
| 3683 | /* Iterate over all scopes */ |
| 3684 | nScopes = VG_(sizeXA)( di->varinfo ); |
| 3685 | for (scopeIx = 0; scopeIx < nScopes; scopeIx++) { |
| 3686 | |
| 3687 | /* Iterate over each (code) address range at the current scope */ |
| 3688 | DiAddrRange* range; |
| 3689 | OSet* /* of DiAddrInfo */ scope |
| 3690 | = *(OSet**)VG_(indexXA)( di->varinfo, scopeIx ); |
| 3691 | tl_assert(scope); |
| 3692 | VG_(OSetGen_ResetIter)(scope); |
| 3693 | while ( (range = VG_(OSetGen_Next)(scope)) ) { |
| 3694 | |
| 3695 | /* Iterate over each variable in the current address range */ |
| 3696 | Word nVars, varIx; |
| 3697 | tl_assert(range->vars); |
| 3698 | nVars = VG_(sizeXA)( range->vars ); |
| 3699 | for (varIx = 0; varIx < nVars; varIx++) { |
| 3700 | |
| 3701 | Bool isVec; |
| 3702 | GXResult res; |
sewardj | 50fde23 | 2008-10-20 16:08:55 +0000 | [diff] [blame] | 3703 | MaybeULong mul; |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3704 | GlobalBlock gb; |
| 3705 | TyEnt* ty; |
| 3706 | DiVariable* var = VG_(indexXA)( range->vars, varIx ); |
| 3707 | tl_assert(var->name); |
| 3708 | if (0) VG_(printf)("at depth %ld var %s ", scopeIx, var->name ); |
| 3709 | |
| 3710 | /* Now figure out if this variable has a constant address |
| 3711 | (that is, independent of FP, SP, phase of moon, etc), |
| 3712 | and if so, what the address is. Any variable with a |
| 3713 | constant address is deemed to be a global so we collect |
| 3714 | it. */ |
| 3715 | if (0) { VG_(printf)("EVAL: "); ML_(pp_GX)(var->gexpr); |
| 3716 | VG_(printf)("\n"); } |
tom | 588658b | 2009-01-22 13:40:12 +0000 | [diff] [blame] | 3717 | res = ML_(evaluate_trivial_GX)( var->gexpr, di ); |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3718 | |
| 3719 | /* Not a constant address => not interesting */ |
tom | 3c9cf34 | 2009-11-12 13:28:34 +0000 | [diff] [blame] | 3720 | if (res.kind != GXR_Addr) { |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3721 | if (0) VG_(printf)("FAIL\n"); |
| 3722 | continue; |
| 3723 | } |
| 3724 | |
| 3725 | /* Ok, it's a constant address. See if we want to collect |
| 3726 | it. */ |
| 3727 | if (0) VG_(printf)("%#lx\n", res.word); |
| 3728 | |
| 3729 | /* Figure out how big the variable is. */ |
sewardj | 50fde23 | 2008-10-20 16:08:55 +0000 | [diff] [blame] | 3730 | mul = ML_(sizeOfType)(di->admin_tyents, var->typeR); |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3731 | |
sewardj | 50fde23 | 2008-10-20 16:08:55 +0000 | [diff] [blame] | 3732 | /* If this var has a type whose size is unknown, zero, or |
| 3733 | impossibly large, it should never have been added. |
| 3734 | ML_(addVar) should have rejected it. */ |
| 3735 | vg_assert(mul.b == True); |
| 3736 | vg_assert(mul.ul > 0); |
| 3737 | if (sizeof(void*) == 4) vg_assert(mul.ul < (1ULL << 32)); |
| 3738 | /* After this point, we assume we can truncate mul.ul to a |
| 3739 | host word safely (without loss of info). */ |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3740 | |
| 3741 | /* skip if non-array and we're only interested in |
| 3742 | arrays */ |
| 3743 | ty = ML_(TyEnts__index_by_cuOff)( di->admin_tyents, NULL, |
| 3744 | var->typeR ); |
| 3745 | vg_assert(ty); |
| 3746 | vg_assert(ty->tag == Te_UNKNOWN || ML_(TyEnt__is_type)(ty)); |
| 3747 | if (ty->tag == Te_UNKNOWN) |
| 3748 | continue; /* perhaps we should complain in this case? */ |
| 3749 | |
| 3750 | isVec = ty->tag == Te_TyArray; |
| 3751 | if (arrays_only && !isVec) continue; |
| 3752 | |
| 3753 | /* Ok, so collect it! */ |
| 3754 | tl_assert(var->name); |
| 3755 | tl_assert(di->soname); |
| 3756 | if (0) VG_(printf)("XXXX %s %s %d\n", var->name, |
| 3757 | var->fileName?(HChar*)var->fileName |
| 3758 | :"??",var->lineNo); |
| 3759 | VG_(memset)(&gb, 0, sizeof(gb)); |
| 3760 | gb.addr = res.word; |
sewardj | 50fde23 | 2008-10-20 16:08:55 +0000 | [diff] [blame] | 3761 | gb.szB = (SizeT)mul.ul; |
sewardj | 9c606bd | 2008-09-18 18:12:50 +0000 | [diff] [blame] | 3762 | gb.isVec = isVec; |
| 3763 | VG_(strncpy)(&gb.name[0], var->name, sizeof(gb.name)-1); |
| 3764 | VG_(strncpy)(&gb.soname[0], di->soname, sizeof(gb.soname)-1); |
| 3765 | tl_assert(gb.name[ sizeof(gb.name)-1 ] == 0); |
| 3766 | tl_assert(gb.soname[ sizeof(gb.soname)-1 ] == 0); |
| 3767 | |
| 3768 | VG_(addToXA)( gvars, &gb ); |
| 3769 | |
| 3770 | } /* for (varIx = 0; varIx < nVars; varIx++) */ |
| 3771 | |
| 3772 | } /* while ( (range = VG_(OSetGen_Next)(scope)) ) */ |
| 3773 | |
| 3774 | } /* for (scopeIx = 0; scopeIx < nScopes; scopeIx++) */ |
| 3775 | |
| 3776 | return gvars; |
| 3777 | } |
| 3778 | |
| 3779 | |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3780 | /*------------------------------------------------------------*/ |
| 3781 | /*--- DebugInfo accessor functions ---*/ |
| 3782 | /*------------------------------------------------------------*/ |
| 3783 | |
sewardj | e3f1e59 | 2009-07-31 09:41:29 +0000 | [diff] [blame] | 3784 | const DebugInfo* VG_(next_DebugInfo)(const DebugInfo* di) |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3785 | { |
| 3786 | if (di == NULL) |
| 3787 | return debugInfo_list; |
| 3788 | return di->next; |
| 3789 | } |
| 3790 | |
sewardj | e3f1e59 | 2009-07-31 09:41:29 +0000 | [diff] [blame] | 3791 | Addr VG_(DebugInfo_get_text_avma)(const DebugInfo* di) |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3792 | { |
| 3793 | return di->text_present ? di->text_avma : 0; |
| 3794 | } |
| 3795 | |
sewardj | e3f1e59 | 2009-07-31 09:41:29 +0000 | [diff] [blame] | 3796 | SizeT VG_(DebugInfo_get_text_size)(const DebugInfo* di) |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3797 | { |
| 3798 | return di->text_present ? di->text_size : 0; |
| 3799 | } |
| 3800 | |
bart | 3898022 | 2013-08-24 17:52:26 +0000 | [diff] [blame] | 3801 | Addr VG_(DebugInfo_get_bss_avma)(const DebugInfo* di) |
| 3802 | { |
| 3803 | return di->bss_present ? di->bss_avma : 0; |
| 3804 | } |
| 3805 | |
| 3806 | SizeT VG_(DebugInfo_get_bss_size)(const DebugInfo* di) |
| 3807 | { |
| 3808 | return di->bss_present ? di->bss_size : 0; |
| 3809 | } |
| 3810 | |
sewardj | e3f1e59 | 2009-07-31 09:41:29 +0000 | [diff] [blame] | 3811 | Addr VG_(DebugInfo_get_plt_avma)(const DebugInfo* di) |
bart | 092b626 | 2008-05-25 16:37:22 +0000 | [diff] [blame] | 3812 | { |
| 3813 | return di->plt_present ? di->plt_avma : 0; |
| 3814 | } |
| 3815 | |
sewardj | e3f1e59 | 2009-07-31 09:41:29 +0000 | [diff] [blame] | 3816 | SizeT VG_(DebugInfo_get_plt_size)(const DebugInfo* di) |
bart | 092b626 | 2008-05-25 16:37:22 +0000 | [diff] [blame] | 3817 | { |
| 3818 | return di->plt_present ? di->plt_size : 0; |
| 3819 | } |
| 3820 | |
sewardj | e3f1e59 | 2009-07-31 09:41:29 +0000 | [diff] [blame] | 3821 | Addr VG_(DebugInfo_get_gotplt_avma)(const DebugInfo* di) |
bart | 092b626 | 2008-05-25 16:37:22 +0000 | [diff] [blame] | 3822 | { |
| 3823 | return di->gotplt_present ? di->gotplt_avma : 0; |
| 3824 | } |
| 3825 | |
sewardj | e3f1e59 | 2009-07-31 09:41:29 +0000 | [diff] [blame] | 3826 | SizeT VG_(DebugInfo_get_gotplt_size)(const DebugInfo* di) |
bart | 092b626 | 2008-05-25 16:37:22 +0000 | [diff] [blame] | 3827 | { |
| 3828 | return di->gotplt_present ? di->gotplt_size : 0; |
| 3829 | } |
| 3830 | |
bart | 6834783 | 2012-09-06 14:08:26 +0000 | [diff] [blame] | 3831 | Addr VG_(DebugInfo_get_got_avma)(const DebugInfo* di) |
| 3832 | { |
| 3833 | return di->got_present ? di->got_avma : 0; |
| 3834 | } |
| 3835 | |
| 3836 | SizeT VG_(DebugInfo_get_got_size)(const DebugInfo* di) |
| 3837 | { |
| 3838 | return di->got_present ? di->got_size : 0; |
| 3839 | } |
| 3840 | |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 3841 | const HChar* VG_(DebugInfo_get_soname)(const DebugInfo* di) |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3842 | { |
| 3843 | return di->soname; |
| 3844 | } |
| 3845 | |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 3846 | const HChar* VG_(DebugInfo_get_filename)(const DebugInfo* di) |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3847 | { |
sewardj | a5acac3 | 2011-09-20 21:59:50 +0000 | [diff] [blame] | 3848 | return di->fsm.filename; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3849 | } |
| 3850 | |
sewardj | e3f1e59 | 2009-07-31 09:41:29 +0000 | [diff] [blame] | 3851 | PtrdiffT VG_(DebugInfo_get_text_bias)(const DebugInfo* di) |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3852 | { |
| 3853 | return di->text_present ? di->text_bias : 0; |
| 3854 | } |
| 3855 | |
sewardj | e3f1e59 | 2009-07-31 09:41:29 +0000 | [diff] [blame] | 3856 | Int VG_(DebugInfo_syms_howmany) ( const DebugInfo *si ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 3857 | { |
| 3858 | return si->symtab_used; |
| 3859 | } |
| 3860 | |
sewardj | e3f1e59 | 2009-07-31 09:41:29 +0000 | [diff] [blame] | 3861 | void VG_(DebugInfo_syms_getidx) ( const DebugInfo *si, |
| 3862 | Int idx, |
sewardj | a5cace0 | 2011-08-15 09:42:34 +0000 | [diff] [blame] | 3863 | /*OUT*/Addr* avma, |
| 3864 | /*OUT*/Addr* tocptr, |
| 3865 | /*OUT*/UInt* size, |
florian | 19f91bb | 2012-11-10 22:29:54 +0000 | [diff] [blame] | 3866 | /*OUT*/HChar** pri_name, |
| 3867 | /*OUT*/HChar*** sec_names, |
sewardj | a5cace0 | 2011-08-15 09:42:34 +0000 | [diff] [blame] | 3868 | /*OUT*/Bool* isText, |
| 3869 | /*OUT*/Bool* isIFunc ) |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 3870 | { |
| 3871 | vg_assert(idx >= 0 && idx < si->symtab_used); |
sewardj | a5cace0 | 2011-08-15 09:42:34 +0000 | [diff] [blame] | 3872 | if (avma) *avma = si->symtab[idx].addr; |
| 3873 | if (tocptr) *tocptr = si->symtab[idx].tocptr; |
| 3874 | if (size) *size = si->symtab[idx].size; |
| 3875 | if (pri_name) *pri_name = si->symtab[idx].pri_name; |
florian | 19f91bb | 2012-11-10 22:29:54 +0000 | [diff] [blame] | 3876 | if (sec_names) *sec_names = (HChar **)si->symtab[idx].sec_names; // FIXME |
sewardj | a5cace0 | 2011-08-15 09:42:34 +0000 | [diff] [blame] | 3877 | if (isText) *isText = si->symtab[idx].isText; |
| 3878 | if (isIFunc) *isIFunc = si->symtab[idx].isIFunc; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3879 | } |
| 3880 | |
| 3881 | |
| 3882 | /*------------------------------------------------------------*/ |
| 3883 | /*--- SectKind query functions ---*/ |
| 3884 | /*------------------------------------------------------------*/ |
| 3885 | |
| 3886 | /* Convert a VgSectKind to a string, which must be copied if you want |
| 3887 | to change it. */ |
| 3888 | const HChar* VG_(pp_SectKind)( VgSectKind kind ) |
| 3889 | { |
| 3890 | switch (kind) { |
| 3891 | case Vg_SectUnknown: return "Unknown"; |
| 3892 | case Vg_SectText: return "Text"; |
| 3893 | case Vg_SectData: return "Data"; |
| 3894 | case Vg_SectBSS: return "BSS"; |
| 3895 | case Vg_SectGOT: return "GOT"; |
| 3896 | case Vg_SectPLT: return "PLT"; |
| 3897 | case Vg_SectOPD: return "OPD"; |
sewardj | 5706ca9 | 2009-01-22 21:18:15 +0000 | [diff] [blame] | 3898 | case Vg_SectGOTPLT: return "GOTPLT"; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3899 | default: vg_assert(0); |
| 3900 | } |
| 3901 | } |
| 3902 | |
| 3903 | /* Given an address 'a', make a guess of which section of which object |
| 3904 | it comes from. If name is non-NULL, then the last n_name-1 |
| 3905 | characters of the object's name is put in name[0 .. n_name-2], and |
| 3906 | name[n_name-1] is set to zero (guaranteed zero terminated). */ |
| 3907 | |
florian | 1636d33 | 2012-11-15 04:27:04 +0000 | [diff] [blame] | 3908 | VgSectKind VG_(DebugInfo_sect_kind)( /*OUT*/HChar* name, SizeT n_name, |
sewardj | e3f1e59 | 2009-07-31 09:41:29 +0000 | [diff] [blame] | 3909 | Addr a) |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3910 | { |
| 3911 | DebugInfo* di; |
| 3912 | VgSectKind res = Vg_SectUnknown; |
| 3913 | |
| 3914 | for (di = debugInfo_list; di != NULL; di = di->next) { |
| 3915 | |
| 3916 | if (0) |
| 3917 | VG_(printf)( |
sewardj | e3f1e59 | 2009-07-31 09:41:29 +0000 | [diff] [blame] | 3918 | "addr=%#lx di=%p %s got=%#lx,%ld plt=%#lx,%ld " |
| 3919 | "data=%#lx,%ld bss=%#lx,%ld\n", |
sewardj | a5acac3 | 2011-09-20 21:59:50 +0000 | [diff] [blame] | 3920 | a, di, di->fsm.filename, |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3921 | di->got_avma, di->got_size, |
| 3922 | di->plt_avma, di->plt_size, |
| 3923 | di->data_avma, di->data_size, |
| 3924 | di->bss_avma, di->bss_size); |
| 3925 | |
| 3926 | if (di->text_present |
| 3927 | && di->text_size > 0 |
| 3928 | && a >= di->text_avma && a < di->text_avma + di->text_size) { |
| 3929 | res = Vg_SectText; |
| 3930 | break; |
| 3931 | } |
| 3932 | if (di->data_present |
| 3933 | && di->data_size > 0 |
| 3934 | && a >= di->data_avma && a < di->data_avma + di->data_size) { |
| 3935 | res = Vg_SectData; |
| 3936 | break; |
| 3937 | } |
| 3938 | if (di->sdata_present |
| 3939 | && di->sdata_size > 0 |
| 3940 | && a >= di->sdata_avma && a < di->sdata_avma + di->sdata_size) { |
| 3941 | res = Vg_SectData; |
| 3942 | break; |
| 3943 | } |
| 3944 | if (di->bss_present |
| 3945 | && di->bss_size > 0 |
| 3946 | && a >= di->bss_avma && a < di->bss_avma + di->bss_size) { |
| 3947 | res = Vg_SectBSS; |
| 3948 | break; |
| 3949 | } |
sewardj | 5706ca9 | 2009-01-22 21:18:15 +0000 | [diff] [blame] | 3950 | if (di->sbss_present |
| 3951 | && di->sbss_size > 0 |
| 3952 | && a >= di->sbss_avma && a < di->sbss_avma + di->sbss_size) { |
| 3953 | res = Vg_SectBSS; |
| 3954 | break; |
| 3955 | } |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3956 | if (di->plt_present |
| 3957 | && di->plt_size > 0 |
| 3958 | && a >= di->plt_avma && a < di->plt_avma + di->plt_size) { |
| 3959 | res = Vg_SectPLT; |
| 3960 | break; |
| 3961 | } |
| 3962 | if (di->got_present |
| 3963 | && di->got_size > 0 |
| 3964 | && a >= di->got_avma && a < di->got_avma + di->got_size) { |
| 3965 | res = Vg_SectGOT; |
| 3966 | break; |
| 3967 | } |
bart | 092b626 | 2008-05-25 16:37:22 +0000 | [diff] [blame] | 3968 | if (di->gotplt_present |
| 3969 | && di->gotplt_size > 0 |
| 3970 | && a >= di->gotplt_avma && a < di->gotplt_avma + di->gotplt_size) { |
| 3971 | res = Vg_SectGOTPLT; |
| 3972 | break; |
| 3973 | } |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3974 | if (di->opd_present |
| 3975 | && di->opd_size > 0 |
| 3976 | && a >= di->opd_avma && a < di->opd_avma + di->opd_size) { |
| 3977 | res = Vg_SectOPD; |
| 3978 | break; |
| 3979 | } |
| 3980 | /* we could also check for .eh_frame, if anyone really cares */ |
| 3981 | } |
| 3982 | |
| 3983 | vg_assert( (di == NULL && res == Vg_SectUnknown) |
| 3984 | || (di != NULL && res != Vg_SectUnknown) ); |
| 3985 | |
| 3986 | if (name) { |
| 3987 | |
| 3988 | vg_assert(n_name >= 8); |
| 3989 | |
sewardj | a5acac3 | 2011-09-20 21:59:50 +0000 | [diff] [blame] | 3990 | if (di && di->fsm.filename) { |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3991 | Int i, j; |
sewardj | a5acac3 | 2011-09-20 21:59:50 +0000 | [diff] [blame] | 3992 | Int fnlen = VG_(strlen)(di->fsm.filename); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3993 | Int start_at = 1 + fnlen - n_name; |
| 3994 | if (start_at < 0) start_at = 0; |
| 3995 | vg_assert(start_at < fnlen); |
| 3996 | i = start_at; j = 0; |
| 3997 | while (True) { |
bart | d5dea1d | 2008-05-25 16:25:51 +0000 | [diff] [blame] | 3998 | vg_assert(j >= 0 && j < n_name); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 3999 | vg_assert(i >= 0 && i <= fnlen); |
sewardj | a5acac3 | 2011-09-20 21:59:50 +0000 | [diff] [blame] | 4000 | name[j] = di->fsm.filename[i]; |
| 4001 | if (di->fsm.filename[i] == 0) break; |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 4002 | i++; j++; |
| 4003 | } |
bart | d5dea1d | 2008-05-25 16:25:51 +0000 | [diff] [blame] | 4004 | vg_assert(i == fnlen); |
sewardj | b8b79ad | 2008-03-03 01:35:41 +0000 | [diff] [blame] | 4005 | } else { |
| 4006 | VG_(snprintf)(name, n_name, "%s", "???"); |
| 4007 | } |
| 4008 | |
| 4009 | name[n_name-1] = 0; |
| 4010 | } |
| 4011 | |
| 4012 | return res; |
| 4013 | |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 4014 | } |
| 4015 | |
sewardj | eadcd86 | 2006-04-04 15:12:44 +0000 | [diff] [blame] | 4016 | /*--------------------------------------------------------------------*/ |
| 4017 | /*--- end ---*/ |
| 4018 | /*--------------------------------------------------------------------*/ |