使用汇编语言 x86 中的 brk() 调用

9

我想动态分配堆内存,然后在这些内存地址中分配值。我知道如何分配内存,但是如何将寄存器中的值分配给第一个动态内存地址呢?以下是我的代码:

    push rbp
    mov rbp, rsp            ;initialize an empy stack to create activation records for the rest of the subroutines                                                                                                                        

    mov rax, 0x2d           ;linux system call for brk()                                                                                                                                                                                  
    mov rbx, 0x0            ;to get the adress of the first adress we are allocating we must have 0 in rbx                                                                                                                                
    int 0x80                ;calls the linux operating system kernel for assistance                                                                                                                                                       
    mov [brk_firstLocation], rax ;the first position in the heap will be returned in rax thus i save the first loaction in a varable called brk_firstLocation                                                                             

    mov rbx, rax            ;the memory adress of the start of the heap is moved in rbx                                                                                                                                                   
    add rbx, 0x14           ;we want 5 bytes worth of data alocated in the heap, so the start adress plus 20 bits                                                                                                                         
    mov rax, 0x2d           ;linux system call for brk()                                                                                                                                                                                  
    int 0x80                ;calls the linux operating system kernel for assistance

例如,如果我想将 rax 中的值 movbrk_firstLocation 中,我该怎么做?
4个回答

10

其他人指出了你的代码中存在的一些问题。我想补充一点,即你不应该将20个比特添加到当前断点(或者像add rbx, 20那样添加20个字节),而应该只添加5个字节。

此外,你的第一个系统调用参数不会在rbx中,而是在rdi中。64位系统调用ABI使用不同的系统调用号、不同的寄存器和不同的指令(syscall而不是int 0x80)与32位ABI(仍可在64位进程中使用)不同。另请参阅标签wiki以获取更多ABI链接。

以下是你代码应该的样子:

push rbp
mov rbp, rsp

;; sys_brk(0)
mov   rax, 12         ; 12 is SYS_brk (/usr/include/asm/unistd_64.h)
mov   rdi, 0          ; rdi for first syscall arg in the 64-bit ABI, not rbx
syscall               ; syscall, not int 0x80, for the 64-bit ABI

mov   qword [brk_firstLocation], rax

;; sys_brk(old_break + 5)
lea   rdi, [rax + 5]  ; add 5 bytes to the break point
mov   rax, 12
syscall               ; set the new breakpoint

此时,您可以使用brk_firstLocation作为指向您想要存储在堆上的任何5字节结构的指针。以下是如何在该内存空间中放置值:

mov   rdi, [brk_firstLocation]   ; load the pointer from memory, if you didn't already have it in a register

mov   byte [rdi], 'A'            ; a char in the first byte
mov   [rdi+1], ecx               ; a 32-bit value in the last 4 bytes.

1
终于有一个使用64位系统调用ABI并正确使用它的答案了。(https://dev59.com/Q3E85IYBdhLWcg3w8IXK) - Peter Cordes
我修复了一些简单但严重的错误,我认为现在已经完全正确(也是这个问题上唯一正确的答案)。 - Peter Cordes

2

int 80h 只用于32位系统调用。64位系统请使用syscall

在汇编中,调用两次sys_brk是多余的 - 你总是知道程序数据的结束位置。只需在那里放置一个标签,就可以得到地址。

以这种方式分配内存小于一页是没有意义的,因为它会以4KB的块分配。

需要理解的是,sys_brk不是堆管理函数,而是低级内存管理。


1
brk内存区域不是程序结束后紧接着的,而是随机的。 - Timothy Baldwin

1
我看到了一些问题:
  • 在x86(32位)上,0x2d是brk系统调用;在x86_64上,则为0xc。
  • brk设置数据段的末尾;成功返回0,失败返回-1。它不会返回"堆中的第一个位置"。这来自于链接器设置为未初始化预分配数据的末尾的符号_end。

所以你需要像这样的东西:

    mov [brk_firstloaction], _end
    mov rbx, [brk_firstlocation]
    add rbx, 0x14         ; space for 5 dwords (20 bytes)
    mov rax, 12
    int 0x80

1
可能sys_brk的C包装器返回0或-1。这是系统调用的常见情况。参考:http://fresh.flatassembler.net/lscr/data/045.html - johnfound
2
实际的系统调用确实会返回新内存区域的末尾或者如果参数为0,则返回当前的末尾。 - automaton
1
即使在64位进程中,“int 0x80”始终是32位ABI。 OP需要“eax = 12” /“syscall”,第一个参数为“rdi”。另请参阅“x86-64上的UNIX和Linux系统调用的调用约定是什么”。 - Peter Cordes
我在哪里可以找到更多关于 _end 符号的信息?是否有符号列表可以参考? - supmethods

0

按照您的做法,调用一次以检索堆的当前底部,然后移动堆的顶部(即brk值)。但是,您的代码在使用64位寄存器集r.x时不正确。如果您的Linux是32位的(如使用45作为系统调用号所暗示的那样),则需要使用32位寄存器集:

mov eax, 45                            ; brk                                                                                                                                                                                  
mov ebx, 0                             ; arg 1: 0 = fail returning brk value in rax                                                                                                                               
int 0x80                               ; syscall
mov dword ptr [brk_firstLocation], rax ; save result
mov eax, 45                            ; brk                                                                                                                                                                                  
mov ebx, 4                             ; arg 1: allocate 4 bytes for a 32-bit int                                                                                                                                
int 0x80                               ; syscall

mov eax, dword ptr [brk_firstLocation] ; reload rax with allocated memory address.
mov dword ptr [eax], 42 ; use result: store 42 in newly allocated storage

当然,您可以重新加载保存的值以便多次重复使用。


这里是否仍存在32位int 0x80和64位syscall内核调用的混淆? - Frank Kotler
1
OP正在编写64位代码,因此在涉及指针的系统调用中使用64位系统调用ABI是必不可少的。这与i386 ABI完全不同(但仍可用于64位进程)。 https://dev59.com/fmoy5IYBdhLWcg3wfeMR#8510471 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接