Ejecutar una biblioteca nativa en Android L. error: solo se admiten ejecutables independientes de posición (PIE)


Cuando corro código nativo en Android L (Nexus 5), obtengo el error.

Error: solo se admiten ejecutables independientes de posición (PIE).

El mismo código se ejecuta correctamente en mi Samsung Galaxy S3 (Android 4.3).

Aquí está mi 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

Sin embargo, cuando reemplazo APP_PLATFORM := android-9 con APP_PLATFORM := android-16 (Como leí aquí , el soporte de PIE apareció en Jelly Been (nivel de API 16)), el mismo archivo ejecutable funciona bien en Android L.

Hay un ¿cómo compilar código nativo usando APP_PLATFORM := android-9 y ejecutarlo en Android L?

Author: Maksim Dmitriev, 2014-07-18

3 answers

Si puedes vivir con solo soporte para Android 4.1+, simplemente configura APP_PLATFORM := android-16 y estarás listo. Detrás de las escenas se pone APP_PIE := true. Su binario se segmentará en SDK antiguos.

Si también necesita soportar niveles más bajos de SDK, necesitará crear dos binarios. Algunas otras respuestas que he visto han recomendado mantener dos árboles de fuentes separados con diferentes APP_PLATFORMs, pero no es necesario hacer eso. Es posible hacer una sola Android.mk salida de un PASTEL y un no-PASTEL binario.

NDK 10c y posteriores:

Asegúrese de que PIE esté deshabilitado de forma predeterminada, ya que habilitarlo manualmente es más fácil que deshabilitarlo. PIE no se habilita de forma predeterminada a menos que su APP_PLATFORM sea >=16. Asegúrate de que tu APP_PLATFORM no esté configurada (por defecto en android-3 o android-14 desde NDK 15), inferior a android-16 o bien esté configurada APP_PIE := false.

Lo siguiente Android.mk luego crea un PIE y un no PIE binario, pero tiene una advertencia (ver abajo):

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)

Entonces tendrás que añadir algún tipo de lógica para invocar el binario correcto en tu código.

Desafortunadamente, esto significa que tendrá que compilar el módulo ejecutable dos veces, lo que puede ser lento. También debe especificar LOCAL_SRC_FILES y cualquier biblioteca dos veces, lo que puede ser frustrante y difícil de rastrear. Lo que puede hacer es compilar el ejecutable principal como una biblioteca estática, y construir ejecutables desde nada más que esa biblioteca estática. Bibliotecas estáticas no requiere PASTEL.

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)

Esto parece funcionar bastante bien, aunque todavía se requiere una cierta cantidad de repeticiones.

NDK 10b:

NDK 10b habilita PIE por defecto y no le permite desactivarlo, excepto con terribles hacks. Realmente, simplemente actualice a 10c. Estoy dejando mi vieja respuesta aquí para referencia, pero no se la recomendaría a nadie.

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)
 47
Author: Simo Kinnunen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-23 08:08:43

El proyecto Chromium lanzó un wrapper que permite que los binarios de PIE se ejecuten en versiones de Android anteriores a JB. Tenga en cuenta que su ejecutable PIE requiere algunas banderas adicionales para hacer que esto funcione:

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

En mi caso, estaba enviando binarios de ~2 MB para arquitecturas 3 y no quería agregar 6 MB de datos sin comprimir al APK solo para continuar soportando ICS. run_pie es extremadamente pequeño (6-7kB) por lo que se ajusta a la factura.

run_pie debe no construirse con las banderas de PASTEL, y en caso de que no se ejecute en Android 5.0+ (porque, por supuesto, los binarios que no son de PIE están prohibidos). Desafortunadamente no se puede construir estáticamente porque necesita estar enlazado con -ldl y NDK solo proporciona una versión compartida de esa biblioteca.

El lado Java podría ser algo así como:

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;
}

Donde busybox es un ejecutable PIE y vive en el directorio de archivos privados de la aplicación.

Ver también: discusiones anteriores sobre este tema aquí y aquí.

Editar JFDee: En mi caso, seguí recibiendo el error "dlopen() failed: Cannot load library" al ejecutar run_pie con mi ejecutable PIE. Tuve que establecer explícitamente LD_LIBRARY_PATH en el directorio en el que residía el ejecutable, es decir, la ruta actual.

En ese caso, la línea de código de ejemplo modificada de la llamada" run_pie"se vería así:

...
    command = "LD_LIBRARY_PATH=. " + dir + "/run_pie " + command;
...
 12
Author: Kevin Cernekee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-17 07:00:46

Construí dos archivos ejecutables: uno con APP_PLATFORM := android-9 y el otro con APP_PLATFORM := android-16. Para ejecutar el código nativo en Java necesito esto:

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
}
 7
Author: Maksim Dmitriev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-03 17:48:06