LiteCoding

Заметки о программировании

smali2java: Больше, чем просто парсер

without comments

Эта небольшая заметка открывает цикл публикаций о том, как устроен smali2java. Пока комментариев и документации к этой утилите явно недостаточно, я буду по возможности объяснять, что и как smali2java делает.

Сначала стоит отметить небольшой хак в классе Ecosystem. Метод processFile считывает содержимое файла в строковый буфер и принудительно добавляет перевод строки в конец буфера. Это связано с тем, что часть примеров компилируемого кода идут без него. Smali спокойно обрабатывает оба варианта, а для smali2java пришлось делать выбор: либо усложнять и перегружать дублированной информацией грамматику, либо применить вышеописанный хак. Я выбрал последнее.

Грамматика хранится по пути ./smali-abnf/smali.abnf. Сама по себе грамматика не является частью smali2java, но она служит основой для генерации парсера утилитой aParse, о которой я уже писал. Список поддерживаемых команд можно найти в самом конце грамматики (их пока что не так уж и много, всего лишь 27, но с каждым коммитом их число растет).

В самой грамматике отдельное внимание стоит уделить регистрам виртуальной машины Dalvik. Smali описывает 2 типа регистров: обычные (используемые в локальных переменных; обозначаются как vXX, например, v0) и параметрические (в которых передаются параметры; обозначаются как pXX, например, p0). Для самой виртуальной машины разницы между этими регистрами нет, а вот для тех, кто пишет на smali, они позволяют повысить продуктивность в разы. В smali есть 2 директивы: .locals <число_обычных_регистров> и .registers <общее_число_регистров>. Первая говорит компилятору, что у нас есть N локальных регистров (которые нумеруются от 0 до N-1), и тогда параметрические регистры начинаются с номера N (т.е. p0 и vN станут синонимами). А вторую используют, чтобы сообщить об общем количестве использованных регистров. И раз прототип метода компилятору известен, он сам может посчитать, с какого индекса стартуют параметрические регистры. Стоит еще заметить, что для нестатических методов в регистре p0 передается «this» (указатель на экземпляр класса), а для значений типа long и double используется пара последовательно идущих регистров (обращение к ним осуществляется по имени регистра с меньшим индексом).

Грамматика smali2java написана таким образом, чтобы парсер в целом ряде случаев на лету отделял 64-битные регистры (в которых хранятся long и double) от 32-битных. Если вы приглядитесь к объявлению правил типа cmdXXXX, то сразу это заметите. Подобное поведение просто необходимо для построения карты регистров, по которой и будет восстанавливаться Java-код метода. Но об этом в следующий раз.

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • LinkedIn
  • Tumblr

Written by Дмитрий Воробьев

Вторник, Январь 15th, 2013 at 06:55

Leave a Reply

You must be logged in to post a comment.