【问题标题】:Assembly: What is the purpose of movl data_items(,%edi,4), %eax in this program汇编:这个程序中 movl data_items(,%edi,4), %eax 的目的是什么
【发布时间】:2018-01-10 01:14:55
【问题描述】:

此程序(来自 Jonathan Bartlett 的 Programming From the Ground Up)循环使用 .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

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

【问题讨论】:

  • 为什么要在这里再做一次? 因为这是汇编语言。一切都只是字节。如果您想加载重叠两个相邻数组元素的 4 个字节,您可以,因为 x86 支持未对齐加载。您可以自行编写每条指令,以完全完成它需要做的事情。
  • 扩展彼得的评论:机器代码和 CPU 不关心您的来源,因此使用 long 指令定义的字节信息对机器不可见。 .long 0x12345678.byte 0x78, 0x56, 0x34, 0x12 都将产生完全相同的 4 字节长序列,因此在运行时无法判断这些字节是如何定义的或它们的目的是什么。这取决于正在运行的代码,以决定如何访问和使用它们。 (如果您在编程时想要“类型”,请使用 C++ 等高级语言,汇编有不同的目标,使机器可以按原样访问)
  • 这仍然很有趣,我删除了 4 和逗号,程序仍然可以正常工作,当我删除括号时,我得到一个“无效操作数”错误

标签: assembly x86 comma


【解决方案1】:

在 AT&T 语法中,内存操作数有 the following syntax1:

displacement(base_register, index_register, scale_factor)

base、index、displacement组件可以任意组合使用,每个组件都可以省略

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

所有这些数据都被结合起来计算您指定的地址,使用以下公式:

effective_address = displacement + base_register + index_register*scale_factor

(顺便说一句,这几乎正是您在 Intel 语法中指定的方式)。

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

movl data_items (,%edi,4), %eax

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

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

因此,您是在告诉 CPU mov从位置 data_items+%edi*4 到寄存器 %eaxlong。

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

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

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

  • .long 不是一个数组声明,只是一个指令,让汇编器发出与其参数的 32 位表示对应的字节;
  • data_items 不是一个数组,只是一个被解析到某个内存位置的符号,就像其他标签一样;事实上,您在它之后放置了一个 .long 指令,这对汇编程序没有特别的意义。

备注

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

【讨论】:

  • offset_register 不是一个很好的名字。在英特尔术语中,它实际上是索引。它是按比例因子缩放的寄存器,即使该因子是1offset 在这里已经有了特定的技术含义(地址中添加到段基础的部分)。
  • 也许他们试图避免“索引”,因为他们想讨论使用 base + disp32 寻址模式(如 data_items(%edi))的数组索引,但那不是“索引寻址”模式”在英特尔术语中。编码中没有 SIB 字节,与 Intel SnB 系列 CPU 上的 matters for micro-fusion 不同。
  • @PeterCordes 是的,我也对这个名字的选择感到有点惊讶,但我很懒惰并坚持了 =)。当我回到家时,我会让它符合英特尔手册。
  • 是的,如果我不是懒惰的话,我会编辑 Wikibook。但基本上我试图将我的努力限制在纠正 SO 上的错误上。如果我真的尝试修复整个网络,我将永远没有时间做其他事情(或者更快地精疲力尽)。
  • @PeterCordes:啊啊,现在变成了挑战;固定在这里,固定there;希望他们会接受编辑。
猜你喜欢
  • 2021-01-15
  • 2011-10-12
  • 2017-03-29
  • 2012-10-10
  • 2015-01-25
  • 1970-01-01
  • 1970-01-01
  • 2011-07-31
  • 1970-01-01
相关资源
最近更新 更多