¿Cómo construyo un ejecutable nativo (línea de comando) para ejecutar en Android?

Tuve éxito al desarrollar una aplicación de Android (GUI) que usa una biblioteca nativa (JNI).

Sin embargo, ahora me gustaría crear un ejecutable que se ejecute desde la línea de comandos (privilegios de root) y no use una GUI en absoluto. ¿Cómo construyo algo así?

A partir de NDK r8d, esto se puede resolver de una manera mucho más simple.

  1. Crea un proyecto con la siguiente jerarquía de directorio:

    project/ jni/ Android.mk Application.mk *.c, *.cpp, *.h, etc. 
  2. Complete Android.mk con el siguiente contenido. Lo más importante es la última línea. Verifique el documento NDK para el significado de las otras variables.

     LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := name-of-your-executable LOCAL_SRC_FILES := a.cpp b.cpp c.cpp etc.cpp LOCAL_CPPFLAGS := -std=gnu++0x -Wall -fPIE # whatever g++ flags you like LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -fPIE -pie # whatever ld flags you like include $(BUILD_EXECUTABLE) # < -- Use this to build an executable. 
  3. Ve al project/ directorio, y simplemente escribe

     ndk-build 

    El resultado se colocará en project/libs//name-of-your-executable .

http://www.bekatul.info/content/native-c-application-android [roto (9 de noviembre de 2015)]

Para resumir el artículo …

El código de prueba es:

 #include //for printf #include //for exit int main(int argc, char **argv) { int i = 1; i+=2; printf("Hello, world (i=%d)!\n", i); return 0; exit(0); } 

El archivo Makefile es:

 APP := test ROOT := /home/dd/android INSTALL_DIR := /data/tmp NDK_PLATFORM_VER := 8 ANDROID_NDK_ROOT := $(ROOT)/android-ndk-r5 ANDROID_NDK_HOST := linux-x86 ANDROID_SDK_ROOT := $(ROOT)/android-sdk-linux_86 PREBUILD := $(ANDROID_NDK_ROOT)/toolchains/arm-eabi-4.4.0/prebuilt/$(ANDROID_NDK_HOST) BIN := $(PREBUILD)/bin/ LIB := $(ANDROID_NDK_ROOT)/platforms/android-$(NDK_PLATFORM_VER)/arch-arm/usr/lib INCLUDE := $(ANDROID_NDK_ROOT)/platforms/android-$(NDK_PLATFORM_VER)/arch-arm/usr/include CC := $(BIN)/arm-eabi-gcc GDB_CLIENT := $(BIN)/arm-eabi-gdb LIBCRT := $(LIB)/crtbegin_dynamic.o LINKER := /system/bin/linker DEBUG := -g CFLAGS := $(DEBUG) -fno-short-enums -I$(INCLUDE) CFLAGS += -Wl,-rpath-link=$(LIB),-dynamic-linker=$(LINKER) -L$(LIB) CFLAGS += -nostdlib -lc all: $(APP) $(APP): $(APP).c $(CC) -o $@ $< $(CFLAGS) $(LIBCRT) install: $(APP) $(ANDROID_SDK_ROOT)/platform-tools/adb push $(APP) $(INSTALL_DIR)/$(APP) $(ANDROID_SDK_ROOT)/platform-tools/adb shell chmod 777 $(INSTALL_DIR)/$(APP) shell: $(ANDROID_SDK_ROOT)/platform-tools/adb shell run: $(ANDROID_SDK_ROOT)/platform-tools/adb shell $(INSTALL_DIR)/$(APP) debug-install: $(ANDROID_SDK_ROOT)/platform-tools/adb push $(PREBUILD)/../gdbserver $(INSTALL_DIR)/gdbserver $(ANDROID_SDK_ROOT)/platform-tools/adb shell chmod 777 $(INSTALL_DIR)/gdbserver debug-go: $(ANDROID_SDK_ROOT)/platform-tools/adb forward tcp:1234: tcp:1234 $(ANDROID_SDK_ROOT)/platform-tools/adb shell $(INSTALL_DIR)/gdbserver :1234 $(INSTALL_DIR)/$(APP) debug: $(GDB_CLIENT) $(APP) clean: @rm -f $(APP).o $(APP) 

El autor almacenó esos archivos en su computadora local linux en:

 /home/dd/android/dev/native/test.c /home/dd/android/dev/native/Makefile 

El autor compiló y probó con:

 dd@abil:~/android/dev/native$ make clean; make; make install; make run /home/dd/android/android-ndk-r5/toolchains/arm-eabi-4.4.0/prebuilt/linux-x86/bin//arm-eabi-gcc -c -fno-short-enums -I/home/dd/android/android-ndk-r5/platforms/android-9/arch-arm/usr/include test.c -o test.o /home/dd/android/android-ndk-r5/toolchains/arm-eabi-4.4.0/prebuilt/linux-x86/bin//arm-eabi-g++ -Wl,--entry=main,-dynamic-linker=/system/bin/linker,-rpath-link=/home/dd/android/android-ndk-r5/platforms/android-9/arch-arm/usr/lib -L/home/dd/android/android-ndk-r5/platforms/android-9/arch-arm/usr/lib -nostdlib -lc -o test test.o /home/dd/android/android-sdk-linux_86/platform-tools/adb push test /data/tmp/test 45 KB/s (2545 bytes in 0.054s) /home/dd/android/android-sdk-linux_86/platform-tools/adb shell chmod 777 /data/tmp/test /home/dd/android/android-sdk-linux_86/platform-tools/adb shell /data/tmp/test Hello, world (i=3)! 

SDK y NDK utilizados fueron:

 source code: /home/dd/android/dev/native android ndk: /home/dd/android/android-ndk-r5 android sdk: /home/dd/android/android-sdk-linux_86 

Sin embargo, la guía de depuración fue la parte realmente buena. Copiar y pegar ...

Establezca la comstackción para habilitar la depuración:

 DEBUG = -g CFLAGS := $(DEBUG) -fno-short-enums -I$(ANDROID_NDK_ROOT)/platforms/android-$(NDK_PLATFORM_VER)/arch-arm/usr/include 

copie el archivo gdbserver ($ (PREBUILD) /../ gdbserver) al emulador, agregue el objective en Makefile que hacerlo más fácil:

 debug-install: $(ANDROID_SDK_ROOT)/platform-tools/adb push $(PREBUILD)/../gdbserver $(INSTALL_DIR)/gdbserver $(ANDROID_SDK_ROOT)/platform-tools/adb shell chmod 777 $(INSTALL_DIR)/gdbserver 

Ahora lo depuraremos @ puerto 1234:

 debug-go: $(ANDROID_SDK_ROOT)/platform-tools/adb forward tcp:1234: tcp:1234 $(ANDROID_SDK_ROOT)/platform-tools/adb shell $(INSTALL_DIR)/gdbserver :1234 $(INSTALL_DIR)/$(APP) 

Entonces ejecútalo:

 dd@abil:~/android/dev/native$ make clean; make; make install; make debug-install; make debug-go /home/dd/android/android-ndk-r5/toolchains/arm-eabi-4.4.0/prebuilt/linux-x86/bin//arm-eabi-gcc -c -g -fno-short-enums -I/home/dd/android/android-ndk-r5/platforms/android-9/arch-arm/usr/include test.c -o test.o /home/dd/android/android-ndk-r5/toolchains/arm-eabi-4.4.0/prebuilt/linux-x86/bin//arm-eabi-g++ -Wl,--entry=main,-dynamic-linker=/system/bin/linker,-rpath-link=/home/dd/android/android-ndk-r5/platforms/android-9/arch-arm/usr/lib -L/home/dd/android/android-ndk-r5/platforms/android-9/arch-arm/usr/lib -nostdlib -lc -o test test.o /home/dd/android/android-sdk-linux_86/platform-tools/adb push test /data/tmp/test 71 KB/s (3761 bytes in 0.051s) /home/dd/android/android-sdk-linux_86/platform-tools/adb shell chmod 777 /data/tmp/test /home/dd/android/android-sdk-linux_86/platform-tools/adb push /home/dd/android/android-ndk-r5/toolchains/arm-eabi-4.4.0/prebuilt/linux-x86/../gdbserver /data/tmp/gdbserver 895 KB/s (118600 bytes in 0.129s) /home/dd/android/android-sdk-linux_86/platform-tools/adb shell chmod 777 /data/tmp/gdbserver /home/dd/android/android-sdk-linux_86/platform-tools/adb forward tcp:1234: tcp:1234 /home/dd/android/android-sdk-linux_86/platform-tools/adb shell /data/tmp/gdbserver :1234 /data/tmp/test Process /data/tmp/test created; pid = 472 Listening on port 1234 

Ahora abre otra consola y ejecuta el depurador:

 dd@abil:~/android/dev/native$ make debug /home/dd/android/android-ndk-r5/toolchains/arm-eabi-4.4.0/prebuilt/linux-x86/bin//arm-eabi-gdb test GNU gdb 6.6 Copyright (C) 2006 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "--host=x86_64-linux-gnu --target=arm-elf-linux"... (gdb) target remote :1234 Remote debugging using :1234 warning: Unable to find dynamic linker breakpoint function. GDB will be unable to debug shared library initializers and track explicitly loaded dynamic code. 0xb0001000 in ?? () (gdb) b main Breakpoint 1 at 0x82fc: file test.c, line 6. (gdb) c Continuing. Error while mapping shared library sections: /system/bin/linker: No such file or directory. Error while mapping shared library sections: libc.so: Success. Breakpoint 1, main (argc=33512, argv=0x0) at test.c:6 6 int i = 1; (gdb) n 7 i+=2; (gdb) pi $1 = 1 (gdb) n 9 printf("Hello, world (i=%d)!\n", i); (gdb) pi $2 = 3 (gdb) c Continuing. Program exited normally. (gdb) quit 

Bueno, está bien. Y la otra consola dará salida adicional así:

 Remote debugging from host 127.0.0.1 gdb: Unable to get location for thread creation breakpoint: requested event is not supported Hello, world (i=3)! Child exited with retcode = 0 Child exited with status 0 GDBserver exiting 

Aquí hay un proyecto de ejemplo que sigue la respuesta de KennyTM. Puede crearlo desde cero o modificar otro proyecto, por ejemplo, hello-jni en las muestras NDK.

jni / main.c:

 #include  int main() { printf("hello\n"); return 0; } 

jni / Application.mk:

 #APP_ABI := all APP_ABI := armeabi-v7a 

jni / Android.mk:

 LOCAL_PATH := $(call my-dir) # first target: the hello-jni example # it shows how to build multiple targets # {{ you may comment it out include $(CLEAR_VARS) LOCAL_MODULE := hello-jni LOCAL_SRC_FILES := hello-jni.c LOCAL_LDLIBS := -llog -L$(LOCAL_PATH)/lib -lmystuff # link to libmystuff.so include $(BUILD_SHARED_LIBRARY) #}} you may comment it out # second target include $(CLEAR_VARS) LOCAL_MODULE := hello LOCAL_SRC_FILES := main.c include $(BUILD_EXECUTABLE) # < -- Use this to build an executable. 

Tengo que señalar que no verá ningún registro en la salida stdout, tendrá que usar adb logcat para verlo.

Entonces, si quieres iniciar sesión:

jni / main.c:

 #include  #include  int main() { printf("hello\n"); __android_log_print(ANDROID_LOG_DEBUG , "~~~~~~", "log %i", 0); // the 3rd arg is a printf-style format string return 0; } 

y la sección correspondiente en jni / Android.mk se convierte en:

 LOCAL_PATH := $(call my-dir) #... include $(CLEAR_VARS) LOCAL_MODULE := hello LOCAL_SRC_FILES := main.c LOCAL_LDLIBS := -llog # no need to specify path for liblog.so include $(BUILD_EXECUTABLE) # < -- Use this to build an executable. 

Como no tengo 50 reputación, no puedo comentar la respuesta de Someone Anywhere, por lo que lo hago aquí como respuesta.

Me gustaría citar un error / una mala interpretación: en lo que se refiere a gdbserver, el comando adb

 $(ANDROID_SDK_ROOT)/platform-tools/adb push $(PREBUILD)/../gdbserver $(INSTALL_DIR)/gdbserver 

nunca podrá funcionar, por razones obvias, porque “entre” directorios $ (PREBUILD) y gdbserver, está el directorio android-arm. Es mejor establecer

 PREBUILDDEBUG=$(ANDROID_NDK_ROOT)/prebuilt/android-arm 

y para reemplazar el comando anterior por

 $(ANDROID_SDK_ROOT)/platform-tools/adb push $(PREBUILDDEBUG)/gdbserver $(INSTALL_DIR)/gdbserver 

Con esto, todo funciona para mí con un dispositivo virtual Android. (Sin error de segmentación). En mi dispositivo real, tengo un error de segmentación. Eso fue el

 make run 

parte. Con respecto a la parte de depuración, ya sea en el emulador o en un dispositivo real, siempre obtengo un “no puedo acceder a la memoria” cuando hago el

 b main 

en modo gdb … ¿Alguna idea? La ayuda sería muy apreciada.