我正在弄乱在模拟器中运行旧的DOS程序的程度,现在到了要跟踪程序堆栈的地步。但是,我遇到了一个问题,特别是如何检测近距离呼叫和远距离呼叫。一些借口:
call
的IP仅将IP推入堆栈,并且有望与ret
配对,而call
仅弹出IP以返回。retf
会将CS和IP都推入堆栈,并有望与//incorrect
target.events.trigger(target, event);
配对,这会同时弹出CS和IP以返回。幸运的是,在开发此程序的时期内,基于BP的堆栈框架非常普遍,因此遍历堆栈似乎不是问题:我只是遵循BP链。不幸的是,获得CS和/或IP很困难,因为我似乎没有办法通过单独查看堆栈来确定呼叫是近距离呼叫还是远距离呼叫。
我有关于可用功能的元数据,因此,如果我已经知道实际的CS和IP,则可以判断一个功能是近距离调用还是远距离调用,但是除非我已经知道它是否是IP和CS,否则我无法弄清楚它是远距离通话或近距离通话。
仅通过猜测并查看我的猜测是否会导致有效的函数查找,我就取得了一些成功,但是我认为这种方法会产生很多误报。
所以我的问题是:DOS时代的调试器如何处理此问题并产生堆栈跟踪?我是否缺少一些算法,或者它们只是在堆栈中编码调试信息? (如果是这种情况,那么我将不得不提出其他建议。)
答案 0 :(得分:1)
仅凭猜测,我从未真正使用过16位x86开发工具(现代或当下):
您知道当前功能的CS:IP值(或触发故障的功能或异常帧中的内容)。
您可能具有元数据,该元数据告诉您这是否是通过远距离调用调用的“远”函数。或者,您可以尝试解码,直到到达retn
或retf
,然后使用它来确定返回地址是近IP
还是远CS:IP
。>
(假设这是一个正常的函数,它以某种ret
返回。或者如果它以jmp
尾调用结尾到另一个函数,则返回地址可能与之匹配,但这是另一个级别并弄清楚jmp
附近是函数的结尾,而不仅仅是大型函数中的跳转是一个模棱两可的问题,没有任何符号元数据。)
但是无论如何,将相同的内容应用于父函数:在成功回溯一级之后,您现在在父函数中call
之后具有指令的CS:IP,并且具有SS:BP值BP链表的列表。
还有,顺便说一句,是的,有一个很好的理由可以广泛使用传统的BP堆栈帧:[SP]
不是有效的16位寻址模式,仅以[BP]
为基数就意味着SS为因此,是的,使用BP来访问堆栈是随机访问的唯一好选择(不只是临时存储的push / pop)。没有理由不先保存/恢复它(在任何其他寄存器之前或保留堆栈空间之前)以形成常规的堆栈帧。