Set location fields in SW when using standard frame stepper
[dyninst.git] / stackwalk / src / x86-swk.C
1 /*
2  * Copyright (c) 1996-2011 Barton P. Miller
3  * 
4  * We provide the Paradyn Parallel Performance Tools (below
5  * described as "Paradyn") on an AS IS basis, and do not warrant its
6  * validity or performance.  We reserve the right to update, modify,
7  * or discontinue this software at any time.  We shall have no
8  * obligation to supply such updates or modifications or any other
9  * form of support to you.
10  * 
11  * By your use of Paradyn, you understand and agree that we (or any
12  * other person or entity with proprietary rights in Paradyn) are
13  * under no obligation to provide either maintenance services,
14  * update services, notices of latent defects, or correction of
15  * defects for Paradyn.
16  * 
17  * This library is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU Lesser General Public
19  * License as published by the Free Software Foundation; either
20  * version 2.1 of the License, or (at your option) any later version.
21  * 
22  * This library is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25  * Lesser General Public License for more details.
26  * 
27  * You should have received a copy of the GNU Lesser General Public
28  * License along with this library; if not, write to the Free Software
29  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30  */
31
32 #include "stackwalk/h/basetypes.h"
33 #include "stackwalk/h/swk_errors.h"
34 #include "stackwalk/h/procstate.h"
35 #include "stackwalk/h/framestepper.h"
36 #include "stackwalk/h/frame.h"
37 #include "stackwalk/h/walker.h"
38
39 #include "stackwalk/src/symtab-swk.h"
40 #include "stackwalk/src/dbgstepper-impl.h"
41 #include "stackwalk/src/x86-swk.h"
42 #include "stackwalk/src/sw.h"
43 #include "stackwalk/src/libstate.h"
44
45 #include "common/h/lru_cache.h"
46
47 #include "dynutil/h/SymReader.h"
48
49 using namespace Dyninst;
50 using namespace Dyninst::Stackwalker;
51
52 static volatile int always_zero = 0;
53
54 bool ProcSelf::getRegValue(Dyninst::MachRegister reg, THR_ID, Dyninst::MachRegisterVal &val)
55 {
56   unsigned long *frame_pointer = NULL;
57
58   if (always_zero) {
59      //Generate some (skipped) code involving frame_pointer before
60      // the assembly snippet.  This keeps gcc from optimizing 
61      // the below snippet by pulling it up into the function prolog.
62      sw_printf("%p%p\n", frame_pointer, &frame_pointer);
63   }
64
65 #if defined(arch_x86_64) && (defined(os_linux) || defined(os_freebsd))
66   __asm__("mov %%rbp, %0\n"
67           : "=r"(frame_pointer));
68 #elif defined(os_linux) || defined(os_freebsd)
69   __asm__("movl %%ebp, %0\n"
70           : "=r"(frame_pointer));
71 #elif defined(os_windows)
72    __asm
73    {
74       mov frame_pointer, ebp ;
75    }   
76 #endif
77
78   frame_pointer = (unsigned long *) *frame_pointer;
79   
80   switch(reg.val())
81   {
82      case Dyninst::x86_64::irip:
83      case Dyninst::x86::ieip:
84      case Dyninst::iReturnAddr:
85         val = (Dyninst::MachRegisterVal) frame_pointer[1];
86         break;
87      case Dyninst::iFrameBase:
88         val = (Dyninst::MachRegisterVal) frame_pointer[0];
89         break;
90      case Dyninst::x86_64::irsp:
91      case Dyninst::x86::iesp:
92      case Dyninst::iStackTop:
93         val = (Dyninst::MachRegisterVal) (frame_pointer + 2);
94         break;      
95      default:
96         sw_printf("[%s:%u] - Request for unsupported register %s\n",
97                   __FILE__, __LINE__, reg.name().c_str());
98         setLastError(err_badparam, "Unknown register passed in reg field");
99   }
100
101   return true;
102 }
103
104 Dyninst::Architecture ProcSelf::getArchitecture()
105 {
106    if (sizeof(void *) == 8)
107       return Arch_x86_64;
108    return Arch_x86;
109 }
110
111 static gcframe_ret_t HandleStandardFrame(const Frame &in, Frame &out, ProcessState *proc)
112 {
113   Address in_fp, out_sp;
114   const unsigned addr_width = proc->getAddressWidth();
115   bool result;
116
117   struct {
118     Address out_fp;
119     Address out_ra;
120   } ra_fp_pair;
121
122   in_fp = in.getFP();
123   out_sp = in_fp + (2 * addr_width);
124
125 #if defined(arch_x86_64)
126   /**
127    * On AMD64 we may be reading from a process with a different
128    * address width than the current one.  We'll do the read at
129    * the correct size, then convert the addresses into the 
130    * local size
131    **/
132   struct {
133      unsigned out_fp;
134      unsigned out_ra;
135   } ra_fp_pair32;
136   if (addr_width != sizeof(Address))
137   {
138      result = proc->readMem(&ra_fp_pair32, in_fp, 
139                             sizeof(ra_fp_pair32));
140      ra_fp_pair.out_fp = (Address) ra_fp_pair32.out_fp;
141      ra_fp_pair.out_ra = (Address) ra_fp_pair32.out_ra;
142   }
143   else
144 #endif
145   {
146     result = proc->readMem(&ra_fp_pair, in_fp, sizeof(ra_fp_pair));
147   }
148
149   if (!result) {
150     sw_printf("[%s:%u] - Couldn't read from %lx\n", __FILE__, __LINE__, in_fp);
151     return gcf_error;
152   }
153   
154   if (!ra_fp_pair.out_ra) {
155      return gcf_not_me;
156   }
157
158   location_t ra_loc, fp_loc;
159   ra_loc.location = loc_address;
160   fp_loc.location = loc_address;
161   ra_loc.val.addr = in_fp + addr_width;
162   fp_loc.val.addr = in_fp + addr_width*2;
163
164   out.setFP(ra_fp_pair.out_fp);
165   out.setRA(ra_fp_pair.out_ra);
166   out.setSP(out_sp);
167   out.setFPLocation(fp_loc);
168   out.setRALocation(ra_loc);
169
170   return gcf_success;
171 }
172
173 bool Walker::checkValidFrame(const Frame &in, const Frame &out)
174 {
175    if (out.getSP() <= in.getSP()) {
176       sw_printf("[%s:%u] - Stackwalk went backwards, %lx to %lx\n",
177                 __FILE__, __LINE__, in.getSP(), out.getSP());
178       return false;
179    }
180    return true;
181 }
182
183 gcframe_ret_t FrameFuncStepperImpl::getCallerFrame(const Frame &in, Frame &out)
184 {
185   if (!in.getFP())
186      return gcf_not_me;
187
188   FrameFuncHelper::alloc_frame_t frame = helper->allocatesFrame(in.getRA());
189   if (frame.first != FrameFuncHelper::standard_frame) {
190      return gcf_not_me;
191   }
192
193   return HandleStandardFrame(in, out, getProcessState());
194 }
195  
196 std::map<Dyninst::PID, LookupFuncStart*> LookupFuncStart::all_func_starts;
197
198 static int hash_address(Address a)
199 {
200    return (int) a;
201 }
202
203 LookupFuncStart::LookupFuncStart(ProcessState *proc_) :
204    FrameFuncHelper(proc_),
205    cache(cache_size, hash_address)
206 {
207    all_func_starts[proc->getProcessId()] = this;
208    ref_count = 1;
209 }
210
211 LookupFuncStart::~LookupFuncStart()
212 {
213    Dyninst::PID pid = proc->getProcessId();
214    all_func_starts.erase(pid);
215 }
216
217 LookupFuncStart *LookupFuncStart::getLookupFuncStart(ProcessState *p)
218 {
219    Dyninst::PID pid = p->getProcessId();
220    std::map<Dyninst::PID, LookupFuncStart*>::iterator i = all_func_starts.find(pid);
221    if (i == all_func_starts.end()) {
222       return new LookupFuncStart(p);
223    }
224    (*i).second->ref_count++;
225    return (*i).second;
226 }
227
228 void LookupFuncStart::releaseMe()
229 {
230    ref_count--;
231    if (!ref_count)
232       delete this;
233 }
234
235 FrameFuncStepperImpl::FrameFuncStepperImpl(Walker *w, FrameStepper *parent_,
236                                            FrameFuncHelper *helper_) :
237    FrameStepper(w),
238    parent(parent_),
239    helper(helper_)
240 {
241    helper = helper_ ? helper_ : LookupFuncStart::getLookupFuncStart(getProcessState());
242 }
243
244 FrameFuncStepperImpl::~FrameFuncStepperImpl()
245 {
246    LookupFuncStart *lookup = dynamic_cast<LookupFuncStart*>(helper);
247    if (lookup)
248       lookup->releaseMe();
249    else if (helper)
250       delete helper;
251 }
252
253 unsigned FrameFuncStepperImpl::getPriority() const
254 {
255   return frame_priority;
256 }
257
258 /**
259  * Look at the first few bytes in the function and see if they contain
260  * the standard set to allocate a stack frame.
261  **/
262 #define FUNCTION_PROLOG_TOCHECK 16
263 static unsigned char push_ebp = 0x55;
264 static unsigned char mov_esp_ebp[2][2] = { { 0x89, 0xe5 },
265                                            { 0x8b, 0xec } };
266 static unsigned char rex_mov_esp_ebp = 0x48;
267
268 FrameFuncHelper::alloc_frame_t LookupFuncStart::allocatesFrame(Address addr)
269 {
270    LibAddrPair lib;
271    unsigned char mem[FUNCTION_PROLOG_TOCHECK];
272    Address func_addr;
273    unsigned cur;
274    int push_ebp_pos = -1, mov_esp_ebp_pos = -1;
275    alloc_frame_t res = alloc_frame_t(unknown_t, unknown_s);
276    bool result;
277    SymReader *reader;
278    Offset off;
279    Symbol_t sym;
280
281    result = checkCache(addr, res);
282    if (result) {
283       sw_printf("[%s:%u] - Cached value for %lx is %d/%d\n",
284                 __FILE__, __LINE__, addr, (int) res.first, (int) res.second);
285       return res;
286    }
287
288    result = proc->getLibraryTracker()->getLibraryAtAddr(addr, lib);
289    if (!result)
290    {
291       sw_printf("[%s:%u] - No library at %lx\n", __FILE__, __LINE__, addr);
292       goto done;
293    }
294
295    reader = LibraryWrapper::getLibrary(lib.first);
296    if (!reader) {
297       sw_printf("[%s:%u] - Failed to open symbol reader %s\n",
298                 __FILE__, __LINE__, lib.first.c_str() );
299       goto done;
300    }   
301    off = addr - lib.second;
302    sym = reader->getContainingSymbol(off);
303    if (!reader->isValidSymbol(sym)) {
304       sw_printf("[%s:%u] - Could not find symbol in binary\n", __FILE__, __LINE__);
305       goto done;
306    }
307    func_addr = reader->getSymbolOffset(sym) + lib.second;
308
309    result = proc->readMem(mem, func_addr, FUNCTION_PROLOG_TOCHECK);
310    if (!result) {
311       sw_printf("[%s:%u] - Error.  Couldn't read from memory at %lx\n",
312                 __FILE__, __LINE__, func_addr);
313       goto done;
314    }
315    
316    //Try to find a 'push (r|e)bp'
317    for (cur=0; cur<FUNCTION_PROLOG_TOCHECK; cur++)
318    {
319       if (mem[cur] == push_ebp)
320       {
321          push_ebp_pos = cur;
322          break;
323       }
324    }
325
326    //Try to find the mov esp->ebp
327    for (; cur<FUNCTION_PROLOG_TOCHECK; cur++)
328    {
329       if (proc->getAddressWidth() == 8) {
330          if (mem[cur] != rex_mov_esp_ebp)
331             continue;
332          cur++;
333       }
334       
335       if (cur+1 >= FUNCTION_PROLOG_TOCHECK) 
336          break;
337       if ((mem[cur] == mov_esp_ebp[0][0] && mem[cur+1] == mov_esp_ebp[0][1]) || 
338           (mem[cur] == mov_esp_ebp[1][0] && mem[cur+1] == mov_esp_ebp[1][1])) {
339          if (proc->getAddressWidth() == 8) 
340             mov_esp_ebp_pos = cur-1;
341          else
342             mov_esp_ebp_pos = cur;
343          break;
344       }
345    }
346
347    if ((push_ebp_pos != -1) && (mov_esp_ebp_pos != -1))
348       res.first = standard_frame;
349    else if ((push_ebp_pos != -1) && (mov_esp_ebp_pos == -1))
350       res.first = savefp_only_frame;
351    else 
352       res.first = no_frame;
353    
354    if ((push_ebp_pos != -1) && (addr <= func_addr + push_ebp_pos))
355       res.second = unset_frame;
356    else if ((mov_esp_ebp_pos != -1) && (addr <= func_addr + mov_esp_ebp_pos))
357       res.second = halfset_frame;
358    else
359       res.second = set_frame;
360
361  done:
362    sw_printf("[%s:%u] - Function containing %lx has frame type %d/%d\n",
363              __FILE__, __LINE__, addr, (int) res.first, (int) res.second);
364    updateCache(addr, res);
365    return res;
366 }
367
368 void LookupFuncStart::updateCache(Address addr, alloc_frame_t result)
369 {
370    cache.insert(addr, result);
371 }
372
373 bool LookupFuncStart::checkCache(Address addr, alloc_frame_t &result)
374 {
375    return cache.lookup(addr, result);
376 }
377
378 void LookupFuncStart::clear_func_mapping(Dyninst::PID pid)
379 {
380    std::map<Dyninst::PID, LookupFuncStart *>::iterator i;
381    i = all_func_starts.find(pid);
382    if (i == all_func_starts.end())
383       return;
384
385    LookupFuncStart *fs = (*i).second;
386    all_func_starts.erase(i);
387    
388    delete fs;
389 }
390
391 gcframe_ret_t DyninstInstrStepperImpl::getCallerFrameArch(const Frame &in, Frame &out, 
392                                                           Address /*base*/, Address lib_base,
393                                                           unsigned /*size*/, unsigned stack_height)
394 {
395   gcframe_ret_t ret = HandleStandardFrame(in, out, getProcessState());
396   if (ret != gcf_success)
397     return ret;
398   out.setRA(out.getRA() + lib_base);
399   out.setSP(out.getSP() + stack_height);
400   return gcf_success;
401 }
402
403
404 gcframe_ret_t DyninstDynamicStepperImpl::getCallerFrameArch(const Frame &in, Frame &out, 
405                                                             Address /*base*/, Address lib_base,
406                                                             unsigned /*size*/, unsigned stack_height,
407                                                             Address orig_ra, bool pEntryExit)
408 {
409   bool result = false;
410   const unsigned addr_width = getProcessState()->getAddressWidth();
411   unsigned long sp_value = 0x0;
412   Address sp_addr = 0x0;
413
414   // Handle frameless instrumentation
415   if (0x0 != orig_ra)
416   {
417     location_t unknownLocation;
418     unknownLocation.location = loc_unknown;
419     out.setRA(orig_ra);
420     out.setFP(in.getFP());
421     out.setSP(in.getSP()); //Not really correct, but difficult to compute and unlikely to matter
422     out.setRALocation(unknownLocation);
423     sw_printf("[%s:%u] - DyninstDynamicStepper handled frameless instrumentation\n",
424               __FILE__, __LINE__);
425     return gcf_success;
426   }
427
428   // Handle case where *previous* frame was entry/exit instrumentation
429   if (pEntryExit)
430   {
431     Address ra_value = 0x0;
432
433     // RA is pointed to by input SP
434     // TODO may have an additional offset in some cases...
435     Address newRAAddr = in.getSP();
436
437     location_t raLocation;
438     raLocation.location = loc_address;
439     raLocation.val.addr = newRAAddr;
440     out.setRALocation(raLocation);
441
442     // TODO handle 64-bit mutator / 32-bit mutatee
443
444     // get value of RA
445     result = getProcessState()->readMem(&ra_value, newRAAddr, addr_width);
446
447     if (!result) {
448       sw_printf("[%s:%u] - Couldn't read from %lx\n", __FILE__, __LINE__, newRAAddr);
449       return gcf_error;
450     }
451
452     out.setRA(ra_value);
453     out.setFP(in.getFP()); // FP stays the same
454     out.setSP(newRAAddr + addr_width);
455     sw_printf("[%s:%u] - DyninstDynamicStepper handled post entry/exit instrumentation\n",
456               __FILE__, __LINE__);
457     return gcf_success;
458   }
459
460   gcframe_ret_t ret = HandleStandardFrame(in, out, getProcessState());
461   if (ret != gcf_success)
462     return ret;
463   out.setRA(out.getRA() + lib_base);
464
465   // For tramps with frames, read the saved stack pointer
466   // TODO does this apply to static instrumentation?
467   if (stack_height)
468   {
469     sp_addr = in.getFP() + stack_height;
470     result = getProcessState()->readMem(&sp_value, sp_addr, addr_width);
471
472     if (!result) {
473       sw_printf("[%s:%u] - Couldn't read from %lx\n", __FILE__, __LINE__, sp_addr);
474       return gcf_error;
475     }
476
477     sw_printf("[%s:%u] - Read SP %p from addr %p, using stack height of 0x%lx\n",
478               __FILE__, __LINE__, sp_value, sp_addr, stack_height);
479     out.setSP(sp_value);
480   }
481
482   sw_printf("[%s:%u] - DyninstDynamicStepper handled normal instrumentation\n",
483             __FILE__, __LINE__);
484   return gcf_success;
485 }
486
487 namespace Dyninst {
488 namespace Stackwalker {
489 void getTrapInstruction(char *buffer, unsigned buf_size, 
490                         unsigned &actual_len, bool include_return)
491 {
492    if (include_return)
493    {
494       assert(buf_size >= 2);
495       buffer[0] = (char) 0xcc; //trap
496       buffer[1] = (char) 0xc3; //ret
497       actual_len = 2;
498       return;
499    }
500    assert(buf_size >= 1);
501    buffer[0] = (char) 0xcc; //trap
502    actual_len = 1;
503    return;
504 }
505 }
506 }
507