【发布时间】:2016-01-02 09:46:27
【问题描述】:
我正在通过编写 C 程序和查看汇编输出来学习汇编。我在页面底部包含了 C 程序以使其更容易。我正在努力理解一条装配线:
cdqe
movzx eax, BYTE PTR [rbp-32+rax]
movsx eax, al
所以我认为 cdqe 将 eax 扩展为 rax(64 位)。很明显,我要打印的字符串适合 al 寄存器,但我不明白 rbp-32+rax 深处发生了什么。谁能帮我解释一下?
.file "string_manip.c"
.intel_syntax noprefix
.section .rodata
.LC0:
.string "Hello"
.string ""
.zero 3
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
mov rbp, rsp
.cfi_def_cfa_register 6
sub rsp, 48
mov rax, QWORD PTR fs:40
mov QWORD PTR [rbp-8], rax
xor eax, eax
mov DWORD PTR [rbp-36], 0
mov eax, DWORD PTR .LC0[rip]
mov DWORD PTR [rbp-32], eax
movzx eax, WORD PTR .LC0[rip+4]
mov WORD PTR [rbp-28], ax
movzx eax, BYTE PTR .LC0[rip+6]
mov BYTE PTR [rbp-26], al
mov WORD PTR [rbp-25], 0
mov BYTE PTR [rbp-23], 0
mov DWORD PTR [rbp-36], 0
jmp .L2
.L3:
mov eax, DWORD PTR [rbp-36]
cdqe
movzx eax, BYTE PTR [rbp-32+rax] <--- what is this doing?
movsx eax, al
mov edi, eax
call putchar
add DWORD PTR [rbp-36], 1
.L2:
cmp DWORD PTR [rbp-36], 5
jle .L3
mov edi, 10
call putchar
mov eax, 0
mov rdx, QWORD PTR [rbp-8]
xor rdx, QWORD PTR fs:40
je .L5
call __stack_chk_fail
.L5:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4"
.section .note.GNU-stack,"",@progbits
#include <string.h>
#include <stdio.h>
int main()
{
int i = 0;
char array[10] = "Hello\0";
for(i=0; i<6; i++)
printf("%c", array[i]);
printf("\n");
return 0;
}
【问题讨论】:
-
它对
rbp-32+rax指向的字节进行零扩展(无符号向上转换)并将其移动到eax;想想uint32_t eax = *(uint8_t *)(rbp - 32 + rax); -
您将有更好的时间阅读
-Og -fverbose-asm的编译器输出。-O0太吵了(到处都是商店和重新加载)。您必须跟踪所有数据移动,以查看重新加载的变量是相同的变量还是不同的变量。我宁愿看-O3的输出来“看看编译器做了什么”,但我已经知道 asm,我唯一感兴趣的是看看编译器是否发现了我没有发现的任何聪明的优化。 :P 但说真的,如果你知道编译器通常做什么,你可以遵循优化的代码。-Og是目前为止最适合学习的,或者说 O1。
标签: c assembly cpu-registers