Почему я получаю «неопределенный символ», когда связываю свою библиотеку stati c с моей программой C? - PullRequest
0 голосов
/ 29 мая 2020

Я пытаюсь создать библиотеку c stati, чтобы позволить мне использовать C ++ API в моей C программе. Для начала я пытаюсь вызвать функцию API с именем APIName::Api_init(); До сих пор я создал библиотеку-оболочку, которая имеет следующее:

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

Кажется, он не находит функцию в файле библиотеки. Где я ошибаюсь? Это мой первый раз, когда я создаю библиотеку, так что это, вероятно, что-то довольно базовое c.

EDIT: я думаю, что проблема может быть связана с тем, что окончательная компиляция (которая создает mycode.so) создает общий объектный файл, и мне нужно, чтобы он запускался сам по себе (без необходимости наличия моего сгенерированного файла библиотеки). Мне нужно запустить последний объект из другой программы, поэтому он должен быть автономным. Есть ли способ связать это, чтобы "связать" все вместе в один окончательный файл so?

Ответы [ 3 ]

1 голос
/ 29 мая 2020

Команда ar rc libapiwrapper.a apiwrapper.o создает библиотеку c stati. Если вы свяжетесь с этим, вам также нужно будет указать его зависимости. Что вам нужно, так это создать общую библиотеку (файл .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

Наконец, это просто вопрос связывания исполняемого файла с этой общей библиотекой вместо stati c библиотека.

0 голосов
/ 29 мая 2020

Вам нужно extern "C" ваш CPP файл так же, как вы делали свой заголовок; иначе имена методов в объекте будут искажены.

#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
0 голосов
/ 29 мая 2020

Я попытался воспроизвести проблему, используя libssl и libcrypto. Так что в моем случае мне нужно создать api_wrapper, который использует функции libssl и libcrypto, и он должен быть автономным. Но, к сожалению, как вы просили в EDIT, две или более разделяемых библиотеки нельзя объединить вместе. Пожалуйста, обратитесь по этой ссылке Объединить несколько разделяемых библиотек .so . Если ваш так называемый different, specialist language также может загружать библиотеку stati c, вы можете действовать следующим образом.

My 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

Поскольку мой код зависит от libssl и libcrypto, Я начал собирать их stati c версии из /usr/lib/x86_64-linux-gnu/. Затем я извлек содержимое libssl.a и libcrypto.a, используя ar x library. Я получил около 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.a и apiconnect.h в другую систему, в которой не установлены libssl или libcrypto. Я попытался скомпилировать тестовую программу, которая вызывает 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

Но, к сожалению, это оказалось что включенные нами объектные файлы зависят от libpthread и libdl, и после включения их в компиляцию мне удалось скомпилировать и запустить программу без ошибок.

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, очевидно, что наш исполняемый файл не зависит ни от libssl, ни от libcrypto, и, конечно, он не зависит от libapi.a, поскольку он статически связан. Но это зависит от libpthread.so и libdl.so, поэтому, если бы я был вами, я бы загружал libpthread.so и libdl.so вместе с libapi.a

Я знаю, что написано конкретно c на libssl и libcrypto, но я надеюсь, вам повезет с библиотеками, которые вы пробуете. Ура :)

...