【问题标题】:Why am I getting "undefined symbol" when I link my static library in my C program?当我在 C 程序中链接静态库时,为什么会出现“未定义的符号”?
【发布时间】:2020-09-16 21:36:21
【问题描述】:

我正在尝试创建一个静态库以允许我在我的 C 程序中使用 C++ API。首先,我尝试调用一个名为APIName::Api_init() 的API 函数;到目前为止,我已经创建了一个“包装器”库,其中包含以下内容:

c_api.h:

#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif
EXTERNC int apiname_api_init(char const * user_app_name);
EXTERNC void apiname_api_shutdown();

#undef EXTERNC

c_api.cpp:

#include "c_api.h"
#include "../include/apiname/api.h"   // Link to header files from API provider
int apiname_api_init(char const * user_app_name) {
        return to_return_code(APIName::Api_init(user_app_name));
}

void apiname_api_shutdown() {
        return APIName::api_shutdown();
}

然后我使用以下命令将其编译成一个名为 apiwrapper.o 的目标文件:

g++  -L../include/libs -lapinameapi  -shared -fPIC -c -D_LINUX_X86_64 -lpthread -lz -lm -lcrypto -lbz2 -I../include -DPROVIDE_LOG_UTILITIES -DTARGET_PLATFORM_LINUX  c_api.cpp -o lapiwrapper.o -lrt

其中apinameapi 是来自API 提供者的共享库。这工作正常。然后我使用以下方法生成库文件:

ar rc libapiwrapper.a apiwrapper.o 

再次,这很好。

现在我创建我的 C 文件 (mycode.c),其中包含:

#include "mycode.h"
#include <stdio.h>
#include <string.h>
#include <time.h>
#ifdef _WIN32
#include <Windows.h>
#else
#include <unistd.h>
#endif
int connectToApi() {

  // Init API
  rc = apiname_api_init("programname");

}

头文件mycode.h包含:

#include <stdbool.h>
#include <stdint.h>
#include "c_api.h"

(显然还有其他内容,但为简洁起见,我省略了)。

所以,我编译我的主程序使用:

gcc  -Linclude/libs -lapiwrapper  -shared -DKXVER=3 -fPIC -D_LINUX_X86_64 -lpthread -lz -lm -lcrypto -lbz2 -Iinclude -DPROVIDE_LOG_UTILITIES -DTARGET_PLATFORM_LINUX  src/mycode.c -o lib/mycode.so -lrt -Wno-discarded-qualifiers -Wno-incompatible-pointer-types

这也很好。然后,我运行程序(这个特定的文件被另一段代码加载),我得到了

undefined symbol: apiname_api_init

似乎没有在库文件中找到该函数。我哪里错了?这是我第一次构建库,所以它可能是相当基本的东西。

编辑:我认为问题可能来自最终编译(创建 mycode.so)正在生成一个共享对象文件,我需要它自己运行(不需要我生成的库文件也在场)。我必须从另一个程序运行最终对象,所以它需要是独立的。有没有一种方法可以将它链接到一起“捆绑”到一个最终的 so 文件中?

【问题讨论】:

  • 我尝试更改顺序,但似乎没有任何区别。还是谢谢。
  • @Sharon 这是一个有趣的问题。但是您能否明确说明“然后,我运行程序”这一特定行。您发布的任何代码中都没有 main() 函数。最终输出的也是.so文件,你在哪里使用呢?
  • @MohithReddy 它是从另一种语言的另一个文件中调用的。所以,我想生成 mycode.so 作为共享对象,然后从其他代码(使用不同的专业语言)可以导入 C 对象并运行它。那有意义吗?所以它会调用类似 mycode.connectToApi() 的东西。

标签: c++ c gcc ld dynamic-linking


【解决方案1】:

命令ar rc libapiwrapper.a apiwrapper.o 创建一个静态库。如果你链接到它,你还需要链接它的依赖项。 您需要的是创建一个共享库(.so 文件)。 您可能需要这样的东西(我已将编译和链接步骤拆分为单独的命令):

g++ -lapinameapi -c -D_LINUX_X86_64 -I../include -DPROVIDE_LOG_UTILITIES -DTARGET_PLATFORM_LINUX c_api.cpp -o lapiwrapper.o
g++ -shared -fPIC -o libapiwrapper.so lapiwrapper.o -L../include/libs -lpthread -lz -lm -lcrypto -lbz2 -lrt

最后只需将可执行文件与这个共享库而不是静态库链接起来。

【讨论】:

    【解决方案2】:

    我尝试使用libssllibcrypto 重现该问题。所以就我而言,我需要生成一个api_wrapper,它使用libssllibcrypto 的功能,它应该是独立的。但不幸的是,正如您在 EDIT 中所要求的那样,两个或多个共享库不能捆绑在一起。请参考此链接Merge multiple .so shared libraries。如果你所谓的different, specialist language也能加载静态库,那么你可以如下进行。

    我的api_wrappers

    apiconnect.c

    #include "apiconnect.h"
    #include <openssl/ssl.h>
    
    int api_connect()
    {
            SSL_library_init();
            OpenSSL_add_all_algorithms();
            SSL_load_error_strings();
    
            return 0;
    }
    

    apiconnect.h

    int api_connect();
    
    • 我是如何产生libapi.a

    由于我的代码依赖于libssllibcrypto,我开始从/usr/lib/x86_64-linux-gnu/ 收集它们的静态版本。然后我使用ar x library提取了libssl.alibcrypto.a的内容。我找到了700 目标文件。 apiconnect.c 被编译生成 apiconnect.o 并添加到提取的目标文件中。然后我将它们全部打包在一起生成libapi.a。下面显示了我的终端会话:

    root@kali:/tmp/apiconnect# ls 
    apiconnect.c  apiconnect.h  libapi
    root@kali:/tmp/apiconnect# cd libapi/
    root@kali:/tmp/apiconnect/libapi# cp /usr/lib/x86_64-linux-gnu/libssl.a /usr/lib/x86_64-linux-gnu/libcrypto.a .
    root@kali:/tmp/apiconnect/libapi# ar x libssl.a 
    root@kali:/tmp/apiconnect/libapi# ar x libcrypto.a 
    root@kali:/tmp/apiconnect/libapi# ls | wc -l 
    702
    root@kali:/tmp/apiconnect/libapi# cd ..
    root@kali:/tmp/apiconnect# gcc -c apiconnect.c 
    root@kali:/tmp/apiconnect# ls 
    apiconnect.c  apiconnect.h  apiconnect.o  libapi
    root@kali:/tmp/apiconnect# cp apiconnect.o libapi/
    root@kali:/tmp/apiconnect# cd libapi/
    root@kali:/tmp/apiconnect/libapi# ls *.a
    libcrypto.a  libssl.a
    root@kali:/tmp/apiconnect/libapi# rm *.a
    root@kali:/tmp/apiconnect/libapi# ar rc libapi.a *
    root@kali:/tmp/apiconnect/libapi# ls libapi.a
    libapi.a
    
    • 测试

    由于我不知道different, specialist language 并且我没有它,为了测试目的,我将libapi.aapiconnect.h 移动到另一个没有安装libssllibcrypto 的系统。我试图编译一个调用api_connect()的测试程序。

    测试程序:

    #include "apiconnect.h"
    
    int main()
    {
       api_connect();
       return 0;
    }
    

    编译:

    jhansi@poseidon:/tmp/apitesting$ ls 
    apiconnect.h  libapi.a  test.c
    jhansi@poseidon:/tmp/apitesting$ cat test.c 
    #include "apiconnect.h"
    
    int main()
    {
    api_connect();
    return 0;
    }
    jhansi@poseidon:/tmp/apitesting$ gcc -L./ test.c -lapi
    .//libapi.a(threads_pthread.o): In function `CRYPTO_THREAD_lock_new':
    (.text+0x46): undefined reference to `pthread_rwlock_init'
    .//libapi.a(threads_pthread.o): In function `CRYPTO_THREAD_read_lock':
    (.text+0x85): undefined reference to `pthread_rwlock_rdlock'
    .//libapi.a(threads_pthread.o): In function `CRYPTO_THREAD_write_lock':
    (.text+0xa5): undefined reference to `pthread_rwlock_wrlock'
    .//libapi.a(threads_pthread.o): In function `CRYPTO_THREAD_unlock':
    (.text+0xc5): undefined reference to `pthread_rwlock_unlock'
    .//libapi.a(threads_pthread.o): In function `CRYPTO_THREAD_lock_free':
    (.text+0xea): undefined reference to `pthread_rwlock_destroy'
    .//libapi.a(threads_pthread.o): In function `CRYPTO_THREAD_run_once':
    (.text+0x115): undefined reference to `pthread_once'
    .//libapi.a(threads_pthread.o): In function `CRYPTO_THREAD_init_local':
    (.text+0x135): undefined reference to `pthread_key_create'
    .//libapi.a(threads_pthread.o): In function `CRYPTO_THREAD_set_local':
    (.text+0x167): undefined reference to `pthread_setspecific'
    .//libapi.a(threads_pthread.o): In function `CRYPTO_THREAD_cleanup_local':
    (.text+0x187): undefined reference to `pthread_key_delete'
    .//libapi.a(threads_pthread.o): In function `openssl_init_fork_handlers':
    (.text+0x1e3): undefined reference to `pthread_once'
    .//libapi.a(threads_pthread.o): In function `fork_once_func':
    (.text+0x16): undefined reference to `pthread_atfork'
    .//libapi.a(threads_pthread.o): In function `CRYPTO_THREAD_get_local':
    (.text+0x153): undefined reference to `pthread_getspecific'
    .//libapi.a(dso_dlfcn.o): In function `dlfcn_globallookup':
    (.text+0x13): undefined reference to `dlopen'
    (.text+0x26): undefined reference to `dlsym'
    (.text+0x31): undefined reference to `dlclose'
    .//libapi.a(dso_dlfcn.o): In function `dlfcn_bind_func':
    (.text+0x1a7): undefined reference to `dlsym'
    (.text+0x272): undefined reference to `dlerror'
    .//libapi.a(dso_dlfcn.o): In function `dlfcn_load':
    (.text+0x2e1): undefined reference to `dlopen'
    (.text+0x359): undefined reference to `dlclose'
    (.text+0x395): undefined reference to `dlerror'
    .//libapi.a(dso_dlfcn.o): In function `dlfcn_pathbyaddr':
    (.text+0x452): undefined reference to `dladdr'
    (.text+0x4c7): undefined reference to `dlerror'
    .//libapi.a(dso_dlfcn.o): In function `dlfcn_unload':
    (.text+0x6a4): undefined reference to `dlclose'
    collect2: error: ld returned 1 exit status
    

    但是很遗憾,原来我们包含的目标文件是依赖于libpthreadlibdl的,在编译包含之后,我成功编译运行程序,没有错误。

    jhansi@poseidon:/tmp/apitesting$ ls 
    apiconnect.h  libapi.a  test.c
    jhansi@poseidon:/tmp/apitesting$ gcc -L./ test.c  -lapi -lpthread -ldl
    jhansi@poseidon:/tmp/apitesting$ ls 
    a.out  apiconnect.h  libapi.a  test.c
    jhansi@poseidon:/tmp/apitesting$ ldd a.out 
            linux-vdso.so.1 (0x00007ffedd2d7000)
            libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fd3d0f30000)
            libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fd3d0d2c000)
            libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd3d093b000)
            /lib64/ld-linux-x86-64.so.2 (0x00007fd3d1633000)
    jhansi@poseidon:/tmp/apitesting$ ./a.out 
    jhansi@poseidon:/tmp/apitesting$ 
    

    ldd a.out 的输出可以看出,我们的可执行文件不依赖于libssllibcrypto,当然它也不依赖于libapi.a,因为它是静态链接的。但它依赖于libpthread.solibdl.so,所以如果我是你,我会加载libpthread.solibdl.so 以及libapi.a

    我知道,所写的内容是特定于 libssllibcrypto 的,但我希望你能在你正在尝试的库中找到一些运气。干杯:)

    【讨论】:

      【解决方案3】:

      您需要像处理标题一样对您的 CPP 文件进行外部“C”;否则方法名称将在对象中被破坏。

      #ifdef __cplusplus
      extern "C" {
      #endif
      
      int apiname_api_init(char const * user_app_name) {
              return to_return_code(APIName::Api_init(user_app_name));
      }
      
      void apiname_api_shutdown() {
              return APIName::api_shutdown();
      }
      
      #ifdef __cplusplus
      }
      #endif
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-03
        • 1970-01-01
        • 2023-01-17
        • 1970-01-01
        • 1970-01-01
        • 2013-09-04
        相关资源
        最近更新 更多