mmap系统调用返回-14(-EFAULT ??)

我正在使用系统调用来实现mmap函数(由于某些原因,我正在手动实现mmap)。

但我得到的返回值-14(-EFAULT,我检查了GDB)whith这个消息:

WARN Nar::Mmap: Memory allocation failed. 

这里是function:

 void *Mmap(void *Address, size_t Length, int Prot, int Flags, int Fd, off_t Offset) { MmapArgument ma; ma.Address = (unsigned long)Address; ma.Length = (unsigned long)Length; ma.Prot = (unsigned long)Prot; ma.Flags = (unsigned long)Flags; ma.Fd = (unsigned long)Fd; ma.Offset = (unsigned long)Offset; void *ptr = (void *)CallSystem(SysMmap, (uint64_t)&ma, Unused, Unused, Unused, Unused); int errCode = (int)ptr; if(errCode < 0) { Print("WARN Nar::Mmap: Memory allocation failed.\n"); return NULL; } return ptr; } 

我写了一个macros(使用像malloc()函数):

 #define Malloc(x) Mmap(0, x, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) 

我用这样的:

 Malloc(45); 

我看了手册页。 我在mmap手册页上找不到关于EFAULT的信息,但是我在mmap2手册页上发现了一些关于EFAULT的信息。

EFAULT从用户空间获取数据的问题。

我认为这意味着将结构传递给系统调用是有问题的。 但是我相信我的结构没有什么问题:

 struct MmapArgument { unsigned long Address; unsigned long Length; unsigned long Prot; unsigned long Flags; unsigned long Fd; unsigned long Offset; }; 

可能是交错结果值有问题吗? 用CallSystem打开一个文件(不存在)给了我-2(-ENOENT),这是正确的。

编辑:CallSystem的完整来源。 打开,写入,closures作品,但mmap(或old_mmap)不起作用。 所有的论点都通过了。

 section .text global CallSystem CallSystem: mov rax, rdi ;RAX mov rbx, rsi ;RBX mov r10, rdx mov r11, rcx mov rcx, r10 ;RCX mov rdx, r11 ;RDX mov rsi, r8 ;RSI mov rdi, r9 ;RDI int 0x80 mov rdx, 0 ;Upper 64bit ret ;Return 

目前还不清楚为什么你通过你的CallSystem函数调用mmap ,我会认为这是你的任务的要求。

你的代码的主要问题是你使用int 0x80 。 如果所有传递给int 0x80的地址都可以用一个32位整数表示,那么这个函数只能工作。 在你的代码中情况并非如此。 这一行:

 MmapArgument ma; 

把你的结构放在堆栈上。 在64位代码中,堆栈位于可寻址地址空间的顶端,远远超出了可以用32位地址表示的地址。 通常堆栈的底部是0x00007FFFFFFFFFFF区域的某处。 int 0x80仅适用于64位寄存器的下半部分,所以有效的基于堆栈的地址会被截断,导致地址不正确。 要进行正确的64位系统调用,最好使用syscall指令

64位系统V ABI在A.2.1 AMD64 Linux coreel约定中有关于syscall接口一般机制的一节。 它说:

  1. 用户级应用程序使用整数寄存器来传递序列%rdi,%rsi,%rdx,%rcx,%r8和%r9。 内核接口使用%rdi,%rsi,%rdx,%r10,%r8和%r9。
  2. 系统调用是通过系统调用指令完成的。 内核破坏寄存器%rcx和%r11。

我们可以通过将systemcallnum作为最后一个参数来创建SystemCall代码的简化版本。 作为第七个参数,它将是堆栈中传递的第一个也是唯一的值。 我们可以把这个值从栈中移到RAX中作为系统调用号。 前6个值被传递到寄存器中,除了RCX,我们可以简单地保留所有的寄存器。 必须将RCX移到R10,因为正常的函数调用和Linux内核SYSCALL约定之间的第四个参数不同。

用于演示目的的一些简化代码可能如下所示:

 global CallSystem section .text CallSystem: mov rax, [rsp+8] ; CallSystem 7th arg is 1st val passed on stack mov r10, rcx ; 4th argument passed to syscall in r10 ; RDI, RSI, RDX, R8, R9 are passed straight through ; to the sycall because they match the inputs to CallSystem syscall ret 

C ++看起来像:

 #include <stdlib.h> #include <sys/mman.h> #include <stdint.h> #include <iostream> using namespace std; extern "C" uint64_t CallSystem (uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, uint64_t arg6, uint64_t syscallnum); int main() { uint64_t addr; addr = CallSystem(static_cast<uint64_t>(NULL), 45, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0, 0x9); cout << reinterpret_cast<void *>(addr) << endl; } 

mmap的情况下,系统调用是0x09。 这可以在文件asm/unistd_64.h

 #define __NR_mmap 9 

其余的参数是典型的mmap新形式。 从手册:

void * mmap(void * addr,size_t length,int prot,int flags,int fd,off_t offset);

如果你的运行在你的可执行文件上(例如strace ./a.out ),你应该找到一个如下的代码:

 mmap(NULL, 45, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fed8e7cc000 

返回值会有所不同,但它应该与演示程序显示的内容相匹配。

你应该能够使这个代码适应你正在做的事情。 这至少应该是一个合理的出发点。


如果要将syscallnum作为第一个参数传递给CallSystem ,则必须修改汇编代码以移动所有寄存器,以便在函数调用约定和syscall约定之间正确对齐。 我把这个作为一个简单的练习留给读者。 这样做会产生效率较低的代码。