寄存器如何用作汇编中的参数?
How do registers work as arguments in assembly?
我想了解程序集如何处理参数和 return 值。
到目前为止,我了解到 %eax 是 return 值并且要加载单个参数,我需要将 %rip + offset
的有效地址加载到 %rid
中使用 leaq var(%rip), %rdi
.
为了了解更多关于参数的信息,我创建了一个 c 程序,它接受 10 个(包括格式字符串在内的 11 个参数)来尝试找出寄存器的顺序。然后,我在 Mac.
上使用 gcc 将 C 代码转换为汇编代码
这是我使用的 C 代码:
#include <stdio.h>
int main(){
printf("%s %s %s %s %s %s %s %s %s %s", "1 ", "2", "3", "4", "5", "6", "7", "8", "9", "10");
return 0;
}
听到的是汇编输出:
.section __TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 13
.globl _main ## -- Begin function main
.p2align 4, 0x90
_main: ## @main
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
pushq %r15
pushq %r14
pushq %rbx
pushq %rax
.cfi_offset %rbx, -40
.cfi_offset %r14, -32
.cfi_offset %r15, -24
subq , %rsp
leaq L_.str.10(%rip), %r10
leaq L_.str.9(%rip), %r11
leaq L_.str.8(%rip), %r14
leaq L_.str.7(%rip), %r15
leaq L_.str.6(%rip), %rbx
leaq L_.str(%rip), %rdi
leaq L_.str.1(%rip), %rsi
leaq L_.str.2(%rip), %rdx
leaq L_.str.3(%rip), %rcx
leaq L_.str.4(%rip), %r8
leaq L_.str.5(%rip), %r9
movl [=11=], %eax
pushq %r10
pushq %r11
pushq %r14
pushq %r15
pushq %rbx
callq _printf
addq , %rsp
xorl %eax, %eax
addq , %rsp
popq %rbx
popq %r14
popq %r15
popq %rbp
retq
.cfi_endproc
## -- End function
.section __TEXT,__cstring,cstring_literals
L_.str: ## @.str
.asciz "%s %s %s %s %s %s %s %s %s %s"
L_.str.1: ## @.str.1
.asciz "1 "
L_.str.2: ## @.str.2
.asciz "2"
L_.str.3: ## @.str.3
.asciz "3"
L_.str.4: ## @.str.4
.asciz "4"
L_.str.5: ## @.str.5
.asciz "5"
L_.str.6: ## @.str.6
.asciz "6"
L_.str.7: ## @.str.7
.asciz "7"
L_.str.8: ## @.str.8
.asciz "8"
L_.str.9: ## @.str.9
.asciz "9"
L_.str.10: ## @.str.10
.asciz "10"
.subsections_via_symbols
在那之后,我清除了删除一些 macOS 专用设置的代码?代码仍然有效。
.text
.globl _main ## -- Begin function main
_main: ## @main
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %rbx
pushq %rax
subq , %rsp
leaq L_.str.10(%rip), %r10
leaq L_.str.9(%rip), %r11
leaq L_.str.8(%rip), %r14
leaq L_.str.7(%rip), %r15
leaq L_.str.6(%rip), %rbx
leaq L_.str(%rip), %rdi
leaq L_.str.1(%rip), %rsi
leaq L_.str.2(%rip), %rdx
leaq L_.str.3(%rip), %rcx
leaq L_.str.4(%rip), %r8
leaq L_.str.5(%rip), %r9
movl [=12=], %eax
pushq %r10
pushq %r11
pushq %r14
pushq %r15
pushq %rbx
callq _printf
addq , %rsp
xorl %eax, %eax
addq , %rsp
popq %rbx
popq %r14
popq %r15
popq %rbp
retq
.data
L_.str: ## @.str
.asciz "%s %s %s %s %s %s %s %s %s %s"
L_.str.1: ## @.str.1
.asciz "1 "
L_.str.2: ## @.str.2
.asciz "2"
L_.str.3: ## @.str.3
.asciz "3"
L_.str.4: ## @.str.4
.asciz "4"
L_.str.5: ## @.str.5
.asciz "5"
L_.str.6: ## @.str.6
.asciz "6"
L_.str.7: ## @.str.7
.asciz "7"
L_.str.8: ## @.str.8
.asciz "8"
L_.str.9: ## @.str.9
.asciz "9"
L_.str.10: ## @.str.10
.asciz "10"
我了解到在代码的开头,基指针被压入堆栈,然后将其复制到堆栈指针中供以后使用。
leaq 然后将每个字符串加载到每个寄存器中,这些寄存器将用作 printf 的参数。
我想知道的是为什么寄存器r10
r11
r14
和r15
在第一个参数加载到内存之前寄存器rsi
rdx
rcx
r8
和 'r9' 在第一个参数之后加载到内存中?另外,为什么使用 r14
和 r15
而不是 r12
和 r13
?
此外,为什么在这种情况下从堆栈指针中添加和减去 8,寄存器的压入和弹出顺序是否重要?
我希望所有的子问题都与这个问题有关,如果没有请告诉我。也让我知道我可能会弄错的任何知识。这是我通过将c转换为汇编所学到的。
首先,看起来您正在使用未优化的代码,因此发生了不需要的事情。
查看未压入堆栈的 printf 调用之前的寄存器状态:
rdi = format string
rsi = 1
rdx = 2
rcx = 3
r8 = 4
r9 = 5
然后 6 .. 10 以相反的顺序压入堆栈。
这应该让您了解调用约定。前六个参数通过寄存器。其余参数在堆栈上传递。
What I want to know is why are registers r10 r11 r14 and r15 before the first argument is loaded into memory and that registers rsi rdx rcx r8 and 'r9' loaded into memory after the first argument?
这只是编译器选择的顺序。
Also why are r14 and r15 used instead of r12 and r13?
同样,这是编译器选择的。不是这些只是被用于临时位置。如果代码经过优化,可能会使用更少的寄存器。
Also why is 8 added and subtracted from the stack pointer in this case and does it matter which order the registers are pushed and popped?
它可能只是编译器生成的一些样板函数代码。
我想了解程序集如何处理参数和 return 值。
到目前为止,我了解到 %eax 是 return 值并且要加载单个参数,我需要将 %rip + offset
的有效地址加载到 %rid
中使用 leaq var(%rip), %rdi
.
为了了解更多关于参数的信息,我创建了一个 c 程序,它接受 10 个(包括格式字符串在内的 11 个参数)来尝试找出寄存器的顺序。然后,我在 Mac.
上使用 gcc 将 C 代码转换为汇编代码这是我使用的 C 代码:
#include <stdio.h>
int main(){
printf("%s %s %s %s %s %s %s %s %s %s", "1 ", "2", "3", "4", "5", "6", "7", "8", "9", "10");
return 0;
}
听到的是汇编输出:
.section __TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 13
.globl _main ## -- Begin function main
.p2align 4, 0x90
_main: ## @main
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
pushq %r15
pushq %r14
pushq %rbx
pushq %rax
.cfi_offset %rbx, -40
.cfi_offset %r14, -32
.cfi_offset %r15, -24
subq , %rsp
leaq L_.str.10(%rip), %r10
leaq L_.str.9(%rip), %r11
leaq L_.str.8(%rip), %r14
leaq L_.str.7(%rip), %r15
leaq L_.str.6(%rip), %rbx
leaq L_.str(%rip), %rdi
leaq L_.str.1(%rip), %rsi
leaq L_.str.2(%rip), %rdx
leaq L_.str.3(%rip), %rcx
leaq L_.str.4(%rip), %r8
leaq L_.str.5(%rip), %r9
movl [=11=], %eax
pushq %r10
pushq %r11
pushq %r14
pushq %r15
pushq %rbx
callq _printf
addq , %rsp
xorl %eax, %eax
addq , %rsp
popq %rbx
popq %r14
popq %r15
popq %rbp
retq
.cfi_endproc
## -- End function
.section __TEXT,__cstring,cstring_literals
L_.str: ## @.str
.asciz "%s %s %s %s %s %s %s %s %s %s"
L_.str.1: ## @.str.1
.asciz "1 "
L_.str.2: ## @.str.2
.asciz "2"
L_.str.3: ## @.str.3
.asciz "3"
L_.str.4: ## @.str.4
.asciz "4"
L_.str.5: ## @.str.5
.asciz "5"
L_.str.6: ## @.str.6
.asciz "6"
L_.str.7: ## @.str.7
.asciz "7"
L_.str.8: ## @.str.8
.asciz "8"
L_.str.9: ## @.str.9
.asciz "9"
L_.str.10: ## @.str.10
.asciz "10"
.subsections_via_symbols
在那之后,我清除了删除一些 macOS 专用设置的代码?代码仍然有效。
.text
.globl _main ## -- Begin function main
_main: ## @main
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %rbx
pushq %rax
subq , %rsp
leaq L_.str.10(%rip), %r10
leaq L_.str.9(%rip), %r11
leaq L_.str.8(%rip), %r14
leaq L_.str.7(%rip), %r15
leaq L_.str.6(%rip), %rbx
leaq L_.str(%rip), %rdi
leaq L_.str.1(%rip), %rsi
leaq L_.str.2(%rip), %rdx
leaq L_.str.3(%rip), %rcx
leaq L_.str.4(%rip), %r8
leaq L_.str.5(%rip), %r9
movl [=12=], %eax
pushq %r10
pushq %r11
pushq %r14
pushq %r15
pushq %rbx
callq _printf
addq , %rsp
xorl %eax, %eax
addq , %rsp
popq %rbx
popq %r14
popq %r15
popq %rbp
retq
.data
L_.str: ## @.str
.asciz "%s %s %s %s %s %s %s %s %s %s"
L_.str.1: ## @.str.1
.asciz "1 "
L_.str.2: ## @.str.2
.asciz "2"
L_.str.3: ## @.str.3
.asciz "3"
L_.str.4: ## @.str.4
.asciz "4"
L_.str.5: ## @.str.5
.asciz "5"
L_.str.6: ## @.str.6
.asciz "6"
L_.str.7: ## @.str.7
.asciz "7"
L_.str.8: ## @.str.8
.asciz "8"
L_.str.9: ## @.str.9
.asciz "9"
L_.str.10: ## @.str.10
.asciz "10"
我了解到在代码的开头,基指针被压入堆栈,然后将其复制到堆栈指针中供以后使用。
leaq 然后将每个字符串加载到每个寄存器中,这些寄存器将用作 printf 的参数。
我想知道的是为什么寄存器r10
r11
r14
和r15
在第一个参数加载到内存之前寄存器rsi
rdx
rcx
r8
和 'r9' 在第一个参数之后加载到内存中?另外,为什么使用 r14
和 r15
而不是 r12
和 r13
?
此外,为什么在这种情况下从堆栈指针中添加和减去 8,寄存器的压入和弹出顺序是否重要?
我希望所有的子问题都与这个问题有关,如果没有请告诉我。也让我知道我可能会弄错的任何知识。这是我通过将c转换为汇编所学到的。
首先,看起来您正在使用未优化的代码,因此发生了不需要的事情。
查看未压入堆栈的 printf 调用之前的寄存器状态:
rdi = format string
rsi = 1
rdx = 2
rcx = 3
r8 = 4
r9 = 5
然后 6 .. 10 以相反的顺序压入堆栈。
这应该让您了解调用约定。前六个参数通过寄存器。其余参数在堆栈上传递。
What I want to know is why are registers r10 r11 r14 and r15 before the first argument is loaded into memory and that registers rsi rdx rcx r8 and 'r9' loaded into memory after the first argument?
这只是编译器选择的顺序。
Also why are r14 and r15 used instead of r12 and r13?
同样,这是编译器选择的。不是这些只是被用于临时位置。如果代码经过优化,可能会使用更少的寄存器。
Also why is 8 added and subtracted from the stack pointer in this case and does it matter which order the registers are pushed and popped?
它可能只是编译器生成的一些样板函数代码。