- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
1)C结构如何传递给汇编函数。我的意思是按值传递,而不是按引用传递。
2)顺便说一下,被调用者如何将结构返回给其调用者?
由于我的母语不是英语,所以我为自己的表现不好而感到抱歉。
我编写了一个简单的程序来验证C结构如何传递给函数。但是结果令人惊讶。寄存器传递了一些值,但将它们压入堆栈则传递了一些值。这是代码。
源代码
#include <stdio.h>
typedef struct {
int age;
enum {Man, Woman} gen;
double height;
int class;
char *name;
} student;
void print_student_info(student s) {
printf("age: %d, gen: %s, height: %f, name: %s\n",
s.age,
s.gen == Man? "Man":"Woman",
s.height, s.name);
}
int main() {
student s;
s.age = 10;
s.gen = Man;
s.height = 1.30;
s.class = 3;
s.name = "Tom";
print_student_info(s);
return 0;
}
6fa: 55 push %rbp
6fb: 48 89 e5 mov %rsp,%rbp
6fe: 48 83 ec 20 sub $0x20,%rsp
702: c7 45 e0 0a 00 00 00 movl $0xa,-0x20(%rbp)
709: c7 45 e4 00 00 00 00 movl $0x0,-0x1c(%rbp)
710: f2 0f 10 05 00 01 00 movsd 0x100(%rip),%xmm0 # 818 <_IO_stdin_used+0x48>
717: 00
718: f2 0f 11 45 e8 movsd %xmm0,-0x18(%rbp)
71d: c7 45 f0 03 00 00 00 movl $0x3,-0x10(%rbp)
724: 48 8d 05 e5 00 00 00 lea 0xe5(%rip),%rax # 810 <_IO_stdin_used+0x40>
72b: 48 89 45 f8 mov %rax,-0x8(%rbp)
72f: ff 75 f8 pushq -0x8(%rbp)
732: ff 75 f0 pushq -0x10(%rbp)
735: ff 75 e8 pushq -0x18(%rbp)
738: ff 75 e0 pushq -0x20(%rbp)
73b: e8 70 ff ff ff callq 6b0 <print_student_info>
740: 48 83 c4 20 add $0x20,%rsp
744: b8 00 00 00 00 mov $0x0,%eax
749: c9 leaveq
74a: c3 retq
74b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
最佳答案
正如其他人所指出的那样,在大多数情况下,按值传递结构通常会受到质疑,但是C语言仍然允许这样做。我将讨论您使用过的代码,即使这不是我本来会做的。
如何传递结构取决于ABI /调用约定。今天有两种主要的64位ABI正在使用(可能还有其他)。 64-bit Microsoft ABI和x86-64 System V ABI。 64位Microsoft ABI很简单,因为按值传递的所有结构都在堆栈中。在x86-64 System V ABI(由Linux / MacOS / BSD使用)中,由于存在一种递归算法,该递归算法用于确定结构是否可以在通用寄存器/矢量寄存器/ X87 FPU的组合中传递,因此更加复杂。堆栈寄存器。如果确定该结构可以在寄存器中传递,则该对象不会出于调用函数的目的而放在堆栈中。如果根据规则它不适合寄存器,那么它将在堆栈的内存中传递。
有迹象表明,您的代码未使用64位Microsoft ABI,因为在进行函数调用之前编译器未保留32字节的影子空间,因此几乎可以肯定这是针对x86-64 System V的编译器ABI。我可以使用禁用了优化功能的GCC编译器,使用在线Godbolt编译器生成问题中的same assembly code。
遍历algorithm for passing aggregate types(如结构和联合)超出了此答案的范围,但是您可以参考3.2.3节“参数传递”,但是我可以说,由于后清理规则,该结构在堆栈上传递说:
如果聚合的大小超过两个8字节,并且前8个字节不是SSE,或者其他8个字节不是SSEUP,则整个参数都将在内存中传递。
碰巧是您的结构曾试图将前两个32位int
值打包在64位寄存器中,而double
放在向量寄存器中,然后将int
放在64位寄存器中位寄存器(由于对齐规则),并且指针在另一个64位寄存器中传递。您的结构将超过两个八个字节(64位)的寄存器,而第一个八个字节(64位)的寄存器不是SSE寄存器,因此编译器会将结构传递到堆栈上。
您有未优化的代码,但是我们可以将代码分解为大块。首先是构建堆栈框架并为局部变量分配空间。如果未启用优化(此处就是这种情况),结构变量s
将构建在堆栈上,然后将该结构的副本推入堆栈以调用print_student_info
。
这将构建堆栈帧并为局部变量分配32字节(0x20)(并保持16字节对齐)。在这种情况下,您的结构恰好是32个字节,遵循natural alignment rules:
6fa: 55 push %rbp
6fb: 48 89 e5 mov %rsp,%rbp
6fe: 48 83 ec 20 sub $0x20,%rsp
s
将以RBP-0x20开始,并以RBP-0x01(包括结尾)结束。该代码在堆栈上构建并初始化
s
变量(
student
结构)。
age
字段的32位int 0xa(10)放在RBP-0x20结构的开头。
Man
的32位枚举放在RBP-0x1c的字段
gen
中:
702: c7 45 e0 0a 00 00 00 movl $0xa,-0x20(%rbp)
709: c7 45 e4 00 00 00 00 movl $0x0,-0x1c(%rbp)
double
)由编译器存储在内存中。您无法在Intel x86处理器上使用一条指令在内存之间移动,因此编译器将双精度值1.30从内存位置RIP + 0x100移至矢量寄存器XMM0,然后将XMM0的低64位移至了RBP-0x18处的堆栈:
710: f2 0f 10 05 00 01 00 movsd 0x100(%rip),%xmm0 # 818 <_IO_stdin_used+0x48>
717: 00
718: f2 0f 11 45 e8 movsd %xmm0,-0x18(%rbp)
height
字段中,将值3放在堆栈上:
71d: c7 45 f0 03 00 00 00 movl $0x3,-0x10(%rbp)
class
的64位地址(在程序的只读数据部分中)被加载到RAX中,然后最终以RBP-0x08的形式移入堆栈中的
Tom
字段。尽管
name
的类型仅为32位(
class
类型),但它被填充为8个字节,因为以下字段
int
必须自然地在8字节边界上对齐,因为指针的大小为8字节。
724: 48 8d 05 e5 00 00 00 lea 0xe5(%rip),%rax # 810 <_IO_stdin_used+0x40>
72b: 48 89 45 f8 mov %rax,-0x8(%rbp)
72f: ff 75 f8 pushq -0x8(%rbp)
732: ff 75 f0 pushq -0x10(%rbp)
735: ff 75 e8 pushq -0x18(%rbp)
738: ff 75 e0 pushq -0x20(%rbp)
73b: e8 70 ff ff ff callq 6b0 <print_student_info>
740: 48 83 c4 20 add $0x20,%rsp
744: b8 00 00 00 00 mov $0x0,%eax
749: c9 leaveq
name
变量(结构)的代码的一部分。
关于c - C结构如何传递给汇编函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57766693/
我试图在图形模式下打印一个字符。通常当我打印我正在做的一个字符时: mov ah,14 ; ah=14 mov al,'x' int 10h ; print the character 这
我试图通过更改其中的一个字节来修改存储在内存中的字符串。我为此使用了 movb,但由于某种原因,给定内存位置的字节没有改变。 在 gdb 调试器上: 14 movb %al, (%r10) # nex
我一直在阅读一些汇编代码,并且开始发现调用指令实际上是与程序计数器相关的。 但是,每当我使用 Visual Studio 或 Windbg 进行调试时,它总是显示 call 0xFFFFFF ...这
我最近一直在使用 Visual C++ 中的内联汇编,我想知道是否可以直接向堆栈上的局部变量添加值,例如: push 5 add [esp], 7 这样做可以吗?我问这个问题是因为我在执行此操作时随机
我有下一个代码: mov al, -5 add al, 132 add al, 1 据我检查,溢出标志和进位标志将在第一个操作中设置,而在第二个操作中,仅设置溢出。 但我不明白为什么: 在无符号数中,
在 64 位 x86 汇编 nasm 中,如何将单个字节从寄存器移动到 .data 节中定义的内存位置? 我知道这有效 global _main section .data quotient db 0
我的汇编代码有问题。我想打印存储在寄存器 cx 中的数字,但是当我尝试打印它时,它打印的是 ascii 字符而不是 ascii 数字,所以我决定编写一个程序将 ascii char 转换为 ascii
为什么第 1B 行的跳转指令(例如)变成了 EBBD? 我知道“jmp”= EB但是BD是怎么计算的呢? 最佳答案 短跳转使用一个带符号的偏移量添加到 JMP 之后的指令地址。 例如,第一个 JMP
以下两者有什么区别: mov eax, [eax+4] 和 add eax, 4 mov eax, [eax] 如果不是,那么汇编器是否会选择哪个来进行某种优化? 最佳答案 这
看《The Shellcoder's Handbook》中的一些汇编和反汇编代码,发现一条指令的序列操作数是不一样的。 例如,在 assembly 上: mov ebx,0 并且,在反汇编时: mov
我有这个非常简单的汇编代码: start: add ax, 100 ; if ax overflow add to bx 1 jmp start 但我不知道如何检测 ax 寄存器溢出,有人可以帮
在 64 位 x86 汇编 nasm 中,如何将单个字节从寄存器移动到 .data 节中定义的内存位置? 我知道这有效 global _main section .data quotient db 0
我的汇编代码有问题。我想打印存储在寄存器 cx 中的数字,但是当我尝试打印它时,它打印的是 ascii 字符而不是 ascii 数字,所以我决定编写一个程序将 ascii char 转换为 ascii
我正在学习一些关于操作系统开发的教程,我发现了一篇关于多重引导 header 。这些是您必须定义的一些“神奇”值才能使用GRUB2。这些是命令: # Declare constants used f
为什么第 1B 行的跳转指令(例如)变成了 EBBD? 我知道“jmp”= EB但是BD是怎么计算的呢? 最佳答案 短跳转使用一个带符号的偏移量添加到 JMP 之后的指令地址。 例如,第一个 JMP
我正在尝试从内存中复制一些单词并使用汇编将其保存到另一个内存地址。我正在尝试为其编写代码,但我不确定其中的某些部分。我将简要描述我想要做什么。 源地址、目标地址和要复制的字数是函数的输入参数。 最佳答
当我们想要像这样创建一个初始化变量时: name db 'zara ali' 我们创建了一个字节大小变量,但我们在其中存储了一个字符串 这怎么可能?? 当我们使用这条指令时: MOV ecx, nam
我还是汇编的新手,我还不知道汇编中的许多命令代码。我想在 16 位寄存器中进行除法。我想打印它的内容。我知道我需要将寄存器的内容转换为 ASCII 进行打印,但同样,我的问题是除法。请帮我。 比如cx
使用有什么区别: c.eq.s $1, $2 bc1t L2 并使用: beq $1, $2, L2 如果他们做同样的事情,为什么有两种分支方式?如果它们不同,那么它们各自的好处是什么
源代码: int main() { int i; for(i=0, i : push rbp 2. 0x000055555555463b :
我是一名优秀的程序员,十分优秀!