Tôi đã chia nhỏ quá trình tháo gỡ để bạn biết cách lắp ráp được tạo ra từ nguồn C.
8(%ebp)
= x
, 12(%ebp)
= y
, 16(%ebp)
= z
arith:
Tạo khung stack:
pushl %ebp
movl %esp,%ebp
Move
x
vào
eax
,
y
vào
edx
:
movl 8(%ebp),%eax
movl 12(%ebp),%edx
t1 = x + y
.
leal
(địa chỉ hiệu quả Load) sẽ thêm
edx
và
eax
, và
t1
sẽ được ở
ecx
:
leal (%edx,%eax),%ecx
int t4 = y * 48;
theo hai bước dưới đây, nhân với 3, sau đó bằng 16.
t4
cuối cùng sẽ được
edx
:
Nhân edx
cho 2 và thêm edx
vào kết quả, ví dụ: edx = edx * 3
:
leal (%edx,%edx,2),%edx
Shift left 4 bits, ie. nhân với 16:
sall $4,%edx
int t2 = z+t1;
.
ecx
ban đầu giữ
t1
,
z
là
16(%ebp)
, ở phần cuối của hướng dẫn
ecx
sẽ tổ chức
t2
:
addl 16(%ebp),%ecx
int t5 = t3 + t4;
.
t3
đơn giản là
x + 4
và thay vì tính và lưu trữ
t3
, biểu thức của
t3
được đặt nội tuyến. Hướng dẫn này cần thiết
(x+4) + t4
, tương tự như
t3
+
t4
. Nó thêm
edx
(
t4
) và
eax
(
x
) và thêm 4 làm số
bù trừ để đạt được kết quả đó.
leal 4(%edx,%eax),%eax
int rval = t2 * t5;
Khá thẳng chuyển tiếp này một; ecx
đại diện cho t2
và eax
đại diện cho t5
. Giá trị trả lại được chuyển lại cho người gọi qua eax
.
imull %ecx,%eax
Phá hủy các stack frame và khôi phục
esp
và
ebp
:
movl %ebp,%esp
popl %ebp
Return từ thói quen:
ret
Từ ví dụ này, bạn có thể thấy rằng resul t là như nhau, nhưng cấu trúc có một chút khác biệt. Rất có thể mã này đã được biên soạn với một số loại tối ưu hóa hoặc ai đó đã tự viết nó như thế để chứng minh một điểm.
Như những người khác đã nói, bạn không thể quay trở lại chính xác nguồn từ việc tháo gỡ. Đó là giải thích của người đọc hội đồng để đưa ra mã C tương đương.
Để giúp với lắp ráp học tập và tìm hiểu tháo gỡ các chương trình C, bạn có thể làm như sau trên Linux:
Compile với thông tin debug (-g
), mà sẽ nhúng các nguồn:
gcc -c -g arith.c
Nếu bạn đang sử dụng máy 64 bit, bạn có thể yêu cầu trình biên dịch tạo một nhị phân 32 bit với cờ -m32
(tôi đã làm như vậy cho ví dụ bên dưới).
Sử dụng objdump để đổ các tập tin đối tượng với nó là nguồn xen kẽ:
objdump -d -S arith.o
-d
= tháo gỡ, -S
= nguồn hiển thị. Bạn có thể thêm -M intel-mnemonic
để sử dụng cú pháp ASM của Intel nếu bạn thích cú pháp đó hơn cú pháp AT & T mà ví dụ của bạn sử dụng.
Output:
arith.o: file format elf32-i386
Disassembly of section .text:
00000000 <arith>:
int arith(int x, int y, int z)
{
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 20 sub $0x20,%esp
int t1 = x+y;
6: 8b 45 0c mov 0xc(%ebp),%eax
9: 8b 55 08 mov 0x8(%ebp),%edx
c: 01 d0 add %edx,%eax
e: 89 45 fc mov %eax,-0x4(%ebp)
int t2 = z+t1;
11: 8b 45 fc mov -0x4(%ebp),%eax
14: 8b 55 10 mov 0x10(%ebp),%edx
17: 01 d0 add %edx,%eax
19: 89 45 f8 mov %eax,-0x8(%ebp)
int t3 = x+4;
1c: 8b 45 08 mov 0x8(%ebp),%eax
1f: 83 c0 04 add $0x4,%eax
22: 89 45 f4 mov %eax,-0xc(%ebp)
int t4 = y * 48;
25: 8b 55 0c mov 0xc(%ebp),%edx
28: 89 d0 mov %edx,%eax
2a: 01 c0 add %eax,%eax
2c: 01 d0 add %edx,%eax
2e: c1 e0 04 shl $0x4,%eax
31: 89 45 f0 mov %eax,-0x10(%ebp)
int t5 = t3 + t4;
34: 8b 45 f0 mov -0x10(%ebp),%eax
37: 8b 55 f4 mov -0xc(%ebp),%edx
3a: 01 d0 add %edx,%eax
3c: 89 45 ec mov %eax,-0x14(%ebp)
int rval = t2 * t5;
3f: 8b 45 f8 mov -0x8(%ebp),%eax
42: 0f af 45 ec imul -0x14(%ebp),%eax
46: 89 45 e8 mov %eax,-0x18(%ebp)
return rval;
49: 8b 45 e8 mov -0x18(%ebp),%eax
}
4c: c9 leave
4d: c3 ret
Như bạn thấy, mà không cần tối ưu hóa trình biên dịch tạo ra một nhị phân lớn hơn ví dụ bạn có. Bạn có thể chơi với điều đó và thêm cờ tối ưu hóa trình biên dịch khi biên soạn (ví dụ: -O1
, -O2
, -O3
). Mức độ tối ưu hóa càng cao, thì việc tháo gỡ sẽ càng có vẻ trừu tượng hơn.
Ví dụ, chỉ với mức 1 tối ưu hóa (gcc -c -g -O1 -m32 arith.c1
), mã lắp ráp sản xuất là ngắn hơn rất nhiều:
00000000 <arith>:
int arith(int x, int y, int z)
{
0: 8b 4c 24 04 mov 0x4(%esp),%ecx
4: 8b 54 24 08 mov 0x8(%esp),%edx
int t1 = x+y;
8: 8d 04 11 lea (%ecx,%edx,1),%eax
int t2 = z+t1;
b: 03 44 24 0c add 0xc(%esp),%eax
int t3 = x+4;
int t4 = y * 48;
f: 8d 14 52 lea (%edx,%edx,2),%edx
12: c1 e2 04 shl $0x4,%edx
int t5 = t3 + t4;
15: 8d 54 11 04 lea 0x4(%ecx,%edx,1),%edx
int rval = t2 * t5;
19: 0f af c2 imul %edx,%eax
return rval;
}
1c: c3 ret
cấp độ bạn đang sử dụng khi bạn đang soạn thảo tối ưu hóa gì? Nếu bạn muốn chuyển đổi theo từng dòng, hãy sử dụng -O0, nếu không, bạn sẽ cần phải tính đến các tối ưu hóa. – Maz
... và đảo ngược mã được tối ưu hóa trở lại thứ tự lệnh C ban đầu không hoàn toàn có thể. –
Bạn có chắc đây là một trình biên dịch ngây thơ không? Có vẻ như nó đã được tối ưu hóa một chút. Viết ra biểu thức đại số mà hàm tính toán và xem liệu bạn có thể khám phá ra điều đó không. –