I believe I understand how the linux x86-64 ABI uses registers and stack to pass parameters to a function (cf. previous ABI discussion). What I'm confused about is if/what registers are expected to be preserved across a function call. That is, what registers are guarenteed not to get clobbered?
我相信我理解linux x86-64 ABI如何使用寄存器和堆栈将参数传递给函数(参见之前的ABI讨论)。我感到困惑的是,在函数调用中是否预期保留了哪些寄存器。也就是说,哪些寄存器被保证不被破坏?
2 个解决方案
#1
43
Here's the complete table of registers and their use from the documentation [PDF Link]:
以下是文档[PDF链接]中完整的寄存器表及其用法:
r12
, r13
, r14
, r15
, rbx
, rsp
, rbp
are the callee-saved registers - they have a "Yes" in the "Preserved across function calls" column.
r12,r13,r14,r15,rbx,rsp,rbp是被调用者保存的寄存器 - 它们在“保留跨函数调用”列中具有“是”。
#2
4
The ABI specifies what a piece of standard-conforming software is allowed to expect. It is written primarily for authors of compilers, linkers and other language processing software. These authors want their compiler to produce code that will work properly with code that is compiled by the same (or a different) compiler. They all have to agree to a set of rules: how are formal arguments to functions passed from caller to callee, how are function return values passed back from callee to caller, which registers are preserved/scratch/undefined across the call boundary, and so on.
ABI指定允许一个符合标准的软件。它主要是为编译器,链接器和其他语言处理软件的作者编写的。这些作者希望他们的编译器能够生成能够与由相同(或不同)编译器编译的代码一起正常工作的代码。它们都必须同意一组规则:函数的形式参数如何从调用者传递给被调用者,函数返回值如何从被调用者传递回调用者,哪些寄存器在调用边界上保留/暂存/未定义,等等上。
For example, one rule states that the generated assembly code for a function must save the value of a preserved register before changing the value, and that the code must restore the saved value before returning to its caller. For a scratch register, the generated code is not required to save and restore the register value; it can do so if it wants, but standard-conforming software is not allowed to depend upon this behavior (if it does it is not standard-conforming software).
例如,一条规则声明生成的函数汇编代码必须在更改值之前保存保留寄存器的值,并且代码必须在返回其调用者之前恢复保存的值。对于临时寄存器,生成的代码不需要保存和恢复寄存器值;如果需要,它可以这样做,但不允许符合标准的软件依赖于这种行为(如果它不是符合标准的软件)。
If you are writing assembly code, you are responsible for playing by these same rules (you are playing the role of the compiler). That is, if your code changes a callee-preserved register, you are responsible for inserting instructions that save and restore the original register value. If your assembly code calls an external function, your code must pass arguments in the standard-conforming way, and it can depend upon the fact that, when the callee returns, preserved register values are in fact preserved.
如果您正在编写汇编代码,则您负责按照相同的规则进行播放(您正在扮演编译器的角色)。也就是说,如果您的代码更改了被调用者保留的寄存器,则您负责插入保存和恢复原始寄存器值的指令。如果汇编代码调用外部函数,则代码必须以符合标准的方式传递参数,并且它可以取决于当被调用者返回时保留的寄存器值实际上被保留的事实。
The rules define how standards-conforming software can get along. However, it is perfectly legal to write (or generate) code that does not play by these rules! Compilers do this all the time, because they know that the rules don't need to be followed under certain circumstances.
规则定义了符合标准的软件如何相处。但是,编写(或生成)不遵守这些规则的代码是完全合法的!编译器一直这样做,因为他们知道在某些情况下不需要遵循规则。
For example, consider a C function named foo that is declared as follows:
例如,考虑一个名为foo的C函数,声明如下:
static foo(int x);
At compile-time, the compiler is 100% certain that this function can only be called by other code in the file(s) it is currently compiling. Function foo
cannot be called by anything else, ever, given the definition of what it means to be static. Because the compiler knows all of the callers of foo
at compile time, the compiler is free to use whatever calling sequence it wants (up to and including not making a call at all, that is, inlining the code for foo
into the callers of foo
.
在编译时,编译器100%确定此函数只能由当前正在编译的文件中的其他代码调用。考虑到静态意义的定义,函数foo不能被其他任何东西调用。因为编译器在编译时知道foo的所有调用者,所以编译器可以*地使用它想要的任何调用序列(包括根本不包括调用,也就是说,将foo的代码内联到foo的调用者中) 。
As an author of assembly code, you can do this too. That is, you can implement a "private agreement" between two or more routines, as long as that agreement doesn't interfere with or violate the expectations of standards-conforming software.
作为汇编代码的作者,您也可以这样做。也就是说,只要该协议不会干扰或违反符合标准的软件的期望,您就可以在两个或更多例程之间实现“私有协议”。
#1
43
Here's the complete table of registers and their use from the documentation [PDF Link]:
以下是文档[PDF链接]中完整的寄存器表及其用法:
r12
, r13
, r14
, r15
, rbx
, rsp
, rbp
are the callee-saved registers - they have a "Yes" in the "Preserved across function calls" column.
r12,r13,r14,r15,rbx,rsp,rbp是被调用者保存的寄存器 - 它们在“保留跨函数调用”列中具有“是”。
#2
4
The ABI specifies what a piece of standard-conforming software is allowed to expect. It is written primarily for authors of compilers, linkers and other language processing software. These authors want their compiler to produce code that will work properly with code that is compiled by the same (or a different) compiler. They all have to agree to a set of rules: how are formal arguments to functions passed from caller to callee, how are function return values passed back from callee to caller, which registers are preserved/scratch/undefined across the call boundary, and so on.
ABI指定允许一个符合标准的软件。它主要是为编译器,链接器和其他语言处理软件的作者编写的。这些作者希望他们的编译器能够生成能够与由相同(或不同)编译器编译的代码一起正常工作的代码。它们都必须同意一组规则:函数的形式参数如何从调用者传递给被调用者,函数返回值如何从被调用者传递回调用者,哪些寄存器在调用边界上保留/暂存/未定义,等等上。
For example, one rule states that the generated assembly code for a function must save the value of a preserved register before changing the value, and that the code must restore the saved value before returning to its caller. For a scratch register, the generated code is not required to save and restore the register value; it can do so if it wants, but standard-conforming software is not allowed to depend upon this behavior (if it does it is not standard-conforming software).
例如,一条规则声明生成的函数汇编代码必须在更改值之前保存保留寄存器的值,并且代码必须在返回其调用者之前恢复保存的值。对于临时寄存器,生成的代码不需要保存和恢复寄存器值;如果需要,它可以这样做,但不允许符合标准的软件依赖于这种行为(如果它不是符合标准的软件)。
If you are writing assembly code, you are responsible for playing by these same rules (you are playing the role of the compiler). That is, if your code changes a callee-preserved register, you are responsible for inserting instructions that save and restore the original register value. If your assembly code calls an external function, your code must pass arguments in the standard-conforming way, and it can depend upon the fact that, when the callee returns, preserved register values are in fact preserved.
如果您正在编写汇编代码,则您负责按照相同的规则进行播放(您正在扮演编译器的角色)。也就是说,如果您的代码更改了被调用者保留的寄存器,则您负责插入保存和恢复原始寄存器值的指令。如果汇编代码调用外部函数,则代码必须以符合标准的方式传递参数,并且它可以取决于当被调用者返回时保留的寄存器值实际上被保留的事实。
The rules define how standards-conforming software can get along. However, it is perfectly legal to write (or generate) code that does not play by these rules! Compilers do this all the time, because they know that the rules don't need to be followed under certain circumstances.
规则定义了符合标准的软件如何相处。但是,编写(或生成)不遵守这些规则的代码是完全合法的!编译器一直这样做,因为他们知道在某些情况下不需要遵循规则。
For example, consider a C function named foo that is declared as follows:
例如,考虑一个名为foo的C函数,声明如下:
static foo(int x);
At compile-time, the compiler is 100% certain that this function can only be called by other code in the file(s) it is currently compiling. Function foo
cannot be called by anything else, ever, given the definition of what it means to be static. Because the compiler knows all of the callers of foo
at compile time, the compiler is free to use whatever calling sequence it wants (up to and including not making a call at all, that is, inlining the code for foo
into the callers of foo
.
在编译时,编译器100%确定此函数只能由当前正在编译的文件中的其他代码调用。考虑到静态意义的定义,函数foo不能被其他任何东西调用。因为编译器在编译时知道foo的所有调用者,所以编译器可以*地使用它想要的任何调用序列(包括根本不包括调用,也就是说,将foo的代码内联到foo的调用者中) 。
As an author of assembly code, you can do this too. That is, you can implement a "private agreement" between two or more routines, as long as that agreement doesn't interfere with or violate the expectations of standards-conforming software.
作为汇编代码的作者,您也可以这样做。也就是说,只要该协议不会干扰或违反符合标准的软件的期望,您就可以在两个或更多例程之间实现“私有协议”。