试图在汇编 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:显然不是sil
或dil
,因为那样你会破坏指针值。但是,是的,任何呼叫破坏的寄存器都会同样好。另外,请参阅我对此答案的更新;您忘记复制终止的 0
字节,因为您在存储之前离开了循环。以上是关于试图在汇编 at&t 中编写 strcpy,没有输出的主要内容,如果未能解决你的问题,请参考以下文章