Запуск автономной библиотеки на Android L. Ошибка: поддерживаются только независимые исполняемые файлы (PIE)

Когда я запускаю собственный код на Android L (Nexus 5), я получаю сообщение об ошибке.

error: поддерживаются только автономные исполняемые файлы (PIE).

Тот же код выполняется правильно на моем Samsung Galaxy S3 (Android 4.3).

Вот мой Application.mk

APP_PROJECT_PATH := $(call my-dir)/.. APP_ABI := armeabi NDK_TOOLCHAIN_VERSION := 4.7 APP_PLATFORM := android-9 APP_GNUSTL_FORCE_CPP_FEATURES := exceptions rtti 

Однако, когда я заменяю APP_PLATFORM := android-9 с APP_PLATFORM := android-16 (Как я здесь читал, поддержка PIE появилась в Jelly Been (API-уровень 16)), тот же исполняемый файл отлично работает на Android L.

Есть ли способ скомпилировать собственный код с помощью APP_PLATFORM := android-9 и запустить его на Android L?

Если вы можете жить только с поддержкой Android 4.1+, просто установите APP_PLATFORM := android-16 и вам будет хорошо идти. За кулисами он устанавливает APP_PIE := true . Ваш двоичный файл будет segfault на старых SDK.

Если вам также необходимо поддерживать более низкие уровни SDK, вам нужно создать два бинарных файла. Некоторые другие ответы, которые я видел, рекомендовали поддерживать два отдельных дерева источников с различными APP_PLATFORM, но вам не нужно это делать. Можно сделать один вывод Android.mk как PIE, так и не-PIE-двоичным.

NDK 10c и более поздние версии:

Убедитесь, что PIE отключен по умолчанию, поскольку включение его вручную проще, чем его отключение. PIE не включается по умолчанию, если ваша APP_PLATFORM не равна> = 16. Убедитесь, что ваша APP_PLATFORM либо не установлена ​​(по умолчанию для android-3, либо android-14 с NDK 15), ниже, чем у android-16, или установите APP_PIE := false .

Следующий Android.mk затем создает PIE и не-PIE-двоичный файл, но имеет оговорку (см. Ниже) :

 LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) # Enable PIE manually. Will get reset on $(CLEAR_VARS). This # is what enabling PIE translates to behind the scenes. LOCAL_CFLAGS += -fPIE LOCAL_LDFLAGS += -fPIE -pie LOCAL_MODULE := mymod LOCAL_SRC_FILES := \ mymod.c include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_MODULE := mymod-nopie LOCAL_SRC_FILES := \ mymod.c include $(BUILD_EXECUTABLE) 

Затем вам нужно будет добавить какую-то логику для вызова правильного двоичного кода в вашем коде.

К сожалению, это означает, что вам придется скомпилировать исполняемый модуль дважды, что может быть медленным. Вам также необходимо указать LOCAL_SRC_FILES и любые библиотеки дважды, что может быть неприятно и сложно отслеживать. Что вы можете сделать, так это скомпилировать основной исполняемый файл как статическую библиотеку и создать исполняемые файлы из ничего, кроме этой статической библиотеки. Статические библиотеки не требуют PIE.

 LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := mymod-common LOCAL_SRC_FILES := \ mymod.c include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) # Enable PIE manually. Will get reset on $(CLEAR_VARS). This # is what enabling PIE translates to behind the scenes. LOCAL_CFLAGS += -fPIE LOCAL_LDFLAGS += -fPIE -pie LOCAL_MODULE := mymod LOCAL_STATIC_LIBRARIES := mymod-common include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_MODULE := mymod-nopie LOCAL_STATIC_LIBRARIES := mymod-common include $(BUILD_EXECUTABLE) 

Кажется, что это работает очень хорошо, хотя требуется определенное количество шаблонов.

NDK 10b:

NDK 10b позволяет PIE по умолчанию и не позволяет вам отключать его, за исключением ужасных хаков. Действительно, просто обновите до 10c. Я оставляю свой старый ответ здесь для справки, но я бы никому не рекомендовал.

 LOCAL_PATH := $(call my-dir) # Forcefully disable PIE globally. This makes it possible to # build some binaries without PIE by adding the necessary flags # manually. These will not get reset by $(CLEAR_VARS). PIE is # force-enabled on NDK 10b so we'll need this even if APP_PIE # is set to false. TARGET_PIE := false NDK_APP_PIE := false include $(CLEAR_VARS) # Enable PIE manually. Will get reset on $(CLEAR_VARS). This # is what enabling PIE translates to behind the scenes. LOCAL_CFLAGS += -fPIE LOCAL_LDFLAGS += -fPIE -pie LOCAL_MODULE := mymod LOCAL_SRC_FILES := \ mymod.c include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_MODULE := mymod-nopie LOCAL_SRC_FILES := \ mymod.c include $(BUILD_EXECUTABLE) 

Проект Chromium выпустил обертку, которая позволяет бинарным файлам PIE работать на версиях до JB Android. Обратите внимание, что для выполнения этой работы вашему исполняемому файлу PIE требуется несколько дополнительных флагов:

 CFLAGS += -fvisibility=default -fPIE LDFLAGS += -rdynamic -fPIE -pie 

В моем случае я отправлял файлы ~ 2 Мбайт для трех архитектур и не хотел добавлять 6 МБ несжатых данных в APK, чтобы продолжить поддержку ICS. run_pie чрезвычайно крошечный (6-7 КБ), чтобы он соответствовал счету.

run_pie не должен строиться с флагами PIE, и его нельзя run_pie на Android 5.0+ (потому что, конечно, бинарные файлы, отличные от PIE, запрещены). К сожалению, он не может быть создан статически, потому что он должен быть связан с -ldl а NDK предоставляет только общую версию этой библиотеки.

Сторона Java может выглядеть примерно так:

 String dir = mContext.getFilesDir().getPath(); String command = dir + "/busybox netstat"; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { command = dir + "/run_pie " + command; } 

где busybox - исполняемый файл PIE и живет в каталоге личных файлов приложения.

См. Также: предыдущие обсуждения этой темы здесь и здесь .

Редактирование JFDee: В моем случае я продолжал получать ошибку «dlopen () не удалось: невозможно загрузить библиотеку» при запуске run_pie с моим исполняемым файлом PIE. Я должен был явно установить LD_LIBRARY_PATH в каталог, в котором находился исполняемый файл, то есть текущий путь.

В этом случае измененная строка кода кода вызова «run_pie» будет выглядеть так:

 ... command = "LD_LIBRARY_PATH=. " + dir + "/run_pie " + command; ... 

Я создал два исполняемых файла: один с APP_PLATFORM := android-9 а другой с APP_PLATFORM := android-16 . Чтобы запустить собственный код в Java, мне нужно следующее:

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { // Run the file which was created using APP_PLATFORM := android-16 } else { // Run the file which was created using APP_PLATFORM := android-9 } 
  • Как связать предварительно созданную общую библиотеку с проектом Android NDK?
  • Eclipse успешно компилируется, но все же дает семантические ошибки
  • Android: как программно найти имя версии Android?
  • Как разрешить java.lang.UnsatisfiedLinkError в NDK на Android?
  • NDK - Как использовать сгенерированную библиотеку .so в другом проекте
  • Ошибки индексатора Eclipse при использовании STL с Android NDK
  • System.loadLibrary (...) не смог найти родную библиотеку в моем случае
  • Ваш APP_BUILD_SCRIPT указывает на неизвестный файл с помощью Android ndk-build
  • Как создать исполняемый файл для оболочки Android
  • Добавление предварительно созданных файлов .so в проект с помощью плагина Android Gradle 0.7.3
  • Как скомпилировать C в исполняемый двоичный файл и запустить его в Android из Android Shell?
  • Давайте будем гением компьютера.