汇编:这个程序中movl data_items(,%edi,4), %eax的作用是什么

Kat*_*atz 3 x86 assembly att addressing-mode array-indexing

该程序(来自 Jonathan Bartlett 的《从头开始编程》)循环访问内存中存储的所有数字,并将.long最大的数字放入 EBX 寄存器中,以便在程序完成时查看。

.section .data
data_items:
    .long 3, 67, 34, 222, 45, 75, 54, 34, 44, 33, 22, 11, 66, 0

.section .text
.globl _start

_start:
    movl $0, %edi
    movl data_items (,%edi,4), %eax
    movl %eax, %ebx
start_loop:
    cmpl $0, %eax
    je loop_exit
    incl %edi
    movl data_items (,%edi,4), %eax
    cmpl %ebx, %eax
    jle start_loop
    movl %eax, %ebx
    jmp start_loop
loop_exit:
    movl $1, %eax
    int $0x80
Run Code Online (Sandbox Code Playgroud)

我不确定(,%edi,4)这个程序的目的。我读到逗号是为了分隔,而 4 是为了提醒我们的计算机数据项中的每个数字都是 4 个字节长。既然我们已经用.long声明了每个数字都是4个字节,为什么我们还需要在这里再声明一次呢?另外,有人可以更详细地解释这两个逗号在这种情况下的用途吗?

Mat*_*lia 5

在 AT&T 语法中,内存操作数具有以下语法1

displacement(base_register, index_register, scale_factor)
Run Code Online (Sandbox Code Playgroud)

基准、分度和位移分量可以任意组合使用,并且每个分量都可以省略

但显然,如果省略基址寄存器,则必须保留逗号,否则汇编器将无法理解您遗漏了哪些组件。

所有这些数据将结合起来计算您指定的地址,公式如下:

effective_address = displacement + base_register + index_register*scale_factor
Run Code Online (Sandbox Code Playgroud)

(顺便说一下,这几乎正是您在英特尔语法中指定的方式)。

因此,有了这些知识,我们就可以解码您的指令:

movl data_items (,%edi,4), %eax
Run Code Online (Sandbox Code Playgroud)

匹配上面的语法,您会看到:

  • data_items是位移;
  • base_register被省略,所以不代入上式;
  • %ediindex_register;
  • 4scale_factor

因此,您告诉 CPU从该位置移动寄存器data_items+%edi*4%eax

*4是必要的,因为数组的每个元素都是 4 字节宽,因此要将索引(在 中%edi)转换为距数组开头的偏移量(以字节为单位),必须将其乘以 4。

既然我们已经用.long声明了每个数字都是4个字节,为什么我们还需要在这里再声明一次呢?

汇编器是低级工具,对类型一无所知。

  • .long不是一个数组声明,只是一个指示汇编器发出与其参数的 32 位表示相对应的字节的指令;
  • data_items不是一个数组,只是一个被解析到某个内存位置的符号,与其他标签完全相同;事实上,.long在它后面放置指令对于汇编器来说没有特别的意义。

笔记

  1. 从技术上讲,还会有段说明符,但考虑到我们正在讨论 Linux 上的 32 位代码,我将完全省略段,因为它们只会增加混乱。

  • @PeterCordes:啊啊,现在它已经成为一个挑战;此处已修复,此处已修复(https://en.wikibooks.org/w/index.php?title=X86_Assembly%2FGAS_Syntax&type=revision&diff=3360455&oldid=3313138);希望他们会接受修改。 (2认同)