精华内容
下载资源
问答
  • 2021-05-16 10:39:34

    1.安装编译环境

    yum -y install gcc

    yum -y install gcc-c++

    yum install -y java-1.8.0-openjdk*

    2.编译C动态库

    准备三个文件:hello.h 、 hello.c 、 main.c

    //hello.h

    #ifndef _HELLO_H_

    #define _HELLO_H_

    void hello();

    #endif /* _HELLO_H_ */

    //hello.c

    #include

    #include "hello.h"

    void hello()

    {

    printf("这是动态链接库接口方法\n");

    }

    //main.c

    #include

    #include "hello.h"

    int main(void)

    {

    hello();

    return 0;

    }

    2.1 编译生成so

    gcc hello.c -fPIC -shared -o libnative.so

    参数说明:

    -fPIC 位置无关码

    -shared 按照共享库的方式来链接

    2.2 可执行程序链接so

    gcc main.c -L. -lnative -o main

    参数说明:

    -L参数:指明要链接的so库所在路径(如-L. 表示当前路径, -L../so 表示当前路径的上一层目录的so子文件夹中)

    -l参数:指明要连接的库的名字,如-lnative 表示要链接libnative.so库

    2.3运行可执行程序

    ./main

    注意:运行的时候会提示找不到链接库,需要配置系统链接库的位置

    配置系统环境变量:

    //当前窗口有效

    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/root/native

    //永久生效

    vim /etc/profile

    export LD_LIBRARY_PATH=/root/native

    source /etc/profile

    3.SpringBoot调用Jni动态库

    准备源码文件:HelloJNI.java、HelloNative.h、HelloNative.c

    //HelloJNI.java

    public class HelloJNI {

    //链接库的方法

    public native static void setNum(int num);

    public native static int get();

    }

    //HelloNative.h

    #include

    #ifndef _Included_HelloJNI

    #define _Included_HelloJNI

    #ifdef __cplusplus

    extern "C" {

    #endif

    JNIEXPORT void JNICALL Java_com_start_printer_HelloJNI_setNum

    (JNIEnv *, jclass, jint);

    JNIEXPORT jint JNICALL Java_com_start_printer_HelloJNI_get

    (JNIEnv *, jclass);

    #ifdef __cplusplus

    }

    #endif

    #endif

    //HelloNative.c

    #include "HelloNative.h"

    int result=888;

    JNIEXPORT void JNICALL Java_com_start_printer_HelloJNI_setNum(JNIEnv * env, jclass jc, jint num)

    {

    result+=num;

    }

    JNIEXPORT jint JNICALL Java_com_start_printer_HelloJNI_get(JNIEnv * env, jclass jc)

    {

    return result;

    }

    配置jdk环境变量:

    vim /etc/profile

    JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-3.b13.el7_5.x86_64

    PATH=$PATH:$JAVA_HOME/bin

    CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

    export JAVA_HOME CLASSPATH PATH

    3.1 编译生成jni的so库

    gcc HelloNative.c -fPIC -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -shared -o libHelloNative.so

    3.2 集成到springboot工程

    拷贝HelloJNI.java文件到jni接口声明的包中

    拷贝so文件到resouces/native文件夹下

    动态加载so类:

    public class NativeLoader {

    /**

    * 加载项目下的native文件,DLL或SO

    *

    * @param dirPath 需要扫描的文件路径,项目下的相对路径

    * @throws IOException

    * @throws ClassNotFoundException

    */

    public synchronized static void loader(String dirPath) throws IOException, ClassNotFoundException {

    Enumeration dir = Thread.currentThread().getContextClassLoader().getResources(dirPath);

    // 获取操作系统类型

    String systemType = System.getProperty("os.name");

    //String systemArch = System.getProperty("os.arch");

    // 获取动态链接库后缀名

    String ext = (systemType.toLowerCase().indexOf("win") != -1) ? ".dll" : ".so";

    while (dir.hasMoreElements()) {

    URL url = dir.nextElement();

    String protocol = url.getProtocol();

    if ("jar".equals(protocol)) {

    JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();

    JarFile jarFile = jarURLConnection.getJarFile();

    // 遍历Jar包

    Enumeration entries = jarFile.entries();

    while (entries.hasMoreElements()) {

    JarEntry jarEntry = entries.nextElement();

    String entityName = jarEntry.getName();

    if (jarEntry.isDirectory() || !entityName.startsWith(dirPath)) {

    continue;

    }

    if (entityName.endsWith(ext)) {

    loadJarNative(jarEntry);

    }

    }

    } else if ("file".equals(protocol)) {

    File file = new File(url.getPath());

    loadFileNative(file, ext);

    }

    }

    }

    private static void loadFileNative(File file, String ext) {

    if (null == file) {

    return;

    }

    if (file.isDirectory()) {

    File[] files = file.listFiles();

    if (null != files) {

    for (File f : files) {

    loadFileNative(f, ext);

    }

    }

    }

    if (file.canRead() && file.getName().endsWith(ext)) {

    try {

    System.load(file.getPath());

    System.out.println("加载native文件 :" + file + "成功!!");

    } catch (UnsatisfiedLinkError e) {

    System.out.println("加载native文件 :" + file + "失败!!请确认操作系统是X86还是X64!!!");

    }

    }

    }

    /**

    * @throws IOException

    * @throws ClassNotFoundException

    * @Title: scanJ

    * @Description 扫描Jar包下所有class

    */

    /**

    * 创建动态链接库缓存文件,然后加载资源文件

    *

    * @param jarEntry

    * @throws IOException

    * @throws ClassNotFoundException

    */

    private static void loadJarNative(JarEntry jarEntry) throws IOException, ClassNotFoundException {

    File path = new File(".");

    //将所有动态链接库dll/so文件都放在一个临时文件夹下,然后进行加载

    //这是应为项目为可执行jar文件的时候不能很方便的扫描里面文件

    //此目录放置在与项目同目录下的natives文件夹下

    String rootOutputPath = path.getAbsoluteFile().getParent() + File.separator;

    String entityName = jarEntry.getName();

    String fileName = entityName.substring(entityName.lastIndexOf("/") + 1);

    System.out.println(entityName);

    System.out.println(fileName);

    File tempFile = new File(rootOutputPath + File.separator + entityName);

    // 如果缓存文件路径不存在,则创建路径

    if (!tempFile.getParentFile().exists()) {

    tempFile.getParentFile().mkdirs();

    }

    // 如果缓存文件存在,则删除

    if (tempFile.exists()) {

    tempFile.delete();

    }

    InputStream in = null;

    BufferedInputStream reader = null;

    FileOutputStream writer = null;

    try {

    //读取文件形成输入流

    in = NativeLoader.class.getResourceAsStream(entityName);

    if (in == null) {

    in = NativeLoader.class.getResourceAsStream("/" + entityName);

    if (null == in) {

    return;

    }

    }

    NativeLoader.class.getResource(fileName);

    reader = new BufferedInputStream(in);

    writer = new FileOutputStream(tempFile);

    byte[] buffer = new byte[1024];

    while (reader.read(buffer) > 0) {

    writer.write(buffer);

    buffer = new byte[1024];

    }

    } catch (IOException e) {

    e.printStackTrace();

    }

    try {

    if (in != null) {

    in.close();

    }

    if (writer != null) {

    writer.close();

    }

    } catch (IOException e) {

    e.printStackTrace();

    }

    try {

    System.out.println("path :" + tempFile.getPath());

    System.load(tempFile.getPath());

    System.out.println("加载native文件 :" + tempFile + "成功!!");

    } catch (UnsatisfiedLinkError e) {

    System.out.println("加载native文件 :" + tempFile + "失败!!请确认操作系统是X86还是X64!!!");

    }

    }

    }

    SpringBoot工程启动类新增初始化加载so的Bean(实现工程启动只加载一次so库)

    @Bean

    public void loadLib() {

    //根据操作系统判断,如果是linux系统则加载c++方法库

    String systemType = System.getProperty("os.name");

    String ext = (systemType.toLowerCase().indexOf("win") != -1) ? ".dll" : ".so";

    if (ext.equals(".so")) {

    try {

    NativeLoader.loader("native");

    } catch (Exception e) {

    System.out.println("加载so库失败");

    }

    }

    System.out.println("loaded");

    }

    加载完成即可调用HelloJNI.java声明的native方法

    3.3 注意事项

    android studio下编译的jni库,在linux下springboot加载不成功。必须在linux重新编译一次动态库才能加载成功。

    更多相关内容
  • VS2017 64位 VTK-9.0.1t编译生成,包含include、lib、bin、share四个文件夹
  • 文件目录结构如下 复制代码 1 dynamiclibapp.c 2 Makefile 3 comm/inc/apue.h 4 comm/errorhandle.c 5 dynamiclib/Makefile 6 dynamiclib/dynamiclib_add.c 7 dynamiclib/dynamiclib_mul.c 8 dynamiclib/inc/...

    文件目录结构如下

    dynamiclibapp.c
    Makefile
    comm/inc/apue.h
    comm/errorhandle.c
    dynamiclib/Makefile
    dynamiclib/dynamiclib_add.c
    dynamiclib/dynamiclib_mul.c
    dynamiclib/inc/dynamiclibs.h
    dynamiclib/libs/
    

    dynamiclib目录
    dynamiclib/inc/dynamiclibs.h 文件内容如下:

    #ifndef __dynamic_libs_h__
    #define __dynamic_libs_h__
    #include "apue.h"
    int dynamic_lib_func_add(int i1, int i2);
    int dynamic_lib_func_mul(int i1, int i2);
    #endif
    

    dynamiclib/dynamiclib_add.c 文件内容如下:

    #include "dynamiclibs.h"
    
    int dynamic_lib_func_add(int i1, int i2) 
     {
    int iret = i1 + i2; 
    printf("... in .so func, %d add %d,return %d\n", i1, i2, iret);
    return iret;
     }
    

    复制代码
    dynamiclib/dynamiclib_mul.c 文件内容如下:

    #include "dynamiclibs.h"
     
     int dynamic_lib_func_mul(int i1, int i2) 
    {
     int iret = i1 * i2; 
    printf("... in .so func, %d multiplys %d, retun %d\n", i1, i2, iret);
    return iret;
     }
    

    dynamiclib/Makefile 文件内容如下:

    1 CC = gcc
    2 CFLAGS = -Wall -g -O -fPIC 需要加上 -fPIC
    3 CXXFLAGS =
    4 INCLUDE = -I ./inc -I …/comm/inc
    5 TARGET = libmytest.so
    6 LIBPATH = ./libs/
    7
    8 vpath %.h ./inc
    9
    10 OBJS = dynamiclib_add.o dynamiclib_mul.o
    11 SRCS = dynamiclib_add.c dynamiclib_mul.c
    12
    13 ( O B J S ) : (OBJS): (OBJS):(SRCS)
    14 $(CC) $(CFLAGS) $(INCLUDE) -c 1 516 a l l : ^ 15 16 all: 1516all:(OBJS)
    17 $(CC) -shared -fPIC -o $(TARGET) $(OBJS) 需要加上 -shared -fPIC
    18 mv $(TARGET) $(LIBPATH)
    19
    20 clean:
    21 rm -f .o
    22 rm -f $(LIBPATH)

    以上文件,就可以生成动态库文件 libmytest.so,应用程序以两种方式加载动态库函数,如下
    
    1. 在编译应用程序时加载动态库

      dynamiclibapp.c 文件内容如下:

    1 #include “apue.h”
    2 #include “dynamiclibs.h”
    3
    4 int main(int argc, char *argv[])
    5 {
    6 err_msg(“step in main\n”);
    7 dynamic_lib_func_add(1, 9);
    8 dynamic_lib_func_mul(1, 9);
    9 err_msg(“step out main\n”);
    10
    11 return 0;
    12 }

    Makefile 文件内容如下:
    

    1 CC = gcc
    2 CFLAGS = -Wall -O -g
    3 CXXFLAGS =
    4 INCLUDE = -I ./comm/inc -I ./dynamiclib/inc
    5 TARGET = dynamiclibapp
    6 LIBVAR = -lmytest 指明需要链接动态库 libmytest.so
    7 LIBPATH = -L./dynamiclib/libs 指明 libmytest.so 的路径
    8 #search paths for errorhandler.c
    9 vpath %.c ./comm
    10 #下行是为依赖项 apue.h 准备的,比如 [errorhandler.o:errorhandler.c apue.h] 里的 apue.h
    11 vpath %.h ./comm/inc
    12
    13 OBJS = errorhandler.o dynamiclibapp.o
    14 #下行的 apue.h,可以不必写出来
    15 errorhandler.o:errorhandler.c apue.h
    16 $(CC) $(CFLAGS) $(INCLUDE) -c $^
    17 dynamiclibapp.o:dynamiclibapp.c apue.h
    18 $(CC) $(CFLAGS) $(INCLUDE) -c 1 920 a l l : ^ 19 20 all: 1920all:(OBJS) $(LIB)
    21 cd ./dynamiclib && make all
    22 $(CC) $(CFLAGS) $(INCLUDE) -o $(TARGET) $(OBJS) $(LIBPATH) $(LIBVAR)
    23 在上行中,在执行编译时,加载了 libmytest.so 中函数
    24 clean:
    25 rm -f .o
    26 rm -f comm/inc/
    .gch
    27 rm -f $(TARGET)
    28 cd ./dynamiclib && make clean
    复制代码
    对于这种方式编译出来的动态库文件,还需要在 /etc/ld.so.conf.d/ 目录中添加 libmytest.so 库文件的路径说明,

    即在 /etc/ld.so.conf.d/ 目录中新建配置文件 mytest.conf,且执行 ldconfig, /etc/ld.so.conf.d/mytest.conf 的文
    
    件内容为 libmytest.so 库文件的绝对路径,例如:
    

    1 /home/lijiangtao/dynamiclib/libs
    如果不在编译应用程序时加载动态库文件里的函数,而是改为在应用程序执行时(比如:程序的main函数启动期

    间,或在程序执行期间)加载 libmytest.so 里函数,那么就可以不需在 /etc/ld.so.conf.d/ 目录中配置 libmytest.so
    
    路径,具体如下所述。
    
    1. 在应用程序执行时加载动态库

      dynamiclibapp.c 文件内容如下:

    1 #include “apue.h”
    2 #include “dynamiclibs.h”
    3 #include <dlfcn.h>
    4
    5 typedef int (*fp_lib_add)(int, int);
    6 typedef int (fp_lib_mul)(int, int);
    7 typedef void
    dlhandle;
    8
    9 dlhandle dll = NULL;
    10 fp_lib_add func_add = NULL;
    11 fp_lib_mul func_mul = NULL;
    12
    13 dlhandle load_dynamic_func(char *psopath, fp_lib_add *padd, fp_lib_mul *pmul);
    14
    15 int main(int argc, char *argv[])
    16 {
    17 char *pso = “/home/lijiangtao/dynamiclib/libs/libmytest.so”;//指定 .so 路径
    18 dll = load_dynamic_func(pso, &func_add, &func_mul);//程序执行时,加载动态函数
    19 err_msg(“step in main\n”);
    20 func_add(1, 9);//执行 add 函数
    21 func_mul(1, 9);//执行 mul 函数
    22 err_msg(“step out main\n”);
    23
    24 return 0;
    25 }
    26
    27 dlhandle load_dynamic_func(char *psopath, fp_lib_add *padd, fp_lib_mul *pmul)
    28 {
    29 if(NULL == psopath ||’\0’ == psopath[0])
    30 return NULL;
    31 char *perrormsg = NULL;
    32 dlhandle dllhandle = dlopen(psopath, RTLD_LAZY);
    33 if(NULL == dllhandle)
    34 {
    35 printf("%s\n", dlerror());
    36 return NULL;
    37 }
    38 if(NULL != padd)
    39 {
    40 *padd = dlsym(dllhandle, “dynamic_lib_func_add”);//加载 add 函数
    41 perrormsg = dlerror();
    42 if(NULL != perrormsg)
    43 printf("%s\n", perrormsg);
    44 }
    45 if(NULL != pmul)
    46 {
    47 *pmul = dlsym(dllhandle, “dynamic_lib_func_mul”);//加载 mul 函数
    48 perrormsg = dlerror();
    49 if(NULL != perrormsg)
    50 printf("%s\n", perrormsg);
    51 }
    52 return dllhandle;
    53 }

    Makefile 文件内容如下:
    

    1 CC = gcc
    2 CFLAGS = -Wall -O -g
    3 CXXFLAGS =
    4 INCLUDE = -I ./comm/inc -I ./dynamiclib/inc
    5 TARGET = dynamiclibapp
    6 LIBVAR = -ldl 需要链接 libdl.so 库
    7 LIBPATH =
    8 #search paths for errorhandler.c
    9 vpath %.c ./comm
    10 #下行是为依赖项 apue.h 准备的,比如 [errorhandler.o:errorhandler.c apue.h] 里的 apue.h
    11 vpath %.h ./comm/inc
    12
    13 OBJS = errorhandler.o dynamiclibapp.o
    14 #下行的 apue.h,可以不必写出来
    15 errorhandler.o:errorhandler.c apue.h
    16 $(CC) $(CFLAGS) $(INCLUDE) -c $^
    17 dynamiclibapp.o:dynamiclibapp.c apue.h
    18 $(CC) $(CFLAGS) $(INCLUDE) -c 1 920 a l l : ^ 19 20 all: 1920all:(OBJS) $(LIB)
    21 cd ./dynamiclib && make all
    22 $(CC) $(CFLAGS) -rdynamic $(INCLUDE) -o $(TARGET) $(OBJS) $(LIBPATH) $(LIBVAR)
    23 在上行,执行编译时并没有加载动态接口函数,而是在应用程序执行时加载的;需要 -rdynamic 选项,
    以确保 dlopen 这些接口可用
    24 clean:
    25 rm -f *.o
    26 rm -f $(TARGET)
    27 cd ./dynamiclib && make clean

    对于这种方式编译出来的动态库文件,不需要在 /etc/ld.so.conf.d/ 目录中配置 libmytest.so 库文件的路径说明
    
    展开全文
  • 随着业务的增大,我们的业务代码也随之增多,包的...结合最近在做的公司的项目,觉得动态加载so文件是一个很好精简apk包的方法。举个例子,视频播放器的SDK(如IJKplayer,VLC player),他们的各种视频的解码器一...

    (转载)http://blog.cyning.cc/2017/07/18/dynamic-load-so/

    随着业务的增大,我们的业务代码也随之增多,包的大小是有增无减,所以适当的时候思考下:怎么做减法–减小包的体积。

    结合最近在做的公司的项目,觉得动态加载so文件是一个很好精简apk包的方法。举个例子,视频播放器的SDK(如IJKplayer,VLC player),他们的各种视频的解码器一般都是通过C/C++编译的so文件,这些so文件其实都不小,这样导致我们从市场上下载的apk包很大,所以能不能让so文件不随apk一起发布呢,而是按需下载(只有当需要播放视频时才去服务器下载,然后再在本地load)。

    为什么要动态加载

    其实刚才已经解释了,可以有效避免apk安装包过大,因为这些so文件是依赖server的下发,本地只是load的过程。
    其次,动态加载可以动态升级so文件,也是动态化的一部分。可以在不发版的情况下,升级so文件。

    动态加载so文件,必须进行安全性校验,避免不必要的安全事故。

    动态加载so文件

    1. System.load(String filePath)

    加载so文件分为动态加载和静态加载。

    1. 静态加载就是通过System.loadLibrary(Sting libname);来直接加载,对于一个app它只能加载system的和我们自己添加到jniLibs下的so文件。
      图2-1图2-1

      这个是我的demo项目的路径,静态加载回去这些路径下找到对应的库,否则抛出异常。

    2. 动态加载这是通过System.load(String filePath)来加载filePath对应路径下的so文件,这个路径不可以是外置SDcard等拓展路径,必须是/data/**{package}下。

    所以下发的so没有权限放到图2-1下,只能通过加载的so文件路径的方式来动态加载so文件。

    方案1: 将so文件copy到/data/**{package}下,system.load(filePath).

    2. 支持静态加载

    但是我们这样做还是解决不了问题,因为有些so文件加载的过程是放到sdkxia的,如百度地图sdk,已经封装了加载so文件(静态加载),即使你已经实现了方案1仍然扔出UnsatisfiedLinkError的异常。
    要弄清这个过程,就必须了解so的加载过程,以我的本地的android skd(Android)为例。
    System源码

    1
    2
    3
    
    public static void loadLibrary(String libname) {
                  Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
       }
    

     

    RunningTime

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    
    synchronized void loadLibrary0(ClassLoader loader, String libname) {
          if (libname.indexOf((int)File.separatorChar) != -1) {
              throw new UnsatisfiedLinkError(
      "Directory separator should not appear in library name: " + libname);
          }
          String libraryName = libname;
          if (loader != null) {
          // 去loade中查找libraryName命令的library
              String filename = loader.findLibrary(libraryName);
              if (filename == null) {
                  // It's not necessarily true that the ClassLoader used
                  // System.mapLibraryName, but the default setup does, and it's
                  // misleading to say we didn't find "libMyLibrary.so" when we
                  // actually searched for "liblibMyLibrary.so.so".
                  throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
                                                 System.mapLibraryName(libraryName) + "\"");
              }
              String error = doLoad(filename, loader);
              if (error != null) {
                  throw new UnsatisfiedLinkError(error);
              }
              return;
          }
    
          String filename = System.mapLibraryName(libraryName);
          List<String> candidates = new ArrayList<String>();
          String lastError = null;
          for (String directory : getLibPaths()) {
              String candidate = directory + filename;
              candidates.add(candidate);
    
              if (IoUtils.canOpenReadOnly(candidate)) {
                  String error = doLoad(candidate, loader);
                  if (error == null) {
                      return; // We successfully loaded the library. Job done.
                  }
                  lastError = error;
              }
          }
    
          if (lastError != null) {
              throw new UnsatisfiedLinkError(lastError);
          }
          throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
      }
    

     

    代码中的loader是ClassLoader的对象,对于Android实际上是PathClassLoader,这个意思就是当有classLoader时就通过PathClassLoaderfindLibrary(libraryName)来加载(这个好像加载class),若无classLoader就通过mapLibraryName1()

    建议大家看下native层怎么实现的:深入理解 System.loadLibrary

    我们加载so看classLoader是怎么实现的,Android 5.0的源码源码:
    BaseDexClassLoader.java的源码

    1
    2
    3
    4
    
    @Override
       public String findLibrary(String name) {
           return pathList.findLibrary(name);
       }
    

     

    pathList就是我们的DexPathList对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
    final class DexPathList {
    
        private static final String DEX_SUFFIX = ".dex";
        /** class definition context */
        private final ClassLoader definingContext;
        /**
         * List of dex/resource (class path) elements.
         * Should be called pathElements, but the Facebook app uses reflection
         * to modify 'dexElements' (http://b/7726934).
         */
        private final Element[] dexElements;
        /** List of native library directories. */
        private final File[] nativeLibraryDirectories;
         …………
        public String findLibrary(String libraryName) {
            String fileName = System.mapLibraryName(libraryName);
            for (File directory : nativeLibraryDirectories) {
                String path = new File(directory, fileName).getPath();
                if (IoUtils.canOpenReadOnly(path)) {
                    return path;
                }
            }
            return null;
        }
        …………
    }
    

    看到了吧,会先找system下的so文件,再找nativeLibraryDirectories下的,而这个nativeLibraryDirectories就是我们的自己项目中jniLibs下对应的so文件的路径。
    当以当我们静态加载时,其实找的so文件就是nativeLibraryDirectories,所以我们可以以此作为突破口,利用反射,将这个nativeLibraryDirectories的开始处加上我们自己放so的文件夹下(感觉像QQ空间对class做patch的方式哦,其实替换旧的so文件这种可以可行的)。
    开始hook啦。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    PathClassLoader pathClassLoader = (PathClassLoader) context.getApplicationContext().getClassLoader();
           try {
               Object pathList = getPathList(pathClassLoader);
                    
               // 拿到nativeLibraryDirectories的Field
               Field nativeLibraryDirectoriesField = pathList.getClass().getDeclaredField("nativeLibraryDirectories");
               nativeLibraryDirectoriesField.setAccessible(true);
               File[] libPaths = (File[]) nativeLibraryDirectoriesField.get(pathList);
               File[] envilLibPaths = new File[libPaths.length + 1];
               // 将存放我们自己so的文件夹加到第一位
               envilLibPaths[0] = dir;
               // 将原来的路径追加到后面
               for (int i = 0; i < libPaths.length; i++) {
                   envilLibPaths[i + 1] = libPaths[i];
               }
               // 将新的nativeLibraryDirectories设置给pathList
               nativeLibraryDirectoriesField.set(pathList, envilLibPaths);
           } catch (NoSuchFieldException e) {
               e.printStackTrace();
           } catch (IllegalAccessException e) {
               e.printStackTrace();
           } catch (ClassNotFoundException e) {
               e.printStackTrace();
           }
    

     

    这个代码是在14-22都是ok的,但是23源码不是这样滴,看源码吧:
    23的源码先放这,hook起来也不难。

    Android 23源码建议hook nativeLibraryPathElements这个而不是nativeLibraryDirectories;

    方案2:Hook DexPathList的nativeLibraryPathElements或者nativeLibraryDirectories,将我们自定义存so文件的文件夹作为他们的第一个元素。

    出现的问题

    刚开始我把所有视频相关的so文件扔到本地的一个文件下,再copy到/data/**{package}下,居然报32-bit instead of 64-bit 这个错误,我把so再放到jniLibs/armeabi下再跑可以啊,后来google了下发现有人在动态化时也遇到了,其中Anjon-github提到了一个方案:只要找任意一个32位的so文件(当然越小越好了)放到主程序中即可,于是我找了个1k的so文件放到了项目的jniLibs/armeabi下居然真的可以,这个原因不知为何,这个涉及到native代码,本人技术有限暂时没找到答案,不知道大家是否更好的解答或者解决方法。

    展开全文
  •  2、动态链接(.so): 动态库还分为两种用法: a) 应用程序运行期间链接动态库,但是在编译期间声明动态库的存在,也是说这种动态库必须在编译时对编译器可见,但编译器却不将此种库编译进可执行文件; b) 在运行...
  • c++ vs2013 一个简单的如何编译成dll文件及共其他程序使用的例子,本例子使用编写数学简单运算为例子,希望对初学者起到学习的效果
  • Linux下gcc编译生成动态链接*.so文件并调用它 gcc -c test.c将生成test.o的目标文件 gcc -o app test.c将生成可执行程序app 动态库*.so在linux下用c和c++编程时经常会碰到,最近在网站找了几篇文章介绍动态库的...

    Linux下gcc编译生成动态链接库*.so文件并调用它

    gcc -c test.c将生成test.o的目标文件
    gcc -o app test.c将生成可执行程序app

    动态库*.so在linux下用c和c++编程时经常会碰到,最近在网站找了几篇文章介绍动态库的编译和链接,总算搞懂了这个之前一直不太了解得东东,这里做个笔记,也为其它正为动态库链接库而苦恼的兄弟们提供一点帮助。
    1、动态库的编译

    下面通过一个例子来介绍如何生成一个动态库。这里有一个头文件:so_test.h,三个.c文件:test_a.c、test_b.c、test_c.c,我们将这几个文件编译成一个动态库:libtest.so。

    //so_test.h:
    #include "stdio.h"
    void test_a();
    void test_b();
    void test_c();

    //test_a.c:
    #include "so_test.h"
    void test_a()
    {
      printf("this is in test_a...\n");
    }


    //test_b.c:
    #include "so_test.h"
    void test_b()
    {
      printf("this is in test_b...\n");
    }

    //test_c.c:
    #include "so_test.h"
    void test_c()
    {
      printf("this is in test_c...\n");
    }
    将这几个文件编译成一个动态库:libtest.so
    $ gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so

    2、动态库的链接
    在1、中,我们已经成功生成了一个自己的动态链接库libtest.so,下面我们通过一个程序来调用这个库里的函数。程序的源文件为:test.c。

    test.c:
    #include "so_test.h"
    int main()
    {
    test_a();
    test_b();
    test_c();
    return 0;
    }
    将test.c与动态库libtest.so链接生成执行文件test:
    $ gcc test.c -L. -ltest -o test
    测试是否动态连接,如果列出libtest.so,那么应该是连接正常了
    $ ldd test
    执行test,可以看到它是如何调用动态库中的函数的。
    3、编译参数解析
    最主要的是GCC命令行的一个选项:
    -shared该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件

    -fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。

    -L.:表示要连接的库在当前目录中

    -ltest:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称

    LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。

    当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用 /sbin/ldconfig来达到同样的目的,不过如果没有root权限,那么只能采用输出LD_LIBRARY_PATH的方法了。

    4、注意

    调用动态库的时候有几个问题会经常碰到,有时,明明已经将库的头文件所在目录 通过 "-I" include进来了,库所在文件通过 "-L"参数引导,并指定了"-l"的库名,但通过ldd命令察看时,就是死活找不到你指定链接的so文件,这时你要作的就是通过修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件来指定动态库的目录。通常这样做就可以解决库无法链接的问题了。

    在linux下可以用export命令来设置这个值,在linux终端下输入:
    export LD_LIBRARY_PATH=/opt/au1200_rm/build_tools/bin: $LD_LIBRARY_PATH:   
    然后再输入:export   
    即会显示是否设置正确   
    export方式在重启后失效,所以也可以用 vim /etc/bashrc ,修改其中的LD_LIBRARY_PATH变量。   
    例如:LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/au1200_rm/build_tools/bin。

     

     

    Linux下静态库,动态库,以及arm平台下库的基本概念

     

    一、基本概念

    1.1、什么是库

    在 windows 平台和 Linux 平台下都大量存在着库。

    本质上来说库是 一种可执行代码的二进制形式,可以被操作系统载入内存执行。

    由于 windows 和 linux 的平台不同(主要是编译器、汇编器和连接器 的不同),因此二者库的二进制是不兼容的。

    本文仅限于介绍 linux 下的库。

    1.2、 库的种类

    linux 下的库有两种:静态库和共享库(动态库)。

    二者的不同点在于代码被载入的时刻不同。

    静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。

    静态用.a为后缀, 例如: libhello.a

    共享库(动态库)的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。

    动态通常用.so为后缀, 例如:libhello.so

    共享库(动态库)的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。

    为了在同一系统中使用不同版本的库,可以在库文件名后加上版本号为后缀,例如: libhello.so.1.0,由于程序连接默认以.so为文件后缀名。所以为了使用这些库,通常使用建立符号连接的方式。

    ln -s libhello.so.1.0 libhello.so.1 ln -s libhello.so.1 libhello.so

    1.3、静态库,动态库文件在linux下是如何生成的:

    以下面的代码为例,生成上面用到的hello库:

    /* hello.c */

    #include "hello.h"

    void sayhello()

    {

    printf("hello,world ");

    }

    首先用gcc编绎该文件,在编绎时可以使用任何合法的编绎参数,例如-g加入调试代码等:

    $gcc -c hello.c -o hello.o

    1、生成静态库 生成静态库使用ar工具,其实ar是archive的意思

    $ar cqs libhello.a hello.o

    2、生成动态库 用gcc来完成,由于可能存在多个版本,因此通常指定版本号:

    $gcc -shared -o libhello.so.1.0 hello.o

     

    1.4、库文件是如何命名的,有没有什么规范:

    在 linux 下,库文件一般放在/usr/lib和/lib下,

    静态库的名字一般为libxxxx.a,其中 xxxx 是该lib的名称;

    动态库的名字一般为libxxxx.so.major.minor,xxxx 是该lib的名称,major是主版本号,minor是副版本号

     

    1.5、可执行程序在执行的时候如何定位共享库(动态库)文件 :

    当系统加载可执行代码(即库文件)的时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径,此时就需要系统动态载入器 (dynamic linker/loader)

    对于 elf 格式的可执行程序,是由 ld-linux.so* 来完成的,它先后搜索 elf 文件的 DT_RPATH 段—环境变量LD_LIBRARY_PATH—/etc/ld.so.cache 文件列表— /lib/,/usr/lib 目录找到库文件后将其载入内存

    如: export LD_LIBRARY_PATH='pwd'

    将当前文件目录添加为共享目录

     

    1.6、使用ldd工具,查看可执行程序依赖那些动态库或着动态库依赖于那些动态库:

    ldd 命令可以查看一个可执行程序依赖的共享库,

    例如 # ldd /bin/lnlibc.so.6

    => /lib/libc.so.6 (0×40021000)/lib/ld-linux.so.2

    => /lib/ld- linux.so.2 (0×40000000)

    可以看到 ln 命令依赖于 libc 库和 ld-linux 库

    使用以下的命令查看arm平台的依赖关系

     

    注意:arm-linux-readelf -d busybox | grep Shared grep后面可以不要,注意大小Shared第一个字母大写

       

     

    1.7、使用nm工具,查看静态库和动态库中有那些函数名(T类表示函数是当前库中定义的,U类表示函数是被调用的,在其它库中定义的,W类是当前库中定义,被其它库中的函数覆盖)。:

    有时候可能需要查看一个库中到底有哪些函数,nm工具可以打印出库中的涉及到的所有符号,这里的库既可以是静态的也可以是动态的。

    nm列出的符号有很多, 常见的有三种::

    一种是在库中被调用,但并没有在库中定义(表明需要其他库支持),用U表示;

    一种是在库中定义的函数,用T表示,这是最常见的;

    另外一种是所 谓的"弱态"符号,它们虽然在库中被定义,但是可能被其他库中的同名符号覆盖,用W表示。

    例如,假设开发者希望知道上文提到的hello库中是否引用了 printf():

    $nm libhello.so | grep printf

    发现printf是U类符号,说明printf被引用,但是并没有在库中定义。

    由此可以推断,要正常使用hello库,必须有其它库支持,使用ldd工具查看hello依赖于哪些库:

    $ldd hello libc.so.6=>/lib/libc.so.6(0x400la000) /lib/ld-linux.so.2=>/lib/ld-linux.so.2 (0x40000000)

    从上面的结果可以继续查看printf最终在哪里被定义,有兴趣可以Go on

     

    1.8、使用ar工具,可以生成静态库,同时可以查看静态库中包含那些.o文件,即有那些源文件构成。

    可以使用 ar -t libname.a 来查看一个静态库由那些.o文件构成。

    可以使用 ar q libname.a xxx1.o xxx2.o xxx3.o ... xxxn.o 生成静态库

     

     

    Linux下进行程序设计时,关于库的使用:

     

    一、gcc/g++命令中关于库的参数:

    -shared: 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件

    -fPIC:表示编译为位置独立(地址无关)的代码,不用此选项的话,编译后的代码是位置相关的,所以动态载入时,是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。

    -L:指定链接库的路径,-L. 表示要连接的库在当前目录中

    -ltest:指定链接库的名称为test,编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称

    LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。

    当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用 /sbin/ldconfig来达到同样的目的,

    不过如果没有root权限,那么只能采用修改LD_LIBRARY_PATH环境变量的方法了。

    调用动态库的时候,有几个问题会经常碰到:

    1、有时,明明已经将库的头文件所在目录 通过 "-I" include进来了,库所在文件通过 "-L"参数引导,并指定了"-l"的库名,但通过ldd命令察看时,就是死活找不到你指定链接的so文件,这时你要作的就是通过修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件来指定动态库的目录。通常这样做就可以解决库无法链接的问题了。

     

    二、静态库链接时搜索路径的顺序:

    1. ld会去找gcc/g++命令中的参数-L;

    2. 再找gcc的环境变量LIBRARY_PATH,它指定程序静态链接库文件搜索路径;

    export LIBRARY_PATH=$LIBRARY_PATH:data/home/billchen/lib

    3. 再找默认库目录 /lib /usr/lib /usr/local/lib,这是当初compile gcc时写在程序内的。

     

    三、动态链接时、执行时搜索路径顺序:

    1. 编译目标代码时指定的动态库搜索路径;

    2. 环境变量LD_LIBRARY_PATH指定动态库搜索路径,它指定程序动态链接库文件搜索路径;

    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:data/home/billchen/lib

    3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径;

    4. 默认的动态库搜索路径/lib;

    5. 默认的动态库搜索路径/usr/lib。

     

    四、静态库和动态链接库同时存在的问题:

    当一个库同时存在静态库和动态库时,比如libmysqlclient.a和libmysqlclient.so同时存在时:

    在Linux下,动态库和静态库同事存在时,gcc/g++的链接程序,默认链接的动态库。

    可以使用下面的方法,给连接器传递参数,看是否链接动态库还是静态库。

    -WI,-Bstatic -llibname //指定让gcc/g++链接静态库

    使用:

    gcc/g++ test.c -o test -WI,-Bstatic -llibname

    -WI,-Bdynamic -llibname //指定让gcc/g++链接动态库

    使用:

    gcc/g++ test.c -o test -WI,-Bdynamic -llibname

    如果要完全静态加在,使用-static参数,即将所有的库以静态的方式链入可执行程序,这样生成的可执行程序,不再依赖任何库,同事出现的问题是,这样编译出来的程序非常大,占用空间。

     

    五、有关环境变量:

    LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径

    LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径

     

    六、动态库升级问题:

    在动态链接库升级时,

    不能使用cp newlib.so oldlib.so,这样有可能会使程序core掉;

    而应该使用:

    rm oldlib.so 然后 cp newlib.so oldlib.so

    或者

    mv oldlib.so oldlib.so_bak 然后 cp newlib.so oldlib.so

    为什么不能用cp newlib.so oldlib.so ?

    在替换so文件时,如果在不停程序的情况下,直接用 cp new.so old.so 的方式替换程序使用的动态库文件会导致正在运行中的程序崩溃。

     

    解决方法:

    解决的办法是采用"rm+cp" 或"mv+cp" 来替代直接"cp" 的操作方法。

    linux系统的动态库有两种使用方法:运行时动态链接库,动态加载库并在程序控制之下使用。

     

    1、为什么在不停程序的情况下,直接用 cp 命令替换程序使用的 so 文件,会使程序崩溃?

    很多同学在工作中遇到过这样一个问题,在替换 so 文件时,如果在不停程序的情况下,直接用cp new.so old.so的方式替换程序使用的动态库文件会导致正在运行中的程序崩溃,退出。

    这与 cp 命令的实现有关,cp 并不改变目标文件的 inode,cp 的目标文件会继承被覆盖文件的属性而非源文件。实际上它是这样实现的:

    strace cp libnew.so libold.so 2>&1 |grep open.*lib.*.so

    open("libnew.so", O_RDONLY|O_LARGEFILE) = 3

    open("libold.so", O_WRONLY|O_TRUNC|O_LARGEFILE) = 4

    在 cp 使用"O_WRONLY|O_TRUNC" 打开目标文件时,原 so 文件的镜像被意外的破坏了。这样动态链接器 ld.so 不能访问到 so 文件中的函数入口。从而导致 Segmentation fault,程序崩溃。ld.so 加载 so 文件及"再定位"的机制比较复杂。

     

    2、怎样在不停止程序的情况下替换so文件,并且保证程序不会崩溃?

    答案是采用"rm+cp" 或"mv+cp" 来替代直接"cp" 的操作方法。

    在用新的so文件 libnew.so 替换旧的so文件 libold.so 时,如果采用如下方法:

    rm libold.so //如果内核正在使用libold.so,那么inode节点不会立刻别删除掉。

    cp libnew.so libold.so

    采用这种方法,目标文件 libold.so 的 inode 其实已经改变了,原来的 libold.so 文件虽然不能用 "ls"查看到,但其 inode 并没有被真正删除,直到内核释放对它的引用。

    (即: rm libold.so,此时,如果ld.so正在加在libold.so,内核就在引用libold.so的inode节点,rm libold.so的inode并没有被真正删除,当ld.so对libold.so的引用结束,inode才会真正删除。这样程序就不会崩溃,因为它还在使用旧的libold.so,当下次再使用libold.so时,已经被替换,就会使用新的libold.so)

    同理,mv只是改变了文件名,其 inode 不变,新文件使用了新的 inode。这样动态链接器 ld.so 仍然使用原来文件的 inode 访问旧的 so 文件。因而程序依然能正常运行。

    (即: mv libold.so ***后,如果程序使用动态库,还是使用旧的inode节点,当下次再使用libold.so时,就会使用新的libold.so)

     

    到这里,为什么直接使用"cp new_exec_file old_exec_file"这样的命令时,系统会禁止这样的操作,并且给出这样的提示"cp: cannot create regular file `old': Text file busy"。这时,我们采用的办法仍然是用"rm+cp"或者"mv+cp"来替代直接"cp",这跟以上提到的so文件的替换有同样的道理。

     

    但是,为什么系统会阻止 cp 覆盖可执行程序,而不阻止覆盖 so 文件呢?

    这是因为 Linux 有个 Demand Paging 机制,所谓"Demand Paging",简单的说,就是系统为了节约物理内存开销,并不会程序运行时就将所有页(page)都加载到内存中,而只有在系统有访问需求时才将其加载。"Demand Paging"要求正在运行中的程序镜像(注意,并非文件本身)不被意外修改,因此内核在启动程序后会锁定这个程序镜像的 inode。

    对于 so 文件,它是靠 ld.so 加载的,而ld.so毕竟也是用户态程序,没有权利去锁定inode,也不应与内核的文件系统底层实现耦合。

     

     

    gcc指定头文件路径及动态链接库路径

       

    本文详细介绍了linux 下gcc头文件指定方法,以及搜索路径顺序的问题。另外,还总结了,gcc动态链接的方法以及路径指定,同样也讨论了搜索路径的顺序问题。本文包含了很多的例子,具有很强的操作性,希望读者自己去走一遍。
    一.#include <>与#include ""

    #include <>直接到系统指定的某些目录中去找某些头文件。
    #include ""先到源文件所在文件夹去找,然后再到系统指定的某些目录中去找某些头文件。

    二.gcc指定头文件的三种情况:

    1.会在默认情况下指定到/usr/include文件夹(更深层次的是一个相对路径,gcc可执行程序的路径是/usr/bin/gcc,那么它在实际工作时指定头文件头径是一种相对路径方法,换算成绝对路径就是加上/usr/include,如#include 就是包含/usr/include/stdio.h)

    2.GCC还使用了-I指定路径的方式,即
    gcc -I 头文件所在文件夹(绝对路径或相对路径均可) 源文件
    举一个例子:
    设当前路径为/root/test,其结构如下:
    include_test.c
    include/include_test.h
    有两种方法访问到include_test.h。
    1. include_test.c中#include "include/include_test.h"然后gcc include_test.c即可
    2. include_test.c中#include 或者#include 然后gcc –I include include_test.c也可

    3. 参数:-nostdinc使编译器不再系统缺省的头文件目录里面找头文件,一般和-I联合使用,明确限定头文件的位置。

    在编译驱动模块时,由于非凡的需求必须强制GCC不搜索系统默认路径,也就是不搜索/usr/include要用参数-nostdinc,还要自己用-I参数来指定内核头文件路径,这个时候必须在Makefile中指定。

    头文件搜索顺序:
    1.由参数-I指定的路径(指定路径有多个路径时,按指定路径的顺序搜索)

    2.然后找gcc的环境变量 C_INCLUDE_PATH, CPLUS_INCLUDE_PATH, OBJC_INCLUDE_PATH

    3.再找内定目录
    /usr/include
    /usr/local/include
    /usr/lib/gcc-lib/i386-linux/2.95.2/include
    /usr/lib/gcc-lib/i386-linux/2.95.2/../../../../include/g++-3
    /usr/lib/gcc-lib/i386-linux/2.95.2/../../../../i386-linux/include

    库文件,但是如果装gcc的时候,是有给定的prefix的话,那么就是
    /usr/include
    prefix/include
    prefix/xxx-xxx-xxx-gnulibc/include
    prefix/lib/gcc-lib/xxxx-xxx-xxx-gnulibc/2.8.1/include

    三.Linux指定动态库路径

    众所周知,Linux动态库的默认搜索路径是/lib和/usr/lib。动态库被创建后,一般都复制到这两个目录中。当程序执行时需要某动态库, 并且该动态库还未加载到内存中,则系统会自动到这两个默认搜索路径中去查找相应的动态库文件,然后加载该文件到内存中,这样程序就可以使用该动态库中的函 数,以及该动态库的其它资源了。在Linux 中,动态库的搜索路径除了默认的搜索路径外,还可以通过以下三种方法来指定。

    1.在配置文件/etc/ld.so.conf中指定动态库搜索路径。
    可以通过编辑配置文件/etc/ld.so.conf来指定动态库的搜索路径,该文件中每行为一个动态库搜索路径。每次编辑完该文件后,都必须运行命令ldconfig使修改后的配置生效。

    举一个例子:
    所有源文件:
    源文件1: lib_test.c
    #include 
    void prt()
    {
    printf("You found me!!!/n");
    }
    源文件2: main.c
    void prt();
    int main()
    {
    prt();
    return 0;
    }
    操作过程:
    我们通过以下命令用源程序lib_test.c来创建动态库 lib_test.so。
    # gcc –o lib_test.o -c lib_test.c
    # gcc -shared -fPIC -o lib_test.so lib_test.o
    #
    或者直接一条指令:
    #gcc –shared –fPIC –o lib_test.so lib_test.c
    #

    注意:
    -fPIC参数声明链接库的代码段是可以共享的,
    -shared参数声明编译为共享库。请注意这次我们编译的共享库的名字叫做
    lib_test.so,这也是Linux共享库的一个命名的惯例了:后缀使用so,而名称使用libxxxx格式。

    接着通过以下命令编译main.c,生成目标程序main.out。
    # gcc -o main.out -L. –l_test main.c
    #

    请注意为什么是-l_test?

    然后把库文件移动到目录/root/lib中。
    # mkdir /root/lib
    # mv lib_test.so /root/lib/ lib_test.so
    #

    最后编辑配置文件/etc/ld.so.conf,在该文件中追加一行/root/lib。

    运行程序main.out:
    # ./main.out
    ./main.out: error while loading shared libraries: lib_test.so: cannot open shared object file: No such file or directory
    #
    出错了,系统未找到动态库lib_test.so。找找原因,原来在编辑完配置文件/etc/ld.so.conf后,没有运行命令ldconfig,所以刚才的修改还未生效。我们运行ldconfig后再试试。

    # ldconfig
    # ./main.out
    You found me!!!
    #
    程序main.out运行成功,并且打印出正确结果。

    2.通过环境变量LD_LIBRARY_PATH指定动态库搜索路径。
    通过设定环境变量LD_LIBRARY_PATH也可以指定动态库搜索路径。当通过该环境变量指定多个动态库搜索路径时,路径之间用冒号":"分隔。下面通过例2来说明本方法。

    举一个例子:
    这次我们把上面得到的文件lib_test.so移动到另一个地方去,如/root下面,然后设置环境变量LD_LIBRARY_PATH找到lib_test.so。设置环境变量方法如下:
    # export LD_LIBRARY_PATH=/root
    #
    然后运行:
    #./main.out
    You found me!!!
    #
    注意:设置环境变量LD_LIBRARY_PATH=/root是不行的,非得export才行。

    3.在编译目标代码时指定该程序的动态库搜索路径。
    还可以在编译目标代码时指定程序的动态库搜索路径。-Wl,表示后面的参数将传给link程序ld(因为gcc可能会自动调用ld)。这里通过gcc 的参数"-Wl,-rpath,"指定
    举一个例子:
    这次我们还把上面得到的文件lib_test.so移动到另一个地方去,如/root/test/lib下面,
    因为我们需要在编译目标代码时指定可执行文件的动态库搜索路径,所以需要用gcc命令重新编译源程序main.c(见程序2)来生成可执行文件main.out。
    # gcc -o main.out -L. –l_test -Wl,-rpath,/root/test/lib main.c
    #

    运行结果:
    # ./main.out
    You found me!!!
    #

    程序./main.out运行成功,输出的结果正是main.c中的函数prt的运行结果。因此程序main.out搜索到的动态库是/root/test/lib/lib_test.so。

    关于-Wl,rpath的使用方法我再举一个例子,应该不难从中看出指定多个路径的方法:
    gcc -Wl,-rpath,/home/arc/test,-rpath,/lib/,-rpath,/usr/lib/,-rpath,/usr/local/lib test.c

    以上介绍了三种指定动态库搜索路径的方法,加上默认的动态库搜索路径/lib和/usr/lib,共五种动态库的搜索路径,那么它们搜索的先后顺序是什么呢?读者可以用下面的方法来试验一下:
    (1) 用前面介绍的方法生成5个lib_test.so放在5个不同的文件夹下面,要求每一个lib_test.so都唯一对应一个搜索路径,并注意main.out程序输出的不同。
    (2) 运行main.out,即可看出他是那个搜索路径下的,然后删除这个路径下的lib_test.so,然后再运行。依此类推操作,即可推出搜索顺序。

    可以得出动态库的搜索路径搜索的先后顺序是:

    1.编译目标代码时指定的动态库搜索路径;

    2.环境变量LD_LIBRARY_PATH指定的动态库搜索路径;

    3.配置文件/etc/ld.so.conf中指定的动态库搜索路径;

    4.默认的动态库搜索路径/lib;

    5.默认的动态库搜索路径/usr/lib。

    在上述1、2、3指定动态库搜索路径时,都可指定多个动态库搜索路径,其搜索的先后顺序是按指定路径的先后顺序搜索的。有兴趣的读者自己验证。
    --------------------- 
    作者:flyztek 
    来源:CSDN 
    原文:https://blog.csdn.net/flyztek/article/details/73612469 
    版权声明:本文为博主原创文章,转载请附上博文链接!

    展开全文
  • C/C++动态加载So编程

    千次阅读 2022-03-26 09:57:13
    dladdr、dlclose、dlerror、dlopen、dlsym等动态链接加载器的编程接口和示例。
  • java加载so文件

    千次阅读 2021-02-28 19:10:49
    java 程序从文件夹动态加载“SomeClass”脚本。然后,该脚本从 Internet 下载....再在Java中通过 System.loadLibrary加载 so 即可 动态注册:直接告诉 native 方法其在JNI 中对应函 数的指针 a) 先编写 Java 的 na...
  • 描述见博客:http://blog.csdn.net/boy313918205/article/details/53645801
  • 动态链接库(so库):在 编译 的时候,库函数中函数的定义没有编译到源文件中,只是指定了库文件的路径,所以,编译之后源文件的执行还是依赖于so库文件。 静态链接库和动态链接库的区别在于,主程序在运行前,静态...
  • 文章目录前言一、环境二、编译so库1.创建头文件 test_so.h2.实现函数2.编译so文件2.netcore 调用 前言 本人一直使用C# ,以前都在windows下开发,最新在Ubuntu使用netcore。考虑有些类库只有C++代码。怎么把C++函数...
  • 前言 这真的是一个大坑,我花了将近一周的时间去搞它,网上搜罗了一圈发现了几个相关的案例,这里来谈一谈它们的优略势. ...httpVueLoader - 从URL加载vue文件 运行时渲染 - 动态运行代码(可以做组件在线编辑器) ...
  • Linux动态加载 在Linux下,动态库格式一般为libxxx.so者libxxx.so.3这种格式。 通常我们在网上下载下来源码包...指明具体库文件径,如“/home/test/libxxx.so”,在链接时就会自动链接该文件。 使用系统路径,如"/li
  • C#源码,控件类库,动态加载DLL LoadDLLTest-C# WinForm 动态加载DLL 通过配置文件,测试之前请先在VS里编译生成所有项目文件,包括DLL文件,源代码完整,实际上是通过窗体可视化界面来演示如何动态加载DLL库文件
  • //动态库接口文件getmaxlen.h #ifndef _GETMAXLEN_H_ #define _GETMAXLEN_H_ int getMaxLen(int *sel,int N); #endif 文件2.动态库程序实现文件 //动态库程序实现文件getmaxlen.c #include "getmaxlen.h" int...
  • linux下加载so文件

    千次阅读 2020-11-09 17:31:33
    记一下最近碰到的一个加载so文件的问题 简介:so文件就是C编译后的文件。适用于linux系统。windows中使用的是dll。 java中的调用方式很简单: HCNetSDK INSTANCE = (HCNetSDK) Native.loadLibrary("hcnetsdk", ...
  • Android动态加载SO文件

    千次阅读 2017-07-18 09:38:14
    动态加载SO文件一般编译进APK的so文件,我们可以通过以下方式去加载SO文件(注意:例如我们的so文件是libscanner.so,那么我们加载so文件的时候去掉前面的lib和后面的.so后缀) System.loadLibrary("scanner");...
  • 文章目录配置观景变量新建.h .c 文件并交叉编译出静态动态库AS加载使用静态动态库Android Studio mk方式加载静态Android Studio 通过 CMake加载静态动态库 我是在 Linux 上编译的方式 配置观景变量 ...
  • 做C语言开发的都知道,在Linux环境中动态链接以 .so结尾;在Windows环境中动态链接以 .dll结尾。在讲使用之前,我先来给大家补补基础。 链接分为 静态链接动态链接
  • QT在Ubuntu系统的开发,由于Linux系统不支持dll文件,而是支持so文件,因此,需要生成so文件从而能够被qt在Linux系统的调用,还望各位博友多多支持
  • 本文主要介绍Linux系统上程序部署时,无法加载SO文件问题解决方案(作者:李栓鹏)。...在Linux上,可执行程序加载动态库SO文件的路径顺序是:系统配置的LD_LIBRARY_PATH路径。(当前路径不在加载SO的...
  • 首先我们先编译一个动态库:1、写一个add.c#include#include "add.h"int add(int a, intb){return a+b;}其中add.h就是下面一句:int add(int a, int b);2、编译add.c:[root@loadlibrary]# gcc -fPIC -c add.c[root@...
  • JNI动态加载so文件

    千次阅读 2017-07-21 23:51:28
    所以还是要写jni来动态加载so文件,把他们桥接起来,Jni怎么写这里就不说了,在c/c++文件里#include 这个头文件,写个函数指针,利用dlopen()函数就可以实现动态加载so文件。这里有如下几点心得体会: ①打包成
  • Android 使用的 Linker 和传统 Linux 使用...一、编写动态库程序 1、创建my_math.h头文件,里面定义两个函数 #ifndef __MY_MATH_H__ #define __MY_MATH_H__ int my_add(int a, int b); int my_sub(int a, int b); #en
  • 有时出于软件灵活性的考虑,我们不会在编译阶段直接关联so文件,而是通过dlopen等几个函数调用动态加载,主要用到的函数接口有: dlopen dlsym dlclose 需要包含的头文件是dlfcn.h,在编译时需要加上-ldl。下面是...
  • Makefile学习笔记06|编译动态链接

    千次阅读 2022-04-26 11:40:41
    Makefile学习笔记06|编译...编译动态链接库涉及到库文件的路径和加载编译选项 -fPIC -shared g++ -shared -fPIC mylib.cpp -o libmylib.so g++ test.cpp -lmylib -L/root/cpp #!/bin/sh LD_LIBRARY_PATH=./;
  • 一、动态库的静态加载 1.编译动态库->链接成共享 gcc -fpic -c math.c -o math.o //自行编写他们的头文件放在同一目录下 gcc -fpic -c show.c -o show.o gcc -shared math.o show.o -o libmath.so //libmath...
  • 添加动态执行的编译环境 options 是个集合,添加内容,字符集,classpath等 * 6.传入JavaFileObject的java文件,是个集合,创建JavaSourceObject实现这个接口,Kind.SOURCE.extension = '.java' * 7.创建任务并...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 342,661
精华内容 137,064
关键字:

编译动态加载so文件

友情链接: sh7734.rar