From: Jeremy F. <je...@go...> - 2002-09-30 21:03:09
|
Well, I decided to look into my suggestion for maintaining the call stack of by "simply" tracking pairs of call/ret instructions. I've decided it is either completely non-viable, or too complex to bother with. While a fine idea in theory, it relies on real code using call/return and not playing too much with return address on the stack. I had thought the main violations of the call/return rule would be some or all of: /* A - PIC code - dealt with already in vg_to_ucode */ call 1f 1: popl %reg /* B - common idiom */ push code_addr ret /* C - manual call */ pop %reg push some_addr jmp *%reg /* D - manual return */ pop %reg jmp *%reg Unfortunately I completely underestimated the twisty-turnyness of the glibc dynamic linker, which basically does arbitrary stack manipulations to manually build stack frames, manually call and manually return, and also does delights such as: xchg %eax, 8(%esp) ret $8 In other words, to keep track of this stuff, Valgrind would have to keep track of all stack accesses (optimistically accesses based off %esp; pessimistically all memory accesses which happen to be in the area), and maintain the call stack that way. I don't think this is viable. A possibly more viable, but complex, approach is to use a hybrid technique: maintain a call stack, and also walk up the call frames using esp/ebp. If they agree, then all is well; if not, use the call stack to resync the call frame walker, rather than using its information directly. I think that would take rather more effort than I was prepared to get slightly more precision in a backtrace, so I'm not going to explore it for now. J |