Согласно: «В принципе, как он узнает смещение кода и как он узнает, в каком dex-файле находится метод?»
Учтите, что эти dex-файлы являются «промежуточными» файлами, интерпретируемыми или скомпилированными с помощью виртуальной машины (ART) или компилятора (dex2oat). Это означает, что нет "смещения" или таблицы перемещения, как в файлах ELF или MACH-O.
Как объяснил Antimony, идентификатор в вызове invoke - это просто индекс в таблице ref одного файла dex. Эта таблица ссылок сопоставляет имя класса, имя метода и подпись к индексу. В итоге виртуальная машина загружает все файлы dex и создает в памяти таблицу классов и методов. Если вызывается invoke (..) get, он проверяет справочную таблицу файла dex и выбирает метод тарета, имя класса и подпись. Теперь он ищет эти три значения в памяти и вызывает.
Пожалуйста, учтите, что вам нужно иметь дело с виртуальной машиной, которая переводит промежуточный язык в архитектурно-зависимый код по адресу: * Runtime (режим интерпретатора) * Runtime, но при необходимости кеширует («Как раз вовремя») * Во время установки вашего приложения («Опережая время»)
Проверьте: https://source.android.com/devices/tech/dalvik/configure
Вот еще один пример, но используя JVM (означает java коды операций):
public final class org.chickenhook.binderfuzzy.BuildConfig
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER
Constant pool:
#1 = Methodref #6.#32 // java/lang/Object."<init>":()V
#2 = String #33 // true
#3 = Methodref #34.#35 // java/lang/Boolean.parseBoolean:(Ljava/lang/String;)Z
#4 = Fieldref #5.#36 // org/chickenhook/binderfuzzy/BuildConfig.DEBUG:Z
#5 = Class #37 // org/chickenhook/binderfuzzy/BuildConfig
#6 = Class #38 // java/lang/Object
#7 = Utf8 DEBUG
#8 = Utf8 Z
#9 = Utf8 APPLICATION_ID
#10 = Utf8 Ljava/lang/String;
#11 = Utf8 ConstantValue
Здесь вы можете увидеть «постоянный пул». В INDEX # 3 у вас есть ссылка на метод java / lang / Boolean.parseBoolean: (Ljava / lang / String;) Z (как я уже сказал, класс, метод и подпись). VM, jit или aot считывает это значение и проверяет память во время выполнения на предмет наличия класса в таблице, где представлены все классы всех промежуточных файлов.
Важно то, что путь к классам указывает на все необходимые промежуточные звенья. В противном случае вы получите исключение ClassNotFound. Он запоминает небольшой бит для компоновщиков времени выполнения, ищущих имена символов и заполняющих таблицу перемещения двоичного файла да, но технически он завершается иначе.