How do I build a native (command line) executable to run on Android?

前端 未结 4 585
囚心锁ツ
囚心锁ツ 2020-11-28 21:04

I\'ve had success with building an Android app (GUI) that uses a native (JNI) library.

However, now I would like to create an executable that runs from the command l

相关标签:
4条回答
  • 2020-11-28 21:57

    As of NDK r8d, this can be solved in a much simpler way.

    1. Create a project with the following directory hierarchy:

      project/
          jni/
              Android.mk
              Application.mk
              *.c, *.cpp, *.h, etc.
      
    2. Fill in Android.mk with the following content. The most important thing is the last line. Check the NDK doc for the meaning of the other 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. Go to the project/ directory, and simply type

      ndk-build
      

      The result will be placed in project/libs/<arch>/name-of-your-executable.

    0 讨论(0)
  • 2020-11-28 21:58

    Someone Somewhere's answer got me going in the right direction, but it contains an error/misprecision.

    As far as gdbserver is concerned, the adb command

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

    will never be able to work, for obvious reasons, because "between" directories $(PREBUILD) and gdbserver, there is the directory android-arm. It is better to set

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

    and to replace the former command by

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

    With this everything works for me with an android virtual device. (No segmentation fault apparentlty.) On my real device, I do have a segmentation fault. That was the

    make run
    

    part. With respect to debug part, be it on emulator or on real device, I always get a "cannot access memory" when I do the

    b main
    

    in gdb mode. I don't know why.

    0 讨论(0)
  • 2020-11-28 22:00

    Here's an example project that follows the KennyTM's answer. You can create it from scratch or modify another project, for example, hello-jni in the NDK samples.

    jni/main.c:

    #include <stdio.h>
    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.
    

    I have to note that you will not see any logging in the stdout output, you will have to use adb logcat to see it.

    So if you want logging:

    jni/main.c:

    #include <stdio.h>
    #include <android/log.h>
    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;
    }
    

    and the corresponding section in jni/Android.mk becomes:

    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.
    
    0 讨论(0)
  • 2020-11-28 22:08

    http://www.bekatul.info/content/native-c-application-android [broken (Nov 9, 2015)]

    To summarize the article...

    The test code is :

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

    The Makefile is :

    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)
    

    The author stored those files on his/hers local linux computer at:

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

    The author then compiled and tested it with:

    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 and NDK used were:

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

    However, the debug guide was the really good part ! Copy and pasted ...

    Set the compile for enable debugging:

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

    copy the gdbserver file ($(PREBUILD)/../gdbserver) to the emulator, add the target in Makefile than to make it easy:

    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
    

    Now we will debug it @ port 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)
    

    Then execute it:

    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
    

    Now open other console and execute the debugger:

    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) p i
    $1 = 1
    (gdb) n
    9               printf("Hello, world (i=%d)!\n", i);
    (gdb) p i
    $2 = 3
    (gdb) c
    Continuing.
    
    Program exited normally.
    (gdb) quit
    

    Well it is ok. And the other console will give additional output like so:

    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
    
    0 讨论(0)
提交回复
热议问题