У меня проблемы с пониманием этой сборки кода x86 (AT&Обозначение T). Мне нужно уметь это понимать (написать функцию C ++, скомпилированную с этим кодом) и выполнить аналогичные упражнения на экзамене.
Можете ли вы объяснить мне, какая часть делает, что и что такое конвенция?
f:
pushl %ebp ; 1
movl %esp, %ebp; 2
pushl %ebx ; 3
subl $36, %esp; 4
movl 8(%ebp), %edx ; 5
movl 12(%ebp), %eax ; 6
movl (%eax), %eax ; 7
movl %edx, 8(%esp) ; 8
leal 16(%ebp), %edx ; 9
movl %edx, 4(%esp) ; 10
movl %eax, (%esp) ; 11
call f; 12
movl %eax, -12(%ebp) ; 13
movl 16(%ebp), %edx ; 14
movl 12(%ebp), %eax ; 15
movl %edx, (%eax) ; 16
movl 12(%ebp), %eax ; 17
movl (%eax), %edx ; 18
movl -12(%ebp), %eax ; 19
movl %edx, 8(%esp) ; 20
leal 8(%ebp), %edx ; 21
movl %edx, 4(%esp) ; 22
movl %eax, (%esp) ; 23
call f; 24
movl %eax, %ebx; 25
movl 16(%ebp), %edx ; 26
movl -12(%ebp), %eax ; 27
movl %edx, 8(%esp) ; 28
movl 12(%ebp), %edx ; 29
movl %edx, 4(%esp) ; 30
movl %eax, (%esp) ; 31
call f; 32
movl %eax, %edx; 33
movl 16(%ebp), %eax ; 34
movl %edx, 8(%esp) ; 35
leal 8(%ebp), %edx ; 36
movl %edx, 4(%esp) ; 37
movl %eax, (%esp) ; 38
call f; 39
movl %ebx, 8(%esp) ; 40
leal -12(%ebp), %edx ; 41
movl %edx, 4(%esp) ; 42
movl %eax, (%esp) ; 43
call f; 44
addl $36, %esp; 45
popl %ebx ; 46
popl %ebp ; 47
ret; 48
Здесь нет прыжков, но есть несколько «call f», означает ли это, что существует бесконечный цикл?
Ниже немного, чтобы помочь вам начать работу.
Шаг 1. Разделите код на логические куски. Ключевыми вещами, которые нужно искать для идентификации логических порций, являются пролог стека и код эпилога, вызовы функций, операторы ветвления и адреса, идентифицированные операторами ветвления.
Шаг 2. Сделайте заметки о том, что делает каждый кусок.
Например …
f:
pushl %ebp
movl %esp, %ebp ; Create the stack frame
pushl %ebx ; and save non-volatile register EBX
subl $36, %esp ; Carve space for 9 32-bit words on the stack
; Notes: 8(%ebp) is the address for the 1st parameter
; 12(%ebp) is the address for the 2nd parameter
; 16(%ebp) is the address for the 3rd parameter
;
; Anything addresses as -#(%ebp) will be a stack variable
; local to this function.
;
; Anything addressed as #(%esp) will be used to pass parameters
; to the sub-function. The advantage of doing it this way is that
; parameters passed to the sub-function do not have to be popped
; after every call to a sub-function.
movl 8(%ebp), %edx ; EDX = 1st parameter
movl 12(%ebp), %eax ; EAX = 2nd parameter
movl (%eax), %eax ; The 2nd parameter is a pointer!
movl %edx, 8(%esp) ; Pass EDX as 3rd parameter to sub-function
leal 16(%ebp), %edx ; EDX = address of 3rd parameter to this function
movl %edx, 4(%esp) ; Passing it as 2nd parameter to sub-function
movl %eax, (%esp) ; Pass EAX as 3rd parameter to sub-function
call f ; Call sub-function
movl %eax, -12(%ebp) ; Save return value to local stack variable
; More Notes:
; I am guessing that this bit of decompiled code was an object file.
; Experience has shown me that when the address sub-functions used by
; CALL are all the same (and match the address of the calling function)
; this is often due to decompiling an object file as opposed to an
; executable. If however, the sub-function address truly is '0xf', then
; this will be a recursive routine that will blow the stack as there is
; no exit condition.
movl 16(%ebp), %edx ; EDX: 3rd parameter passed to function
; likely modified by previous CALL
movl 12(%ebp), %eax ; EAX: 2nd parameter passed to function
movl %edx, (%eax) ; Save EDX to the location pointed to by the 2nd parameter
movl 12(%ebp), %eax ; EAX: 2nd parameter passed to function (recall it's a ptr)
movl (%eax), %edx ; ... and so on ...
movl -12(%ebp), %eax
movl %edx, 8(%esp)
leal 8(%ebp), %edx)
movl %edx, 4(%esp)
movl %eax, (%esp)
call f
movl %eax, %ebx
movl 16(%ebp), %edx
movl -12(%ebp), %eax
movl %edx, 8(%esp)
movl 12(%ebp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call f
movl %eax, %edx
movl 16(%ebp), %eax
movl %edx, 8(%esp)
leal 8(%ebp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call f
movl %ebx, 8(%esp)
leal -12(%ebp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call f
addl $36, %esp ; Reclaim that carved stack space
popl %ebx ; Restore the non-volatile register EBX
popl %ebp ; Restore to the caller's stack frame
ret ; Return
Я оставляю все остальное для вас. Я надеюсь, что это поможет вам.
Эта функция f
является рекурсивной функцией без завершения рекурсии. Что-то вроде
void f(int a, int b, int c)
{
f(a,b,c);
//....
}
Прекратите оценивать дизассемблирование, так как не стоит получать такой плохой код на любом языке высокого уровня.
Я пришел к решению:
int f (int i, int* j, int k) {
int n = f(*j, &k, i);
*j = k;
f( f(n, &i, *j), &n, f(k, &i, f(n, j, k)) );
return 0;
}
при компиляции моего кода
g ++ -m32 -S a.cpp
Я получаю следующий код сборки:
_Z1fiPii:
.LFB971:
.cfi_startproc
.cfi_personality 0, __gxx_personality_v0
.cfi_lsda 0, .LLSDA971
pushl% ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl% esp,% ebp
.cfi_def_cfa_register 5
pushl% ebx
Subl $ 36,% esp
.cfi_offset 3, -12
MOVL 8 (% EBP),% EDX
MOVL 12 (% EBP),% EAX
MOVL (% eax),% eax
movl% edx, 8 (% esp)
leal 16 (% ebp),% edx
movl% edx, 4 (% esp)
movl% eax, (% esp)
.LEHB0:
позвонить _Z1fiPii
movl% eax, -12 (% ebp)
MOVL 16 (% EBP),% EDX
MOVL 12 (% EBP),% EAX
movl% edx, (% eax)
MOVL 16 (% EBP),% EDX
MOVL -12 (% ebp),% eax
movl% edx, 8 (% esp)
MOVL 12 (% EBP),% EDX
movl% edx, 4 (% esp)
movl% eax, (% esp)
позвонить _Z1fiPii
MOVL 16 (% EBP),% EDX
movl% eax, 8 (% esp)
leal 8 (% ebp),% eax
movl% eax, 4 (% esp)
movl% edx, (% esp)
позвонить _Z1fiPii
movl% eax,% ebx
MOVL 12 (% EBP),% EAX
movl (% eax),% edx
MOVL -12 (% ebp),% eax
movl% edx, 8 (% esp)
leal 8 (% ebp),% ecx
movl% ecx, 4 (% esp)
movl% eax, (% esp)
позвонить _Z1fiPii
movl% ebx, 8 (% esp)
leal -12 (% ebp),% edx
movl% edx, 4 (% esp)
movl% eax, (% esp)
позвонить _Z1fiPii
.LEHE0:
movl $ 0,% eax
jmp .L5
.L4:
movl% eax, (% esp)
.LEHB1:
вызвать _Unwind_Resume
.LEHE1:
.L5:
addl $ 36,% esp
popl% ebx
.cfi_restore 3
popl% ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
RET
.cfi_endproc
Это эквивалентно тому, который был вставлен раньше?