Incremental linking - messed up call stack?

Started by
4 comments, last by CipherCraft 16 years, 2 months ago
Hey, I'm showing a call stack in our assert dialog, which is convenient to see how the assert got to be fired. And it works well... most of the time. The debug build, when needed, works like a charm. The release build (with symbol information) is not so friendly. When linking incrementally the callstack ends up being too short and only shows the assert handling code. But after a full link the stack trace is fully complete and sane. And this is true for each type of link, all the time. I've tried unloading modules in case they were left behind after the last run, but no joy. I can't find anything specific on my problem online; Google is currently not on speaking terms with me, apparently. [bawling] So... Any of you, please, tell me you've come across this before and know just the fix for this. tia, CipherCraft
Advertisement
Are you compiling with stack frames enabled?
Quote:Original post by Antheus
Are you compiling with stack frames enabled?


Yes. A full link does provide a proper trace.
To understand this, we will need to have a look at how incremental linking works.
Normally when a function is added or removed, all addresses after its new location would have changed and you'd have to do a full re-link. Incremental linking avoids this by wiping out the removed function's code with 0xCC (int 3/breakpoint to ensure it's not called) or appending newly added functions on to the newly extended code segment. What makes this work is that functions are now called indirectly, via thunks. When a function is moved, only its thunk need be updated; other code that uses it is unaffected.

In your case, the stack trace is getting a return address, looking at the previous instruction and seeing a call to the thunk. Since it doesn't know to follow the ensuing JMP at the thunk function, you aren't getting the real call target.
For things to work with (evil) incremental builds, this logic will have to be added; you could also switch to StackWalk in such cases because it ought to be able to handle them.

HTH+HAND
E8 17 00 42 CE DC D2 DC E4 EA C4 40 CA DA C2 D8 CC 40 CA D0 E8 40E0 CA CA 96 5B B0 16 50 D7 D4 02 B2 02 86 E2 CD 21 58 48 79 F2 C3
Quote:Original post by Jan Wassenberg
To understand this, we will need to have a look at how incremental linking works.
Normally when a function is added or removed, all addresses after its new location would have changed and you'd have to do a full re-link. Incremental linking avoids this by wiping out the removed function's code with 0xCC (int 3/breakpoint to ensure it's not called) or appending newly added functions on to the newly extended code segment.


Right. That's my understanding also.

Quote:Original post by Jan Wassenberg
What makes this work is that functions are now called indirectly, via thunks. When a function is moved, only its thunk need be updated; other code that uses it is unaffected.
In your case, the stack trace is getting a return address, looking at the previous instruction and seeing a call to the thunk. Since it doesn't know to follow the ensuing JMP at the thunk function, you aren't getting the real call target.


News to me! This explains alot. I have not found anything on thunks in this situation before. I'll try and read up; any good hints on where to start looking?
Are these the symbols that are marked as virtual by any chance?

Quote:Original post by Jan Wassenberg
For things to work with (evil) incremental builds, this logic will have to be added; you could also switch to StackWalk in such cases because it ought to be able to handle them.


(evil) I know, but we need the 40 seconds we gain by using incremental. Or so I'm told. [wink]

StackWalk, hmmm.... Does it work better than StackWalk64, which I'm supposed to be using according to MSDN? Or am I missing your point?

++rating anyways, for providing just that spot of information that I needed to keep my sanity. [wink]

Thanks for taking the time and sorry for all the follow up questions.
A small follow up.

- The thunking is done only for non-static functions; static functions are unaffected.
- The combination release / incremental:yes is deadly for reporting proper call stacks.

I still haven't found a solution. [sad] I would need to detect static functions and add some logic to find that extra JMP.

I'll have to put this on the back burner; other stuff has a higher priority. But I'd really like it to work all of the time.

CipherCraft

This topic is closed to new replies.

Advertisement