试图在汇编 at&t 中编写 strcpy,没有输出

Posted

技术标签:

【中文标题】试图在汇编 at&t 中编写 strcpy,没有输出【英文标题】:Trying to write strcpy in assembly at&t, no output 【发布时间】:2019-12-18 15:50:15 【问题描述】:

我正在尝试在汇编中编写char *my_strcpy(char *dest, const char *source);,at&t 语法,其行为应该与 C 中的 strcpy 完全相同。我的 c 文件如下所示:

【问题讨论】:

我自己不使用 AT&T 语法,但我很确定 mov %rsi, %rdi 只是将 rsi 中的指针复制到 rdi。要移动字节,您需要执行 mov (%rsi), %al; mov %al, (%rdi) 之类的操作。 我试图做 movb %rsi, %rdi 但它说的参数太多.. 您的意思是它在返回之前会出现段错误?你应该这么说,而不仅仅是“什么都不输出”! 注意strcpy应该返回char *,而不是size_t,并且返回值应该等于dest。这就是您的代码实际执行的操作,但可能不是您认为的那样。 不要破坏您的问题。我回滚了删除大部分问题的编辑。 【参考方案1】:
.globl my_strcpy

my_strcpy:
    push %rbp
    mov %rsp, %rbp
    mov %rdi, %rax
    jmp copy_loop

跳跃毫无意义。

copy_loop:
    cmp $0, (%rsi)

您没有指定这应该是 8、16、32 还是 64 位比较。当我组装它时,我得到一个 32 位的比较;例如它查看地址%rsi 处的32 位字是否等于0。您需要将其更改为 cmpb $0, (%rsi)

    je end
    mov %rsi, %rdi

正如用户 500 所指出的,这会将 %rsi 寄存器中的地址复制到 %rdi 寄存器中,并覆盖它。这不是你想要的。您可能想要像movb (%rsi), (%rdi) 这样的指令,但实际上并不存在这样的指令:x86 没有这样一条指令来将内存移动到内存(特殊例外:请参阅movsb 指令)。因此,您需要首先将地址 %rsi 处的字节复制到寄存器中,然后使用另一条指令将其向前复制,例如mov (%rsi), %cl ; mov %cl, (%rdi)。请注意,使用 8 位 %cl 寄存器可以清楚地表明这些应该是单字节移动。

movzbl (%rsi), %ecx 是在现代 x86 上加载字节的更有效方式。您仍然可以通过使用mov %cl, (%rdi) 读取 CL 来存储它,但是覆盖整个 RCX 而不是合并到 RCX 中更好。

    addq $1, %rsi
    addq $1, %rdi

您可能想了解inc 指令,但add 很好。

    je copy_loop

我想你的意思是jmp copy_loop,因为这里的跳转应该是无条件地发生的。 (或者你应该重新排列你的循环,以便条件分支可以在底部。由于你想复制终止的0字节,你可以复制并然后检查0,比如dowhile(c != 0) )

end:
    leave
    ret

【讨论】:

谢谢 我已经理解了我的错误,但是除了将其保存到 %al 寄存器中之外,还有其他方法可以一次移动 1 个字节吗? @catrev:是的,正如这个答案已经显示的那样,您可以将其加载到 %cl 或您想要的任何其他 8 位寄存器中!或者使用movsb,就像这个答案也提到的那样,但这很慢。 (不过,如果您关心性能,在找到对齐边界后,您可以使用 SSE2 一次复制 16 个字节。) 所以我可以使用任何寄存器? @catrev:嗯,你已经在使用%rax 来存储返回值,所以除非你改变它,否则你不能使用%al%ah。而%rbx是被调用者保存的寄存器,所以不能使用%bl%bh,除非你想保存和恢复%rbx。这就是我选择%cl的原因。 @catrev:显然不是sildil,因为那样你会破坏指针值。但是,是的,任何呼叫破坏的寄存器都会同样好。另外,请参阅我对此答案的更新;您忘记复制终止的 0 字节,因为您在存储之前离开了循环。

以上是关于试图在汇编 at&t 中编写 strcpy,没有输出的主要内容,如果未能解决你的问题,请参考以下文章

用不到 4 行汇编编写这个练习 AT&T

AT&T汇编语法与x86语法基本区别

英特尔 AT&T 汇编程序的逐步执行?

C指针原理-AT&T汇编

汇编(AT&T 32 位)scanf 问题

x86 汇编 (AT&T):如何在运行时为变量动态分配内存?