Debugging 16位实模式程序的调试器是如何产生堆栈跟踪的?

Debugging 16位实模式程序的调试器是如何产生堆栈跟踪的?,debugging,x86,real-mode,Debugging,X86,Real Mode,我在仿真器中运行旧的DOS程序,我已经到了想跟踪程序堆栈的地步。但是,我遇到了一个问题,特别是如何检测近距离呼叫和远距离呼叫。一些借口: 近调用仅将IP推送到堆栈上,并且预期将与只弹出要返回的IP的ret配对 far调用将CS和IP都推到堆栈上,并预期与一个retf配对,后者将弹出CS和IP以返回堆栈 没有办法知道调用是近调用还是远调用,除非知道调用它的是哪种指令,或者它使用哪种返回 幸运的是,在该程序开发期间,基于BP的堆栈帧非常常见,因此遍历堆栈似乎不是问题:我只是遵循BP链。不幸的是,

我在仿真器中运行旧的DOS程序,我已经到了想跟踪程序堆栈的地步。但是,我遇到了一个问题,特别是如何检测近距离呼叫和远距离呼叫。一些借口:

  • 调用
    仅将IP推送到堆栈上,并且预期将与只弹出要返回的IP的
    ret
    配对
  • far
    调用
    将CS和IP都推到堆栈上,并预期与一个
    retf
    配对,后者将弹出CS和IP以返回堆栈
  • 没有办法知道调用是近调用还是远调用,除非知道调用它的是哪种指令,或者它使用哪种返回
幸运的是,在该程序开发期间,基于BP的堆栈帧非常常见,因此遍历堆栈似乎不是问题:我只是遵循BP链。不幸的是,获取CS和/或IP是很困难的,因为我似乎无法通过查看堆栈来确定调用是近调用还是远调用

我有可用的函数元数据,因此如果我已经知道实际的CS和IP,我可以判断函数是近调用还是远调用,但我无法计算出IP和CS,除非我已经知道它是远调用还是近调用

我只是通过猜测,看看我的猜测是否会导致有效的函数查找,取得了一些成功,但我认为这种方法会产生很多误报


所以我的问题是: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环境(包括低级代码),但我不记得当时调试器是如何工作的。