Как передать аргументы методу, загруженному из статической библиотеки в CPP

Я пытаюсь написать программу для использования статической библиотеки кода 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 за терпение по поводу моей проблемы.

1

Решение

Функции никогда не имеют нулевых адресов, поэтому 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 авария).

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]