GCC 4.8 build fixes: ensure all extern template declarations are in fact extern'ed...
[dyninst.git] / stackwalk / src / x86-wanderer.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/walker.h"
32 #include "stackwalk/h/frame.h"
33 #include "stackwalk/h/swk_errors.h"
34 #include "stackwalk/h/procstate.h"
35
36 #include "stackwalk/src/x86-swk.h"
37 #include "stackwalk/src/symtab-swk.h"
38 #include "stackwalk/src/libstate.h"
39 #include "stackwalk/src/sw.h"
40
41 #include "common/src/Types.h"
42
43 #include "common/h/SymReader.h"
44
45 using namespace Dyninst;
46 using namespace Stackwalker;
47
48
49 StepperWandererImpl::StepperWandererImpl(Walker *walker_,
50                                          StepperWanderer *parent_,
51                                          WandererHelper *whelper_,
52                                          FrameFuncHelper *fhelper_) :
53   FrameStepper(walker_),
54   whelper(whelper_),
55   fhelper(fhelper_),
56   parent(parent_)
57 {
58 }
59
60 StepperWandererImpl::~StepperWandererImpl()
61 {
62 }
63
64 unsigned StepperWandererImpl::getPriority() const
65 {
66    return wanderer_priority;
67 }
68
69 void StepperWandererImpl::registerStepperGroup(StepperGroup *group) {
70   FrameStepper::registerStepperGroup(group);
71 }
72
73 gcframe_ret_t StepperWandererImpl::getCallerFrame(const Frame &in, Frame &out)
74 {
75    sw_printf("[%s:%u] - Wanderer attempting to walk from 0x%lx\n",
76              FILE__, __LINE__, in.getRA());
77
78    const unsigned addr_width = getProcessState()->getAddressWidth();
79    std::vector<std::pair<Address, Address> > candidate;
80
81    Address current_stack = in.getSP();
82    unsigned num_words_tried = 0;
83    Address word;
84    bool result;
85    bool found_exact_match = false;
86    Address found_base = 0x0;
87    Address found_ra = 0x0;
88    FrameFuncHelper::alloc_frame_t alloc_res;
89
90    do {
91       result = getWord(word, current_stack);
92       if (!result) {
93          sw_printf("[%s:%u] - getWord returned false\n", FILE__, __LINE__);
94          return gcf_not_me;
95       }
96       
97       Address target;
98       if (whelper->isPrevInstrACall(word, target))
99       {
100         // in func = exact match
101         // outside func = reject
102         // unknown = candidate
103         WandererHelper::pc_state pcs = whelper->isPCInFunc(target, in.getRA());
104         switch (pcs)
105         {
106           case WandererHelper::outside_func:
107             // TODO re-enable this heuristic for Dyninst when the Analysis Stepper is working
108             if (whelper->requireExactMatch()) {  
109             sw_printf("[%s:%u] - Wanderer discarded word 0x%lx at 0x%lx\n",
110                       FILE__, __LINE__, word, current_stack);
111             // not a candidate
112             break;
113             }
114           case WandererHelper::unknown_s:
115             sw_printf("[%s:%u] - Wanderer added word 0x%lx at 0x%lx as candidate return "
116                       " address\n", FILE__, __LINE__, word, current_stack);
117             candidate.push_back(std::pair<Address, Address>(word, current_stack));
118             break;
119
120           case WandererHelper::in_func:
121             sw_printf("[%s:%u] - Wanderer thinks word 0x%lx at 0x%lx  is return "
122                       " address\n", FILE__, __LINE__, word, current_stack);
123             found_base = current_stack;
124             found_ra = word;
125             found_exact_match = true;
126             break;
127         }
128       }
129
130       if (found_exact_match) break;
131       current_stack += addr_width;
132       num_words_tried++;
133    } while (num_words_tried < MAX_WANDERER_DEPTH);
134
135    if (!found_exact_match && !candidate.size()) {
136       sw_printf("[%s:%u] - Wanderer couldn't find anything in %u words\n",
137                 FILE__, __LINE__, MAX_WANDERER_DEPTH);
138       return gcf_not_me;
139    }
140
141    if (!found_exact_match && candidate.size()) {
142       /**
143        * If we ever want to rely on candidates, then uncomment the above
144        * push_back and the below code.  This trades false negatives for
145        * potential false positives, but I'm not sure it's worth it.
146        **/
147       if (whelper->requireExactMatch())
148       {
149         return gcf_not_me;
150       }
151       else
152       {
153         found_ra = candidate[0].first;
154         found_base = candidate[0].second;
155       }
156    }
157
158    out.setRA(found_ra);
159    out.setSP(found_base + addr_width);
160
161    alloc_res = fhelper->allocatesFrame(in.getRA());
162    if (alloc_res.first == FrameFuncHelper::savefp_only_frame &&
163        alloc_res.second != FrameFuncHelper::unset_frame) {
164       Address new_fp;
165       result = getProcessState()->readMem(&new_fp, out.getSP(), 
166                                           getProcessState()->getAddressWidth());
167       if (!result) {
168          sw_printf("[%s:%u] - Error, couln't read from stack at %lx\n",
169                    FILE__, __LINE__, out.getSP());
170          return gcf_error;
171       }
172       out.setFP(new_fp);
173    }
174    else {
175       out.setFP(in.getFP());
176    }
177
178    return gcf_success;
179    
180 }
181
182 bool StepperWandererImpl::getWord(Address &word_out, Address start)
183 {
184    const unsigned addr_width = getProcessState()->getAddressWidth();
185    if (start < 1024) {
186       sw_printf("[%s:%u] - %lx too low to be valid memory\n",
187                 FILE__, __LINE__, start);
188       return false;
189    }
190    word_out = 0x0;
191    bool result = getProcessState()->readMem(&word_out, start, addr_width);
192    if (!result) {
193       sw_printf("[%s:%u] - Wanderer couldn't read from stack at 0x%lx\n",
194                 FILE__, __LINE__, start);
195       return false;
196    }
197
198    return true;
199 }
200
201 bool WandererHelper::isPrevInstrACall(Address addr, Address &target)
202 {
203     return callchecker->isPrevInstrACall(addr, target);   
204 }
205
206 WandererHelper::pc_state WandererHelper::isPCInFunc(Address func_entry, Address pc)
207 {
208    if (!func_entry || !pc)
209       return unknown_s;
210    if (func_entry == pc)
211       return in_func;
212
213    SymReader *reader = NULL;
214    LibAddrPair func_lib, pc_lib;
215    LibraryState *tracker = proc->getLibraryTracker();
216    Offset pc_offset, func_entry_offset, func_offset;
217    bool result;
218    Symbol_t func_symbol, pc_symbol;
219    Section_t section;
220
221    result = tracker->getLibraryAtAddr(func_entry, func_lib);
222    if (!result) {
223       sw_printf("[%s:%u] - Failed to find library at %lx\n",
224                 FILE__, __LINE__, func_entry);
225       return unknown_s;
226    }
227    func_entry_offset = func_entry - func_lib.second;
228
229    reader = LibraryWrapper::getLibrary(func_lib.first);
230    if (!reader) {
231       sw_printf("[%s:%u] - Failed to open reader for %s\n", 
232                 FILE__, __LINE__, func_lib.first.c_str());
233       goto reader_fail;
234    }
235    
236    section = reader->getSectionByAddress(func_entry_offset);
237    if (reader->getSectionName(section) == std::string(".plt")) {
238       sw_printf("[%s:%u] - %lx is a PLT entry, trying to map to real target\n",
239                 FILE__, __LINE__, func_entry);
240       int got_offset = -1;
241       Address got_abs = 0x0;
242       if (proc->getAddressWidth() == 4) {
243          //32-bit mode.  Recognize common PLT idioms
244          #define MAX_PLT32_IDIOM_SIZE 6
245          unsigned char buffer[MAX_PLT32_IDIOM_SIZE];
246          result = proc->readMem(buffer, func_entry, MAX_PLT32_IDIOM_SIZE);
247          if (buffer[0] == 0xff && buffer[1] == 0xa3) {
248             //Indirect jump off of ebx
249             got_offset = *((int32_t*) (buffer+2));
250          }
251          else if (buffer[0] == 0xff && buffer[1] == 0x25) {
252             //Indirect jump through absolute
253             got_abs = *((uint32_t*) (buffer+2));
254          }
255          else {
256             sw_printf("[%s:%u] - Unrecognized PLT idiom at %lx: ",
257                       FILE__, __LINE__, func_entry);
258             for (unsigned i=0; i<MAX_PLT32_IDIOM_SIZE; i++) {
259                sw_printf("%x ", buffer[i]);
260             }
261             sw_printf("\n");
262          }
263       }
264       if (proc->getAddressWidth() == 8) {
265          //32-bit mode.  Recognize common PLT idioms
266 #define MAX_PLT64_IDIOM_SIZE 6
267          unsigned char buffer[MAX_PLT64_IDIOM_SIZE];
268          result = proc->readMem(buffer, func_entry, MAX_PLT64_IDIOM_SIZE);
269          if (buffer[0] == 0xff && buffer[1] == 0x25) {
270             //PC Relative jump indirect
271             got_abs = *((int32_t *) (buffer+2)) + func_entry + 6;
272          }
273          else {
274             sw_printf("[%s:%u] - Unrecognized PLT idiom at %lx: ",
275                       FILE__, __LINE__, func_entry);
276             for (unsigned i=0; i<MAX_PLT32_IDIOM_SIZE; i++) {
277                sw_printf("%x ", buffer[i]);
278             }
279             sw_printf("\n");
280          }
281       }
282       
283       if (got_offset != -1) {
284          sw_printf("[%s:%u] - Computed PLT to be going through GOT offset %d\n",
285                    FILE__, __LINE__, got_offset);
286          Section_t got_section = reader->getSectionByName(".got");
287          if (reader->isValidSection(got_section)) {
288             got_abs = got_offset + reader->getSectionAddress(got_section) + 
289                       func_lib.second;
290          }
291       }
292
293       if (got_abs) {
294          Address real_target;
295          result = proc->readMem(&real_target, got_abs, proc->getAddressWidth());
296          sw_printf("[%s:%u] - Computed PLT to be going through GOT abs %lx to "
297                    "real target %lx\n", FILE__, __LINE__, got_abs, real_target);
298          return isPCInFunc(real_target, pc);
299       }
300
301    }
302
303    result = tracker->getLibraryAtAddr(pc, pc_lib);
304    if (!result) {
305       sw_printf("[%s:%u] - Failed to find library at %lx\n",
306                 FILE__, __LINE__, pc);
307       return unknown_s;
308    }
309    pc_offset = pc - pc_lib.second;
310
311    if (func_entry > pc) {
312       sw_printf("[%s:%u] - func_entry %lx is greater than pc %lx\n",
313                 FILE__, __LINE__, func_entry, pc);
314       return outside_func;
315    }
316    
317    if (pc_lib != func_lib)
318    {
319       sw_printf("[%s:%u] - %lx and %lx are from different libraries\n",
320                 FILE__, __LINE__, func_entry, pc);
321       return outside_func;
322    }
323
324    func_symbol = reader->getContainingSymbol(func_entry_offset);
325    if (!reader->isValidSymbol(func_symbol)) {
326       sw_printf("[%s:%u] - No functions begin at %lx\n", 
327                 FILE__, __LINE__, func_entry_offset);
328       goto reader_fail;
329    }
330
331    pc_symbol = reader->getContainingSymbol(pc_offset);
332    if (!reader->isValidSymbol(pc_symbol)) {
333       sw_printf("[%s:%u] - No functions begin at %lx\n", 
334                 FILE__, __LINE__, func_entry_offset);
335       goto reader_fail;
336    }
337
338    func_offset = reader->getSymbolOffset(func_symbol);
339    pc_offset = reader->getSymbolOffset(pc_symbol);
340    sw_printf("[%s:%u] - Decided func at offset %lx and pc-func at offset %lx\n",
341              FILE__, __LINE__, func_offset, pc_offset);
342    if (func_offset == pc_offset)
343    {
344      return in_func;
345    }
346    else
347    {
348      return unknown_s;
349    }
350  reader_fail:   
351
352    //We don't have much to work with.  This is all heuristics anyway, so assume
353    // that if pc is within 8k of func_entry that it's a part of the function.
354    if (pc >= func_entry && pc < func_entry + 8192)
355    {
356      return in_func;
357    }
358    else
359    {
360      return unknown_s;
361    }
362 }
363
364 bool WandererHelper::requireExactMatch()
365 {
366   // By default, require that candidate callsites actually call to the
367   // current function
368   return true;
369 }
370
371 WandererHelper::WandererHelper(ProcessState *proc_) :
372    proc(proc_)
373 {
374     callchecker = new CallChecker(proc_);   
375 }
376
377 WandererHelper::~WandererHelper()
378 {
379     delete callchecker;
380 }