Я использую dtrace для печати всех objc_msgSend в моем коде. Благодаря тому, что я сделал до сих пор, я вижу имя селектора, но не могу получить правильное имя класса.
Это мой сценарий dtrace:
#!/usr/sbin/dtrace -qs
pid$target::objc_msgSend:entry
{
self->isa = *(long *)copyin(arg0, 8);
printf("-[%s %s]\n",
copyinstr(*(long *)copyin(self->isa + 16, 8)),
copyinstr(arg1));
}
и я предполагаю, что Я бы объект-получатель имеет следующую структуру:
typedef struct objc_class {
struct objc_class *isa;
struct objc_class *super_class;
char *name;
...
}
В моей голове, чтобы достичь имени, указатель должен быть перемещен 2 * sizeof (objc_class *), что составляет 16, и мы получаем указатель имени размера 8. Поэтому я ожидал увидеть имя класса, но я получаю вместо этого напечатан мусор.
Есть идеи, что я делаю не так?
Моя система Mavericks x64.
После ковыряться Исходный код среды выполнения Obj-C для 64-битной архитектуры и файл «objc-private.h», это «формула» для получения имени класса из указателя класса:
#define RW_REALIZED (1<<31)
#define RW_FUTURE (1<<30)
#define CLASS_FAST_FLAG_MASK 3
#define TAG_MASK 1
#define TAG_SLOT_SHIFT 0
#define TAG_SLOT_MASK 0xf
extern "C" Class objc_debug_taggedpointer_classes[]; // Available in 10.9 for tagged pointers decoding
static const char* ClassNameFromInstance(id instance) {
char* ptr0 = (char*)instance;
char* ptr1;
if ((long)ptr0 & TAG_MASK) {
long slot = ((long)ptr0 >> TAG_SLOT_SHIFT) & TAG_SLOT_MASK;
ptr1 = (char*)objc_debug_taggedpointer_classes[slot]; // struct objc_class pointer
} else {
ptr1 = *(char**)ptr0; // struct objc_class pointer i.e. instance ISA
}
char* ptr2 = *((char**)(((long)ptr1 + 32) & ~CLASS_FAST_FLAG_MASK)); // struct class_ro_t or struct class_rw_t pointer
uint32_t flags = *((uint32_t*)ptr2); // struct class_ro_t or struct class_rw_t flags
char* ptr3;
if ((flags & RW_REALIZED) || (flags & RW_FUTURE)) {
ptr3 = *((char**)((long)ptr2 + 8)); // struct class_ro_t pointer from struct class_rw_t pointer
} else {
ptr3 = ptr2; // struct class_ro_t pointer same as struct class_rw_t pointer
}
const char* name = *((char**)((long)ptr3 + 24)); // Name string pointer from struct class_ro_t pointer
return name;
}
Который в dtrace становится в этом примере скриптом, который регистрирует создание объектов Obj-C & разрушение:
#!/usr/bin/env dtrace -s
#pragma D option quiet
pid$target:libobjc.A.dylib:class_createInstance:entry
{
ptr1 = *(long*)copyin(arg0, 8); /* arg0 is Class pointer */
ptr2 = *(long*)copyin((ptr1 + 32) & ~3, 8);
flags = *(int*)copyin(ptr2, 4);
ptr3 = (flags & (1 << 31)) || (flags & (1 << 30)) ? *(long*)copyin(ptr2 + 8, 8) : ptr2;
ptr4 = *(long*)copyin(ptr3 + 24, 8);
self->class = copyinstr(ptr4);
}
pid$target:libobjc.A.dylib:class_createInstance:return
{
printf("[+] %s = %p\n", self->class, arg1); /* arg1 is instance pointer */
self->class = 0;
}
pid$target:libobjc.A.dylib:object_dispose:entry
/arg0 != 0/
{
ptr0 = *(long*)copyin(arg0, 8); /* arg0 is instance pointer */
ptr1 = *(long*)copyin(ptr0, 8); /* TODO: Handle tagged pointers */
ptr2 = *(long*)copyin((ptr1 + 32) & ~3, 8);
ptr3 = (flags & (1 << 31)) || (flags & (1 << 30)) ? *(long*)copyin(ptr2 + 8, 8) : ptr2;
ptr4 = *(long*)copyin(ptr3 + 24, 8);
class = copyinstr(ptr4);
printf("[-] %s = %p\n", class, arg0);
}
ВАЖНЫЙ Этот сценарий dtrace не обрабатывает помеченные указатели. Обязательно установите переменную среды «DYLD_SHARED_REGION = избежать» при использовании этого сценария, как описано Вот.
Других решений пока нет …