StackwalkerAPI fixes
[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   out.setFP(ra_fp_pair.out_fp);
159   out.setRA(ra_fp_pair.out_ra);
160   out.setSP(out_sp);
161
162   return gcf_success;
163 }
164
165 bool Walker::checkValidFrame(const Frame &in, const Frame &out)
166 {
167    if (out.getSP() <= in.getSP()) {
168       sw_printf("[%s:%u] - Stackwalk went backwards, %lx to %lx\n",
169                 __FILE__, __LINE__, in.getSP(), out.getSP());
170       return false;
171    }
172    return true;
173 }
174
175 gcframe_ret_t FrameFuncStepperImpl::getCallerFrame(const Frame &in, Frame &out)
176 {
177   if (!in.getFP())
178      return gcf_not_me;
179
180   FrameFuncHelper::alloc_frame_t frame = helper->allocatesFrame(in.getRA());
181   if (frame.first != FrameFuncHelper::standard_frame) {
182      return gcf_not_me;
183   }
184
185   return HandleStandardFrame(in, out, getProcessState());
186 }
187  
188 std::map<Dyninst::PID, LookupFuncStart*> LookupFuncStart::all_func_starts;
189
190 static int hash_address(Address a)
191 {
192    return (int) a;
193 }
194
195 LookupFuncStart::LookupFuncStart(ProcessState *proc_) :
196    FrameFuncHelper(proc_),
197    cache(cache_size, hash_address)
198 {
199    all_func_starts[proc->getProcessId()] = this;
200    ref_count = 1;
201 }
202
203 LookupFuncStart::~LookupFuncStart()
204 {
205    Dyninst::PID pid = proc->getProcessId();
206    all_func_starts.erase(pid);
207 }
208
209 LookupFuncStart *LookupFuncStart::getLookupFuncStart(ProcessState *p)
210 {
211    Dyninst::PID pid = p->getProcessId();
212    std::map<Dyninst::PID, LookupFuncStart*>::iterator i = all_func_starts.find(pid);
213    if (i == all_func_starts.end()) {
214       return new LookupFuncStart(p);
215    }
216    (*i).second->ref_count++;
217    return (*i).second;
218 }
219
220 void LookupFuncStart::releaseMe()
221 {
222    ref_count--;
223    if (!ref_count)
224       delete this;
225 }
226
227 FrameFuncStepperImpl::FrameFuncStepperImpl(Walker *w, FrameStepper *parent_,
228                                            FrameFuncHelper *helper_) :
229    FrameStepper(w),
230    parent(parent_),
231    helper(helper_)
232 {
233    helper = helper_ ? helper_ : LookupFuncStart::getLookupFuncStart(getProcessState());
234 }
235
236 FrameFuncStepperImpl::~FrameFuncStepperImpl()
237 {
238    LookupFuncStart *lookup = dynamic_cast<LookupFuncStart*>(helper);
239    if (lookup)
240       lookup->releaseMe();
241    else if (helper)
242       delete helper;
243 }
244
245 unsigned FrameFuncStepperImpl::getPriority() const
246 {
247   return frame_priority;
248 }
249
250 /**
251  * Look at the first few bytes in the function and see if they contain
252  * the standard set to allocate a stack frame.
253  **/
254 #define FUNCTION_PROLOG_TOCHECK 16
255 static unsigned char push_ebp = 0x55;
256 static unsigned char mov_esp_ebp[2][2] = { { 0x89, 0xe5 },
257                                            { 0x8b, 0xec } };
258 static unsigned char rex_mov_esp_ebp = 0x48;
259
260 FrameFuncHelper::alloc_frame_t LookupFuncStart::allocatesFrame(Address addr)
261 {
262    LibAddrPair lib;
263    unsigned char mem[FUNCTION_PROLOG_TOCHECK];
264    Address func_addr;
265    unsigned cur;
266    int push_ebp_pos = -1, mov_esp_ebp_pos = -1;
267    alloc_frame_t res = alloc_frame_t(unknown_t, unknown_s);
268    bool result;
269    SymReader *reader;
270    Offset off;
271    Symbol_t sym;
272
273    result = checkCache(addr, res);
274    if (result) {
275       sw_printf("[%s:%u] - Cached value for %lx is %d/%d\n",
276                 __FILE__, __LINE__, addr, (int) res.first, (int) res.second);
277       return res;
278    }
279
280    result = proc->getLibraryTracker()->getLibraryAtAddr(addr, lib);
281    if (!result)
282    {
283       sw_printf("[%s:%u] - No library at %lx\n", __FILE__, __LINE__, addr);
284       goto done;
285    }
286
287    reader = LibraryWrapper::getLibrary(lib.first);
288    if (!reader) {
289       sw_printf("[%s:%u] - Failed to open symbol reader %s\n",
290                 __FILE__, __LINE__, lib.first.c_str() );
291       goto done;
292    }   
293    off = addr - lib.second;
294    sym = reader->getContainingSymbol(off);
295    if (!reader->isValidSymbol(sym)) {
296       sw_printf("[%s:%u] - Could not find symbol in binary\n", __FILE__, __LINE__);
297       goto done;
298    }
299    func_addr = reader->getSymbolOffset(sym) + lib.second;
300
301    result = proc->readMem(mem, func_addr, FUNCTION_PROLOG_TOCHECK);
302    if (!result) {
303       sw_printf("[%s:%u] - Error.  Couldn't read from memory at %lx\n",
304                 __FILE__, __LINE__, func_addr);
305       goto done;
306    }
307    
308    //Try to find a 'push (r|e)bp'
309    for (cur=0; cur<FUNCTION_PROLOG_TOCHECK; cur++)
310    {
311       if (mem[cur] == push_ebp)
312       {
313          push_ebp_pos = cur;
314          break;
315       }
316    }
317
318    //Try to find the mov esp->ebp
319    for (; cur<FUNCTION_PROLOG_TOCHECK; cur++)
320    {
321       if (proc->getAddressWidth() == 8) {
322          if (mem[cur] != rex_mov_esp_ebp)
323             continue;
324          cur++;
325       }
326       
327       if (cur+1 >= FUNCTION_PROLOG_TOCHECK) 
328          break;
329       if ((mem[cur] == mov_esp_ebp[0][0] && mem[cur+1] == mov_esp_ebp[0][1]) || 
330           (mem[cur] == mov_esp_ebp[1][0] && mem[cur+1] == mov_esp_ebp[1][1])) {
331          if (proc->getAddressWidth() == 8) 
332             mov_esp_ebp_pos = cur-1;
333          else
334             mov_esp_ebp_pos = cur;
335          break;
336       }
337    }
338
339    if ((push_ebp_pos != -1) && (mov_esp_ebp_pos != -1))
340       res.first = standard_frame;
341    else if ((push_ebp_pos != -1) && (mov_esp_ebp_pos == -1))
342       res.first = savefp_only_frame;
343    else 
344       res.first = no_frame;
345    
346    if ((push_ebp_pos != -1) && (addr <= func_addr + push_ebp_pos))
347       res.second = unset_frame;
348    else if ((mov_esp_ebp_pos != -1) && (addr <= func_addr + mov_esp_ebp_pos))
349       res.second = halfset_frame;
350    else
351       res.second = set_frame;
352
353  done:
354    sw_printf("[%s:%u] - Function containing %lx has frame type %d/%d\n",
355              __FILE__, __LINE__, addr, (int) res.first, (int) res.second);
356    updateCache(addr, res);
357    return res;
358 }
359
360 void LookupFuncStart::updateCache(Address addr, alloc_frame_t result)
361 {
362    cache.insert(addr, result);
363 }
364
365 bool LookupFuncStart::checkCache(Address addr, alloc_frame_t &result)
366 {
367    return cache.lookup(addr, result);
368 }
369
370 void LookupFuncStart::clear_func_mapping(Dyninst::PID pid)
371 {
372    std::map<Dyninst::PID, LookupFuncStart *>::iterator i;
373    i = all_func_starts.find(pid);
374    if (i == all_func_starts.end())
375       return;
376
377    LookupFuncStart *fs = (*i).second;
378    all_func_starts.erase(i);
379    
380    delete fs;
381 }
382
383 gcframe_ret_t DyninstInstrStepperImpl::getCallerFrameArch(const Frame &in, Frame &out, 
384                                                           Address /*base*/, Address lib_base,
385                                                           unsigned /*size*/, unsigned stack_height)
386 {
387   gcframe_ret_t ret = HandleStandardFrame(in, out, getProcessState());
388   if (ret != gcf_success)
389     return ret;
390   out.setRA(out.getRA() + lib_base);
391   out.setSP(out.getSP() + stack_height);
392   return gcf_success;
393 }
394
395
396 gcframe_ret_t DyninstDynamicStepperImpl::getCallerFrameArch(const Frame &in, Frame &out, 
397                                                             Address /*base*/, Address lib_base,
398                                                             unsigned /*size*/, unsigned stack_height,
399                                                             Address orig_ra, bool pEntryExit)
400 {
401   bool result = false;
402   const unsigned addr_width = getProcessState()->getAddressWidth();
403   unsigned long sp_value = 0x0;
404   Address sp_addr = 0x0;
405
406   // Handle frameless instrumentation
407   if (0x0 != orig_ra)
408   {
409     location_t unknownLocation;
410     unknownLocation.location = loc_unknown;
411     out.setRA(orig_ra);
412     out.setFP(in.getFP());
413     out.setSP(in.getSP()); //Not really correct, but difficult to compute and unlikely to matter
414     out.setRALocation(unknownLocation);
415     sw_printf("[%s:%u] - DyninstDynamicStepper handled frameless instrumentation\n",
416               __FILE__, __LINE__);
417     return gcf_success;
418   }
419
420   // Handle case where *previous* frame was entry/exit instrumentation
421   if (pEntryExit)
422   {
423     Address ra_value = 0x0;
424
425     // RA is pointed to by input SP
426     // TODO may have an additional offset in some cases...
427     Address newRAAddr = in.getSP();
428
429     location_t raLocation;
430     raLocation.location = loc_address;
431     raLocation.val.addr = newRAAddr;
432     out.setRALocation(raLocation);
433
434     // TODO handle 64-bit mutator / 32-bit mutatee
435
436     // get value of RA
437     result = getProcessState()->readMem(&ra_value, newRAAddr, addr_width);
438
439     if (!result) {
440       sw_printf("[%s:%u] - Couldn't read from %lx\n", __FILE__, __LINE__, newRAAddr);
441       return gcf_error;
442     }
443
444     out.setRA(ra_value);
445     out.setFP(in.getFP()); // FP stays the same
446     out.setSP(newRAAddr + addr_width);
447     sw_printf("[%s:%u] - DyninstDynamicStepper handled post entry/exit instrumentation\n",
448               __FILE__, __LINE__);
449     return gcf_success;
450   }
451
452   gcframe_ret_t ret = HandleStandardFrame(in, out, getProcessState());
453   if (ret != gcf_success)
454     return ret;
455   out.setRA(out.getRA() + lib_base);
456
457   // For tramps with frames, read the saved stack pointer
458   // TODO does this apply to static instrumentation?
459   if (stack_height)
460   {
461     sp_addr = in.getFP() + stack_height;
462     result = getProcessState()->readMem(&sp_value, sp_addr, addr_width);
463
464     if (!result) {
465       sw_printf("[%s:%u] - Couldn't read from %lx\n", __FILE__, __LINE__, sp_addr);
466       return gcf_error;
467     }
468
469     sw_printf("[%s:%u] - Read SP %p from addr %p, using stack height of 0x%lx\n",
470               __FILE__, __LINE__, sp_value, sp_addr, stack_height);
471     out.setSP(sp_value);
472   }
473
474   sw_printf("[%s:%u] - DyninstDynamicStepper handled normal instrumentation\n",
475             __FILE__, __LINE__);
476   return gcf_success;
477 }
478
479 namespace Dyninst {
480 namespace Stackwalker {
481 void getTrapInstruction(char *buffer, unsigned buf_size, 
482                         unsigned &actual_len, bool include_return)
483 {
484    if (include_return)
485    {
486       assert(buf_size >= 2);
487       buffer[0] = (char) 0xcc; //trap
488       buffer[1] = (char) 0xc3; //ret
489       actual_len = 2;
490       return;
491    }
492    assert(buf_size >= 1);
493    buffer[0] = (char) 0xcc; //trap
494    actual_len = 1;
495    return;
496 }
497 }
498 }
499