I'm writing a pty native extension and want to link libutil so that I may use forkpty and openpty from <pty.h>
.
I'm using the two commands taken from the official guide:
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
and I'm getting the following error:
/home/crunchex/work/dart-sdk/bin/dart: symbol lookup error: /home/crunchex/work/pty/bin/packages/pty/libpty.so: undefined symbol: forkpty
This may be more of a g++/gcc question, but as far as I can tell I'm doing that part right by adding -lutil and including <pty.h>
. libutil.so is installed on my Ubuntu 14.04 system, so I'm fairly certain it's there.
Here's my test extension:
#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;
}
Copied from https://code.google.com/p/dart/issues/detail?id=22257#c4
The problem is that libutil
, part of libc6
, needs to be linked into your native extension shared library on the link command line, not the compile command line.
First, the -lutil
library specification should go on the linking line, rather than the compiling line:
gcc -shared -Wl,-soname,libpty.so -o libpty.so pty.o -lutil
This puts a dependency on the shared library libutil.so
into your shared library, and when it is loaded by dlload, the dependencies are also loaded and linked.
This fails unless the -lutil
option is put after pty.o
on your command
line, since linked libraries must be put in reverse dependency order on the linker command line.
After doing this, the output of objdump on libpty.so includes:
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
and running the test program main.dart no longer fails.
If you don't want to link a shared library into your library, then you need a static library, but there are many problems with this - it is not impossible, but much harder.
Then, the problem is that you may only have libutil.so on your system, not libutil.a
, so your shared library will need to load libutil when it is loaded.
The dlopen
function used by Dart to load your shared library should
recursively load other shared libraries it depends on, but this may or may not be working. When I compile with -lutil
in the link step, the shared libraries shown by ldd libpty.so are just libc.so.6, and some standard linker ones ld-linux-.. and linux-vdso. So I don't see libutil there.
To link the functions you need statically into your shared library, you
would need something like
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
But since the libutil.a
in the distribution is not compiled with -wPIC
, it can't be linked into a shared library:
/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
I think the best bet is to make the shared library dependency upon
libutil.so
work.