【问题标题】:endianness doesn't affect writing but reading in memory字节序不影响写入但在内存中读取
【发布时间】:2021-12-12 13:17:23
【问题描述】:

我已经得出一个结论,即在小端和大端中。

我们从左到右写入内存,这意味着数字 0x00FF 在两个系统中都将被写入如下:

1000:00

1001:FF

但是字节序的读取不同。

我们会在 little endian 中读取这两个字节

1000:00

1001:FF

作为 0xFF00 和大端,我们将其读取为 0x00FF

现在你可能会说为什么如果我这样做:

mov word [esp],0x00FF

在 little endian 处理器中,结果将是 0x00FF,但我说在 little endian 中,结果将是 0xFF00,所以它完全揭穿了我所说的。

好吧,看来汇编程序只是将数字反转为 0xFF00 看看:

如果汇编器不反转数字,我们会将其读取为 0xFF00。

所以基本上是因为汇编器把它颠倒了,我们把数字写成

1000:FF

1001:00

在内存中,我们将从最低有效字节开始读取它,因此我们将得到 0x00FF

我是对的,还是它的工作方式不同?

【问题讨论】:

  • 数字 0x00FF 在两个系统中都会写成如下。如果您将其写为单个指令中的一个字而不是一次一个字节,则不正确。
  • 如果计算机系统以一种格式写入字节并以另一种格式读取它们,它将如何工作?内存的全部意义在于能够往返值 - 写入的内容在读取时应该返回相同的值。两种字节序系统都将字节从低位写入高位(地址方面),只是小字节序中的低字节在较大字长的情况下不太重要(读取和写入)。
  • @AngryJohn 汇编器不会convert 0x00FF 到 0xFF00。发生的情况是,在小端架构中,如果您将一个值为 0x00FF 的字写入内存,它将 LSB 写入较低地址,将 MSB 写入较高地址。回读与此一致。说 0x00FF 被“转换”为 0xFF00 是说交换了字节以使 FF 为 MSB,但事实并非如此。正如我在第一条评论中提到的,您在两个系统中以相同方式编写的语句(00 写入地址 1000,FF 写入地址 1001)是不正确的。
  • @AngryJohn 我无法解释这张图片,因为我不知道你的图片来自哪里,也不知道在每个平台上执行了哪些指令导致了这张图片。您没有提供足够的信息。
  • Are machine code instructions fetched in little endian 4-byte words on an Intel x86-64 architecture? 几乎重复 - x86 机器码以 little-endian 形式存储立即数,因此存储立即数指令将它们的立即数从机器码原封不动地复制到目标位置。

标签: assembly x86 fasm


【解决方案1】:

字节序是跨越多个存储单元(通常是字节)的数值之间的关系,可以表示为一对用于分解和重组的公式——用于将单个值(需要多个字节)转换为字节序列,以及从字节序列返回到单个值。

(字节序并没有告诉我们处理器如何执行这些操作,只是它们根据下面的公式工作。所以,具体来说,我们不知道使用什么时间顺序来满足公式;公式是与时间无关,但只对序列中的字节顺序敏感。)

例如,在 16 位中,我们有一个值 0x1234,并将其作为字节序列存储在内存中,即低字节存储在低地址,高字节存储在高地址,其中高地址 = 低地址 + 1。


以下公式使用 little endian 分解值:

lower byte  = 0x1234 & 0x00FF            = 0x34
higher byte = 0x1234 / 256 = 0x1234 >> 8 = 0x12

小端重组公式是

value = lower byte + higher byte * 256 = 0x34 + 0x12 * 256 = 0x1234

对于 big endian,公式(与 little endian 相比)交换了哪个字节被乘/除:

lower byte  = 0x1234 / 256    = 0x1234 >> 8 = 0x12
higher byte = 0x1234 & 0x00FF =               0x34

和重组:

value = lower byte * 256 + higher byte = 0x12 * 256 + 0x34 = 0x1234

这些公式内置在处理器中并且是预先众所周知的,因此,当汇编器组装数据时,如下所示:

.data
dw 0x1234

它知道 (1) 这是 16 位数据并且 (2) 目标硬件是小端。因此,它将按照小端分解的公式将 0x34、0x12 作为字节放入内存中。 (同样,这不是时间顺序,而是相对顺序。)

对于指令,我们可以说汇编器根据机器代码指令集架构对指令和所需的任何立即数进行编码。当立即数具体化时,它会作为指令解码的一部分返回。由于英特尔处理器的工作方式,机器代码指令中的编码也将出现小端,但是,编码可能比用汇编语言编写的立即数的完整大小短。无论如何,处理器都会在内部重新构造适当的常量,然后使用它。如果将立即数存储到内存中,它将使用小端分解公式来创建两个字节的序列来存储,就像将寄存器的值存储到内存中一样。


由于公式对 (decomp/recomp) 读取最后写入为 16 位原始值的 16 位位置,因此原始值返回。只有当我们将该位置视为单个字节时,我们才需要关注字节顺序。

不幸的是,调试器将内存作为单个字节转储,当数据是多字节数据时,我们会遇到字节序。无法仅从内存转储中判断其中存储了哪种值,是 16 位还是 8 位值。然而,这些信息在程序及其机器代码指令中,以它们处理这些位置的方式(关于它是使用 16 位内存访问还是 8 位内存访问)。


当程序始终以相同的方式使用相同的内存时,它将获得预期值。但是在汇编程序中存在很多逻辑错误的机会。此类错误包括使用错误的大小、使用错误的符号、初始化失败。高级语言具有阻止前两种的类型,并且好的语言还具有检测未初始化变量的功能。但在机器代码中,每条指令都会重复物理存储的相关处理以实现一致性。

(需要明确的是,将 16 位值视为字节序列并不总是错误的,有时这是必要的,即在文件中或汇编器/编译器中存储数字时)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-27
    相关资源
    最近更新 更多