c-为什么我自己在NASM上写的memcpy不能复制超过34000000字节



我正在学习nasm。我已经编写了一个简单的函数,可以将内存从源复制到目标。我在C.中测试

            section .text
            global _myMemcpy
_myMemcpy:
            mov eax, [esp + 4]
            mov ecx, [esp + 8]
            add [esp + 12], eax
            lp:
                   mov dl, [ecx]
                   mov [eax], dl
                   inc eax
                   inc ecx
                   cmp eax, [esp + 12]
                   jl lp
            endlp:
                    mov eax, [esp + 4]
                    ret

和C程序:

#include <string.h>
#define Times 340000000
extern void* _myMemcpy(void* dest, void* src, size_t size);
char sr[Times];
char ds[Times];
int main(void)
{
    memset(sr, 'a', Times);
    _myMemcpy(ds, sr, Times);
    return 0;
}

我目前使用的是Ubuntu操作系统。当我用$ nasm -f elf m.asm && gcc -Wall -m32 m.o p.c && ./a.out编译并链接这两个文件时,当Times的值小于34000000时,它可以正常工作。当它更大时,_myMemcpy只将源的最后一个字节复制到目标。我不知道问题出在哪里。每一个建议都是有用的。

您正在对指针进行签名比较;不要那样做。在这种情况下使用jne,因为您将始终在出口点达到完全相等

或者,如果您想要用指针进行关系比较,通常像jbjae这样的无符号条件最有意义。(将虚拟地址空间视为平坦的线性4GiB是正常的,最低地址为0,因此需要在该范围的中间进行增量才能工作(。

对于大于300iB大小的数组,以及PIE可执行文件的默认链接器脚本,显然其中一个数组将跨越带正负号的2GiB边界1。所以你计算的结束指针将是";否定的";如果你把它当作一个有符号的整数。(与x86-64不同,在x86-64中,跨越虚拟地址空间中间的非规范"洞"意味着数组永远无法跨越有符号环绕边界:指针比较在64位x86中应该是有符号的还是无符号的?-有时在那里使用有符号比较是有意义的。(

如果您单步执行并查看指针值,以及使用size += dest(add [esp + 12], eax(创建的内存值,则应该使用调试器看到这一点。作为一个带符号的操作,它溢出以创建一个负的end_pointer,而起始指针仍然是正的。pos < neg在第一次迭代中为false,因此循环退出,您可以在单步执行时看到这一点。


脚注1:在我的系统上,在GDB(禁用ASLR(下,在start将可执行文件映射到Linux的PIE默认基本地址(地址空间的下半部分的2/3,即0x5555…(之后,我用您的测试用例检查了地址:

  • sr0x56559040
  • ds0x6a998d40
  • dsp /x sizeof(ds) + ds处结束=0x7edd8a40

因此,如果它大得多,它将穿过0x80000000。这就是为什么340000000避免了你的错误,但更大的尺寸会暴露它

顺便说一句,在32位内核下,Linux默认内核和用户空间之间的地址空间为3:1,所以即使在那里也有可能发生这种情况。但在64位内核下,32位进程可以拥有整个4GiB的地址空间。(除了内核保留的一两个页面:另请参阅为什么在64位内核上的32位Linux进程中可以';t I mmap(MAP_FIXED(最高的虚拟页面?(?。这也意味着,像你正在做的那样(ISO C承诺这样做是有效的(,形成一个指向任何数组的过去一端的指针,不会换行,并且仍然会在指针上方与对象进行比较。(

在64位模式下不会发生这种情况:有足够的地址空间在用户和内核之间均匀分配,并且在高和低范围之间存在一个巨大的非规范漏洞。

相关内容

  • 没有找到相关文章

最新更新