Merge branch 'master' of git.dyninst.org:/pub/dyninst
[dyninst.git] / stackwalk / src / dbginfo-stepper.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/framestepper.h"
32 #include "stackwalk/h/frame.h"
33 #include "stackwalk/h/procstate.h"
34 #include "stackwalk/h/swk_errors.h"
35 #include "stackwalk/h/steppergroup.h"
36 #include "stackwalk/h/walker.h"
37 #include "stackwalk/src/dbgstepper-impl.h"
38 #include "stackwalk/src/linuxbsd-swk.h"
39 #include "stackwalk/src/libstate.h"
40 #include "common/h/dyntypes.h"
41 #include "common/h/VariableLocation.h"
42 #include "common/src/Types.h"
43 #include "dwarf/h/dwarfFrameParser.h"
44 #include "dwarf/h/dwarfHandle.h"
45
46 #include "symtabAPI/h/Symtab.h"
47
48 using namespace Dyninst;
49 using namespace Stackwalker;
50 using namespace Dwarf;
51
52 static std::map<std::string, DwarfFrameParser::Ptr> dwarf_info;
53
54 #include <stdarg.h>
55 #include "dwarf.h"
56 #include "libdwarf.h"
57 #include "elf/h/Elf_X.h"
58
59 static DwarfFrameParser::Ptr getAuxDwarfInfo(std::string s)
60 {
61    static std::map<std::string, DwarfFrameParser::Ptr > dwarf_aux_info;
62    
63    std::map<std::string, DwarfFrameParser::Ptr >::iterator i = dwarf_aux_info.find(s);
64    if (i != dwarf_aux_info.end())
65       return i->second;
66    
67    SymReader *orig_reader = LibraryWrapper::getLibrary(s);
68    if (!orig_reader) {
69       sw_printf("[%s:%u] - Error.  Could not find elf handle for %s\n",
70                 FILE__, __LINE__, s.c_str());
71       return DwarfFrameParser::Ptr();
72    }
73    Elf_X *orig_elf = (Elf_X *) orig_reader->getElfHandle();
74    if (!orig_elf) {
75       sw_printf("[%s:%u] - Error. Could not find elf handle for file %s\n",
76                 FILE__, __LINE__, s.c_str());
77       dwarf_aux_info[s] = DwarfFrameParser::Ptr();
78       return DwarfFrameParser::Ptr();
79    }
80
81    DwarfHandle::ptr dwarf = DwarfHandle::createDwarfHandle(s, orig_elf);
82    assert(dwarf);
83    
84    // FIXME for ppc, if we ever support debug walking on ppc
85    Architecture arch;
86    if (orig_elf->wordSize() == 4)
87       arch = Dyninst::Arch_x86;
88    else
89       arch = Dyninst::Arch_x86_64;
90
91    DwarfFrameParser::Ptr dresult = DwarfFrameParser::create(*dwarf->frame_dbg(), arch);
92    dwarf_aux_info[s] = dresult;
93    return dresult;
94 }
95
96
97 DebugStepperImpl::DebugStepperImpl(Walker *w, DebugStepper *parent) :
98    FrameStepper(w),
99    last_addr_read(0),
100    last_val_read(0),
101    addr_width(0),
102    parent_stepper(parent),
103    cur_frame(NULL),
104    depth_frame(NULL)
105 {
106 }
107
108 bool DebugStepperImpl::ReadMem(Address addr, void *buffer, unsigned size)
109 {
110    bool result = getProcessState()->readMem(buffer, addr, size);
111    
112    last_addr_read = 0;
113    if (!result)
114       return false;
115    if (size != addr_width)
116       return false;
117    
118    last_addr_read = addr;
119    if (addr_width == 4) {
120       uint32_t v = *((uint32_t *) buffer);
121       last_val_read = v;
122    }
123    else if (addr_width == 8) {
124       uint64_t v = *((uint64_t *) buffer);
125       last_val_read = v;
126    }
127    else {
128       assert(0); //Unknown size
129    }
130
131    return true;
132 }
133
134 location_t DebugStepperImpl::getLastComputedLocation(unsigned long value)
135 {
136    location_t loc;
137    if (last_addr_read && last_val_read == value) {
138       loc.val.addr = last_addr_read;
139       loc.location = loc_address;
140    }
141    else {
142       loc.val.addr = 0;
143       loc.location = loc_unknown;
144    }
145    last_addr_read = 0;
146    last_val_read = 0;
147    return loc;
148 }
149
150 bool DebugStepperImpl::GetReg(MachRegister reg, MachRegisterVal &val)
151 {
152    using namespace SymtabAPI;
153    
154    const Frame *prevDepthFrame = depth_frame;
155   
156    if (reg.isFramePointer()) {
157       val = static_cast<MachRegisterVal>(depth_frame->getFP());
158       return true;
159    }
160
161    if (reg.isStackPointer()) {
162       val = static_cast<MachRegisterVal>(depth_frame->getSP());
163       return true;
164    }
165    
166    if (reg.isPC()) {
167       val = static_cast<MachRegisterVal>(depth_frame->getRA());
168       return true;
169    }
170
171    depth_frame = depth_frame->getPrevFrame();
172    if (!depth_frame)
173    {
174       bool bres =  getProcessState()->getRegValue(reg, cur_frame->getThread(), val);
175       depth_frame = prevDepthFrame;
176       return bres;
177    }
178
179    Offset offset;
180    void *symtab_v = NULL;
181    std::string lib;
182    depth_frame->getLibOffset(lib, offset, symtab_v);
183    Symtab *symtab = (Symtab*) symtab_v;
184    if (!symtab)
185    {
186      depth_frame = prevDepthFrame;
187      return false;
188    }
189
190    bool result = symtab->getRegValueAtFrame(offset, reg, val, this);
191    depth_frame = prevDepthFrame;
192    return result;
193 }
194
195 gcframe_ret_t DebugStepperImpl::getCallerFrame(const Frame &in, Frame &out)
196 {
197    LibAddrPair lib;
198    bool result;
199
200    if (lookupInCache(in, out)) {
201        result = getProcessState()->getLibraryTracker()->getLibraryAtAddr(out.getRA(), lib);
202        if (result) {
203            // Hit, and valid RA found
204            return gcf_success;
205        }
206    }
207
208    // This error check is duplicated in BottomOfStackStepper.
209    // We should always call BOSStepper first; however, we need the
210    // library for the debug stepper as well. If this becomes
211    // a performance problem we can cache the library info in
212    // the input frame.
213    result = getProcessState()->getLibraryTracker()->getLibraryAtAddr(in.getRA(), lib);
214    if (!result) {
215       sw_printf("[%s:%u] - Stackwalking through an invalid PC at %lx\n",
216                 FILE__, __LINE__, in.getRA());
217       return gcf_error;
218    }
219    Address pc = in.getRA() - lib.second;
220    sw_printf("[%s:%u] Dwarf-based stackwalking, using local address 0x%lx from 0x%lx - 0x%lx\n",
221              FILE__, __LINE__, pc, in.getRA(), lib.second);
222    if (in.getRALocation().location != loc_register && !in.nonCall()) {
223       /**
224        * If we're here, then our in.getRA() should be pointed at the
225        * instruction following a call.  We could either use the
226        * call instruction's debug info (pc - 1) or the following
227        * instruction's debug info (pc) to continue the stackwalk.
228        *
229        * In most cases it doesn't matter.  Because of how DWARF debug
230        * info is defined, the stack doesn't change between these two points.
231        *
232        * However, if the call is a non-returning call (e.g, a call to exit)
233        * then the next instruction may not exist or may be part of a separate
234        * block with different debug info.  In these cases we want to use the
235        * debug info associated with the call.  So, we subtract 1 from the
236        * pc to get at the call instruction.
237        **/
238       pc = pc - 1;
239    }
240
241    /**
242     * Some system libraries on some systems have their debug info split
243     * into separate files, usually in /usr/lib/debug/.  Check these 
244     * for DWARF debug info
245     **/
246    DwarfFrameParser::Ptr dauxinfo = getAuxDwarfInfo(lib.first);
247    if (!dauxinfo || !dauxinfo->hasFrameDebugInfo()) {
248       sw_printf("[%s:%u] - Library %s does not have stackwalking debug info\n",
249                  FILE__, __LINE__, lib.first.c_str());
250       return gcf_not_me;
251    }
252
253    bool isVsyscallPage = false;
254 #if defined(os_linux)
255    isVsyscallPage = (strstr(lib.first.c_str(), "[vsyscall-") != NULL);
256 #endif
257
258    sw_printf("[%s:%u] - Using DWARF debug file info for %s\n", 
259                    FILE__, __LINE__, lib.first.c_str());
260    cur_frame = &in;
261    gcframe_ret_t gcresult = getCallerFrameArch(pc, in, out, dauxinfo, isVsyscallPage);
262    cur_frame = NULL;
263    
264    result = getProcessState()->getLibraryTracker()->getLibraryAtAddr(out.getRA(), lib);
265    if (!result) return gcf_not_me;
266    
267    if (gcresult == gcf_success) {
268       sw_printf("[%s:%u] - Success walking with DWARF aux file\n",
269                 FILE__, __LINE__);
270       return gcf_success;
271    }
272    
273    return gcresult;
274 }
275
276 void DebugStepperImpl::registerStepperGroup(StepperGroup *group)
277 {
278    addr_width = group->getWalker()->getProcessState()->getAddressWidth();
279    if (addr_width == 4)
280       group->addStepper(parent_stepper, 0, 0xffffffff);
281 #if defined(arch_64bit)
282    else if (addr_width == 8)
283       group->addStepper(parent_stepper, 0, 0xffffffffffffffff);
284 #endif
285    else
286       assert(0 && "Unknown architecture word size");
287 }
288
289 unsigned DebugStepperImpl::getPriority() const
290 {
291    return debugstepper_priority;
292 }
293
294 DebugStepperImpl::~DebugStepperImpl()
295 {
296 }
297
298 #if defined(arch_x86) || defined(arch_x86_64)
299 gcframe_ret_t DebugStepperImpl::getCallerFrameArch(Address pc, const Frame &in, 
300                                                    Frame &out, DwarfFrameParser::Ptr dinfo,
301                                                    bool isVsyscallPage)
302 {
303    MachRegisterVal frame_value, stack_value, ret_value;
304    bool result;
305    FrameErrors_t frame_error = FE_No_Error;
306
307    addr_width = getProcessState()->getAddressWidth();
308
309    depth_frame = cur_frame;
310
311    result = dinfo->getRegValueAtFrame(pc, Dyninst::ReturnAddr,
312                                       ret_value, this, frame_error);
313
314    if (!result && frame_error == FE_No_Frame_Entry && isVsyscallPage) {
315       //Work-around kernel bug.  The vsyscall page location was randomized, but
316       // the debug info still has addresses from the old, pre-randomized days.
317       // See if we get any hits by assuming the address corresponds to the 
318       // old PC.
319       pc += 0xffffe000;
320       result = dinfo->getRegValueAtFrame(pc, Dyninst::ReturnAddr,
321                                          ret_value, this, frame_error);
322    }
323    if (!result) {
324       sw_printf("[%s:%u] - Couldn't get return debug info at %lx, error: %u\n",
325                 FILE__, __LINE__, in.getRA(), frame_error);
326       return gcf_not_me;
327    }
328    location_t ra_loc = getLastComputedLocation(ret_value);
329    
330    Dyninst::MachRegister frame_reg;
331    if (addr_width == 4)
332       frame_reg = x86::ebp;
333    else
334       frame_reg = x86_64::rbp;
335
336    result = dinfo->getRegValueAtFrame(pc, frame_reg,
337                                       frame_value, this, frame_error);
338    if (!result) {
339       sw_printf("[%s:%u] - Couldn't get frame debug info at %lx\n",
340                  FILE__, __LINE__, in.getRA());
341       return gcf_not_me;
342    }
343    location_t fp_loc = getLastComputedLocation(frame_value);
344
345    result = dinfo->getRegValueAtFrame(pc, Dyninst::FrameBase,
346                                       stack_value, this, frame_error);
347    if (!result) {
348       sw_printf("[%s:%u] - Couldn't get stack debug info at %lx\n",
349                  FILE__, __LINE__, in.getRA());
350       return gcf_not_me;
351    }
352    location_t sp_loc = getLastComputedLocation(stack_value);   
353
354    if (isVsyscallPage) {
355       // RHEL6 has broken DWARF in the vsyscallpage; it has
356       // a double deref for the stack pointer. We detect this
357       // (as much as we can...) and ignore it
358       if (stack_value < in.getSP()) {
359          stack_value = 0;
360          sp_loc.location = loc_unknown;
361       }
362    }
363     
364    Address MAX_ADDR;
365    if (addr_width == 4) {
366        MAX_ADDR = 0xffffffff;
367    } 
368 #if defined(arch_64bit)
369    else if (addr_width == 8){
370        MAX_ADDR = 0xffffffffffffffff;
371    }
372 #endif 
373    else {
374        assert(0 && "Unknown architecture word size");
375    }
376
377    if(ra_loc.val.addr > MAX_ADDR || fp_loc.val.addr > MAX_ADDR || sp_loc.val.addr > MAX_ADDR) return gcf_not_me;
378
379    out.setRA(ret_value);
380    out.setFP(frame_value);
381    out.setSP(stack_value);
382    out.setRALocation(ra_loc);
383    out.setFPLocation(fp_loc);
384    out.setSPLocation(sp_loc);
385
386    addToCache(in, out);
387
388    return gcf_success;
389 }
390
391 void DebugStepperImpl::addToCache(const Frame &cur, const Frame &caller) {
392   const location_t &calRA = caller.getRALocation();
393
394   const location_t &calFP = caller.getFPLocation();
395
396   unsigned raDelta = (unsigned) -1;
397   unsigned fpDelta = (unsigned) -1;  
398   unsigned spDelta = (unsigned) -1;
399
400   if (calRA.location == loc_address) {
401     raDelta = calRA.val.addr - cur.getSP();
402   }
403
404   if (calFP.location == loc_address) {
405     fpDelta = calFP.val.addr - cur.getSP();
406   }
407
408   spDelta = caller.getSP() - cur.getSP();
409   
410   cache_[cur.getRA()] = cache_t(raDelta, fpDelta, spDelta);
411 }
412
413 bool DebugStepperImpl::lookupInCache(const Frame &cur, Frame &caller) {
414   dyn_hash_map<Address,cache_t>::iterator iter = cache_.find(cur.getRA());
415   if (iter == cache_.end()) {
416       return false;
417   }
418
419   addr_width = getProcessState()->getAddressWidth();
420
421   if (iter->second.ra_delta == (unsigned) -1) {
422       return false;
423   }
424   if (iter->second.fp_delta == (unsigned) -1) {
425     return false;
426   }
427   assert(iter->second.sp_delta != (unsigned) -1);
428   
429   Address MAX_ADDR;
430    if (addr_width == 4) {
431        MAX_ADDR = 0xffffffff;
432    } 
433 #if defined(arch_64bit)
434    else if (addr_width == 8){
435        MAX_ADDR = 0xffffffffffffffff;
436    }
437 #endif 
438    else {
439        assert(0 && "Unknown architecture word size");
440        return false;
441    }
442
443   location_t RA;
444   RA.location = loc_address;
445   RA.val.addr = cur.getSP() + iter->second.ra_delta;
446   RA.val.addr %= MAX_ADDR;
447
448   location_t FP;
449   FP.location = loc_address;
450   FP.val.addr = cur.getSP() + iter->second.fp_delta;
451
452   FP.val.addr %= MAX_ADDR;
453   int buffer[10];
454
455   caller.setRALocation(RA);
456   ReadMem(RA.val.addr, buffer, addr_width);
457   caller.setRA(last_val_read);
458
459   caller.setFPLocation(FP);  
460   ReadMem(FP.val.addr, buffer, addr_width);
461   caller.setFP(last_val_read);
462
463   caller.setSP(cur.getSP() + iter->second.sp_delta);
464
465   return true;
466 }
467
468 #endif
469