Linux 如何构建原生(命令行)可执行文件以在 Android 上运行?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9460251/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 04:51:05  来源:igfitidea点击:

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

androidlinux

提问by Someone Somewhere

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

我已经成功构建了一个使用本机 (JNI) 库的 Android 应用程序 (GUI)。

However, now I would like to create an executable that runs from the command line (root privileges) and does not use a GUI at all. How do I build something like that?

但是,现在我想创建一个从命令行(root 权限)运行并且根本不使用 GUI 的可执行文件。我如何构建这样的东西?

采纳答案by kennytm

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

从 NDK r8d 开始,这可以通过更简单的方式解决。

  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.

  1. 创建一个具有以下目录层次结构的项目:

    project/
        jni/
            Android.mk
            Application.mk
            *.c, *.cpp, *.h, etc.
    
  2. 用以下内容填写Android.mk。最重要的是最后一行。检查 NDK 文档以了解其他变量的含义。

    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. 转到project/目录,然后只需键入

    ndk-build
    

    结果将放在project/libs/<arch>/name-of-your-executable.

回答by Someone Somewhere

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

http://www.bekatul.info/content/native-c-application-android[损坏(2015 年 11 月 9 日)]

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:

作者将这些文件存储在他/她的本地 linux 计算机上:

/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:

使用的 SDK 和 NDK 是:

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:

将 gdbserver 文件 ($(PREBUILD)/../gdbserver) 复制到模拟器,在 Makefile 中添加目标而不是使其更容易:

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:

现在我们将调试它@端口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
(gdb) n
9               printf("Hello, world (i=%d)!\n", i);
(gdb) p i
 = 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

回答by Olorin

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

有人某处的答案让我朝着正确的方向前进,但它包含错误/不准确。

As far as gdbserver is concerned, the adb command

就 gdbserver 而言,adb 命令

$(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

将永远无法工作,原因很明显,因为“在”目录 $(PREBUILD) 和 gdbserver 之间,有目录 android-arm。最好设置一下

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

有了这个,一切都适用于我的 android 虚拟设备。(没有明显的分段错误。)在我的真实设备上,我确实有分段错误。那是

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.

在 gdb 模式下。我不知道为什么。

回答by 18446744073709551615

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

这是一个遵循 KennyTM 答案的示例项目。您可以从头开始创建它或修改另一个项目,例如,hello-jni在 NDK 示例中。

jni/main.c:

jni/main.c:

#include <stdio.h>
int main() {
    printf("hello\n");
    return 0;
}

jni/Application.mk:

jni/Application.mk:

#APP_ABI := all
APP_ABI := armeabi-v7a

jni/Android.mk:

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 logcatto see it.

我必须注意,您不会在 stdout 输出中看到任何日志记录,您必须使用adb logcat才能看到它。

So if you want logging:

所以如果你想记录:

jni/main.c:

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.mkbecomes:

jni/Android.mk 中的对应部分变为:

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.