Move the address lookup implementation from SymtabAPI to libcommon, so it can be...
[dyninst.git] / stackwalk / src / x86-swk.C
1 /*
2  * Copyright (c) 1996-2009 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.val())
72   {
73      case Dyninst::x86_64::irip:
74      case Dyninst::x86::ieip:
75      case Dyninst::iReturnAddr:
76         val = (Dyninst::MachRegisterVal) frame_pointer[1];
77         break;
78      case Dyninst::iFrameBase:
79         val = (Dyninst::MachRegisterVal) frame_pointer[0];
80         break;
81      case Dyninst::x86_64::irsp:
82      case Dyninst::x86::iesp:
83      case Dyninst::iStackTop:
84         val = (Dyninst::MachRegisterVal) (frame_pointer + 2);
85         break;      
86      default:
87         sw_printf("[%s:%u] - Request for unsupported register %s\n",
88                   __FILE__, __LINE__, reg.name());
89         setLastError(err_badparam, "Unknown register passed in reg field");
90   }
91
92   return true;
93 }
94
95 Dyninst::Architecture ProcSelf::getArchitecture()
96 {
97    if (sizeof(void *) == 8)
98       return Arch_x86_64;
99    return Arch_x86;
100 }
101
102 static gcframe_ret_t HandleStandardFrame(const Frame &in, Frame &out, ProcessState *proc)
103 {
104   Address in_fp, out_sp;
105   const unsigned addr_width = proc->getAddressWidth();
106   bool result;
107
108   struct {
109     Address out_fp;
110     Address out_ra;
111   } ra_fp_pair;
112
113   in_fp = in.getFP();
114   out_sp = in_fp + addr_width;
115
116 #if defined(arch_x86_64)
117   /**
118    * On AMD64 we may be reading from a process with a different
119    * address width than the current one.  We'll do the read at
120    * the correct size, then convert the addresses into the 
121    * local size
122    **/
123   struct {
124      unsigned out_fp;
125      unsigned out_ra;
126   } ra_fp_pair32;
127   if (addr_width != sizeof(Address))
128   {
129      result = proc->readMem(&ra_fp_pair32, in_fp, 
130                             sizeof(ra_fp_pair32));
131      ra_fp_pair.out_fp = (Address) ra_fp_pair32.out_fp;
132      ra_fp_pair.out_ra = (Address) ra_fp_pair32.out_ra;
133   }
134   else
135 #endif
136   {
137     result = proc->readMem(&ra_fp_pair, in_fp, sizeof(ra_fp_pair));
138   }
139
140   if (!result) {
141     sw_printf("[%s:%u] - Couldn't read from %lx\n", __FILE__, __LINE__, out_sp);
142     return gcf_error;
143   }
144   
145   if (!ra_fp_pair.out_ra) {
146      return gcf_not_me;
147   }
148
149   out.setFP(ra_fp_pair.out_fp);
150   out.setRA(ra_fp_pair.out_ra);
151   out.setSP(out_sp);
152
153   return gcf_success;
154 }
155
156 bool Walker::checkValidFrame(const Frame &in, const Frame &out)
157 {
158    if (out.getSP() <= in.getSP()) {
159       sw_printf("[%s:%u] - Stackwalk went backwards, %lx to %lx\n",
160                 __FILE__, __LINE__, in.getSP(), out.getSP());
161       return false;
162    }
163    return true;
164 }
165
166 gcframe_ret_t FrameFuncStepperImpl::getCallerFrame(const Frame &in, Frame &out)
167 {
168   if (!in.getFP())
169      return gcf_not_me;
170
171   FrameFuncHelper::alloc_frame_t frame = helper->allocatesFrame(in.getRA());
172   if (frame.first != FrameFuncHelper::standard_frame) {
173      return gcf_not_me;
174   }
175
176   return HandleStandardFrame(in, out, getProcessState());
177 }
178  
179 std::map<Dyninst::PID, LookupFuncStart*> LookupFuncStart::all_func_starts;
180
181 #if defined(cap_cache_func_starts)
182 static int hash_address(Address a)
183 {
184    return (int) a;
185 }
186 #endif
187
188 LookupFuncStart::LookupFuncStart(ProcessState *proc_) :
189    FrameFuncHelper(proc_)
190 #if defined(cap_cache_func_starts)
191    , cache(cache_size, hash_address)
192 #endif
193 {
194    all_func_starts[proc->getProcessId()] = this;
195    ref_count = 1;
196 }
197
198 LookupFuncStart::~LookupFuncStart()
199 {
200    Dyninst::PID pid = proc->getProcessId();
201    all_func_starts.erase(pid);
202 }
203
204 LookupFuncStart *LookupFuncStart::getLookupFuncStart(ProcessState *p)
205 {
206    Dyninst::PID pid = p->getProcessId();
207    std::map<Dyninst::PID, LookupFuncStart*>::iterator i = all_func_starts.find(pid);
208    if (i == all_func_starts.end()) {
209       return new LookupFuncStart(p);
210    }
211    (*i).second->ref_count++;
212    return (*i).second;
213 }
214
215 void LookupFuncStart::releaseMe()
216 {
217    ref_count--;
218    if (!ref_count)
219       delete this;
220 }
221
222 FrameFuncStepperImpl::FrameFuncStepperImpl(Walker *w, FrameStepper *parent_,
223                                            FrameFuncHelper *helper_) :
224    FrameStepper(w),
225    parent(parent_),
226    helper(helper_)
227 {
228    helper = helper_ ? helper_ : LookupFuncStart::getLookupFuncStart(getProcessState());
229 }
230
231 FrameFuncStepperImpl::~FrameFuncStepperImpl()
232 {
233    LookupFuncStart *lookup = dynamic_cast<LookupFuncStart*>(helper);
234    if (lookup)
235       lookup->releaseMe();
236    else if (helper)
237       delete helper;
238 }
239
240 unsigned FrameFuncStepperImpl::getPriority() const
241 {
242   return frame_priority;
243 }
244
245 #if defined(cap_stackwalker_use_symtab)
246
247 /**
248  * Look at the first few bytes in the function and see if they contain
249  * the standard set to allocate a stack frame.
250  **/
251 #define FUNCTION_PROLOG_TOCHECK 12
252 static unsigned char push_ebp = 0x55;
253 static unsigned char mov_esp_ebp[2][2] = { { 0x89, 0xe5 },
254                                            { 0x8b, 0xec } };
255 static unsigned char rex_mov_esp_ebp = 0x48;
256
257 FrameFuncHelper::alloc_frame_t LookupFuncStart::allocatesFrame(Address addr)
258 {
259    LibAddrPair lib;
260    Symtab *symtab = NULL;
261    SymtabAPI::Function *func = NULL;
262    unsigned char mem[FUNCTION_PROLOG_TOCHECK];
263    Address func_addr;
264    unsigned cur;
265    int push_ebp_pos = -1, mov_esp_ebp_pos = -1;
266    alloc_frame_t res = alloc_frame_t(unknown_t, unknown_s);
267    bool result;
268
269    result = checkCache(addr, res);
270    if (result) {
271       sw_printf("[%s:%u] - Cached value for %lx is %d/%d\n",
272                 __FILE__, __LINE__, addr, (int) res.first, (int) res.second);
273       return res;
274    }
275
276    result = proc->getLibraryTracker()->getLibraryAtAddr(addr, lib);
277    if (!result)
278    {
279       sw_printf("[%s:%u] - No library at %lx\n", __FILE__, __LINE__, addr);
280       goto done;
281    }
282
283    symtab = SymtabWrapper::getSymtab(lib.first);
284    if (!symtab) {
285       sw_printf("[%s:%u] - Error. SymtabAPI couldn't open %s\n",
286                 __FILE__, __LINE__, lib.first.c_str());
287       goto done;
288    }
289
290    result = symtab->getContainingFunction(addr - lib.second, func);
291    if (!result || !func) {
292       sw_printf("[%s:%u] - Error.  Address %lx wasn't part of a function\n",
293                 __FILE__, __LINE__, addr);
294       goto done;
295    }
296    func_addr = func->getOffset() + lib.second;
297    
298    result = proc->readMem(mem, func_addr, FUNCTION_PROLOG_TOCHECK);
299    if (!result) {
300       sw_printf("[%s:%u] - Error.  Couldn't read from memory at %lx\n",
301                 __FILE__, __LINE__, func_addr);
302       goto done;
303    }
304    
305    //Try to find a 'push (r|e)bp'
306    for (cur=0; cur<FUNCTION_PROLOG_TOCHECK; cur++)
307    {
308       if (mem[cur] == push_ebp)
309       {
310          push_ebp_pos = cur;
311          break;
312       }
313    }
314
315    //Try to find the mov esp->ebp
316    for (; cur<FUNCTION_PROLOG_TOCHECK; cur++)
317    {
318       if (proc->getAddressWidth() == 8) {
319          if (mem[cur] != rex_mov_esp_ebp)
320             continue;
321          cur++;
322       }
323       
324       if (cur+1 >= FUNCTION_PROLOG_TOCHECK) 
325          break;
326       if ((mem[cur] == mov_esp_ebp[0][0] && mem[cur+1] == mov_esp_ebp[0][1]) || 
327           (mem[cur] == mov_esp_ebp[1][0] && mem[cur+1] == mov_esp_ebp[1][1])) {
328          if (proc->getAddressWidth() == 8) 
329             mov_esp_ebp_pos = cur-1;
330          else
331             mov_esp_ebp_pos = cur;
332          break;
333       }
334    }
335
336    if (push_ebp_pos != -1 && mov_esp_ebp_pos != -1)
337       res.first = standard_frame;
338    else if (push_ebp_pos != -1 && mov_esp_ebp_pos == -1)
339       res.first = savefp_only_frame;
340    else 
341       res.first = no_frame;
342    
343    if (push_ebp_pos != -1 && addr <= func_addr + push_ebp_pos)
344       res.second = unset_frame;
345    else if (mov_esp_ebp_pos != -1 && addr <= func_addr + mov_esp_ebp_pos)
346       res.second = halfset_frame;
347    else
348       res.second = set_frame;
349
350  done:
351    sw_printf("[%s:%u] - Function containing %lx has frame type %d/%d\n",
352              __FILE__, __LINE__, addr, (int) res.first, (int) res.second);
353    updateCache(addr, res);
354    return res;
355 }
356
357 #if defined(cap_cache_func_starts)
358 void LookupFuncStart::updateCache(Address addr, alloc_frame_t result)
359 {
360    cache.insert(addr, result);
361 }
362
363 bool LookupFuncStart::checkCache(Address addr, alloc_frame_t &result)
364 {
365    return cache.lookup(addr, result);
366 }
367
368 #else
369 void LookupFuncStart::updateCache(Address /*addr*/, alloc_frame_t /*result*/)
370 {
371    return;
372 }
373
374 bool LookupFuncStart::checkCache(Address /*addr*/, alloc_frame_t &/*result*/)
375 {
376    return false;
377 }
378 #endif
379
380 void LookupFuncStart::clear_func_mapping(Dyninst::PID pid)
381 {
382    std::map<Dyninst::PID, LookupFuncStart *>::iterator i;
383    i = all_func_starts.find(pid);
384    if (i == all_func_starts.end())
385       return;
386
387    LookupFuncStart *fs = (*i).second;
388    all_func_starts.erase(i);
389    
390    delete fs;
391 }
392
393 gcframe_ret_t DebugStepperImpl::getCallerFrameArch(Address pc, const Frame &in, 
394                                                    Frame &out, Symtab *symtab)
395 {
396    MachRegisterVal frame_value, stack_value, ret_value;
397    bool result;
398
399    result = symtab->getRegValueAtFrame(pc, Dyninst::ReturnAddr,
400                                        ret_value, this);
401    if (!result) {
402       sw_printf("[%s:%u] - Couldn't get return debug info at %lx\n",
403                 __FILE__, __LINE__, in.getRA());
404       return gcf_not_me;
405    }
406
407    unsigned addr_width = getProcessState()->getAddressWidth();
408    
409    
410    Dyninst::MachRegister frame_reg;
411    if (addr_width == 4)
412       frame_reg = x86::ebp;
413    else
414       frame_reg = x86_64::rbp;
415
416    result = symtab->getRegValueAtFrame(pc, frame_reg,
417                                        frame_value, this);
418    if (!result) {
419       sw_printf("[%s:%u] - Couldn't get frame debug info at %lx\n",
420                  __FILE__, __LINE__, in.getRA());
421       return gcf_not_me;
422    }
423
424    result = symtab->getRegValueAtFrame(pc, Dyninst::FrameBase,
425                                        stack_value, this);
426    if (!result) {
427       sw_printf("[%s:%u] - Couldn't get stack debug info at %lx\n",
428                  __FILE__, __LINE__, in.getRA());
429       return gcf_not_me;
430    }
431
432    out.setRA(ret_value);
433    out.setFP(frame_value);
434    out.setSP(stack_value);
435
436    return gcf_success;
437 }
438 #else
439
440 FrameFuncHelper::alloc_frame_t LookupFuncStart::allocatesFrame(Address /*addr*/)
441 {
442    return FrameFuncHelper::alloc_frame_t(unknown_t, unknown_s);
443 }
444 #endif
445
446 gcframe_ret_t DyninstInstrStepperImpl::getCallerFrameArch(const Frame &in, Frame &out, Address /*base*/, Address lib_base,
447                                                           unsigned /*size*/, unsigned stack_height)
448 {
449   gcframe_ret_t ret = HandleStandardFrame(in, out, getProcessState());
450   if (ret != gcf_success)
451     return ret;
452   out.setRA(out.getRA() + lib_base);
453   out.setSP(out.getSP() + stack_height);
454   return gcf_success;
455 }
456
457 namespace Dyninst {
458 namespace Stackwalker {
459 void getTrapInstruction(char *buffer, unsigned buf_size, 
460                         unsigned &actual_len, bool include_return)
461 {
462    if (include_return)
463    {
464       assert(buf_size >= 2);
465       buffer[0] = 0xcc; //trap
466       buffer[1] = 0xc3; //ret
467       actual_len = 2;
468       return;
469    }
470    assert(buf_size >= 1);
471    buffer[0] = 0xcc; //trap
472    actual_len = 1;
473    return;
474 }
475 }
476 }
477