2016-12-05 1 views
1

Итак, у меня есть метод, содержащий 5 аргументов. Как и ожидалось, регистры заявляют прямо перед его называют:Сбой в objc_msgSend при разыменовании регистра (% r11), но я не понимаю почему

$rdi: The receiver 
$rsi: the selector for the method 
$rdx: first arg 
$rcx: second arg 
$r8: third arg 
$r9: fourth arg 
$r10 fifth arg 

В методе, первое, что он делает это вызов другой метод, Objective-C

Это, в свою очередь, вызывает objc_msgSend (см смещение +58):

MyApp`-[GTMOAuth2WindowController webView:resource:willSendRequest:redirectResponse:fromDataSource:]: 
    0x10044a1a0 <+0>: pushq %rbp 
    0x10044a1a1 <+1>: movq %rsp, %rbp 
    0x10044a1a4 <+4>: subq $0x40, %rsp 
    0x10044a1a8 <+8>: movq 0x10(%rbp), %rax 
    0x10044a1ac <+12>: movq %rdi, -0x10(%rbp) 
    0x10044a1b0 <+16>: movq %rsi, -0x18(%rbp) 
    0x10044a1b4 <+20>: movq %rdx, -0x20(%rbp) 
    0x10044a1b8 <+24>: movq %rcx, -0x28(%rbp) 
    0x10044a1bc <+28>: movq %r8, -0x30(%rbp) 
    0x10044a1c0 <+32>: movq %r9, -0x38(%rbp) 
    0x10044a1c4 <+36>: movq %rax, -0x40(%rbp) 
    0x10044a1c8 <+40>: movq -0x10(%rbp), %rax 
    0x10044a1cc <+44>: movq -0x38(%rbp), %rdx 
    0x10044a1d0 <+48>: movq 0x2ffda9(%rip), %rsi  ; "handleCookiesForResponse:" 
    0x10044a1d7 <+55>: movq %rax, %rdi 
    0x10044a1da <+58>: callq 0x1005839a2    ; symbol stub for: objc_msgSend 

, который затем переходит в инструкции по objc_msgSend:

libobjc.A.dylib`objc_msgSend: 
-> 0x7fff9084a0c0 <+0>: testq %rdi, %rdi 
    0x7fff9084a0c3 <+3>: je  0x7fff9084a140   ; <+128> 
    0x7fff9084a0c6 <+6>: testb $0x1, %dil 
    0x7fff9084a0ca <+10>: jne 0x7fff9084a14b   ; <+139> 
    0x7fff9084a0cd <+13>: movabsq $0x7ffffffffff8, %r11 
    0x7fff9084a0d7 <+23>: andq (%rdi), %r11 
    0x7fff9084a0da <+26>: movq %rsi, %r10 
    0x7fff9084a0dd <+29>: andl 0x18(%r11), %r10d 

и я иногда сбой на смещении +29, когда процессор пытается разыменовать регистр %r11.

Мой вопрос в том, почему objc_msgSend разыскивают, что регистрируются? Согласно System V ABI, который является регистром царапин. Но он разыгрывается каждый раз objc_msgSend, и я не могу понять, для чего он используется.

Мой сбой происходит, когда есть недопустимый указатель в %r11

Это выглядит как на +23, то %rdi регистр (указатель на приемник) разыменовывается и andq полукольцо с %r11, но я не ' t получить то, что это делает. Но, возможно, если бы получатель был освобожден здесь, %r11 был бы заполнен барахлом?

Эта теория подтверждается этим assembly source w/ comments

Где я думаю, что он заявляет %r11 используется для «класса = само-> Isa» isa свойства
.

Что будет означать, что объект высвобождается, поскольку свойство isa является кайфом

Если бы это было так, как я мог от этого защититься?

Можно ли проверить if(self), прежде чем звонить objc_msgSend?

+0

Самое первое, что 'msgSend' делает, это' if (self) ', поэтому делать это до вызова не поможет. Как вы говорите, 'self' указывает на мусор, но, к сожалению, это не' NULL'. – Jester

+0

Есть ли способ проверить, содержит ли адрес 'self' действительный объект, а не мусор? –

ответ

4

К сожалению, связанный вами сайт устарел. Это не объясняет точную версию objc_msgSend, которую вы используете.

Что вам нужно знать, чтобы понять выход дизассемблера, так это то, что среда выполнения Objective-C теперь имеет функцию «non-pointer isa». Another page on that site объясняет не указатель isa, но я подведу итог.

Поле isa объекта было исторически указателем на класс объекта. Вам не нужны полные 64 бита для этого указателя, потому что ни одна из операционных систем Apple не использует полное 64-разрядное адресное пространство. Многие биты адреса класса всегда равны нулю.

Вместо того чтобы тратить все эти биты на каждый объект, не указатель isa использует бит для других вещей, например, для хранения счетчика ссылок объекта. Это означает, что если вы хотите, чтобы указатель на класс, вам нужно установить эти другие биты обратно к нулю, чтобы получить действительный адрес. Вычисление isa & 0x7ffffffffff8 отключает все несогласованные биты, поэтому вы получаете действительный указатель на класс ...

... если поле isa не было повреждено. Если поле isa повреждено, вы получаете мусор. Если мусор недействительный адрес, вы получаете сбой.

Что здесь происходит, вы перезаписали память, содержащую объект, так что поле isa больше недействительно.

Чтобы решить эту проблему, читайте о how to find zombies. Если это не поможет, посмотрите this WWDC video about using the address sanitizer.

+0

Спасибо за подробный ответ и ссылки :) Проблема в том, что я не могу воспроизвести эту проблему, она попала через HockeyApp, поэтому я отлаживаю это строго, глядя на журналы сбоев и сборку. Вы знаете, как я могу переписать память, содержащую исходный объект-получатель? Я бы предположил, что система сможет защитить от этого - только запись в память, которая была выпущена? –

+0

В рамках одного процесса такой защиты нет. ARC (и Swift) помогают избежать ошибок управления памятью, но вы все равно можете их сделать. Я предлагаю вам протестировать ваше приложение как можно больше в разделе Sanitizer и посмотреть, есть ли у вас предупреждения. –

+0

Спасибо за ссылку. Я смотрел видео, и у меня был другой вопрос, если бы вы не возражали. В 25: 12-й, она рассказывает о теневом сопоставлении. И объясняет, как работает Sanizanter Address, добавив инструкцию перед каждым доступом к памяти, чтобы проверить, отражен ли адрес, а затем ** сбой **, если найдено true. Мой вопрос в том, что если бы вы не запускали Sanitizer, не нарушилось бы приложение при этом недопустимом доступе к памяти? –

2

Как вы говорите,% r11 в этой точке является указателем isa от self. Если self - это память мусора в этой точке, то неудивительно, что это первое слово указывает на мусор.

Чтобы было ясно, когда вы говорите: «Будет ли проверка, чтобы убедиться, что (я) до вызова objc_msgSend достаточно?» Я предполагаю, что вы не имеете в виду, что вы сами звоните objc_msgSend. (Никогда не делайте этого.) Проверка self перед вызовом этого метода не поможет вам, если это мусор. Это указатель не-0, так что это правда. (Если бы это было 0, мы уже выполнили бы поручение через nil-messaging в верхней части objc_msgSend.)

Вы как-то испортили эту память. Может быть, перевыпуск (хотя я сомневаюсь, что в этом случае). Возможно, у вас есть структуры данных C и разбил ваш стек (что кажется более вероятным). Может быть, объект освобождается от другого потока? Это может быть много.

+0

Спасибо за ваше время ^^. Трудно отладить это, потому что я не могу воспроизвести его, я ухожу из журналов сбоев из HockeyApp и просматриваю сборку в Xcode. Не могли бы вы рассказать о том, как структуры данных C могут разбивать стек, и что это значит? Или помогите мне со ссылкой на чтение? Разве это SO-поток, о чем вы говорите? http://stackoverflow.com/a/1347464/2415178 –

+0

Я сказал, что «стек разбит», когда я действительно имел в виду «сбежать от конца структуры данных С, как правило, массива». Учитывая крушение, это было бы скорее в куче, чем в стеке. Посмотрите на «Защитные края Мэглок» Xcode, чтобы помочь найти такую ​​ошибку. Другие параметры управления памятью в профиле> Выполнить> Диагностика также могут быть полезны для этого (см. Ответ rob mayoff для ссылок). –

+0

(Кстати, то, что вы знаете и не знаете, действительно уникально для меня, что говорит о том, что вы очень много учитесь на достаточно продвинутом уровне, не имея большого фона в теме. Я впечатлен, но это немного сложно ответить. У вас странное сочетание понимания ABI и обратного инженерного objc_msgSend, но не то, как работает общее управление памятью C. Опять же, я впечатлен, но простите нас, если предположим, что вы знаете больше, чем вы может на самом деле и ответить на неправильном уровне.) –

Смежные вопросы