为什么通过寄存器调用子程序?
Why is a subroutine called via register?
我目前正在学习汇编以更好地理解编码,但有些事情我无法完全理解,非常感谢您的帮助。
有时在代码中我看到:
...
add edx, 1
call relative_offset_to_current_ip
...
现在我可以跟随跳转并检查代码,但有时我也会看到
...
add edx,1
call ebx
...
所以在添加调试器之前我无法理解真正的代码在哪里。
我恳请帮助我解决以下问题:
“注册调用版”是否也有当前IP的相对地址?
为什么放在寄存器里?将要调用的例程代码不应该已经是
躺在代码部分等待?
是否可以在没有调试器的情况下跟踪通过寄存器进行的调用?
Does the "register calling version" also have a relative address to the current IP?
当目标地址作为数据提供时,它是某个函数的第一条指令的内存地址。它可以作为参数传递,在这里或那里使用。使用函数指针的调用者应该只使用未修改的指针,否则您将无法到达函数的第一条指令。可以从多个不同位置调用相同的函数指针(指针、数据),但仍应将控制转移到函数的第一条指令。
相比之下,使用 pc 相对寻址完成的对同一函数的不同调用分别独立调整以指向函数的第一条指令。当通过指针调用时,这种独立调整既不可能也没有必要。
但是,所有形式的调用都会获得一个 return 地址(并将其提供给函数以在其 return 处使用),该地址确实相对于调用站点(但此处使用的偏移量仅仅是特定调用指令的大小;指令中没有为此编码的 pc 相对偏移量。
Why is it put in a register?
大多数语言都允许first-class函数,也就是说我们可以有一个引用函数的变量或参数。当用户定义函数指针(可能作为函数的参数),然后调用函数指针引用的函数时,需要进行间接调用。调用寄存器指令很有用,但不是绝对必要的,因为间接调用的效果可以通过生成 return 地址,将其放在正确的位置,然后使用间接跳转来组成。
动态链接库可以使用程序员不可见的函数指针(除非在 assembly/machine 代码中调试)。当某些代码的位置在运行时之前是未知的,因此在某些系统上可能会发生这种情况,因此作为数据提供——本质上是一个函数指针,一旦被识别(当动态加载的库被实际加载时)就保持不变。如果目标对于 pc 相关调用的地址太远,也会发生这种情况(太远因指令集而异)。
面向对象语言中的虚拟方法调度使用程序员不可见的函数指针(在他们的 OO 语言中)。这些 OO 语言的运行时维护一组 table 函数指针,其中特定对象(接收消息)确定要查询哪个 table 以获取适当的函数指针以调用正确的虚拟方法。
Shouldn't the routine code that is about to be called already be laying in the code section waiting?
通常是的,但是由于上述原因,程序(此时)只知道在哪里使用数据(函数指针)。
Can calls made via register even be traced without a debugger?
如果您不调试,我不知道您所说的跟踪是什么意思 — 例如,一种跟踪是在调试器中单步执行。
另一方面,控制流不是随机的,并且在某种意义上由程序员预测table,如果他们知道代码应该做什么。
我目前正在学习汇编以更好地理解编码,但有些事情我无法完全理解,非常感谢您的帮助。
有时在代码中我看到:
...
add edx, 1
call relative_offset_to_current_ip
...
现在我可以跟随跳转并检查代码,但有时我也会看到
...
add edx,1
call ebx
...
所以在添加调试器之前我无法理解真正的代码在哪里。
我恳请帮助我解决以下问题:
“注册调用版”是否也有当前IP的相对地址?
为什么放在寄存器里?将要调用的例程代码不应该已经是 躺在代码部分等待?
是否可以在没有调试器的情况下跟踪通过寄存器进行的调用?
Does the "register calling version" also have a relative address to the current IP?
当目标地址作为数据提供时,它是某个函数的第一条指令的内存地址。它可以作为参数传递,在这里或那里使用。使用函数指针的调用者应该只使用未修改的指针,否则您将无法到达函数的第一条指令。可以从多个不同位置调用相同的函数指针(指针、数据),但仍应将控制转移到函数的第一条指令。
相比之下,使用 pc 相对寻址完成的对同一函数的不同调用分别独立调整以指向函数的第一条指令。当通过指针调用时,这种独立调整既不可能也没有必要。
但是,所有形式的调用都会获得一个 return 地址(并将其提供给函数以在其 return 处使用),该地址确实相对于调用站点(但此处使用的偏移量仅仅是特定调用指令的大小;指令中没有为此编码的 pc 相对偏移量。
Why is it put in a register?
大多数语言都允许first-class函数,也就是说我们可以有一个引用函数的变量或参数。当用户定义函数指针(可能作为函数的参数),然后调用函数指针引用的函数时,需要进行间接调用。调用寄存器指令很有用,但不是绝对必要的,因为间接调用的效果可以通过生成 return 地址,将其放在正确的位置,然后使用间接跳转来组成。
动态链接库可以使用程序员不可见的函数指针(除非在 assembly/machine 代码中调试)。当某些代码的位置在运行时之前是未知的,因此在某些系统上可能会发生这种情况,因此作为数据提供——本质上是一个函数指针,一旦被识别(当动态加载的库被实际加载时)就保持不变。如果目标对于 pc 相关调用的地址太远,也会发生这种情况(太远因指令集而异)。
面向对象语言中的虚拟方法调度使用程序员不可见的函数指针(在他们的 OO 语言中)。这些 OO 语言的运行时维护一组 table 函数指针,其中特定对象(接收消息)确定要查询哪个 table 以获取适当的函数指针以调用正确的虚拟方法。
Shouldn't the routine code that is about to be called already be laying in the code section waiting?
通常是的,但是由于上述原因,程序(此时)只知道在哪里使用数据(函数指针)。
Can calls made via register even be traced without a debugger?
如果您不调试,我不知道您所说的跟踪是什么意思 — 例如,一种跟踪是在调试器中单步执行。
另一方面,控制流不是随机的,并且在某种意义上由程序员预测table,如果他们知道代码应该做什么。