如何在汇编中使用 malloc

3

我正在尝试在nasm的Intel x86_64汇编中使用malloc动态分配内存,但是我不知道如何使用它。

例如,如果您想分配8个字节的内存区域,应该将8推送到堆栈上,然后调用malloc,像这样吗?

extern _malloc

section .text
    global _my_function

_my_function:
    push 20
    call _malloc
    ret

我应该将20移动到rdi寄存器,这通常是函数的第一个参数,就像这样吗?

extern _malloc

section .text
    global _my_function

_my_function:
    mov rdi, 20
    call _malloc
    ret

我尝试了这两个方法,但都没有起作用,而且我也没有在nasm中找到任何malloc文档。

我正在尝试使用malloc重新编写字符串库中的strdup函数,以下是我的实际代码:

extern _ft_strlen
extern _ft_strcpy
extern _malloc

section .text
    global _ft_strdup

_ft_strdup:
    push rsi
    push rdi                ; rdi = str
    call _ft_strlen         ; rax = ft_strlen(str)
    mov r9, rdi             ; save rdi (str) into r9
    mov rdi, rax            ; rdi = len
    inc rdi                 ; rdi = len + 1
    call _malloc            ; rax = new_str (allocated)
    cmp eax, 0              ; if malloc failed
    je _failure             ; └──► return NULL
    mov rsi, r9             ; rsi = str
    mov rdi, rax            ; rdi = new_str
    call _ft_strcpy         ; ft_strcpy(new_str, str)
    pop rdi
    pop rsi
    ret

_failure:
    xor rax, rax
    pop rdi
    pop rsi
    ret

section .text
    global _ft_strcpy

_ft_strcpy:
    push rdi
    push rsi
    jmp _loop

_loop:
    mov r8b, BYTE [rdi]     ; Save *dst into r8b
    mov r9b, BYTE [rsi]     ; Save *src into r8b
    cmp r9b, 0              ; if *src == '\0'
    je finish               ; └──► exit from _loop
    mov [rdi], r9           ; *dst = r9 (r9 = *src)
    inc rdi                 ; dst++
    inc rsi                 ; src++
    jmp _loop

finish:
    mov [rdi], r9           ; *dst = r9 (r9 = *src = '\0')
    pop rsi
    pop rdi
    mov rax, rdi            ; rax = initial value of dst
    ret                     ; Return rax (dst pointer)

section .text
    global _ft_strlen

_ft_strlen:
    push rdi
    xor rax, rax            ; rax = 0
    jmp _loop

_loop:
    cmp [rdi], byte 0       ; if *str == '\0'
    je finish               ; └──► exit from _loop
    inc rax                 ; rax++ (len)
    inc rdi                 ; str++
    jmp _loop

finish:
    pop rdi
    ret                     ; Return rax (len)

当我调用ft_strdup时,sanitize会报告一个“在未知地址0x000000000000上的SEGV”错误。
我是这样调用函数的:
int main(int ac, char **av)
{
    char new_str = ft_strdup(av[1]);
    return (0);
}

4
是的,请展示所有代码作为一个最小完整可运行实例(MCVE)。同时使用调试器定位错误并向后追踪其原因。话虽如此,mov rdi, 20; call _malloc 应该是可以的。请注意返回值在 rax 而不仅仅是 eax 中,所以进行 NULL 检查时需要测试它(但这不是问题的原因)。此外,不清楚 mov rax, r12 的目的是什么。根据标准的调用惯例,rdi 是调用方保存寄存器,所以在 _ft_strlen 后不能依赖于它的值。对于 _malloc 调用中的 r9 也是同样的情况,这可能是你的问题所在。 - Jester
哦,是的,我忘记删掉 mov rax, r12 这条语句了。我的错,让我编辑帖子添加其他函数。 - lfalkau
5
你要为哪个操作系统编程?请添加相应的标签。 - fuz
1
@fuz 那是为什么呢?在我看来是正确的。而且它也能工作。问题就像我说的,是 malloc 销毁了他用来保存字符串指针的 r9 - Jester
是的,没错!你可以发布答案了!我只是在 _malloc 调用之前添加了一个 push r9 语句,在其后添加了一个 pop r9,它起作用了。 - lfalkau
显示剩余2条评论
1个回答

2

解决方案 - jester

在nasm中使用Intel x86_64的Malloc

  • 要分配的字节数放入rdi寄存器。
  • 返回的内存区域指针在rax寄存器中。
mov rdi, 20
call _malloc

根据标准调用约定,在调用_ft_strlen后您不能依赖rdi的值,对于_malloc调用后的r9也是一样。这是因为这两个寄存器都是调用者保存的寄存器
为了保留r9的值,
push r9
call _malloc
pop r9

汇编语言编程技巧:
  • 将你的所有代码显示为最小可重现示例
  • 使用调试器定位错误并向后追溯导致错误的原因(你可以使用gdb和一个很棒的.gdbinit文件)

1
请注意,ABI要求在“call”之前将RSP对齐到16字节。在功能相同的情况下,单个“push”会使堆栈失去对齐。通常编译器在函数入口处一次性保留空间以使用“mov”而不是在单个调用周围使用“push”/“pop”来溢出/重新加载内容。如果您想要一个值在调用之间保持不变,通常您只需将其保存在类似RBX或R15的调用保留寄存器中(您在整个函数周围保存/恢复一次,而不是在函数内部每次调用时都保存/恢复)。 - Peter Cordes

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