use compiler intrinsics on x64
[dyninst.git] / stackwalk / src / x86-swk.C
1 /*
2  * See the dyninst/COPYRIGHT file for copyright information.
3  * 
4  * We provide the Paradyn Tools (below described as "Paradyn")
5  * on an AS IS basis, and do not warrant its validity or performance.
6  * We reserve the right to update, modify, or discontinue this
7  * software at any time.  We shall have no obligation to supply such
8  * updates or modifications or any other form of support to you.
9  * 
10  * By your use of Paradyn, you understand and agree that we (or any
11  * other person or entity with proprietary rights in Paradyn) are
12  * under no obligation to provide either maintenance services,
13  * update services, notices of latent defects, or correction of
14  * defects for Paradyn.
15  * 
16  * This library is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU Lesser General Public
18  * License as published by the Free Software Foundation; either
19  * version 2.1 of the License, or (at your option) any later version.
20  * 
21  * This library is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24  * Lesser General Public License for more details.
25  * 
26  * You should have received a copy of the GNU Lesser General Public
27  * License along with this library; if not, write to the Free Software
28  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
29  */
30
31 #include "stackwalk/h/basetypes.h"
32 #include "stackwalk/h/swk_errors.h"
33 #include "stackwalk/h/procstate.h"
34 #include "stackwalk/h/framestepper.h"
35 #include "stackwalk/h/frame.h"
36 #include "stackwalk/h/walker.h"
37
38 #include "stackwalk/src/symtab-swk.h"
39 #include "stackwalk/src/dbgstepper-impl.h"
40 #include "stackwalk/src/x86-swk.h"
41 #include "stackwalk/src/sw.h"
42 #include "stackwalk/src/libstate.h"
43
44 #include "common/src/lru_cache.h"
45
46 #include "common/h/SymReader.h"
47
48 using namespace Dyninst;
49 using namespace Dyninst::Stackwalker;
50
51 static volatile int always_zero = 0;
52
53 bool ProcSelf::getRegValue(Dyninst::MachRegister reg, THR_ID, Dyninst::MachRegisterVal &val)
54 {
55   unsigned long *frame_pointer = NULL;
56
57   if (always_zero) {
58      //Generate some (skipped) code involving frame_pointer before
59      // the assembly snippet.  This keeps gcc from optimizing 
60      // the below snippet by pulling it up into the function prolog.
61      sw_printf("%p%p\n", frame_pointer, &frame_pointer);
62   }
63
64 #if defined(arch_x86_64) && (defined(os_linux) || defined(os_freebsd))
65   __asm__("mov %%rbp, %0\n"
66           : "=r"(frame_pointer));
67 #elif defined(os_linux) || defined(os_freebsd)
68   __asm__("movl %%ebp, %0\n"
69           : "=r"(frame_pointer));
70 #elif defined(os_windows)
71 #ifdef _WIN64
72   assert(0); // FIXME WIN64-PORTING: Figure out a solution without inline asm, perhaps StackWalk64()?
73 #else
74    __asm
75    {
76       mov frame_pointer, ebp ;
77    }
78 #endif
79 #endif
80
81   frame_pointer = (unsigned long *) *frame_pointer;
82   
83   switch(reg.val())
84   {
85      case Dyninst::x86_64::irip:
86      case Dyninst::x86::ieip:
87      case Dyninst::iReturnAddr:
88         val = (Dyninst::MachRegisterVal) frame_pointer[1];
89         break;
90      case Dyninst::iFrameBase:
91         val = (Dyninst::MachRegisterVal) frame_pointer[0];
92         break;
93      case Dyninst::x86_64::irsp:
94      case Dyninst::x86::iesp:
95      case Dyninst::iStackTop:
96         val = (Dyninst::MachRegisterVal) (frame_pointer + 2);
97         break;      
98      default:
99         sw_printf("[%s:%u] - Request for unsupported register %s\n",
100                   FILE__, __LINE__, reg.name().c_str());
101         setLastError(err_badparam, "Unknown register passed in reg field");
102   }
103
104   return true;
105 }
106
107 Dyninst::Architecture ProcSelf::getArchitecture()
108 {
109    if (sizeof(void *) == 8)
110       return Arch_x86_64;
111    return Arch_x86;
112 }
113
114 static gcframe_ret_t HandleStandardFrame(const Frame &in, Frame &out, ProcessState *proc)
115 {
116   Address in_fp, out_sp;
117   const unsigned addr_width = proc->getAddressWidth();
118   bool result;
119
120   struct {
121     Address out_fp;
122     Address out_ra;
123   } ra_fp_pair;
124
125   in_fp = in.getFP();
126   out_sp = in_fp + (2 * addr_width);
127
128 #if defined(arch_x86_64)
129   /**
130    * On AMD64 we may be reading from a process with a different
131    * address width than the current one.  We'll do the read at
132    * the correct size, then convert the addresses into the 
133    * local size
134    **/
135   struct {
136      unsigned out_fp;
137      unsigned out_ra;
138   } ra_fp_pair32;
139   if (addr_width != sizeof(Address))
140   {
141      result = proc->readMem(&ra_fp_pair32, in_fp, 
142                             sizeof(ra_fp_pair32));
143      ra_fp_pair.out_fp = (Address) ra_fp_pair32.out_fp;
144      ra_fp_pair.out_ra = (Address) ra_fp_pair32.out_ra;
145   }
146   else
147 #endif
148   {
149     result = proc->readMem(&ra_fp_pair, in_fp, sizeof(ra_fp_pair));
150   }
151
152   if (!result) {
153     sw_printf("[%s:%u] - Couldn't read from %lx\n", FILE__, __LINE__, in_fp);
154     return gcf_error;
155   }
156   
157   if (!ra_fp_pair.out_ra) {
158      return gcf_not_me;
159   }
160
161   location_t ra_loc, fp_loc;
162   ra_loc.location = loc_address;
163   fp_loc.location = loc_address;
164   ra_loc.val.addr = in_fp + addr_width;
165   fp_loc.val.addr = in_fp + addr_width*2;
166
167   out.setFP(ra_fp_pair.out_fp);
168   out.setRA(ra_fp_pair.out_ra);
169   out.setSP(out_sp);
170   out.setFPLocation(fp_loc);
171   out.setRALocation(ra_loc);
172
173   return gcf_success;
174 }
175
176 bool Walker::checkValidFrame(const Frame &in, const Frame &out)
177 {
178    if (out.getSP() <= in.getSP() && out.getSPLocation().location != loc_unknown) {
179       sw_printf("[%s:%u] - Stackwalk went backwards, %lx to %lx\n",
180                 FILE__, __LINE__, in.getSP(), out.getSP());
181       return false;
182    }
183    return true;
184 }
185
186 gcframe_ret_t FrameFuncStepperImpl::getCallerFrame(const Frame &in, Frame &out)
187 {
188   if (!in.getFP())
189      return gcf_not_me;
190
191   FrameFuncHelper::alloc_frame_t frame = helper->allocatesFrame(in.getRA());
192   if (frame.first != FrameFuncHelper::standard_frame) {
193      return gcf_not_me;
194   }
195
196   return HandleStandardFrame(in, out, getProcessState());
197 }
198  
199 std::map<Dyninst::PID, LookupFuncStart*> LookupFuncStart::all_func_starts;
200
201 static int hash_address(Address a)
202 {
203    return (int) a;
204 }
205
206 LookupFuncStart::LookupFuncStart(ProcessState *proc_) :
207    FrameFuncHelper(proc_),
208    cache(cache_size, hash_address)
209 {
210    all_func_starts[proc->getProcessId()] = this;
211    ref_count = 1;
212 }
213
214 LookupFuncStart::~LookupFuncStart()
215 {
216    Dyninst::PID pid = proc->getProcessId();
217    all_func_starts.erase(pid);
218 }
219
220 LookupFuncStart *LookupFuncStart::getLookupFuncStart(ProcessState *p)
221 {
222    Dyninst::PID pid = p->getProcessId();
223    std::map<Dyninst::PID, LookupFuncStart*>::iterator i = all_func_starts.find(pid);
224    if (i == all_func_starts.end()) {
225       return new LookupFuncStart(p);
226    }
227    (*i).second->ref_count++;
228    return (*i).second;
229 }
230
231 void LookupFuncStart::releaseMe()
232 {
233    ref_count--;
234    if (!ref_count)
235       delete this;
236 }
237
238 FrameFuncStepperImpl::FrameFuncStepperImpl(Walker *w, FrameStepper *parent_,
239                                            FrameFuncHelper *helper_) :
240    FrameStepper(w),
241    parent(parent_),
242    helper(helper_)
243 {
244    helper = helper_ ? helper_ : LookupFuncStart::getLookupFuncStart(getProcessState());
245 }
246
247 FrameFuncStepperImpl::~FrameFuncStepperImpl()
248 {
249    LookupFuncStart *lookup = dynamic_cast<LookupFuncStart*>(helper);
250    if (lookup)
251       lookup->releaseMe();
252    else if (helper)
253       delete helper;
254 }
255
256 unsigned FrameFuncStepperImpl::getPriority() const
257 {
258   return frame_priority;
259 }
260
261 /**
262  * Look at the first few bytes in the function and see if they contain
263  * the standard set to allocate a stack frame.
264  **/
265 #define FUNCTION_PROLOG_TOCHECK 16
266 static unsigned char push_ebp = 0x55;
267 static unsigned char mov_esp_ebp[2][2] = { { 0x89, 0xe5 },
268                                            { 0x8b, 0xec } };
269 static unsigned char rex_mov_esp_ebp = 0x48;
270
271 FrameFuncHelper::alloc_frame_t LookupFuncStart::allocatesFrame(Address addr)
272 {
273    LibAddrPair lib;
274    unsigned char mem[FUNCTION_PROLOG_TOCHECK];
275    Address func_addr;
276    unsigned cur;
277    int push_ebp_pos = -1, mov_esp_ebp_pos = -1;
278    alloc_frame_t res = alloc_frame_t(unknown_t, unknown_s);
279    bool result;
280    SymReader *reader;
281    Offset off;
282    Symbol_t sym;
283
284    result = checkCache(addr, res);
285    if (result) {
286       sw_printf("[%s:%u] - Cached value for %lx is %d/%d\n",
287                 FILE__, __LINE__, addr, (int) res.first, (int) res.second);
288       return res;
289    }
290
291    result = proc->getLibraryTracker()->getLibraryAtAddr(addr, lib);
292    if (!result)
293    {
294       sw_printf("[%s:%u] - No library at %lx\n", FILE__, __LINE__, addr);
295       goto done;
296    }
297
298    reader = LibraryWrapper::getLibrary(lib.first);
299    if (!reader) {
300       sw_printf("[%s:%u] - Failed to open symbol reader %s\n",
301                 FILE__, __LINE__, lib.first.c_str() );
302       goto done;
303    }   
304    off = addr - lib.second;
305    sym = reader->getContainingSymbol(off);
306    if (!reader->isValidSymbol(sym)) {
307       sw_printf("[%s:%u] - Could not find symbol in binary\n", FILE__, __LINE__);
308       goto done;
309    }
310    func_addr = reader->getSymbolOffset(sym) + lib.second;
311
312    result = proc->readMem(mem, func_addr, FUNCTION_PROLOG_TOCHECK);
313    if (!result) {
314       sw_printf("[%s:%u] - Error.  Couldn't read from memory at %lx\n",
315                 FILE__, __LINE__, func_addr);
316       goto done;
317    }
318    
319    //Try to find a 'push (r|e)bp'
320    for (cur=0; cur<FUNCTION_PROLOG_TOCHECK; cur++)
321    {
322       if (mem[cur] == push_ebp)
323       {
324          push_ebp_pos = cur;
325          break;
326       }
327    }
328
329    //Try to find the mov esp->ebp
330    for (; cur<FUNCTION_PROLOG_TOCHECK; cur++)
331    {
332       if (proc->getAddressWidth() == 8) {
333          if (mem[cur] != rex_mov_esp_ebp)
334             continue;
335          cur++;
336       }
337       
338       if (cur+1 >= FUNCTION_PROLOG_TOCHECK) 
339          break;
340       if ((mem[cur] == mov_esp_ebp[0][0] && mem[cur+1] == mov_esp_ebp[0][1]) || 
341           (mem[cur] == mov_esp_ebp[1][0] && mem[cur+1] == mov_esp_ebp[1][1])) {
342          if (proc->getAddressWidth() == 8) 
343             mov_esp_ebp_pos = cur-1;
344          else
345             mov_esp_ebp_pos = cur;
346          break;
347       }
348    }
349
350    if ((push_ebp_pos != -1) && (mov_esp_ebp_pos != -1))
351       res.first = standard_frame;
352    else if ((push_ebp_pos != -1) && (mov_esp_ebp_pos == -1))
353       res.first = savefp_only_frame;
354    else 
355       res.first = no_frame;
356    
357    if ((push_ebp_pos != -1) && (addr <= func_addr + push_ebp_pos))
358       res.second = unset_frame;
359    else if ((mov_esp_ebp_pos != -1) && (addr <= func_addr + mov_esp_ebp_pos))
360       res.second = halfset_frame;
361    else
362       res.second = set_frame;
363
364  done:
365    sw_printf("[%s:%u] - Function containing %lx has frame type %d/%d\n",
366              FILE__, __LINE__, addr, (int) res.first, (int) res.second);
367    updateCache(addr, res);
368    return res;
369 }
370
371 void LookupFuncStart::updateCache(Address addr, alloc_frame_t result)
372 {
373    cache.insert(addr, result);
374 }
375
376 bool LookupFuncStart::checkCache(Address addr, alloc_frame_t &result)
377 {
378    return cache.lookup(addr, result);
379 }
380
381 void LookupFuncStart::clear_func_mapping(Dyninst::PID pid)
382 {
383    std::map<Dyninst::PID, LookupFuncStart *>::iterator i;
384    i = all_func_starts.find(pid);
385    if (i == all_func_starts.end())
386       return;
387
388    LookupFuncStart *fs = (*i).second;
389    all_func_starts.erase(i);
390    
391    delete fs;
392 }
393
394 gcframe_ret_t DyninstInstrStepperImpl::getCallerFrameArch(const Frame &in, Frame &out, 
395                                                           Address /*base*/, Address lib_base,
396                                                           unsigned /*size*/, unsigned stack_height)
397 {
398   gcframe_ret_t ret = HandleStandardFrame(in, out, getProcessState());
399   if (ret != gcf_success)
400     return ret;
401   out.setRA(out.getRA() + lib_base);
402   out.setSP(out.getSP() + stack_height);
403   return gcf_success;
404 }
405
406
407 gcframe_ret_t DyninstDynamicStepperImpl::getCallerFrameArch(const Frame &in, Frame &out, 
408                                                             Address /*base*/, Address lib_base,
409                                                             unsigned /*size*/, unsigned stack_height,
410                                                             bool aligned,
411                                                             Address orig_ra, bool pEntryExit)
412 {
413   bool result = false;
414   const unsigned addr_width = getProcessState()->getAddressWidth();
415   unsigned long sp_value = 0x0;
416   Address sp_addr = 0x0;
417
418   sw_printf("[%s:%u] - DyninstDynamicStepper with lib_base 0x%lx, stack-height %d, orig_ra 0x%lx, aligned %d %s\n",
419             FILE__, __LINE__, lib_base, stack_height, orig_ra, aligned, pEntryExit ? "<entry/exit>" : "<normal>");
420   sw_printf("[%s:%u] - incoming frame has RA 0x%lx, SP 0x%lx, FP 0x%lx\n",
421             FILE__, __LINE__, in.getRA(), in.getSP(), in.getFP());
422   // Handle frameless instrumentation
423   if (0x0 != orig_ra)
424   {
425     location_t unknownLocation;
426     unknownLocation.location = loc_unknown;
427     out.setRA(orig_ra);
428     out.setFP(in.getFP());
429     out.setSP(in.getSP()); //Not really correct, but difficult to compute and unlikely to matter
430     out.setRALocation(unknownLocation);
431     sw_printf("[%s:%u] - DyninstDynamicStepper handled frameless instrumentation\n",
432               FILE__, __LINE__);
433     return gcf_success;
434   }
435
436
437   // Handle case where *previous* frame was entry/exit instrumentation
438   if (pEntryExit)
439   {
440     Address ra_value = 0x0;
441
442     // RA is pointed to by input SP
443     // TODO may have an additional offset in some cases...
444     Address newRAAddr = in.getSP();
445
446     location_t raLocation;
447     raLocation.location = loc_address;
448     raLocation.val.addr = newRAAddr;
449     out.setRALocation(raLocation);
450
451     // TODO handle 64-bit mutator / 32-bit mutatee
452
453     // get value of RA
454     result = getProcessState()->readMem(&ra_value, newRAAddr, addr_width);
455
456     if (!result) {
457       sw_printf("[%s:%u] - Couldn't read from %lx\n", FILE__, __LINE__, newRAAddr);
458       return gcf_error;
459     }
460
461     out.setRA(ra_value);
462     out.setFP(in.getFP()); // FP stays the same
463     out.setSP(newRAAddr + addr_width);
464     sw_printf("[%s:%u] - DyninstDynamicStepper handled post entry/exit instrumentation\n",
465               FILE__, __LINE__);
466     return gcf_success;
467   }
468
469   gcframe_ret_t ret = HandleStandardFrame(in, out, getProcessState());
470   if (ret != gcf_success)
471     return ret;
472   out.setRA(out.getRA() + lib_base);
473
474   // For tramps with frames, read the saved stack pointer
475   // TODO does this apply to static instrumentation?
476   if (stack_height)
477   {
478     sp_addr = in.getFP() + stack_height;
479     result = getProcessState()->readMem(&sp_value, sp_addr, addr_width);
480
481     if (!result) {
482       sw_printf("[%s:%u] - Couldn't read from %lx\n", FILE__, __LINE__, sp_addr);
483       return gcf_error;
484     }
485
486     sw_printf("[%s:%u] - Read SP %p from addr %p, using stack height of 0x%lx\n",
487               FILE__, __LINE__, sp_value, sp_addr, stack_height);
488     out.setSP(sp_value);
489   }
490
491   sw_printf("[%s:%u] - DyninstDynamicStepper handled normal instrumentation\n",
492             FILE__, __LINE__);
493   return gcf_success;
494 }
495
496 namespace Dyninst {
497 namespace Stackwalker {
498 void getTrapInstruction(char *buffer, unsigned buf_size, 
499                         unsigned &actual_len, bool include_return)
500 {
501    if (include_return)
502    {
503       assert(buf_size >= 2);
504       buffer[0] = (char) 0xcc; //trap
505       buffer[1] = (char) 0xc3; //ret
506       actual_len = 2;
507       return;
508    }
509    assert(buf_size >= 1);
510    buffer[0] = (char) 0xcc; //trap
511    actual_len = 1;
512    return;
513 }
514 }
515 }
516