Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 如何导航和描述此函数试图从给定的汇编代码中实现什么?_C_Assembly_X86_X86 64 - Fatal编程技术网

C 如何导航和描述此函数试图从给定的汇编代码中实现什么?

C 如何导航和描述此函数试图从给定的汇编代码中实现什么?,c,assembly,x86,x86-64,C,Assembly,X86,X86 64,在我的教科书中,我在x86-64汇编中获得了以下功能: foo: # line 1 movl $0, %eax # line 2 movl $0, %r8d # line 3 jmp .L2 # line 4 .L3: # line 5 addl

在我的教科书中,我在x86-64汇编中获得了以下功能:

foo:                             # line 1
       movl $0, %eax             # line 2
       movl $0, %r8d             # line 3
       jmp .L2                   # line 4
.L3:                             # line 5
       addl $1, %eax             # line 6
.L2:                             # line 7
       cmpl %esi, %eax           # line 8
       jge .L5                   # line 9
       movslq %eax, %rcx         # line 10
       cmpl %edx, (%rdi,%rcx,4)  # line 11
       jne .L3                   # line 12
       addl $1, %r8d             # line 13 
       jmp .L3                   # line 14
.L5:                             # line 15
       movl %r8d, %eax           # line 16
       ret                       # line 17
到目前为止,我认为我已经能够用适当的C类型计算出函数的C签名。由于大小说明符都是l,我假设它返回一个int,函数的所有参数也都是int

int foo(int arg1, int arg2, int arg3)
通过进一步检查,此函数还包含for循环。使用与所用寄存器名相对应的变量名,例如,将eax用于%eax,我认为循环结构如下:

for (eax = 0; eax < esi; eax++)
然而,我在描述这个函数实际要实现的功能时遇到了很多困难。跳转指令和movslq%eax,%rcx是让我感到困惑的东西。有人能帮我浏览这个函数的集合并帮助我理解它想要实现什么吗?我对汇编还比较陌生,我还不完全习惯阅读它。为了提高我对汇编的理解,任何帮助或建议都将不胜感激。

您看到的movslq实际上是指令,在AT&T语法中被称为movslq是的,愚蠢,我知道。后缀l long=4字节,q fourple=8字节表示两个操作数的大小。在EAX到RCX的情况下,这是一个简单的移动,符号扩展从较小的寄存器到较大的寄存器

跳转有点复杂,但您可以将程序集转换为伪C代码,如下所示:

傅: movl$0,%eax eax=0 movl$0,%r8d r8d=0 jmp.L2转到L2 .L3: 添加$1,%eax eax++ .L2: cmpl%esi,%eax如果eax>=esi jge.L5转到L5 movslq%eax,%rcx rcx=eax cmpl%edx、%rdi、%rcx,如果rdi[rcx]!=4edx jne.L3转到L3 加成$1,%r8d r8d++ jmp.L3转到L3 .L5: 移动%r8d,%eax 返回r8d 这里的要点是:

x86-64 System V调用约定使用RDI、RSI、RDX、[…]作为参数,RAX作为返回值。 我们可以看到RDI按原样使用,而其他两个参数被视为4字节参数:我们只看到ESI和EDX出现。 要返回的实际值位于R8D中,也是4个字节。 循环变量为EAX,初始cmpl%esi,%EAX;L5是循环的控制条件,这意味着ESI保存数组的总长度。此外,JGE指令是有符号的,这意味着EAX和ESI被视为有符号的4字节整数,即C int。 比较cmpl%edx,%rdi,%rcx,4使我们了解到rdi用作4字节元素数组的基址,例如int,因为此操作取消了rdi+rcx*4的引用,以获取AT&T术语l后缀中的长4字节。 该比较检查EDX第二个参数是否等于RDI引用的数组中位置RCX处的元素。我们可以看到RCX直接来自EAX,因此这是在检查当前索引处的元素。 如果比较失败,我们就不增加R8D。否则,如果成功,我们将增加R8D。这是反向逻辑,但我们可以简单地否定它,以理解只有当当前索引处的元素等于EDX第三个参数时,R8D才会增加。 考虑到所有这些,我们可以继续简化代码:

int funcint*rdi,int esi,int edx{ int-eax=0; int r8d=0; for;;eax++{ 如果eax>=esi 返回r8d; int rcx=eax; 如果rdi[rcx]!=edx 持续 r8d++; } 返回r8d; } 此时,应该非常清楚该函数的作用:它扫描一个整数数组,计算与给定值匹配的元素数

我们可以更简洁地将其改写为:

int funcint*rdi,int esi,int edx{ int r8d=0; int eax; 对于eax=0;eax谢谢你富有洞察力的回答。它给了我很多帮助,当我做其他问题,类似的一个我张贴。我想我只是没有考虑x86-64过程调用约定。