GDB Disassembler 显示汇编代码 - 但与我预期的方式不同
GDB Disassembler shows assembly code - but in a different way than I expected
我最近开始使用gdb反汇编器,想看看它如何真正显示汇编代码,是否合乎逻辑(尝试调试C程序,计算链表长度的函数)。
这是C代码(不是我的,必须注明出处this site):
int length() {
int length = 0;
struct node *current;
for(current = head; current != NULL; current = current->next) {
length++;
}
return length;
}
编译:
gcc linkedlist.c -o linkedlist
这是反汇编结果(英特尔风格):
0x00000000000012a8 <+0>: endbr64
0x00000000000012ac <+4>: push rbp
0x00000000000012ad <+5>: mov rbp,rsp
0x00000000000012b0 <+8>: mov DWORD PTR [rbp-0xc],0x0
0x00000000000012b7 <+15>: mov rax,QWORD PTR [rip+0x2d5a] # 0x4018 <head>
0x00000000000012be <+22>: mov QWORD PTR [rbp-0x8],rax
0x00000000000012c2 <+26>: jmp 0x12d4 <length+44>
0x00000000000012c4 <+28>: add DWORD PTR [rbp-0xc],0x1
0x00000000000012c8 <+32>: mov rax,QWORD PTR [rbp-0x8]
0x00000000000012cc <+36>: mov rax,QWORD PTR [rax+0x8]
0x00000000000012d0 <+40>: mov QWORD PTR [rbp-0x8],rax
0x00000000000012d4 <+44>: cmp QWORD PTR [rbp-0x8],0x0
0x00000000000012d9 <+49>: jne 0x12c4 <length+28>
0x00000000000012db <+51>: mov eax,DWORD PTR [rbp-0xc]
0x00000000000012de <+54>: pop rbp
0x00000000000012df <+55>: ret
真正困扰我的是,我注意到的一件小事,也许你注意到的更多,是它不是我所教的汇编代码的类型。我记得 teachers/professors 一遍又一遍地说:“不要使用 mov ,0x0,只需 xor ,”
但是在这里,它的内部确实:
DWORD PTR [rbp-0xc],0x0
我假设是变量的初始化int length = 0;
我的问题是,为什么它没有显示最有效的代码?如果它不能做到这一点(可能一切都不完美)——那么为什么它不知道检测数字 0
的初始化并执行 xor 操作而不是 mov (自动),这对性能真的很重要吗(如果是,是什么因素?)
也许还有更多行本来可以 replaces/disregarded,但我作为初学者没有注意到它们,但我确实注意到了这一行..有什么解释吗?
通常,gcc 编译时默认启用 -O0
选项。它生成的代码与源文件中编写的代码完全相同,没有任何优化。编译器可以优化具有许多可能选项的代码,如下例所示:
struct node
{
struct node *next;
};
int length(struct node *head) {
int length = 0;
struct node *current;
for(current = head; current != NULL; current = current->next) {
length++;
}
return length;
}
int __attribute__((optimize("-O3"))) length1(struct node *head) {
int length = 0;
struct node *current;
for(current = head; current != NULL; current = current->next) {
length++;
}
return length;
}
int __attribute__((optimize("-Os"))) length2(struct node *head) {
int length = 0;
struct node *current;
for(current = head; current != NULL; current = current->next) {
length++;
}
return length;
}
int __attribute__((optimize("-Og"))) length3(struct node *head) {
int length = 0;
struct node *current;
for(current = head; current != NULL; current = current->next) {
length++;
}
return length;
}
和代码
length:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-24], rdi
mov DWORD PTR [rbp-4], 0
mov rax, QWORD PTR [rbp-24]
mov QWORD PTR [rbp-16], rax
jmp .L2
.L3:
add DWORD PTR [rbp-4], 1
mov rax, QWORD PTR [rbp-16]
mov rax, QWORD PTR [rax]
mov QWORD PTR [rbp-16], rax
.L2:
cmp QWORD PTR [rbp-16], 0
jne .L3
mov eax, DWORD PTR [rbp-4]
pop rbp
ret
length1:
xor eax, eax
test rdi, rdi
je .L8
.L7:
mov rdi, QWORD PTR [rdi]
add eax, 1
test rdi, rdi
jne .L7
ret
.L8:
ret
length2:
xor eax, eax
.L12:
test rdi, rdi
je .L14
mov rdi, QWORD PTR [rdi]
inc eax
jmp .L12
.L14:
ret
length3:
mov eax, 0
jmp .L16
.L17:
add eax, 1
mov rdi, QWORD PTR [rdi]
.L16:
test rdi, rdi
jne .L17
ret
我最近开始使用gdb反汇编器,想看看它如何真正显示汇编代码,是否合乎逻辑(尝试调试C程序,计算链表长度的函数)。
这是C代码(不是我的,必须注明出处this site):
int length() {
int length = 0;
struct node *current;
for(current = head; current != NULL; current = current->next) {
length++;
}
return length;
}
编译:
gcc linkedlist.c -o linkedlist
这是反汇编结果(英特尔风格):
0x00000000000012a8 <+0>: endbr64
0x00000000000012ac <+4>: push rbp
0x00000000000012ad <+5>: mov rbp,rsp
0x00000000000012b0 <+8>: mov DWORD PTR [rbp-0xc],0x0
0x00000000000012b7 <+15>: mov rax,QWORD PTR [rip+0x2d5a] # 0x4018 <head>
0x00000000000012be <+22>: mov QWORD PTR [rbp-0x8],rax
0x00000000000012c2 <+26>: jmp 0x12d4 <length+44>
0x00000000000012c4 <+28>: add DWORD PTR [rbp-0xc],0x1
0x00000000000012c8 <+32>: mov rax,QWORD PTR [rbp-0x8]
0x00000000000012cc <+36>: mov rax,QWORD PTR [rax+0x8]
0x00000000000012d0 <+40>: mov QWORD PTR [rbp-0x8],rax
0x00000000000012d4 <+44>: cmp QWORD PTR [rbp-0x8],0x0
0x00000000000012d9 <+49>: jne 0x12c4 <length+28>
0x00000000000012db <+51>: mov eax,DWORD PTR [rbp-0xc]
0x00000000000012de <+54>: pop rbp
0x00000000000012df <+55>: ret
真正困扰我的是,我注意到的一件小事,也许你注意到的更多,是它不是我所教的汇编代码的类型。我记得 teachers/professors 一遍又一遍地说:“不要使用 mov ,0x0,只需 xor ,”
但是在这里,它的内部确实:
DWORD PTR [rbp-0xc],0x0
我假设是变量的初始化int length = 0;
我的问题是,为什么它没有显示最有效的代码?如果它不能做到这一点(可能一切都不完美)——那么为什么它不知道检测数字 0
的初始化并执行 xor 操作而不是 mov (自动),这对性能真的很重要吗(如果是,是什么因素?)
也许还有更多行本来可以 replaces/disregarded,但我作为初学者没有注意到它们,但我确实注意到了这一行..有什么解释吗?
通常,gcc 编译时默认启用 -O0
选项。它生成的代码与源文件中编写的代码完全相同,没有任何优化。编译器可以优化具有许多可能选项的代码,如下例所示:
struct node
{
struct node *next;
};
int length(struct node *head) {
int length = 0;
struct node *current;
for(current = head; current != NULL; current = current->next) {
length++;
}
return length;
}
int __attribute__((optimize("-O3"))) length1(struct node *head) {
int length = 0;
struct node *current;
for(current = head; current != NULL; current = current->next) {
length++;
}
return length;
}
int __attribute__((optimize("-Os"))) length2(struct node *head) {
int length = 0;
struct node *current;
for(current = head; current != NULL; current = current->next) {
length++;
}
return length;
}
int __attribute__((optimize("-Og"))) length3(struct node *head) {
int length = 0;
struct node *current;
for(current = head; current != NULL; current = current->next) {
length++;
}
return length;
}
和代码
length:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-24], rdi
mov DWORD PTR [rbp-4], 0
mov rax, QWORD PTR [rbp-24]
mov QWORD PTR [rbp-16], rax
jmp .L2
.L3:
add DWORD PTR [rbp-4], 1
mov rax, QWORD PTR [rbp-16]
mov rax, QWORD PTR [rax]
mov QWORD PTR [rbp-16], rax
.L2:
cmp QWORD PTR [rbp-16], 0
jne .L3
mov eax, DWORD PTR [rbp-4]
pop rbp
ret
length1:
xor eax, eax
test rdi, rdi
je .L8
.L7:
mov rdi, QWORD PTR [rdi]
add eax, 1
test rdi, rdi
jne .L7
ret
.L8:
ret
length2:
xor eax, eax
.L12:
test rdi, rdi
je .L14
mov rdi, QWORD PTR [rdi]
inc eax
jmp .L12
.L14:
ret
length3:
mov eax, 0
jmp .L16
.L17:
add eax, 1
mov rdi, QWORD PTR [rdi]
.L16:
test rdi, rdi
jne .L17
ret