2016-02-12 83 views
3

我使用的系統調用實現MMAP功能(我採取的mmap手動因爲一些原因。)mmap系統調用返回-14(-EFAULT?)

但我得到的返回值-14( - EFAULT,我用GDB檢查)蒙山此消息:

WARN Nar::Mmap: Memory allocation failed. 

這裏是功能:

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; 
} 

我寫了一個宏(用那樣的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傳遞給系統調用會出錯。 但我相信沒有什麼是錯我的結構:

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

也許東西是錯誤的交接結果值? 用CallSystem打開一個文件(不存在)給了我-2(-ENOENT),這是正確的。

編輯:CallSystem的完整源代碼。打開,寫入,關閉作品,但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 
+0

「CallSystem」的後期源代碼。 – gudok

+2

請提供最少但完整的示例代碼。此外,刪除所有宏(它們是邪惡的,考慮內聯函數或常量)和所有不必要的轉換。那麼,如果使用'strace'運行最小示例和使用庫存'mmap()'的等價物,會發生什麼?噢,請選擇C和C++之一。 –

+0

沒有代碼可以將MmapArgument的格式轉換爲系統調用所需的格式。此代碼只能通過魔法工作。 –

回答

2

目前還不清楚爲什麼你是通過你的CallSystem函數調用mmap,我會認爲這是你的任務的要求。

您的代碼的主要問題是您使用的是int 0x80。這隻有在所有傳遞到int 0x80的地址都可以用32位整數表示時纔有效。在你的代碼中情況並非如此。此行:

MmapArgument ma; 

將您的結構放置在堆棧上。在64位代碼中,堆棧位於可尋址地址空間的頂端,遠遠超出了可以用32位地址表示的地址。通常堆棧的底部是0x00007FFFFFFFFFFF區域中的某處。 int 0x80僅適用於64位寄存器的下半部分,因此基於堆棧的有效地址會被截斷,導致地址不正確。爲了使合適的64位的系統調用,優選使用syscall指令

64-bit System V ABI具有第A.2.1 AMD64 Linux內核約定上爲syscall接口的一般機制的部分。它說:

  1. 用戶級應用程序作爲整數寄存器用於使序列%RDI,%RSI,%RDX,RCX%,%R 8和R 9%。內核接口使用%rdi, %rsi,%rdx,%r10,%r8和%r9。
  2. 系統調用是通過syscall指令完成的。內核銷燬 寄存器%rcx和%r11。

我們可以通過將systemcallnum作爲最後一個參數創建您SystemCall代碼的簡化版本。作爲第7個參數,它將是堆棧中傳遞的第一個也是唯一的值。我們可以將該值從堆棧移動到RAX以用作系統呼叫號碼。前6個值通過寄存器傳遞,除了RCX之外,我們可以簡單地保留所有寄存器。 RCX必須移動到R10,因爲第4個參數在正常函數調用和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的情況下的系統調用是0×09。這可以在文件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(即strace ./a.out),你應該找到一條線,看起來像這樣,如果它的工作原理:

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

返回值會有所不同,但應符合什麼樣的示範程序顯示。

你應該能夠適應這個代碼,你正在做什麼。這至少應該是一個合理的起點。


如果你想在syscallnum傳遞作爲第一個參數CallSystem你將不得不修改彙編代碼,將所有的寄存器,以便他們的函數調用約定和syscall公約之間的正確對齊。我把這個作爲一個簡單的練習留給讀者。這樣做會產生效率較低的代碼。