Инструментарий байт-кода Dalvik - слияние типов регистров

Я делаю какой-то инструментарий байт-кода dalvik, используя dexlib2. Тем не менее, есть пара оставшихся вопросов. Слияние типов регистров, которое, кажется, происходит после инструкций goto а ловить блоки, точнее по соответствующей метке, как-то выводит неожиданный тип регистра, который, в свою очередь, ломает инструментированный код.

Вставляемые инструкции выглядят следующим образом:

move(-wide,-object,/16,/from16) vNew, v0
const-string v0, "some string"
invoke-static, {v0}, LPathToSomeClass;->SomeMethod(Ljava/lang/String;)V
move(..) v0, vNew

Таким образом, v0 используется для хранения некоторого параметра для вызова статической функции, а vNew — это новый (локальный) регистр для хранения и восстановления исходного содержимого v0. Тип регистра v0 выводится заранее, чтобы получить право инструкция перемещения, т. е. перемещение по ширине, перемещение или перемещение объекта. Однако, когда этот код включается в какой-либо блок try, инструментарий ломается. Результат baksmali (baksmali d -b "" --register-info ALL,FULLMERGE --offsets ) показывает, что тип v0 после инструкции const-string (которая является Reference,L/java/lang/String) считается входом для процедуры слияния, происходящей, например, на соответствующей метке блока catch. Предполагая, что тип перед вставленным кодом был Reference,[I (int array) результирующий тип теперь Reference,L/java/lang/Object (что приводит к ошибке проверки), хотя последняя инструкция перемещения восстанавливает исходный тип регистра.

Теперь к моим вопросам:

1) Когда на самом деле происходит это слияние?

2) Почему процедура слияния учитывает тип v0 после инструкции const-string? Рассматривает ли он каждую инструкцию, изменяющую тип любого регистра?

3) Эта проблема связана только с блоками try-catch?

4) Каковы ограничения для блоков try-catch в этом вопросе?

5) Есть ли решение этой проблемы, кроме создания собственного метода для каждого кода для ввода без параметров? Так можно ли использовать дополнительный регистр для решения этой проблемы?

6) Могу ли я с помощью dexlib2 обнаруживать блоки try-catch и определять набор инструкций, которые они включают?

7) Есть ли какие-либо заметки/литература, обсуждающие эту проблему, например. процедура слияния и сопутствующие технические детали, например. дополнительные ограничения/ограничения для приборки?

Я высоко ценю любую помощь в этом вопросе. Заранее спасибо!

3 метода стилизации элементов HTML
3 метода стилизации элементов HTML
Когда дело доходит до применения какого-либо стиля к нашему HTML, существует три подхода: встроенный, внутренний и внешний. Предпочтительным обычно...
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
0
0
194
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

При слиянии регистров в начале блока catch существует входящее ребро от каждой инструкции в блоке try, которая может генерировать. Выдавать могут только определенные инструкции, что определяется флагом кода операции CAN_THROW.

В вашем конкретном примере инструкция invoke-static после инструкции const-string может бросить, и поэтому есть край непосредственно перед этой инструкцией до начала блока catch.

Если вы сделаете шаг назад, выполнение может перескочить с любой инструкции в блоке try, которая может вызвать бросок, на начало блока catch. Таким образом, код в блоке catch должен быть подготовлен для того, чтобы регистры находились в состоянии, согласующемся с содержимым регистра, непосредственно перед любой из тех инструкций, которые могут генерировать ошибки.

Так, например, если есть один возможный «переход» из блока try в блок catch, где регистр содержит примитивный тип int, и другой возможный переход, где он содержит объект, этот регистр считается «конфликтующим», поскольку регистр может содержать любой тип в этой точке кода, и эти два типа несовместимы друг с другом. Например. примитивный int никогда не может быть передан чему-то, ожидающему ссылочный тип, и наоборот. И в байт-коде нет механизма для статической проверки типа регистра.

Одним из возможных решений может быть разделение блока try в том месте, где вы вставляете свою инструментацию, чтобы сама инструментация не была покрыта блоком try, а обе «стороны» исходного кода. И имейте в виду, что в байт-коде один и тот же блок catch может использоваться несколькими блоками try, поэтому вы можете разделить исходный блок try на два, и оба они будут ссылаться на исходный блок catch.

В противном случае вам просто нужно найти какой-то способ управления регистрами, чтобы избежать этой проблемы.

Что касается 6), см. Реализация метода.getTryBlocks(), который даст вам список блоков try в этом методе. Каждый блок try указывает, где он начинается, сколько инструкций он охватывает и все связанные с ним блоки catch (разные блоки catch для разных исключений).

Я вижу проблему. Могу ли я использовать инструкцию invoke-range (которая должна принимать первые 256 регистров в качестве параметров???) с одним параметром (start==end) и использовать вместо v0 новый дополнительный (локальный) регистр? Это должно избежать необходимости сохранять и восстанавливать содержимое v0 (инструкции перемещения должны быть ненужными) и того факта, что стандартная инструкция вызова позволяет использовать только первые 16 регистров в качестве параметров, больше не будет ограничением. Это должно работать при условии, что инструкция const-string также допускает более 16 регистров в качестве первого параметра, верно?

auermich 24.05.2019 14:43

Да, вы можете использовать invoke-range с start==end для вызова метода с одним регистром. Хотя есть методы с> 256 регистрами, вам все равно нужно быть готовым обработать (или иным образом игнорировать :)) этот случай.

JesusFreke 24.05.2019 20:51

Начиная с source.android.com/devices/tech/dalvik/dalvik-bytecode, const-string использует 8 бит для кодирования регистра назначения, так что да. Он может использовать v0-v255

JesusFreke 24.05.2019 20:52

До сих пор я никогда не сталкивался с методом с более чем 256 регистрами, либо я буду игнорировать их, либо, я думаю, я могу использовать регистр, который не затрагивается во всем блоке try-catch, если это возможно?

auermich 28.05.2019 18:23

Другие вопросы по теме