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

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

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

ошибка: поддерживаются только независимые исполняемые файлы (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?

4b9b3361

Ответ 1

Я построил два исполняемых файла: один с 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
}

Ответ 2

Если вы можете жить только с поддержкой 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 не равнa >= 16. Убедитесь, что ваша APP_PLATFORM либо не установлена ​​(по умолчанию используется андроид-3, либо андроид-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)

Ответ 3

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

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

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

run_pie не следует создавать с помощью флагов 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;
...