StackwalkerAPI support for analysis based frame unwinding
[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 "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 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 static int hash_address(Address a)
182 {
183    return (int) a;
184 }
185
186 LookupFuncStart::LookupFuncStart(ProcessState *proc_) :
187    FrameFuncHelper(proc_),
188    cache(cache_size, hash_address)
189 {
190    all_func_starts[proc->getProcessId()] = this;
191    ref_count = 1;
192 }
193
194 LookupFuncStart::~LookupFuncStart()
195 {
196    Dyninst::PID pid = proc->getProcessId();
197    all_func_starts.erase(pid);
198 }
199
200 LookupFuncStart *LookupFuncStart::getLookupFuncStart(ProcessState *p)
201 {
202    Dyninst::PID pid = p->getProcessId();
203    std::map<Dyninst::PID, LookupFuncStart*>::iterator i = all_func_starts.find(pid);
204    if (i == all_func_starts.end()) {
205       return new LookupFuncStart(p);
206    }
207    (*i).second->ref_count++;
208    return (*i).second;
209 }
210
211 void LookupFuncStart::releaseMe()
212 {
213    ref_count--;
214    if (!ref_count)
215       delete this;
216 }
217
218 FrameFuncStepperImpl::FrameFuncStepperImpl(Walker *w, FrameStepper *parent_,
219                                            FrameFuncHelper *helper_) :
220    FrameStepper(w),
221    parent(parent_),
222    helper(helper_)
223 {
224    helper = helper_ ? helper_ : LookupFuncStart::getLookupFuncStart(getProcessState());
225 }
226
227 FrameFuncStepperImpl::~FrameFuncStepperImpl()
228 {
229    LookupFuncStart *lookup = dynamic_cast<LookupFuncStart*>(helper);
230    if (lookup)
231       lookup->releaseMe();
232    else if (helper)
233       delete helper;
234 }
235
236 unsigned FrameFuncStepperImpl::getPriority() const
237 {
238   return frame_priority;
239 }
240
241 /**
242  * Look at the first few bytes in the function and see if they contain
243  * the standard set to allocate a stack frame.
244  **/
245 #define FUNCTION_PROLOG_TOCHECK 12
246 static unsigned char push_ebp = 0x55;
247 static unsigned char mov_esp_ebp[2][2] = { { 0x89, 0xe5 },
248                                            { 0x8b, 0xec } };
249 static unsigned char rex_mov_esp_ebp = 0x48;
250
251 FrameFuncHelper::alloc_frame_t LookupFuncStart::allocatesFrame(Address addr)
252 {
253    LibAddrPair lib;
254    unsigned char mem[FUNCTION_PROLOG_TOCHECK];
255    Address func_addr;
256    unsigned cur;
257    int push_ebp_pos = -1, mov_esp_ebp_pos = -1;
258    alloc_frame_t res = alloc_frame_t(unknown_t, unknown_s);
259    bool result;
260    SymReader *reader;
261    Offset off;
262    Symbol_t sym;
263
264    result = checkCache(addr, res);
265    if (result) {
266       sw_printf("[%s:%u] - Cached value for %lx is %d/%d\n",
267                 __FILE__, __LINE__, addr, (int) res.first, (int) res.second);
268       return res;
269    }
270
271    result = proc->getLibraryTracker()->getLibraryAtAddr(addr, lib);
272    if (!result)
273    {
274       sw_printf("[%s:%u] - No library at %lx\n", __FILE__, __LINE__, addr);
275       goto done;
276    }
277
278    reader = LibraryWrapper::getLibrary(lib.first);
279    if (!reader) {
280       sw_printf("[%s:%u] - Failed to open symbol reader %s\n",
281                 __FILE__, __LINE__, lib.first.c_str() );
282       goto done;
283    }   
284    off = addr - lib.second;
285    sym = reader->getContainingSymbol(off);
286    if (!reader->isValidSymbol(sym)) {
287       sw_printf("[%s:%u] - Could not find symbol in binary\n", __FILE__, __LINE__);
288       goto done;
289    }
290    func_addr = reader->getSymbolOffset(sym) + lib.second;
291
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 void LookupFuncStart::updateCache(Address addr, alloc_frame_t result)
351 {
352    cache.insert(addr, result);
353 }
354
355 bool LookupFuncStart::checkCache(Address addr, alloc_frame_t &result)
356 {
357    return cache.lookup(addr, result);
358 }
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 DyninstInstrStepperImpl::getCallerFrameArch(const Frame &in, Frame &out, 
374                                                           Address /*base*/, Address lib_base,
375                                                           unsigned /*size*/, unsigned stack_height)
376 {
377   gcframe_ret_t ret = HandleStandardFrame(in, out, getProcessState());
378   if (ret != gcf_success)
379     return ret;
380   out.setRA(out.getRA() + lib_base);
381   out.setSP(out.getSP() + stack_height);
382   return gcf_success;
383 }
384
385 #include "analysis_stepper.h"
386 gcframe_ret_t AnalysisStepperImpl::getCallerFrameArch(height_pair_t height,
387                                                       const Frame &in, Frame &out)
388 {
389    Address in_sp = in.getSP();
390    StackAnalysis::Height pc_height = height.first;
391    StackAnalysis::Height fp_height = height.second;
392
393    ProcessState *proc = getProcessState();
394
395    Address ret_addr = 0;
396    
397    if (pc_height == StackAnalysis::Height::bottom) {
398       sw_printf("[%s:%u] - Analysis didn't find a stack height\n", 
399                 __FILE__, __LINE__);
400       return gcf_not_me;
401    }
402
403    Address ret_loc = in_sp - pc_height.height() - proc->getAddressWidth();
404
405    bool result = proc->readMem(&ret_addr, ret_loc, proc->getAddressWidth());
406    if (!result) {
407       sw_printf("[%s:%u] - Error reading from return location %lx on stack\n",
408                 __FILE__, __LINE__, ret_addr);
409       return gcf_not_me;
410    }
411    location_t ra_loc;
412    ra_loc.val.addr = ret_loc;
413    ra_loc.location = loc_address;
414    out.setRALocation(ra_loc);
415    out.setRA(ret_addr);
416    out.setSP(ret_loc + proc->getAddressWidth());
417
418    Address fp_addr = 0;
419    Address fp_loc = 0;
420    if (fp_height != StackAnalysis::Height::bottom) {
421       fp_loc = ret_loc + fp_height.height() - proc->getAddressWidth();
422       result = proc->readMem(&fp_addr, fp_loc, proc->getAddressWidth());
423       if (result) {
424          out.setFP(fp_addr);
425          location_t fp_loc;
426          ra_loc.val.addr = fp_addr;
427          ra_loc.location = loc_address;
428          out.setFPLocation(fp_loc);
429       }
430       else { 
431          sw_printf("[%s:%u] - Failed to read FP value\n", __FILE__, __LINE__);
432       }
433    }
434    else {
435       sw_printf("[%s:%u] - Did not find frame pointer in analysis\n",
436                 __FILE__, __LINE__);
437    }
438
439    return gcf_success;
440 }
441
442 namespace Dyninst {
443 namespace Stackwalker {
444 void getTrapInstruction(char *buffer, unsigned buf_size, 
445                         unsigned &actual_len, bool include_return)
446 {
447    if (include_return)
448    {
449       assert(buf_size >= 2);
450       buffer[0] = 0xcc; //trap
451       buffer[1] = 0xc3; //ret
452       actual_len = 2;
453       return;
454    }
455    assert(buf_size >= 1);
456    buffer[0] = 0xcc; //trap
457    actual_len = 1;
458    return;
459 }
460 }
461 }
462