Add StackwalkerAPI frame stepper that walks through instrumentation.
[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 static gcframe_ret_t HandleStandardFrame(const Frame &in, Frame &out, ProcessState *proc)
92 {
93   Address in_fp, out_sp;
94   const unsigned addr_width = proc->getAddressWidth();
95   bool result;
96
97   struct {
98     Address out_fp;
99     Address out_ra;
100   } ra_fp_pair;
101
102   in_fp = in.getFP();
103   out_sp = in_fp + addr_width;
104
105 #if defined(arch_x86_64)
106   /**
107    * On AMD64 we may be reading from a process with a different
108    * address width than the current one.  We'll do the read at
109    * the correct size, then convert the addresses into the 
110    * local size
111    **/
112   struct {
113      unsigned out_fp;
114      unsigned out_ra;
115   } ra_fp_pair32;
116   if (addr_width != sizeof(Address))
117   {
118      result = proc->readMem(&ra_fp_pair32, in_fp, 
119                             sizeof(ra_fp_pair32));
120      ra_fp_pair.out_fp = (Address) ra_fp_pair32.out_fp;
121      ra_fp_pair.out_ra = (Address) ra_fp_pair32.out_ra;
122   }
123   else
124 #endif
125   {
126     result = proc->readMem(&ra_fp_pair, in_fp, sizeof(ra_fp_pair));
127   }
128
129   if (!result) {
130     sw_printf("[%s:%u] - Couldn't read from %lx\n", __FILE__, __LINE__, out_sp);
131     return gcf_error;
132   }
133   
134   if (!ra_fp_pair.out_ra) {
135      return gcf_not_me;
136   }
137
138   out.setFP(ra_fp_pair.out_fp);
139   out.setRA(ra_fp_pair.out_ra);
140   out.setSP(out_sp);
141
142   return gcf_success;
143 }
144
145
146 gcframe_ret_t FrameFuncStepperImpl::getCallerFrame(const Frame &in, Frame &out)
147 {
148   if (!in.getFP())
149      return gcf_not_me;
150
151   FrameFuncHelper::alloc_frame_t frame = helper->allocatesFrame(in.getRA());
152   if (frame.first != FrameFuncHelper::standard_frame) {
153      return gcf_not_me;
154   }
155
156   return HandleStandardFrame(in, out, getProcessState());
157 }
158  
159 std::map<Dyninst::PID, LookupFuncStart*> LookupFuncStart::all_func_starts;
160
161 #if defined(cap_cache_func_starts)
162 static int hash_address(Address a)
163 {
164    return (int) a;
165 }
166 #endif
167
168 LookupFuncStart::LookupFuncStart(ProcessState *proc_) :
169    FrameFuncHelper(proc_)
170 #if defined(cap_cache_func_starts)
171    , cache(cache_size, hash_address)
172 #endif
173 {
174    all_func_starts[proc->getProcessId()] = this;
175    ref_count = 1;
176 }
177
178 LookupFuncStart::~LookupFuncStart()
179 {
180    Dyninst::PID pid = proc->getProcessId();
181    all_func_starts.erase(pid);
182 }
183
184 LookupFuncStart *LookupFuncStart::getLookupFuncStart(ProcessState *p)
185 {
186    Dyninst::PID pid = p->getProcessId();
187    std::map<Dyninst::PID, LookupFuncStart*>::iterator i = all_func_starts.find(pid);
188    if (i == all_func_starts.end()) {
189       return new LookupFuncStart(p);
190    }
191    (*i).second->ref_count++;
192    return (*i).second;
193 }
194
195 void LookupFuncStart::releaseMe()
196 {
197    ref_count--;
198    if (!ref_count)
199       delete this;
200 }
201
202 FrameFuncStepperImpl::FrameFuncStepperImpl(Walker *w, FrameStepper *parent_,
203                                            FrameFuncHelper *helper_) :
204    FrameStepper(w),
205    parent(parent_),
206    helper(helper_)
207 {
208    helper = helper_ ? helper_ : LookupFuncStart::getLookupFuncStart(getProcessState());
209 }
210
211 FrameFuncStepperImpl::~FrameFuncStepperImpl()
212 {
213    LookupFuncStart *lookup = dynamic_cast<LookupFuncStart*>(helper);
214    if (lookup)
215       lookup->releaseMe();
216    else if (helper)
217       delete helper;
218 }
219
220 unsigned FrameFuncStepperImpl::getPriority() const
221 {
222   return 0x10020;
223 }
224
225 #if defined(cap_stackwalker_use_symtab)
226
227 /**
228  * Look at the first few bytes in the function and see if they contain
229  * the standard set to allocate a stack frame.
230  **/
231 #define FUNCTION_PROLOG_TOCHECK 12
232 static unsigned char push_ebp = 0x55;
233 static unsigned char mov_esp_ebp[2][2] = { { 0x89, 0xe5 },
234                                            { 0x8b, 0xec } };
235 static unsigned char rex_mov_esp_ebp = 0x48;
236
237 FrameFuncHelper::alloc_frame_t LookupFuncStart::allocatesFrame(Address addr)
238 {
239    LibAddrPair lib;
240    Symtab *symtab = NULL;
241    SymtabAPI::Function *func = NULL;
242    unsigned char mem[FUNCTION_PROLOG_TOCHECK];
243    Address func_addr;
244    unsigned cur;
245    int push_ebp_pos = -1, mov_esp_ebp_pos = -1;
246    alloc_frame_t res = alloc_frame_t(unknown_t, unknown_s);
247    bool result;
248
249    result = checkCache(addr, res);
250    if (result) {
251       sw_printf("[%s:%u] - Cached value for %lx is %d/%d\n",
252                 __FILE__, __LINE__, addr, (int) res.first, (int) res.second);
253       return res;
254    }
255
256    result = proc->getLibraryTracker()->getLibraryAtAddr(addr, lib);
257    if (!result)
258    {
259       sw_printf("[%s:%u] - No library at %lx\n", __FILE__, __LINE__, addr);
260       goto done;
261    }
262
263    symtab = SymtabWrapper::getSymtab(lib.first);
264    if (!symtab) {
265       sw_printf("[%s:%u] - Error. SymtabAPI couldn't open %s\n",
266                 __FILE__, __LINE__, lib.first.c_str());
267       goto done;
268    }
269
270    result = symtab->getContainingFunction(addr - lib.second, func);
271    if (!result || !func) {
272       sw_printf("[%s:%u] - Error.  Address %lx wasn't part of a function\n",
273                 __FILE__, __LINE__, addr);
274       goto done;
275    }
276    func_addr = func->getOffset() + lib.second;
277    
278    result = proc->readMem(mem, func_addr, FUNCTION_PROLOG_TOCHECK);
279    if (!result) {
280       sw_printf("[%s:%u] - Error.  Couldn't read from memory at %lx\n",
281                 __FILE__, __LINE__, func_addr);
282       goto done;
283    }
284    
285    //Try to find a 'push (r|e)bp'
286    for (cur=0; cur<FUNCTION_PROLOG_TOCHECK; cur++)
287    {
288       if (mem[cur] == push_ebp)
289       {
290          push_ebp_pos = cur;
291          break;
292       }
293    }
294
295    //Try to find the mov esp->ebp
296    for (; cur<FUNCTION_PROLOG_TOCHECK; cur++)
297    {
298       if (proc->getAddressWidth() == 8) {
299          if (mem[cur] != rex_mov_esp_ebp)
300             continue;
301          cur++;
302       }
303       
304       if (cur+1 >= FUNCTION_PROLOG_TOCHECK) 
305          break;
306       if ((mem[cur] == mov_esp_ebp[0][0] && mem[cur+1] == mov_esp_ebp[0][1]) || 
307           (mem[cur] == mov_esp_ebp[1][0] && mem[cur+1] == mov_esp_ebp[1][1])) {
308          if (proc->getAddressWidth() == 8) 
309             mov_esp_ebp_pos = cur-1;
310          else
311             mov_esp_ebp_pos = cur;
312          break;
313       }
314    }
315
316    if (push_ebp_pos != -1 && mov_esp_ebp_pos != -1)
317       res.first = standard_frame;
318    else if (push_ebp_pos != -1 && mov_esp_ebp_pos == -1)
319       res.first = savefp_only_frame;
320    else 
321       res.first = no_frame;
322    
323    if (push_ebp_pos != -1 && addr <= func_addr + push_ebp_pos)
324       res.second = unset_frame;
325    else if (mov_esp_ebp_pos != -1 && addr <= func_addr + mov_esp_ebp_pos)
326       res.second = halfset_frame;
327    else
328       res.second = set_frame;
329
330  done:
331    sw_printf("[%s:%u] - Function containing %lx has frame type %d/%d\n",
332              __FILE__, __LINE__, addr, (int) res.first, (int) res.second);
333    updateCache(addr, res);
334    return res;
335 }
336
337 #if defined(cap_cache_func_starts)
338 void LookupFuncStart::updateCache(Address addr, alloc_frame_t result)
339 {
340    cache.insert(addr, result);
341 }
342
343 bool LookupFuncStart::checkCache(Address addr, alloc_frame_t &result)
344 {
345    return cache.lookup(addr, result);
346 }
347
348 #else
349 void LookupFuncStart::updateCache(Address /*addr*/, alloc_frame_t /*result*/)
350 {
351    return;
352 }
353
354 bool LookupFuncStart::checkCache(Address /*addr*/, alloc_frame_t &/*result*/)
355 {
356    return false;
357 }
358 #endif
359
360 void LookupFuncStart::clear_func_mapping(Dyninst::PID pid)
361 {
362    std::map<Dyninst::PID, LookupFuncStart *>::iterator i;
363    i = all_func_starts.find(pid);
364    if (i == all_func_starts.end())
365       return;
366
367    LookupFuncStart *fs = (*i).second;
368    all_func_starts.erase(i);
369    
370    delete fs;
371 }
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 = x86::EBP;
393    else
394       frame_reg = x86_64::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 DyninstInstrStepperImpl::getCallerFrameArch(const Frame &in, Frame &out, Address /*base*/, Address lib_base,
427                                                           unsigned /*size*/, unsigned stack_height)
428 {
429   gcframe_ret_t ret = HandleStandardFrame(in, out, getProcessState());
430   if (ret != gcf_success)
431     return ret;
432   out.setRA(out.getRA() + lib_base);
433   out.setSP(out.getSP() + stack_height);
434   return gcf_success;
435 }
436
437 namespace Dyninst {
438 namespace Stackwalker {
439 void getTrapInstruction(char *buffer, unsigned buf_size, 
440                         unsigned &actual_len, bool include_return)
441 {
442    if (include_return)
443    {
444       assert(buf_size >= 2);
445       buffer[0] = 0xcc; //trap
446       buffer[1] = 0xc3; //ret
447       actual_len = 2;
448       return;
449    }
450    assert(buf_size >= 1);
451    buffer[0] = 0xcc; //trap
452    actual_len = 1;
453    return;
454 }
455 }
456 }
457