【问题标题】:Assembly : Returning 64 bits pointer address (nasm unix x64)汇编:返回 64 位指针地址(nasm unix x64)
【发布时间】:2015-03-31 03:03:52
【问题描述】:

我尝试使用 asm 重现 strcat 标准 c 函数。

这是我的 C 测试主要内容:

char    *ft_strcat(char *s1, const char *s2);

int main(void)
{
    char str1[60];
    str1[0] = 'a';
    str1[1] = '\0';
    char str2[] = "poney";

    printf("\n>> Test de ft_strcat <<\n\n");
    printf("str1 (%p) = \"%s\"\n", str1, str1);
    printf("str2 (%p) = \"%s\"\n", str2, str2);
    printf("ft_strcat(str1, str2) : %p\n", ft_strcat(str1, str2));
    printf("str1 (%p) = \"%s\"\n", str1, str1);
   
    return (0);
}

还有我的汇编代码

section .text
global _ft_strcat

_ft_strcat:
mov rax, qword rdi    ; save pointer address in rdi to return it later

start:
    cmp [rdi], byte 0
    jz next
    inc rdi
    jmp start

next:
    cmp [rsi], byte 0
    je end
    mov r11, [rsi]
    mov [rdi], r11
    inc rdi
    inc rsi
    jmp next

end:
    mov [rdi], byte 0
    ret                ; return rax

结果如下:

str1 (0x7fff5fbffb30) = "a"

str2 (0x7fff5fbffb20) = "小马"

ft_strcat(str1, str2) : 0x5fbffb30

str1 (0x7fff5fbffb30) = "aponey"

看来我的指针地址的高 32 位已经消失了。我无法解释为什么。

我知道这不仅仅是一个 printf 问题,因为如果我尝试打印字符串而不是 ft_strcat 返回的指针地址,我会得到一个段错误。

有什么想法吗?

【问题讨论】:

  • ret 8 ;从 rax 返回 8 个字节?一点也不。
  • 你打算在这里使用什么调用约定?您的代码似乎没有使用堆栈框架,而 C 通常将其参数放入其中。
  • 是的,实际上这个 ret 很愚蠢,我不得不删除它。它也使我的程序段错误好几次。
  • 它是 intel x64,我通过 rdi 和 rsi 获取函数参数并通过 rax 返回
  • 您在循环的每次迭代中移动 8 个字节。这是低效的,会导致未对齐的内存访问并访问超出源字符串和目标字符串末尾的内存。使用字节寄存器并在移动后测试0,或使用movsb 指令对rsirdi 执行加载、存储和递增。

标签: c pointers assembly 64-bit nasm


【解决方案1】:

错误就在这里:

mov rax, qword rdi    ; save pointer address in rdi to return it later

我认为应该是:

mov rax,rdi   ; save pointer address in rdi to return it later

另外我认为主循环应该像这样重写(不是最佳的,因为它一次只有一个字符,但考虑到你在每个循环中增加一次 rsirdi,我认为这就是你打算这样做):

next:
    cmp byte [rsi],0
    je end
    mov cl,[rsi]  ;load 8 bits
    mov [rdi],cl  ;store 8 bits
    inc rdi
    inc rsi
    jmp next

【讨论】:

  • 我添加了 qword 来尝试强制大小,但我自己回答说,我是 C 函数原型问题。
  • 你的意思是我的代码每次复制 8 个字符吗?它会导致任何段错误吗?
  • @Rox Teddy:访问超出数组边界的内存肯定会调用未定义的行为。如果目标数组不比源字符串长至少 6 个字节,则您正在覆盖内存中的其他内容。即使读取超出字符串末尾的内存也会调用未定义的行为。
  • @mcleod_ideafix:进一步优化代码:针对0 测试cl 复制和递增将节省1 个内存访问和1 个jmp 循环。
【解决方案2】:

好吧,我的错,这不是 asm 问题。我通过在我的 main 顶部添加 #include "libfts.h" 解决了这个问题。

如果没有函数原型,我想程序无法判断函数返回的是 1 字节还是 8 字节..

在这一行:

mov rax, qword rdi

不需要 qword,因为从/到 64 位寄存器的移动已经复制了 8 个字节

我还在我的 Makefile 中添加了编译器标志来防止这种错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-02
    • 1970-01-01
    相关资源
    最近更新 更多