在这种特殊情况下,您是否建议使用程序集访问参数

would you recommend using assembly to access arguments in this exceptional case?

本文关键字:程序集 参数 访问 是否 特殊情况下      更新时间:2023-10-16

考虑以下不会内联的函数,并假设x86为平台:

void doSomething(int & in){
//do something
}

首先,我不确定这种情况会发生,但由于我认为这是可能的,我会问,如果在任何调用方中,无论何时调用该函数,要提供的参数都正好位于调用方堆栈帧的顶部,因此在被调用的函数中,可以用汇编语言通过ebp寄存器(在被调用方将esp的内容移动到ebp中之后)访问它,你建议我们忽略为函数声明一个参数,在这种特殊情况下使用汇编访问我们的参数,或者只保留函数定义的原样,让编译器做它该做的事情?由于我在任何地方都没有读过编译器会将这种特殊情况视为调用约定的一个因素,我认为它只会生成代码,将指向参数的指针传递给被调用方堆栈帧或寄存器

之一

首先,它很容易被破坏——例如,你得到了一个不同版本的编译器,它以不同的方式生成代码。或者您更改优化功能。不要介意突然需要在不同的地方使用doSomething,然后它就不起作用了,因为变量不再在堆栈的顶部。

其次,假设函数内的代码足够短,编译器很可能会内联函数,这样就不会"丢失"任何东西。

第三,在现代编译器中,一个参数通常会在寄存器中传递,因此当启用优化时,这没有任何好处。

如果你真的认为这有好处,并且编译器不会内联或以其他方式优化代码[你看过生成的代码吗?],那么试着使用forceinlinealways_inline或编译器中调用的任何代码(大多数编译器都有这样的选项)。如果这不起作用,请使用宏手动内联。或者简单地将代码移动到"复制粘贴"所调用的位置。

您的注释"要提供的参数正好位于调用方堆栈帧的顶部,因此在被调用函数中,通过ebp register访问它"包含了一个事实误解。

这是因为以下几点:

  • 您假设了一个基于堆栈的调用约定,即在call调用函数之前,调用方将函数参数push添加到堆栈中。通常情况并非如此;即使在32位x86上,也有基于非堆栈的调用约定(例如,Windows fastcall或32位linux内核中使用的GNU GCC)。如果使用这样的方法,则不会在堆栈顶部找到参数,而是在。。。用于保存第一个参数的任何寄存器


    但是,即使您有基于堆栈的参数传递。。。仍然:

  • 您已经忽略了,在x86上,call指令至少会将返回地址推送到堆栈顶部,因此当以这种方式到达的函数的第一条指令正在执行时,ESP不会指向该函数的第一个arg,而是指向返回地址
  • 您已经错过了EBP是一个被调用者保存(保存在函数调用中)的寄存器,而不是由体系结构代表您初始化的——生成的代码必须显式地设置它。因此,想要使用它(即使只是作为帧指针)的函数必须在使用它之前将其保存在某个位置。这意味着正常序言将具有push EBP; mov EBP, ESP(您不能执行MOV EBP, ESP,因为这将覆盖调用方的EBP,而这是无效的/您可能不会执行的)。因此,如果要引用函数的第一个参数,则需要[ EBP + 8 ]而不是[ EBP ]
    如果不是使用帧指针,则第一个参数(由于用于到达推送返回地址的函数的call)位于[ ESP + 4 ]而不是[ ESP ]

我希望这能澄清一点。

我同意其他海报的观点,即澄清这个问题会有所帮助,你到底想实现什么,以及为什么你认为汇编语言在这里可能有用。

不,我不会。调用约定可能有所不同(在x86和x86_64之间);参数可以被推送到堆栈或放入寄存器,我不确定你是否能确定它们会在哪里

除非您真的知道自己在做什么,否则在程序集中编写它可能会导致未定义的行为代码。