Наверное одна из наиболее часто используемых команд при отладке с использованием GDB это bt/backtrace/where. Этой командой можно раскрутить стек и увидеть каким образом мы пришли к текущему этапу выполнения. Достаточно знать несколько деталей работы со стеком, чтобы получить ту же самую информацию во время работы приложения.
Вызов метода в Objective-C сводится к выполнению Си функции objc_msgSend или нескольких ее вариаций. Сама функция после определения кода который нужно выполнить делает переход к нему, оставляя неизменным стек, т.о. и в выводе GDB не видно objc_msgSend.
Выполнение каждой функции начинается с помещения в стек указателя на стек фрейм предыдущей функции и сохранении фрейма стека текущей:
Указатель на stack frame хранится в регистре %ebp.
Стек растет от старших адресов к младшим, следующим образом он будет выглядеть при вызове метода -[SomeObject setObj:(NSObject*)arg1]
+------------------------+ |
| (NSObject*)arg1 | |
+------------------------+ |
| указатель на строку | \|/
| selector, "setObj:" | '
+------------------------+
| SomeObject* |
+------------------------+
| %eip, адрес возврата |
+------------------------+
| %ebp, last stack frame | <- "push %ebp", в начале функции
+------------------------+ ..
'`- "mov %esp,%ebp", %ebp указывает сюда
Таким образом в %ebp при выполнении текущей функции лежит адрес на стеке, где лежит значение %ebp от предыдущей выплоняемой функции из которой был произведен вызов текущей. Воспользовавшись знаниями выше, можно получить следующую функцию:
Вызов метода в Objective-C сводится к выполнению Си функции objc_msgSend или нескольких ее вариаций. Сама функция после определения кода который нужно выполнить делает переход к нему, оставляя неизменным стек, т.о. и в выводе GDB не видно objc_msgSend.
Выполнение каждой функции начинается с помещения в стек указателя на стек фрейм предыдущей функции и сохранении фрейма стека текущей:
push %ebp
mov %esp,%ebp
Указатель на stack frame хранится в регистре %ebp.
Стек растет от старших адресов к младшим, следующим образом он будет выглядеть при вызове метода -[SomeObject setObj:(NSObject*)arg1]
+------------------------+ |
| (NSObject*)arg1 | |
+------------------------+ |
| указатель на строку | \|/
| selector, "setObj:" | '
+------------------------+
| SomeObject* |
+------------------------+
| %eip, адрес возврата |
+------------------------+
| %ebp, last stack frame | <- "push %ebp", в начале функции
+------------------------+ ..
'`- "mov %esp,%ebp", %ebp указывает сюда
Таким образом в %ebp при выполнении текущей функции лежит адрес на стеке, где лежит значение %ebp от предыдущей выплоняемой функции из которой был произведен вызов текущей. Воспользовавшись знаниями выше, можно получить следующую функцию:
static id getSender()Вызов данной функции вернет объект из которого был произведен вызов текущего метода, а с небольшой модификацией можно получить и метод из которого был произведен вызов.
{
id result = nil;
__asm( "push %%ebx\n"
"push %%ebp\n"
"mov (%%ebp),%%ebx\n"
"mov %%ebx,%%ebp\n"
"mov (%%ebp),%%ebx\n"
"mov %%ebx,%%ebp\n"
"mov 8(%%ebp),%%ebx\n"
"mov %%ebx,%0\n"
"pop %%ebp\n"
"pop %%ebx"
:"=r"(result)
);
return result;
}