In NodeJS I'm building an interface to a shared object in C. I have the following code:
#include <node.h>
#include "libcustom_encryption.h"
namespace demo {
using v8::Exception;
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::String;
using v8::Value;
//
// This is the implementation of the "add" method
// Input arguments are passed using the
// const FunctionCallbackInfo<Value>& args struct
//
void DeviceGetVersion(const FunctionCallbackInfo<Value>& args)
{
char ver[10] = {0};
unsigned int ver_size = 0;
device_get_version(ver, ver_size);
Isolate* isolate = args.GetIsolate();
//
// 1. Save the value in to a isolate thing
//
Local<Value> str = String::NewFromUtf8(isolate, "Test");
//
// 2. Set the return value (using the passed in
// FunctionCallbackInfo<Value>&)
//
args.GetReturnValue().Set(str);
}
void Init(Local<Object> exports)
{
NODE_SET_METHOD(exports, "devicegetversion", DeviceGetVersion);
}
NODE_MODULE(addon, Init)
}
node-gyp configure: works
node-gyp build: works
LD_LIBRARY_PATH=libs/ node index.js: doesn't work
I get the following error:
node: symbol lookup error: /long_path/build/Release/app.node: undefined symbol: _Z18device_get_versionPcS_Phj
The when the function is called it gets prepended and appended with random characters. I'm assuming this is random data are some noise from memory. It seams as if the size brakes to call the function is bigger then it should.
I'm not that experienced with mixing C++ and C, I would love to get an explanation on what is happening hear.
Tech specs:
GCC Version: gcc version 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC)
NodeJS Version: v6.2.0
the function is called it gets prepended and appended with random characters
It is called
name mangling that happens in C++.
The actual error here is that compiled module can not link to function device_get_version().
Your possible actions:
add the implementation of device_get_version to your module
properly link this function
simply remove that line and the error will disappear
UPD.
device_get_version may actually be a C function which is treated as a C++ function (you can tell it by mangled name it has).
Make sure your function declared as
extern "C" {
void device_get_version(...);
}
Related
EDIT: Nearly got the answer, I just dont completely understand it, see last paragraph.
I try to build a shared lua library and use it within a larger project. When calling the script which loads the shared library from shell everything works. However, when I wrap the script within another shell, I get a runtime error when loading the library. Dependent on the script it is just any call to a lua function from c (i.e. lua_pushnumber). Here is a minimal example.
totestlib.cpp:
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
int init(lua_State *L) {
lua_toboolean(L, -1);
return 0;
}
static const struct luaL_Reg testlib[] = {
{"init", init},
{NULL, NULL}
};
extern "C"
int luaopen_libtotestlib(lua_State *L) {
luaL_newlib(L, testlib);
return 1;
}
Compiled with: g++ -shared -fPIC -I./lua-5.4.4/src -L./lua-5.4.4/src totestlib.cpp -o libtotestlib.so
testlib.lua (testing shared library):
testlib.lua
print("start")
testlib = require("libtotestlib")
print("done")
testlib.init(true)
print("called")
Calling the lua script using ./lua-5.4.4/src/lua testlib.lua works. Everything is printed. Wrapping script in the following c++ code does not work:
call_testlib.cpp
extern "C" {
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}
#include <unistd.h>
static lua_State *L;
int main(int argc, char *argv[]) {
L = luaL_newstate();
luaL_openlibs(L);
int tmp = luaL_loadfile(L, "testlib.lua");
if(tmp != 0) {
return 1;
}
tmp = lua_pcall(L, 0, 0, 0);
if(tmp != 0) {
printf("error pcall\n");
return 1;
}
}
Compiled with g++ call_testlib.cpp -o ./call_testlib -I./lua-5.4.4/src -L./lua-5.4.4/src -llua it prints "error pcall". If I print the error message on the lua stack, I get:
string error loading module 'libtotestlib' from file './libtotestlib.so':
./libtotestlib.so: undefined symbol: luaL_checkversion_
In this case the undefined symbol is luaL_checkversion_ (which I dont call myself), but with other scripts it is usually the first lua_... function that I call.
I have tried several things to fix this. For example, linking -llua when compiling the shared library, but this does not work (and should not be the problem as calling the script itself works). I also tried to load preload the library from c++ (as done in this question) instead of from lua, but I guess it does not really make a difference and I am getting the same error. I also uninstalled all lua versions from my path to make sure I always use the same version.
What is the difference between calling the script directly from shell and calling it inside a c function? Am I doing something wrong?
EDIT: Nearly got the answer. When using MYCFLAGS= -fPIC when compiling lua I can link lua to the shared library. At least this one works, but does not seem like a good solution to me and does not really answer my question: Why can lua itself (from shell) somehow add these symbols to the library while the wrapped c version can not? Additionally, my program has lua once linked in the shared library and once in the compiled C++ project (not optimal imo).
I'm trying to use a function written in C++ from lua. Given below is the cpp file:
extern "C"
{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
static int add_5(lua_State *L)
{
double d = lua_tonumber(L, 1); /* get argument */
d=d+5;
lua_pushnumber(L, d); /* push result */
return 1; /* number of results */
}
static const struct luaL_Reg mylib [] =
{
{"add_5", add_5},
{NULL, NULL} /* sentinel */
};
extern "C"
{
int luaopen_mylib (lua_State *L)
{
//luaL_newlib(L, mylib);
luaL_register(L, NULL, mylib);
return 1;
}
}
I compiled the above code by g++ using the following command:
g++ -shared -o mylib.so test.cpp -fPIC
I'm getting the following error on the lua interpreter:
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
> local temp = require "mylib"
attempt to index a string value
stack traceback:
[C]: ?
[C]: in function 'require'
stdin:1: in main chunk
[C]: ?
Please note that I can't upgrade the version of Lua due to some reasons.
The second argument to luaL_register is the library name. You can leave it as NULL, but if you do, luaL_register will try to insert the registered functions into the table it expects to find on the top of the stack (and in your code there's no table on top of the stack). For the general case of registering a library, it's easiest to pass your library name as the second parameter.
Note that LHF suggests not doing it that way, since it automatically puts the libary's table into the global table, whereas the user of the library might want to have it only as a local variable. The alternative is to create your own table with lua_newtable before calling luaL_register (with a null name).
I'm following the example here. My complete code is
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/IR/InstIterator.h"
using namespace llvm;
namespace {
struct Hello2 : public FunctionPass {
static char ID; // Pass identification, replacement for typeid
Hello2() : FunctionPass(ID) {}
bool runOnFunction(Function &F) override {
inst_iterator iter = inst_begin(F); // causes a segfault
return false;
}
};
}
char Hello2::ID = 0;
static RegisterPass<Hello2> Y("hello2", "Hello World Pass");
following the "HelloWorld" pass example explained here. When I followed the example exactly, it worked fine, but with the modified pass code above, I get the segfault when I run opt.
(I'm using the same "hello.c" file for input as in the "HelloWorld" pass example, compiling it with clang, running make and calling my library with opt just as in the example.)
What's causing my segfault, and is there any way to test for it/avoid it?
EDIT
I traced the segfault to line 61 of InstIterator.h: BI = BB->begin();. When my code reaches that point, BBs is non-NULL, but BB=BBs->begin() is NULL. Thus the dereference of BB results in a segfault. The question of why BB is NULL (and why the constructor doesn't check for that) remains.
This problem was resolved when my system did an automatic update and got a new version of several llvm-3.5 packages (today). Previously, opt --version returned
LLVM version 3.5
Optimized build.
Built Mar 23 2014 (21:41:30).
Default target: x86_64-pc-linux-gnu
Host CPU: corei7
now it returns
LLVM version 3.5.0
Optimized build.
Built Jan 27 2015 (00:14:48).
Default target: x86_64-pc-linux-gnu
Host CPU: corei7
That's all there is to my question, really, but I think it's an interesting thing to have answered.
With the addition of support for shared libraries in go, this is possible now.
calculator.go:
// package name: calculator
package main
import "C"
//export Sum
func Sum(x, y float64) float64 {
return x + y
}
func main() {
}
node-calculator.cc:
#include "calculator.h"
#include <node.h>
namespace calc {
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Value;
using v8::Number;
using v8::Exception;
void add(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
// Check the number of arguments passed.
if (args.Length() < 2) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "Wrong number of arguments")));
return;
}
// Check the argument types
if (!args[0]->IsNumber() || !args[1]->IsNumber()) {
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "Wrong arguments")));
return;
}
// Perform the operation
Local<Number> num = Number::New(isolate, Sum(args[0]->NumberValue(), args[1]->NumberValue()));
// Set the return value (using the passed in
// FunctionCallbackInfo<Value>&)
args.GetReturnValue().Set(num);
}
void init(Local<Object> exports) {
NODE_SET_METHOD(exports, "add", add);
}
NODE_MODULE(calculator, init)
}
binding.gyp:
{
"targets": [
{
"target_name": "node-calculator",
"sources": [
"node-calculator.cc"
],
"libraries": [
"../calculator.a"
],
},
],
}
test.js:
const calculator = require('./build/Release/node-calculator');
console.log('4+5=', calculator.add(4, 5));
Build:
go build -buildmode c-archive -o calculator.a calculator.go
node-gyp configure
node-gyp build
Output:
#> node test.js
4+5= 9
Native module for node.js must deeply interact with V8 process which contains a lot of v8 concepts such as gc, javascript context, ...
And I don't think V8 has exposed compatible and stable APIs for other language to interact with it. That is why node.js native addon should be built with C++ and always imports V8 C++ headers.
But you can use GO to write node.js native addons by wrapping GO code with C++:
file: module.go
package main
func Add(a, b int) int {
return a + b
}
file: module.c
#include <node.h>
#include <v8.h>
using namespace v8;
extern int go_add(int, int) __asm__ ("example.main.Add");
void init(Handle<Object> exports) {
// call go_add
}
NODE_MODULE(module, init)
More about "how to call GO functionn from C/C++":
Call Go functions from C
Edit:
Please see #jdi comments and the link: https://groups.google.com/forum/#!msg/golang-nuts/FzPbOwbTlPs/dAJVWQHx6m4J
Quote: It might be doable for simple things like add (that don't generate garbage or require the runtime), but it's not supported (yet) by either compiler as far as I know. Part of the work is done for linux (see golang.org/issue/256), but there are a number of open questions (what happens when you load two shared objects? etc)
Just to repost this as an answer instead of a comment...
I followed up with golang-nuts mailing list regarding the support for writing extensions in Go for other languages. The source of the response can be found here.
It might be doable for simple things like add (that don't generate
garbage or require the runtime), but it's not supported (yet) by
either compiler as far as I know. Part of the work is done for linux
(see golang.org/issue/256), but there are a number of open questions
(what happens when you load two shared objects? etc)
So really, there doesn't seem to be much point in writing an extension in Go, yet, as most of the language features would not be available, and you are already in C/C++ land anyways to add the wrapper for the entry point.
Background:
I've found myself with the unenviable task of porting a C++ GNU/Linux application over to Windows. One of the things this application does is search for shared libraries on specific paths and then loads classes out of them dynamically using the posix dlopen() and dlsym() calls. We have a very good reason for doing loading this way that I will not go into here.
The Problem:
To dynamically discover symbols generated by a C++ compiler with dlsym() or GetProcAddress() they must be unmangled by using an extern "C" linkage block. For example:
#include <list>
#include <string>
using std::list;
using std::string;
extern "C" {
list<string> get_list()
{
list<string> myList;
myList.push_back("list object");
return myList;
}
}
This code is perfectly valid C++ and compiles and runs on numerous compilers on both Linux and Windows. It, however, does not compile with MSVC because "the return type is not valid C". The workaround we've come up with is to change the function to return a pointer to the list instead of the list object:
#include <list>
#include <string>
using std::list;
using std::string;
extern "C" {
list<string>* get_list()
{
list<string>* myList = new list<string>();
myList->push_back("ptr to list");
return myList;
}
}
I've been trying to find an optimal solution for the GNU/Linux loader that will either work with both the new functions and the old legacy function prototype or at least detect when the deprecated function is encountered and issue a warning. It would be unseemly for our users if the code just segfaulted when they tried to use an old library. My original idea was to set a SIGSEGV signal handler during the call to get_list (I know this is icky - I'm open to better ideas). So just to confirm that loading an old library would segfault where I thought it would I ran a library using the old function prototype (returning a list object) through the new loading code (that expects a pointer to a list) and to my surprise it just worked. The question I have is why?
The below loading code works with both function prototypes listed above. I've confirmed that it works on Fedora 12, RedHat 5.5, and RedHawk 5.1 using gcc versions 4.1.2 and 4.4.4. Compile the libraries using g++ with -shared and -fPIC and the executable needs to be linked against dl (-ldl).
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <list>
#include <string>
using std::list;
using std::string;
int main(int argc, char **argv)
{
void *handle;
list<string>* (*getList)(void);
char *error;
handle = dlopen("library path", RTLD_LAZY);
if (!handle)
{
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
dlerror();
*(void **) (&getList) = dlsym(handle, "get_list");
if ((error = dlerror()) != NULL)
{
printf("%s\n", error);
exit(EXIT_FAILURE);
}
list<string>* libList = (*getList)();
for(list<string>::iterator iter = libList->begin();
iter != libList->end(); iter++)
{
printf("\t%s\n", iter->c_str());
}
dlclose(handle);
exit(EXIT_SUCCESS);
}
As aschepler says, its because you got lucky.
As it turns out, the ABI used for gcc (and most other compilers) for both x86 and x64 returns 'large' structs (too big to fit in a register) by passing an extra 'hidden' pointer arg to the function, which uses that pointer as space to store the return value, and then returns the pointer itself. So it turns out that a function of the form
struct foo func(...)
is roughly equivlant to
struct foo *func(..., struct foo *)
where the caller is expected to allocate space for a 'foo' (probably on the stack) and pass in a pointer to it.
So it just happens that if you have a function that is expecting to be called this way (expecting to return a struct) and instead call it via a function pointer that returns a pointer, it MAY appear to work -- if the garbage bits it gets for the extra arg (random register contents left there by the caller) happen to point to somewhere writable, the called function will happily write its return value there and then return that pointer, so the called code will get back something that looks a like a valid pointer to the struct it is expecting. So the code may superficially appear to work, but its actually probably clobbering a random bit of memory that may be important later.