Updates for StackwalkerAPI
[dyninst.git] / stackwalk / src / x86-swk.C
1 /*
2  * Copyright (c) 1996-2008 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 "symtabAPI/h/Function.h"
44
45 #if defined(cap_cache_func_starts)
46 #include "common/h/lru_cache.h"
47 #endif
48
49 using namespace Dyninst;
50 using namespace Dyninst::Stackwalker;
51
52 bool ProcSelf::getRegValue(Dyninst::MachRegister reg, THR_ID, Dyninst::MachRegisterVal &val)
53 {
54   unsigned long *frame_pointer;
55
56 #if defined(arch_x86_64) && defined(os_linux)
57   __asm__("mov %%rbp, %0\n"
58           : "=r"(frame_pointer));
59 #elif defined(os_linux)
60   __asm__("movl %%ebp, %0\n"
61           : "=r"(frame_pointer));
62 #elif defined(os_windows)
63    __asm
64    {
65       mov frame_pointer, ebp ;
66    }   
67 #endif
68
69   frame_pointer = (unsigned long *) *frame_pointer;
70   
71   switch(reg)
72   {
73     case Dyninst::MachRegPC:
74       val = (Dyninst::MachRegisterVal) frame_pointer[1];
75       break;
76     case Dyninst::MachRegFrameBase:
77       val = (Dyninst::MachRegisterVal) frame_pointer[0];
78       break;      
79     case Dyninst::MachRegStackBase:
80        val = (Dyninst::MachRegisterVal) (frame_pointer + 2);
81       break;      
82     default:
83        sw_printf("[%s:%u] - Request for unsupported register %d\n",
84                  __FILE__, __LINE__, reg);
85        setLastError(err_badparam, "Unknown register passed in reg field");
86   }
87
88   return true;
89 }
90
91 gcframe_ret_t FrameFuncStepperImpl::getCallerFrame(const Frame &in, Frame &out)
92 {
93   Address in_fp, out_sp;
94   bool result;
95   const unsigned addr_width = getProcessState()->getAddressWidth();
96
97   struct {
98     Address out_fp;
99     Address out_ra;
100   } ra_fp_pair;
101
102   if (!in.getFP())
103      return gcf_not_me;
104
105   FrameFuncHelper::alloc_frame_t frame = helper->allocatesFrame(in.getRA());
106   if (frame.first != FrameFuncHelper::standard_frame) {
107      return gcf_not_me;
108   }
109
110   in_fp = in.getFP();
111   out_sp = in_fp + addr_width;
112 #if defined(arch_x86_64)
113   /**
114    * On AMD64 we may be reading from a process with a different
115    * address width than the current one.  We'll do the read at
116    * the correct size, then convert the addresses into the 
117    * local size
118    **/
119   struct {
120      unsigned out_fp;
121      unsigned out_ra;
122   } ra_fp_pair32;
123   if (addr_width != sizeof(Address))
124   {
125      result = getProcessState()->readMem(&ra_fp_pair32, in_fp, 
126                                          sizeof(ra_fp_pair32));
127      ra_fp_pair.out_fp = (Address) ra_fp_pair32.out_fp;
128      ra_fp_pair.out_ra = (Address) ra_fp_pair32.out_ra;
129   }
130   else
131 #endif
132      result = getProcessState()->readMem(&ra_fp_pair, in_fp, 
133                                          sizeof(ra_fp_pair));
134   if (!result) {
135     sw_printf("[%s:%u] - Couldn't read from %lx\n", __FILE__, __LINE__, out_sp);
136     return gcf_error;
137   }
138   
139   if (!ra_fp_pair.out_ra) {
140      return gcf_not_me;
141   }
142
143   out.setFP(ra_fp_pair.out_fp);
144   out.setRA(ra_fp_pair.out_ra);
145   out.setSP(out_sp);
146
147   return gcf_success;
148 }
149  
150 class LookupFuncStart : public FrameFuncHelper
151 {
152 private:
153    static std::map<Dyninst::PID, LookupFuncStart*> all_func_starts;
154    LookupFuncStart(ProcessState *proc_);
155    int ref_count;
156 private:
157    void updateCache(Address addr, alloc_frame_t result);
158    bool checkCache(Address addr, alloc_frame_t &result);
159 #if defined(cap_cache_func_starts)
160    //We need some kind of re-entrant safe synhronization before we can
161    // globally turn this caching on, but it would sure help things.
162    static const unsigned int cache_size = 64;
163    LRUCache<Address, alloc_frame_t> cache;
164 #endif
165 public:
166    static LookupFuncStart *getLookupFuncStart(ProcessState *p);
167    void releaseMe();
168    virtual alloc_frame_t allocatesFrame(Address addr);
169    ~LookupFuncStart();
170 };
171
172 std::map<Dyninst::PID, LookupFuncStart*> LookupFuncStart::all_func_starts;
173
174 #if defined(cap_cache_func_starts)
175 static int hash_address(Address a)
176 {
177    return (int) a;
178 }
179 #endif
180
181 LookupFuncStart::LookupFuncStart(ProcessState *proc_) :
182    FrameFuncHelper(proc_)
183 #if defined(cap_cache_func_starts)
184    , cache(cache_size, hash_address)
185 #endif
186 {
187    all_func_starts[proc->getProcessId()] = this;
188    ref_count = 1;
189 }
190
191 LookupFuncStart::~LookupFuncStart()
192 {
193    Dyninst::PID pid = proc->getProcessId();
194    all_func_starts.erase(pid);
195 }
196
197 LookupFuncStart *LookupFuncStart::getLookupFuncStart(ProcessState *p)
198 {
199    Dyninst::PID pid = p->getProcessId();
200    std::map<Dyninst::PID, LookupFuncStart*>::iterator i = all_func_starts.find(pid);
201    if (i == all_func_starts.end()) {
202       return new LookupFuncStart(p);
203    }
204    (*i).second->ref_count++;
205    return (*i).second;
206 }
207
208 void LookupFuncStart::releaseMe()
209 {
210    ref_count--;
211    if (!ref_count)
212       delete this;
213 }
214
215 FrameFuncStepperImpl::FrameFuncStepperImpl(Walker *w, FrameStepper *parent_,
216                                            FrameFuncHelper *helper_) :
217    FrameStepper(w),
218    parent(parent_),
219    helper(helper_)
220 {
221    helper = helper_ ? helper_ : LookupFuncStart::getLookupFuncStart(getProcessState());
222 }
223
224 FrameFuncStepperImpl::~FrameFuncStepperImpl()
225 {
226    LookupFuncStart *lookup = dynamic_cast<LookupFuncStart*>(helper);
227    if (lookup)
228       lookup->releaseMe();
229    else if (helper)
230       delete helper;
231 }
232
233 unsigned FrameFuncStepperImpl::getPriority() const
234 {
235   return 0x10020;
236 }
237
238 #if defined(cap_stackwalker_use_symtab)
239
240 /**
241  * Look at the first few bytes in the function and see if they contain
242  * the standard set to allocate a stack frame.
243  **/
244 #define FUNCTION_PROLOG_TOCHECK 12
245 static unsigned char push_ebp = 0x55;
246 static unsigned char mov_esp_ebp[2][2] = { { 0x89, 0xe5 },
247                                            { 0x8b, 0xec } };
248 static unsigned char rex_mov_esp_ebp = 0x48;
249
250 FrameFuncHelper::alloc_frame_t LookupFuncStart::allocatesFrame(Address addr)
251 {
252    LibAddrPair lib;
253    Symtab *symtab = NULL;
254    SymtabAPI::Function *func = NULL;
255    unsigned char mem[FUNCTION_PROLOG_TOCHECK];
256    Address func_addr;
257    unsigned cur;
258    int push_ebp_pos = -1, mov_esp_ebp_pos = -1;
259    alloc_frame_t res = alloc_frame_t(unknown_t, unknown_s);
260    bool result;
261
262    result = checkCache(addr, res);
263    if (result) {
264       sw_printf("[%s:%u] - Cached value for %lx is %d/%d\n",
265                 __FILE__, __LINE__, addr, (int) res.first, (int) res.second);
266       return res;
267    }
268
269    result = proc->getLibraryTracker()->getLibraryAtAddr(addr, lib);
270    if (!result)
271    {
272       sw_printf("[%s:%u] - No library at %lx\n", __FILE__, __LINE__, addr);
273       goto done;
274    }
275
276    symtab = SymtabWrapper::getSymtab(lib.first);
277    if (!symtab) {
278       sw_printf("[%s:%u] - Error. SymtabAPI couldn't open %s\n",
279                 __FILE__, __LINE__, lib.first.c_str());
280       goto done;
281    }
282
283    result = symtab->getNearestFunction(addr - lib.second, func);
284    if (!result || !func) {
285       sw_printf("[%s:%u] - Error.  Address %lx wasn't part of a function\n",
286                 __FILE__, __LINE__, addr);
287       goto done;
288    }
289    func_addr = func->getAddress() + lib.second;
290    
291    result = proc->readMem(mem, func_addr, FUNCTION_PROLOG_TOCHECK);
292    if (!result) {
293       sw_printf("[%s:%u] - Error.  Couldn't read from memory at %lx\n",
294                 __FILE__, __LINE__, func_addr);
295       goto done;
296    }
297    
298    //Try to find a 'push (r|e)bp'
299    for (cur=0; cur<FUNCTION_PROLOG_TOCHECK; cur++)
300    {
301       if (mem[cur] == push_ebp)
302       {
303          push_ebp_pos = cur;
304          break;
305       }
306    }
307
308    //Try to find the mov esp->ebp
309    for (; cur<FUNCTION_PROLOG_TOCHECK; cur++)
310    {
311       if (proc->getAddressWidth() == 8) {
312          if (mem[cur] != rex_mov_esp_ebp)
313             continue;
314          cur++;
315       }
316       
317       if (cur+1 >= FUNCTION_PROLOG_TOCHECK) 
318          break;
319       if ((mem[cur] == mov_esp_ebp[0][0] && mem[cur+1] == mov_esp_ebp[0][1]) || 
320           (mem[cur] == mov_esp_ebp[1][0] && mem[cur+1] == mov_esp_ebp[1][1])) {
321          if (proc->getAddressWidth() == 8) 
322             mov_esp_ebp_pos = cur-1;
323          else
324             mov_esp_ebp_pos = cur;
325          break;
326       }
327    }
328
329    if (push_ebp_pos != -1 && mov_esp_ebp_pos != -1)
330       res.first = standard_frame;
331    else if (push_ebp_pos != -1 && mov_esp_ebp_pos == -1)
332       res.first = savefp_only_frame;
333    else 
334       res.first = no_frame;
335    
336    if (push_ebp_pos != -1 && addr <= func_addr + push_ebp_pos)
337       res.second = unset_frame;
338    else if (mov_esp_ebp_pos != -1 && addr <= func_addr + mov_esp_ebp_pos)
339       res.second = halfset_frame;
340    else
341       res.second = set_frame;
342
343  done:
344    sw_printf("[%s:%u] - Function containing %lx has frame type %d/%d\n",
345              __FILE__, __LINE__, addr, (int) res.first, (int) res.second);
346    updateCache(addr, res);
347    return res;
348 }
349
350 #if defined(cap_cache_func_starts)
351 void LookupFuncStart::updateCache(Address addr, alloc_frame_t result)
352 {
353    cache.insert(addr, result);
354 }
355
356 bool LookupFuncStart::checkCache(Address addr, alloc_frame_t &result)
357 {
358    return cache.lookup(addr, result);
359 }
360
361 #else
362 void LookupFuncStart::updateCache(Address /*addr*/, alloc_frame_t /*result*/)
363 {
364    return;
365 }
366
367 bool LookupFuncStart::checkCache(Address /*addr*/, alloc_frame_t &/*result*/)
368 {
369    return false;
370 }
371 #endif
372
373 gcframe_ret_t DebugStepperImpl::getCallerFrameArch(Address pc, const Frame &in, 
374                                                    Frame &out, Symtab *symtab)
375 {
376    MachRegisterVal frame_value, stack_value, ret_value;
377    bool result;
378
379    result = symtab->getRegValueAtFrame(pc, MachRegReturn,
380                                        ret_value, this);
381    if (!result) {
382       sw_printf("[%s:%u] - Couldn't get return debug info at %lx\n",
383                 __FILE__, __LINE__, in.getRA());
384       return gcf_not_me;
385    }
386
387    unsigned addr_width = getProcessState()->getAddressWidth();
388    
389    
390    Dyninst::MachRegister frame_reg;
391    if (addr_width == 4)
392       frame_reg = EBP;
393    else
394       frame_reg = RBP;
395
396    result = symtab->getRegValueAtFrame(pc, frame_reg,
397                                        frame_value, this);
398    if (!result) {
399       sw_printf("[%s:%u] - Couldn't get frame debug info at %lx\n",
400                  __FILE__, __LINE__, in.getRA());
401       return gcf_not_me;
402    }
403
404    result = symtab->getRegValueAtFrame(pc, MachRegFrameBase,
405                                        stack_value, this);
406    if (!result) {
407       sw_printf("[%s:%u] - Couldn't get stack debug info at %lx\n",
408                  __FILE__, __LINE__, in.getRA());
409       return gcf_not_me;
410    }
411
412    out.setRA(ret_value);
413    out.setFP(frame_value);
414    out.setSP(stack_value);
415
416    return gcf_success;
417 }
418 #else
419
420 FrameFuncHelper::alloc_frame_t LookupFuncStart::allocatesFrame(Address /*addr*/)
421 {
422    return FrameFuncHelper::alloc_frame_t(unknown_t, unknown_s);
423 }
424 #endif
425
426 gcframe_ret_t UninitFrameStepperImpl::getCallerFrame(const Frame &in, Frame &out)
427 {
428    bool result;
429    int offset;
430    if (!alloc_frame)
431       return gcf_not_me;
432    FrameFuncHelper::alloc_frame_t frame_state = alloc_frame->allocatesFrame(in.getRA());
433
434    if (frame_state.second != FrameFuncHelper::unset_frame && 
435        frame_state.second != FrameFuncHelper::halfset_frame)
436       return gcf_not_me;
437
438    if (frame_state.second == FrameFuncHelper::unset_frame)
439       offset = getProcessState()->getAddressWidth();
440    else //frame_state == FrameFuncHelper::halfset_frame
441       offset = getProcessState()->getAddressWidth()*2;
442    out.setSP(in.getSP() + offset);
443       
444    Address pc;
445    location_t pc_loc;
446    
447    pc_loc.location = loc_address;
448    pc_loc.val.addr = offset + in.getSP();
449    result = getProcessState()->readMem(&pc, pc_loc.val.addr, 
450                                        getProcessState()->getAddressWidth());
451    if (!result) {
452       sw_printf("[%s:%u] - Error reading from stack at address %x\n",
453                 __FILE__, __LINE__, pc_loc.val.addr);
454       return gcf_error;
455    }
456    out.setRALocation(pc_loc);
457    out.setRA(pc);
458
459    location_t fp_loc;
460    fp_loc.location = loc_register;
461    if (getProcessState()->getAddressWidth() == 4)
462       fp_loc.val.reg = EBP;
463    else 
464       fp_loc.val.reg = RBP;
465    out.setFP(in.getFP());
466    out.setFPLocation(fp_loc);
467
468    out.setSP(pc_loc.val.addr + getProcessState()->getAddressWidth());
469
470    return gcf_success;
471 }
472
473 UninitFrameStepperImpl::UninitFrameStepperImpl(Walker *w, 
474                                                FrameFuncHelper *f,
475                                                FrameStepper *parent_) :
476    FrameStepper(w),
477    parent(parent_)
478 {
479    alloc_frame = f ? f : LookupFuncStart::getLookupFuncStart(w->getProcessState());
480 }
481
482 UninitFrameStepperImpl::~UninitFrameStepperImpl()
483 {
484    LookupFuncStart *lookup = dynamic_cast<LookupFuncStart*>(alloc_frame);
485    if (lookup)
486       lookup->releaseMe();
487    else if (alloc_frame)
488       delete alloc_frame;
489 }
490
491 UninitFrameStepper::UninitFrameStepper(Walker *w, FrameFuncHelper *f) :
492    FrameStepper(w)
493 {
494    impl = new UninitFrameStepperImpl(w, f, this);
495 }
496
497 gcframe_ret_t UninitFrameStepper::getCallerFrame(const Frame &in, Frame &out)
498 {
499    if (impl)
500       return impl->getCallerFrame(in, out);
501    sw_printf("[%s:%u] - Error,  UninitFrame used on unsupported platform\n",
502              __FILE__, __LINE__);
503    setLastError(err_unsupported, "Uninitialized frame walking not supported " 
504                 "on this platform");
505    return gcf_error;
506 }
507
508 unsigned UninitFrameStepper::getPriority() const
509 {
510    if (impl)
511       return impl->getPriority();
512    sw_printf("[%s:%u] - Error,  UninitFrame used on unsupported platform\n",
513              __FILE__, __LINE__);
514    setLastError(err_unsupported, "Uninitialized frame walking not supported " 
515                 "on this platform");
516    return 0;
517 }
518
519 void UninitFrameStepper::registerStepperGroup(StepperGroup *group)
520 {
521    if (impl) {
522       impl->registerStepperGroup(group);
523       return;
524    }
525    sw_printf("[%s:%u] - Error,  UninitFrame used on unsupported platform\n",
526              __FILE__, __LINE__);
527    setLastError(err_unsupported, "Uninitialized frame walking not supported " 
528                 "on this platform");
529 }
530
531 UninitFrameStepper::~UninitFrameStepper()
532 {
533    if (impl)
534       delete impl;
535    impl = NULL;
536 }
537
538 unsigned UninitFrameStepperImpl::getPriority() const
539 {
540    return 0x10950;
541 }
542
543 namespace Dyninst {
544 namespace Stackwalker {
545 void getTrapInstruction(char *buffer, unsigned buf_size, 
546                         unsigned &actual_len, bool include_return)
547 {
548    if (include_return)
549    {
550       assert(buf_size >= 2);
551       buffer[0] = 0xcc; //trap
552       buffer[1] = 0xc3; //ret
553       actual_len = 2;
554       return;
555    }
556    assert(buf_size >= 1);
557    buffer[0] = 0xcc; //trap
558    actual_len = 1;
559    return;
560 }
561 }
562 }
563