Debugging 16位实模式程序的调试器是如何产生堆栈跟踪的?
我在仿真器中运行旧的DOS程序,我已经到了想跟踪程序堆栈的地步。但是,我遇到了一个问题,特别是如何检测近距离呼叫和远距离呼叫。一些借口:Debugging 16位实模式程序的调试器是如何产生堆栈跟踪的?,debugging,x86,real-mode,Debugging,X86,Real Mode,我在仿真器中运行旧的DOS程序,我已经到了想跟踪程序堆栈的地步。但是,我遇到了一个问题,特别是如何检测近距离呼叫和远距离呼叫。一些借口: 近调用仅将IP推送到堆栈上,并且预期将与只弹出要返回的IP的ret配对 far调用将CS和IP都推到堆栈上,并预期与一个retf配对,后者将弹出CS和IP以返回堆栈 没有办法知道调用是近调用还是远调用,除非知道调用它的是哪种指令,或者它使用哪种返回 幸运的是,在该程序开发期间,基于BP的堆栈帧非常常见,因此遍历堆栈似乎不是问题:我只是遵循BP链。不幸的是,
- 近
仅将IP推送到堆栈上,并且预期将与只弹出要返回的IP的调用
配对ret
- far
将CS和IP都推到堆栈上,并预期与一个调用
配对,后者将弹出CS和IP以返回堆栈retf
- 没有办法知道调用是近调用还是远调用,除非知道调用它的是哪种指令,或者它使用哪种返回
所以我的问题是:DOS时代的调试器是如何处理这个问题并产生堆栈跟踪的?我是否缺少一些算法,或者他们只是在堆栈中编码调试信息?(如果是这样的话,那么我必须想出一些别的办法。)只是猜测一下,我从来没有实际使用过16位x86开发工具(现代或过去): 您知道当前函数的CS:IP值(或从异常帧触发故障或其他内容的函数) 您可能有元数据来告诉您这是否是一个用远调用调用的“远”函数。或者,您可以尝试解码,直到到达
retn
或retf
,然后使用它来确定返回地址是近IP
还是远CS:IP
(假设这是一个正常的函数,返回时带有某种类型的ret
。或者如果它以对另一个函数的jmp
尾调用结束,那么返回地址可能与之匹配,但这是另一个级别的假设。并且计算出一个接近jmp
的函数是函数的结束,而不是一个函数中的跳转。)ge函数是一个不明确的问题,没有任何符号元数据。)
但无论如何,对父函数应用相同的东西:在成功回溯一级后,现在在父函数中调用后有指令的CS:IP,以及BP链表的SS:BP值
顺便说一句,是的,传统的BP堆栈帧被广泛使用有一个很好的理由:[SP]
不是一种有效的16位寻址模式,而且只有[BP]
作为基数意味着SS作为一个段,所以是的,使用BP访问堆栈是随机访问的唯一好选择(而不仅仅是临时的推送/弹出)没有理由不先保存/恢复它(在任何其他寄存器或预留堆栈空间)以形成一个常规的堆栈帧。如果你在这里没有得到答案,考虑加入和请求那里。(但是不要两者都做,未宣布的,它只会导致分裂知识。)<代码> [SP]
不是有效的16位寻址模式,只有[BP]
作为基数意味着SS是一个段,所以是的,使用BP访问堆栈是随机访问的唯一好选择(不仅仅是临时的push/pop)。没有理由不先保存/还原它以制作传统的旧堆栈框架。我们的配套站点可能更适合这个问题。从1985年起,我广泛使用了16位x86环境(包括低级代码),但我不记得当时调试器是如何工作的。