Как вы связываете внешние общие библиотеки с собственным расширением?

Я пишу собственное расширение pty и хочу связать libutil, чтобы я мог использовать forkpty и openpty из <pty.h>,

Я использую две команды, взятые из официального руководства:

g++ -fPIC -lutil -I/home/crunchex/work/dart-sdk -c pty.cc -o pty.o
gcc -shared -Wl,-soname,libpty.so -o libpty.so pty.o

и я получаю следующую ошибку:

/home/crunchex/work/dart-sdk/bin/dart: symbol lookup error: /home/crunchex/work/pty/bin/packages/pty/libpty.so: undefined symbol: forkpty

Это может быть больше вопросом g++/gcc, но, насколько я могу судить, я делаю эту часть правильно, добавив -lutil и включив <pty.h>, libutil.so установлен в моей системе Ubuntu 14.04, поэтому я вполне уверен, что он там есть.

Вот мое тестовое расширение:

#include <string.h>
#include <pty.h>

#include "include/dart_api.h"

Dart_NativeFunction ResolveName(Dart_Handle name,
                                int argc,
                                bool* auto_setup_scope);

DART_EXPORT Dart_Handle pty_Init(Dart_Handle parent_library) {
  if (Dart_IsError(parent_library)) {
    return parent_library;
  }

  Dart_Handle result_code =
      Dart_SetNativeResolver(parent_library, ResolveName, NULL);
  if (Dart_IsError(result_code)) {
    return result_code;
  }

  return Dart_Null();
}

Dart_Handle HandleError(Dart_Handle handle) {
  if (Dart_IsError(handle)) {
    Dart_PropagateError(handle);
  }
  return handle;
}

//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//
//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//
void PtyFork(Dart_NativeArguments args) {
  Dart_EnterScope();

  struct winsize winp;
  winp.ws_col = 80;
  winp.ws_row = 24;
  winp.ws_xpixel = 0;
  winp.ws_ypixel = 0;
  int master = -1;
  char name[40];
  pid_t pid = forkpty(&master, name, NULL, &winp);

  Dart_ExitScope();
}
//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//
//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\//

struct FunctionLookup {
  const char* name;
  Dart_NativeFunction function;
};

FunctionLookup function_list[] = {
  {"PtyFork", PtyFork},
  {NULL, NULL}};

Dart_NativeFunction ResolveName(Dart_Handle name, int argc, bool* auto_setup_scope) {
  if (!Dart_IsString(name)) return NULL;
  Dart_NativeFunction result = NULL;
  Dart_EnterScope();
  const char* cname;
  HandleError(Dart_StringToCString(name, &cname));

  for (int i=0; function_list[i].name != NULL; ++i) {
    if (strcmp(function_list[i].name, cname) == 0) {
      result = function_list[i].function;
      break;
    }
  }

  Dart_ExitScope();
  return result;
}

2 ответа

Решение

Скопировано из https://code.google.com/p/dart/issues/detail?id=22257

Проблема в том, что libutil, часть libc6, должен быть связан с вашей собственной общей библиотекой расширения в командной строке ссылки, а не в командной строке компиляции.

Во-первых, -lutil Спецификация библиотеки должна идти в строке компоновки, а не в строке компиляции: gcc -shared -Wl,-soname,libpty.so -o libpty.so pty.o -lutil

Это ставит зависимость от общей библиотеки libutil.so в вашу общую библиотеку, и когда она загружается с помощью dlload, зависимости также загружаются и связываются.

Это не удается, если -lutil опция ставится после pty.o по вашей команде
строка, поскольку связанные библиотеки должны быть расположены в обратном порядке зависимости в командной строке компоновщика.

После этого выходные данные objdump на libpty.so включают в себя:

   objdump -x libpty.so 
Dynamic Section: 
   NEEDED               libutil.so.1 
   NEEDED               libc.so.6 
   SONAME               libpty.so 
   INIT                 0x00000000000009c0 
   FINI                 0x0000000000000db4 
   INIT_ARRAY           0x0000000000201dd0 
.... 
Version References: 
   required from libutil.so.1: 
     0x09691a75 0x00 04 GLIBC_2.2.5 
   required from libc.so.6: 
     0x09691a75 0x00 03 GLIBC_2.2.5 
     0x0d696914 0x00 02 GLIBC_2.4 
.... 
0000000000000000  w      *UND*        0000000000000000               
_ITM_registerTMCloneTable 
0000000000000000       F *UND*        0000000000000000               
forkpty@@GLIBC_2.2.5 
0000000000000000  w    F *UND*        0000000000000000               
__cxa_finalize@@GLIBC_2.2.5 
00000000000009c0 g     F .init        0000000000000000              _init 

и запуск тестовой программы main.dart больше не дает сбоя.

Если вы не хотите связывать разделяемую библиотеку с вашей библиотекой, вам нужна статическая библиотека, но с этим много проблем - это не невозможно, но гораздо сложнее. Тогда проблема в том, что в вашей системе может быть только libutil.so, а не libutil.aпоэтому ваша общая библиотека должна будет загрузить libutil, когда она будет загружена.

dlopen Функция, используемая Dart для загрузки вашей общей библиотеки, должна
рекурсивно загружать другие разделяемые библиотеки, от которых это зависит, но это может работать, а может и не работать. Когда я собираю с -lutil на этапе компоновки разделяемые библиотеки, показанные ldd libpty.so, представляют собой просто libc.so.6, а некоторые стандартные компоновщики - ld-linux-.. и linux-vdso. Так что я не вижу там libutil.

Чтобы связать нужные функции статически в общую библиотеку, вы
понадобится что-то вроде

gcc -shared -Wl,-whole-archive /usr/lib/x86_64-linux-gnu/libutil.a   
-Wl,-no-whole-archive -Wl,-soname,libpty.so -o libpty.so pty.o 

Но так как libutil.a в дистрибутив не скомпилирован -wPIC, он не может быть связан с общей библиотекой:

/usr/bin/ld: /usr/lib/x86_64-linux-gnu/libutil.a(login.o): relocation   
R_X86_64_32 against `.rodata.str1.1' can not be used when making a shared object; recompile with -fPIC 
/usr/lib/x86_64-linux-gnu/libutil.a(login.o): error adding symbols: Bad   
value 

Я думаю, что лучше всего сделать зависимость общей библиотеки от
libutil.so Работа.

Если об этом сообщается:

/home/crunchex/work/pty/bin/packages/pty/libpty.so: undefined symbol: forkpty

тогда инструмент определенно нуждался в том, чтобы ваша libpty или предоставила этот символ, или запросила связь с другой библиотекой, которая его предоставляет. Итак, это:

gcc -shared -Wl,-soname,libpty.so -o libpty.so pty.o

определенно неполный.

Обычно, когда подобные инструменты позволяют вам создать свою собственную библиотеку, которая может потребовать вызова некоторых функций, которые предоставляет "пользователь вашей библиотеки", тогда, по крайней мере, она должна быть связана с библиотекой "заглушки". Обычно предоставляется библиотека с именем наподобие libXXXXstubVV.a (VV может быть номером версии или чем-то вроде).

Может случиться так, что это как-то связано с проблемой, возникающей в статических библиотеках. Для статических библиотек это не может быть решено иначе, чем в правильном порядке. Для динамических библиотек это обычно решается путем предоставления запроса на связывание с некоторой зависимой библиотекой, которая, как ожидается, предоставит недостающие символы - и эта библиотека-заглушка должна это сделать.

Другие вопросы по тегам