assembly - x86 中是否允许访问跨越零边界的内存?

标签 assembly x86 x86-64 intel cpu-architecture

在 x861 中是否允许单次访问跨越 00xFFFFFF... 之间的边界?

例如,假设 eax(64 位中的 rax)为零,则允许以下访问:

mov ebx, DWORD [eax - 2]

我对 x86(32 位)和 x86-64 都感兴趣,以防答案不同。

<小时/>

1 当然,考虑到该区域已映射到您的进程等中。

最佳答案

我刚刚测试了这个 EFI 程序。 (并且它按预期工作了。)如果您想重现此结果,您将需要 efi_printf 的实现,或其他方式来查看结果。

#include <stdint.h>
#include "efi.h"

uint8_t *p = (uint8_t *)0xfffffffffffffffcULL;

int main()
{
    uint64_t cr3;
    asm("mov %%cr3, %0" : "=r"(cr3));
    uint64_t *pml4 = (uint64_t *)(cr3 & ~0xfffULL);

    efi_printf("cr3 %lx\n", cr3);
    efi_printf("pml4[0] %lx\n", pml4[0]);
    uint64_t *pdpt = (uint64_t *)(pml4[0] & ~0xfffULL);
    efi_printf("pdpt[0] %lx\n", pdpt[0]);
    if (!(pdpt[0] & 1)) {
        pdpt[0] = (uint64_t)efi_alloc_pages(EFI_BOOT_SERVICES_DATA, 1) | 0x03;
        efi_printf("pdpt[0] %lx\n", pdpt[0]);
    }
    uint64_t *pd = (uint64_t *)(pdpt[0] & ~0xfffULL);
    efi_printf("pd[0] %lx\n", pd[0]);
    if (!(pd[0] & 1)) {
        pd[0] = (uint64_t)efi_alloc_pages(EFI_BOOT_SERVICES_DATA, 1) | 0x03;
        efi_printf("pd[0] %lx\n", pd[0]);
    }
    if (!(pd[0] & 0x80)) {
        uint64_t *pt = (uint64_t *)(pd[0] & ~0xfffULL);
        efi_printf("pt[0] %lx\n", pt[0]);
        if (!(pt[0] & 1)) {
            pt[0] = (uint64_t)efi_alloc_pages(EFI_BOOT_SERVICES_DATA, 1) | 0x03;
            efi_printf("pt[0] %lx\n", pt[0]);
        }
    }

    efi_printf("[0] = %08x\n", *(uint32_t *)(p+4));

    efi_printf("pml4[0x1ff] %lx\n", pml4[0x1ff]);
    if (pml4[0x1ff] == 0) {

        uint64_t *pt = (uint64_t *)efi_alloc_pages(EFI_BOOT_SERVICES_DATA, 4);
        uint64_t x = (uint64_t)pt;

        efi_printf("pt = %p\n", pt);

        pml4[0x1ff] = x | 0x3;
        pt[0x1ff] = x + 0x1000 | 0x3;
        pt[0x3ff] = x + 0x2000 | 0x3;
        pt[0x5ff] = x + 0x3000 | 0x3;

        *(uint32_t *)p = 0xabcdabcd;
        *(uint32_t *)(p + 4) = 0x12341234;

        efi_printf("[0] = %08x\n", *(uint32_t *)(p+4));
        efi_printf("[fffffffffffc] = %08x\n", *(uint32_t *)(x + 0x3ffc));

        *(uint32_t *)(p + 2) = 0x56785678;

        efi_printf("p[0] = %08x\n", ((uint32_t *)p)[0]);
        efi_printf("p[1] = %08x\n", ((uint32_t *)p)[1]);
    }

    return 0;
}

如果它按预期工作,最后 4 行应该是:

[0] = 12341234
[fffffffffffc] = ABCDABCD
p[0] = 5678ABCD
p[1] = 12345678

从内存的最后一个 16 位字开始写入值 0x56785678,并且应该回绕到内存的第一个 16 位字。

<小时/>

注意:p需要是全局变量,否则GCC将*(p+4)更改为ud2

关于assembly - x86 中是否允许访问跨越零边界的内存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47702410/

相关文章:

c - C程序中的汇编代码(为masm编写)(用gcc编译)......怎么样?

assembly - `movl (%eax), %eax`中的括号是什么意思?

architecture - 将ARM指令转换为i386指令

编译器 : Understanding assembly code generated from small programs

x86 - x86_64 CPU 能否在流水线的同一阶段执行两个相同的操作?

assembly - 操作码的十六进制值

performance - 每个时钟性能。 - 我可以对同一条指令使用不同的寄存器吗?

assembly - 有人可以解释一下这个直接组装的 x86 JMP 操作码吗?

x86-64 - AMD 架构上是否有与 Intel 的 MSR_SMI_COUNT 等效的寄存器?

math - 如何使用 32 位除法指令执行 64 位除法?