Я пытаюсь написать программу для использования статической библиотеки кода C ++ в другой код C ++. Первый код C ++ hello.cpp
:
#include <iostream>
#include <string.h>
using namespace std;
extern "C" void say_hello(const char* name) {
cout << "Hello " << name << "!\n";
}
int main(){
return 0;
}
Я сделал статическую библиотеку из этого кода, hello.a
, используя эту команду:
g++ -o hello.a -static -fPIC hello.cpp -ldl
Вот второй код C ++ для использования библиотеки, say_hello.cpp
:
#include <iostream>
#include <string>
#include <dlfcn.h>
using namespace std;
int main(){
void* handle = dlopen("./hello.a", RTLD_LAZY);
cout<<handle<<"\n";
if (!handle) {
cerr<<"Cannot open library: "<<dlerror()<<'\n';
return 1;
}
typedef void (*hello_t)();
dlerror(); // reset errors
hello_t say_hello = (hello_t) dlsym(handle, "say_hello");
const char *dlsym_error = dlerror();
if (dlsym_error) {
cerr<<"Cannot load symbol 'say_hello': "<<dlsym_error<<'\n';
dlclose(handle);
return 1;
}
say_hello("World");
dlclose(handle);
return 0;
}
Потом я скомпилировал say_hello.cpp
с помощью:
g++ -W -ldl say_hello.cpp -o say_hello
и побежал ./say_hello
в командной строке. Я ожидал получить Hello World!
как вывод, но я получил это вместо:
0x8ea4020
Hello ▒▒▒▒!
В чем проблема? Есть ли хитрость, чтобы сделать совместимость для аргумента метода, как то, что мы используем в ctypes
или что?
Если это поможет, я использую Ленни.
РЕДАКТИРОВАТЬ 1:
Я изменил код и использовал динамическую библиотеку ‘hello.so’, которую я создал с помощью этой команды:
g++ -o hello.so -shared -fPIC hello.cpp -ldl
6-я строка кода изменилась на:
void* handle = dlopen("./hello.so", RTLD_LAZY);
Когда я пытался скомпилировать say_hello.cpp
Я получил эту ошибку:
say_hello.cpp: In function ‘int main()’:
say_hello.cpp:21: error: too many arguments to function
Я также попытался скомпилировать его с помощью этой строки:
g++ -Wall -rdynamic say_hello.cpp -ldl -o say_hello
Но та же ошибка возникла. Поэтому я убрал аргумент "World"
и он был скомпилирован без ошибок; но когда я запускаю исполняемый файл, я получаю тот же вывод, который упоминал ранее.
РЕДАКТИРОВАТЬ 2:
Исходя из предложений @Basile Starynkevitch, я изменил свой say_hello.cpp
код к этому:
#include <iostream>
#include <string>
#include <dlfcn.h>
using namespace std;
int main(){
void* handle = dlopen("./hello.so", RTLD_LAZY);
cout<<handle<<"\n";
if (!handle) {
cerr<<"Cannot open library: "<<dlerror()<<'\n';
return 1;
}
typedef void hello_sig(const char *);
void* hello_ad = dlsym(handle, "say_hello");
if (!hello_ad){
cerr<<"dlsym failed:"<<dlerror()<<endl;
return 1;
}
hello_sig* fun = reinterpret_cast<hello_sig*>(hello_ad);
fun("from main");
fun = NULL;
hello_ad = NULL;
dlclose(handle);
return 0;
}
До этого я использовал нижнюю строку, чтобы сделать .so
файл:
g++ -Wall -fPIC -g -shared hello.cpp -o hello.so
Потом я скомпилировал say_hello.cpp
с этой командой:
g++ -Wall -rdynamic -g say_hello.cc -ldl -o say_hello
А затем запустил его, используя ./say_hello
, Теперь все идет как надо. Спасибо @Basile Starynkevitch за терпение по поводу моей проблемы.
Функции никогда не имеют нулевых адресов, поэтому dlsym
на имя функции (или на самом деле на любое имя, определенное в C ++ или C) не может быть NULL
без сбоев:
hello_t say_hello = (hello_t) dlsym(handle, "say_hello");
if (!say_hello) {
cerr<<"Cannot load symbol 'say_hello': "<<dlerror()<<endl;
exit(EXIT_FAILURE);
};
А также dlopen (3) задокументировано динамически загружать только динамические библиотеки (не статичные!). Из этого следует общие объекты (*.so
) в ELF формат. Читать статью Дреппера Как использовать общие библиотеки
Я полагаю, вы могли найти ошибку в dlopen
(см. также его POSIX длопен Спецификация); должно произойти сбой для статической библиотеки hello.a
; это всегда используется на позиции независимых разделяемых библиотек (лайк hello.so
).
Вам следует dlopen
только позиционно-независимый код общие объекты составлено с
g++ -Wall -O -shared -fPIC hello.cpp -o hello.so
или если у вас есть несколько исходных файлов C ++:
g++ -Wall -O -fPIC src1.cc -c -o src1.pic.o
g++ -Wall -O -fPIC src2.cc -c -o src2.pic.o
g++ -shared src1.pic.o src2.pic.o -o yourdynlib.so
Вы могли бы удалить -O
флаг оптимизации или добавить -g
для отладки или заменить его -O2
если ты хочешь.
и это работает очень хорошо: мой ПЛАВИТЬСЯ Project (язык, специфичный для предметной области для расширения GCC) часто использует это (генерирует код C ++, на лету создает компиляцию, как описано выше, затем dlopen
в результате общий объект). И мой manydl.c Пример демонстрирует, что вы можете dlopen
большое количество (разных) общих объектов в Linux (как правило, миллионы и, как минимум, сотни тысяч). На самом деле ограничением является адресное пространство.
Кстати, вы не должны dlopen
что-то имеющее main
функция, так как main
по определению определяется в основном вызове программы (возможно, косвенно) dlopen
,
Также порядок аргументов g++
имеет большое значение; Вы должны скомпилировать основную программу с
g++ -Wall -rdynamic say_hello.cpp -ldl -o say_hello
-rdynamic
Флаг необходим, чтобы позволить загруженный плагин (hello.so
) вызывать функции изнутри вашего say_hello
программа.
В целях отладки всегда проходить -Wall -g
в g++
выше.
Кстати, вы могли бы в принципе dlopen
общий объект, который не имеет PIC (т.е. не был скомпилирован с -fPIC
); но гораздо лучше dlopen
какой-то общий объект PIC.
Читайте также Библиотека программ HowTo и C ++ dlopen mini-howto (потому что название искажения).
файл helloshared.cc
(мой крошечный исходный код плагина в C ++)
#include <iostream>
#include <string.h>
using namespace std;
extern "C" void say_hello(const char* name) {
cout << __FILE__ << ":" << __LINE__ << " hello "<< name << "!" << endl;
}
и я собираю это с:
g++ -Wall -fPIC -g -shared helloshared.cc -o hello.so
Основная программа находится в файле mainhello.cc
:
#include <iostream>
#include <string>
#include <dlfcn.h>
#include <stdlib.h>
using namespace std;
int main() {
cout << __FILE__ << ":" << __LINE__ << " starting." << endl;
void* handle = dlopen("./hello.so", RTLD_LAZY);
if (!handle) {
cerr << "dlopen failed:" << dlerror() << endl;
exit(EXIT_FAILURE);
};
// signature of loaded function
typedef void hello_sig_t(const char*);
void* hello_ad = dlsym(handle,"say_hello");
if (!hello_ad) {
cerr << "dlsym failed:" << dlerror() << endl;
exit(EXIT_FAILURE);
}
hello_sig_t* fun = reinterpret_cast<hello_sig_t*>(hello_ad);
fun("from main");
fun = NULL; hello_ad = NULL;
dlclose(handle);
cout << __FILE__ << ":" << __LINE__ << " ended." << endl;
return 0;
}
с которым я собираю
g++ -Wall -rdynamic -g mainhello.cc -ldl -o mainhello
Тогда я бегу ./mainhello
с ожидаемым результатом:
mainhello.cc:7 starting.
helloshared.cc:5 hello from main!
mainhello.cc:24 ended.
Обратите внимание, что подпись hello_sig_t
в mainhello.cc
должно быть совместимо (гомоморфно, то есть так же, как) с функцией say_hello
из helloshared.cc
плагин, иначе это неопределенное поведение (и у вас, вероятно, будет SIGSEGV
авария).
Других решений пока нет …