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