Correct way of installing postgresql extension with custom library - c

TL;DR
One has to compile their custom library as shared library:
gcc -c -fPIC warp_client.c -o warp_client.o
gcc -shared warp_client.o libwarp-client.so
Include the shared library and additional dependencies of that shared library in the Postgresql Makefile with the flags SHLIB_LINK and PG_LDFLAGS(Here the bachelor_fdw.c is the extension to compile):
EXTENSION = bachelor_fdw
MODULE_big = bachelor_fdw
DATA = bachelor_fdw--0.1.sql
OBJS = bachelor_fdw.o
PG_LIBS = -lpq
SHLIB_LINK = -lwarp_client -lucp
PG_LDFLAGS += -L/usr/lib/warpdrive/ -L/usr/lib/ucx/
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
Include the directories of the shared libraries into the environment variable LD_LIBRARY_PATH of Postgresql. For that, one has to add a line to the file 'environment' in the main Postgresql directory and restart Postgresql. Here is mine:
$ cat /etc/postgresql/12/main/environment
# environment variables for postgres processes
# This file has the same syntax as postgresql.conf:
# VARIABLE = simple_value
# VARIABLE2 = 'any value!'
# I. e. you need to enclose any value which does not only consist of letters,
# numbers, and '-', '_', '.' in single quotes. Shell commands are not
# evaluated.
LD_LIBRARY_PATH='/usr/include/:/usr/include/ucx/:/usr/lib/:/usr/lib/ucx/'
I am trying to create a foreign data wrapper, which uses a custom library from me. The fdw compiles and installs fine, but when using it, symbols to my library are undefined. What is the proper way of using custom c code as library in a postgresql extension and what am i doing wrong? Here are the steps i took:
Compile my library (warp_client.c) with flag -fPIC into an object file.
gcc -c -fPIC warp_client.c -o static/warp_client.o
Create static library from the object file.
ar -rcs out/libwarp_client.a static/warp_client.o
Copy libwarp_client.a and warp_client.h into the postgresql extension project root.
Compile postgresql extension with the following makefile.
EXTENSION = bachelor_fdw
MODULE_big = bachelor_fdw
DATA = bachelor_fdw--0.1.sql libwarp_client.a
OBJS = bachelor_fdw.o
HEADERS = warp_client.h
ifdef DEBUG
$(info $(shell echo "debug ist an"))
endif
PG_LIBS = -lpq
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
make USE_PGXS=1 install
Try to create the extension. The extension makes a call to a library function in it's _PG_INI() function. Error comes up:
CREATE EXTENSION IF NOT EXISTS bachelor_fdw;
psql:only_create.sql:3: ERROR: could not load library "/usr/lib/postgresql/12/lib/bachelor_fdw.so": /usr/lib/postgresql/12/lib/bachelor_fdw.so: undefined symbol: warpclient_getData
The warp_client.h has the function headers and warp_client.c has the functions. warp_client.c includes "warp_client.h", bachelor_fdw.c (the extension) includes "warp_client.h".
warp_client.h:
#ifndef TEST_FIELD_UCP_WARP_CLIENT_H
#define TEST_FIELD_UCP_WARP_CLIENT_H
#include <ucp/api/ucp.h>
int warpclient_queryServer(char *server_addr_local, int port, int useINet6, char *query);
void *warpclient_getData();
int warpclient_cleanup();
#endif //TEST_FIELD_UCP_WARP_CLIENT_H
Any more desired info? I would be really glad for any help.
EDIT 1
I use the functions from warp_client.h inside of bachelor_fdw.c. Do i still need to export them? I thought only functions, which get called from the postgresql server needs to be exported.
Here is part of bachelor_fdw.c:
#include <warp_client.h>
#include "postgres.h"
#include "foreign/fdwapi.h"
#include "foreign/foreign.h"
#include "nodes/nodes.h"
#include "optimizer/pathnode.h"
#include "optimizer/planmain.h"
...
PG_MODULE_MAGIC;
/*
* SQL functions
*/
PG_FUNCTION_INFO_V1(bachelor_fdw_handler);
PG_FUNCTION_INFO_V1(bachelor_fdw_validator);
/*
* Extension initialization functions
*/
extern void _PG_init(void);
extern void _PG_fini(void);
/*
* FDW callback routines
*/
static void bachelorBeginForeignScan(ForeignScanState *node, int eflags);
static TupleTableSlot *bachelorIterateForeignScan(ForeignScanState *node);
static void bachelorReScanForeignScan(ForeignScanState *node);
static void bachelorEndForeignScan(ForeignScanState *node);
static void bachelorGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid);
static void bachelorGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid);
static ForeignScan* bachelorGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid, ForeignPath *best_path, List *tlist, List *scan_clauses, Plan *outer_plan);
void _PG_init(void){
int ret = 0;
void *data;
ret = warpclient_queryServer(NULL, -1, 0, "SELECT TEST FROM TEST;");
elog_debug("Testquery for server. Return code (%d)...\n", ret);
while(NULL != (data = warpclient_getData())){
elog_debug("Data received as fdw: %s\n", data);
}
elog_debug("Finished receiving data.\n");
/* Call cleanup */
ret = warpclient_cleanup();
elog_debug("Warpclient cleanup (%d)...\n", ret);
}
And here is part of warp_client.c:
#include "warp_client.h"
...
int warpclient_cleanup(){
int ret = 0;
//free buffers
free(recvbuffer->buffer);
free(recvbuffer);
/* Close the endpoint to the server */
debugmsg("Close endpoint.\n");
ep_close();
/* releasing UCX ressources */
ucp_worker_destroy(ucp_worker);
ucp_cleanup(ucp_context);
return ret;
}
int warpclient_queryServer(char *server_addr_local, int port, int useINet6, char *query){
/*
* Initialize important connection variables
*/
debugmsg("Initializing connection variables...\n");
if(NULL != server_addr_local) server_addr = server_addr_local;
if((port >= 0) && (port <= UINT16_MAX)) server_port = port;
if(useINet6) ai_family = AF_INET6;
int ret;
/* Initialize the UCX required objects worker and context*/
debugmsg("Initializing context and worker...\n");
ret = init_context_and_worker();
if (ret != 0) {
fprintf(stderr, "Initializing worker or context failed! Exiting..\n");
return -2;
}
/*
* UCP objects: client_ep as communication endpoint for the worker.
* status for function error code check.
*/
ucs_status_t status;
/* ep initialization and exchange with server over sockets */
debugmsg("Creating Client endpoint.\n");
status = create_client_endpoint();
if (status != UCS_OK) {
fprintf(stderr, "failed to start client (%s)\n", ucs_status_string(status));
return -1;
}
ret = send_query(query);
if(ret!=0){
debugmsg("Failed to connect to Server.\n");
}
return ret;
}
EDIT 2
I managed to get a good step forward thanks to Laurenz Albe. But i still have a problem with a shared library used in my shared library. Do I also need to link to shared libraries used in my own shared library, even though i linked that as i compiled my shared library before distribution?
what I did:
I added SHLIB_LINK = -lwarp_client to the Makefile and also needed the line PG_LDFLAGS += -L. for the linker to find libwarp_client.so.
I also managed to include the environment variable LD_LIBRARY_PATH for the postgres service, so that it can find my library in the standard places. And removed the library from the DATA flag in the Makefile.
New Makefile:
EXTENSION = bachelor_fdw
MODULE_big = bachelor_fdw
DATA = bachelor_fdw--0.1.sql
OBJS = bachelor_fdw.o
ifdef DEBUG
$(info $(shell echo "debug ist an"))
endif
PG_LIBS = -lpq
SHLIB_LINK = -lwarp_client
PG_LDFLAGS += -L.
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
Enrivonment variables:
/proc/1551/environ | xargs -0 -n 1 echo
LD_LIBRARY_PATH=/usr/include/:/usr/include/ucx/:/usr/lib/:/usr/lib/ucx/
...
When using CREATE on the extension, my library gets used but postgres complains about another shared library, which my library uses.
psql:only_create.sql:3: ERROR: could not load library "/usr/lib/postgresql/12/lib/bachelor_fdw.so": /usr/lib/warpdrive/libwarp_client.so: undefined symbol: ucp_ep_create
The error clearly says, it uses my shared library from a subdirectory "warpdrive" in the included standard directory. The shared library from UCP is also in that standard directory:
ls /usr/lib/ucx
cmake libjucx.so.0.0.0 libucp.a libucs.la libuct.so
jucx-1.12.1.jar libucm.a libucp.la libucs.so libuct.so.0
libjucx.a libucm.la libucp.so libucs.so.0 libuct.so.0.0.0
libjucx.la libucm.so libucp.so.0 libucs.so.0.0.0 pkgconfig
libjucx.so libucm.so.0 libucp.so.0.0.0 libuct.a ucx
libjucx.so.0 libucm.so.0.0.0 libucs.a libuct.la

That looks like warpclient_getData gets used in your code, but you didn't link your shared object with the library that provides the function. Add the library to the SHLIB_LINK variable:
SHLIB_LINK = -lwarp
(That example assumes a library called libwarp.so.)

Related

Is it possible to wrap a dynamic object in an ELF dynamically linked binary?

I am attempting to wrap a glibc symbol with my own definition of that symbol. For functions that glibc exposes this has so far been as easy as defining a __wrap_function_name in my source code and then adding Wl,--wrap=external_function_name to the linker flags of my build system's linking steps. In practice this then would look something like:
extern "C" void __wrap_external_function_name(void) {
my_internal function();
}
However I have recently attempted the same on a variable that glibc exposes, in this case __signgam. I again defined the linker flags for its wrapper, but I am not sure how and if it is even possible to define the wrapper for a variable. I attempted __wrap__signgam = signgam, but that had no effect. In fact the symbol table when exposed with objdump -T binary | grep signgam had the following content, showing that even though the wrap function is defined, the original symbol remains untouched:
0000000000000000 DO *UND* 0000000000000000 GLIBC_2.23 __signgam
0000000000000000 DO *UND* 0000000000000000 GLIBC_2.2.5 signgam
0000000001509d24 g DO .bss 0000000000000004 Base __wrap___signgam
Is there a canonical way to wrap these dynamic objects?
You can use the dynamic load library (e.g. -ldl) and use some of its functions, notably, dlsym.
There are three steps:
First create your source file.
Creat a shared library (.so file) from that source file
Invoke target program (set environment variable LD_PRELOAD to point to your .so file
Note that to intercept a given function, your function has to be defined with the same name.
You can have as many intercept functions as you want in your source file.
Here is some sample source code for intercepting (e.g.) read ...
// NOTE: need _GNU_SOURCE above for RTLD_NEXT in dlfcn.h
#define _GNU_SOURCE
#include <unistd.h>
#include <dlfcn.h>
#include <string.h>
static void
msg(const char *buf)
{
int len;
len = strlen(buf);
write(1,buf,len);
}
// read -- read a file
ssize_t
read(int unit,void *buf,size_t buflen)
{
static ssize_t (*proc)(int,void *,size_t) = NULL;
ssize_t rlen;
// get pointer to real function (only needs to be done once)
if (proc == NULL)
proc = dlsym(RTLD_NEXT,"read");
// do [whatever] stuff before real read ...
msg("PHONY: before\n");
// invoke the real function
rlen = proc(unit,buf,buflen);
// do [whatever] stuff after real read ...
msg("PHONY: after\n");
return rlen;
}
Here's a sample target program:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int
main(void)
{
int fd;
int rlen;
char buf[100];
fd = open("/etc/passwd",O_RDONLY);
rlen = read(fd,buf,sizeof(buf));
close(fd);
printf("main: read %d bytes\n",rlen);
return 0;
}
Now create a shared library (.so).
Note that below, there's only one source file. But, you can create the library from as many separate source files as you wish (e.g. you could put one intercept function per source file or put all in one source file, as you choose).
Here's a [crude] Makefile (that creates the shared library and the sample target program):
all: mylib.so target
read.o: read.c
cc -c -fPIC read.c
mylib.so: read.o
cc -shared -o mylib.so read.o -ldl
target: target.c
cc -o target target.c
test:
env LD_PRELOAD=./mylib.so ./target
clean:
rm -f mylib.so *.o target
Now, to invoke the target program (e.g.):
make test
Here's the generated output of the test:
env LD_PRELOAD=./mylib.so ./target
PHONY: before
PHONY: after
main: read 100 bytes

avoid LD_PRELOAD: Wrap library and provide functionality requested from libc

I have a shared library, say somelib.so, which uses ioctl from libc (according to objdump).
My goal is to write a new library that wraps around somelib.so and provides a custom ioctl. I want to avoid preloading a library to ensure that only the calls in somelib.so use the custom ioctl.
Here is my current snippet:
typedef int (*entryfunctionFromSomelib_t) (int par, int opt);
typedef int (*ioctl_t) (int fd, int request, void *data);
ioctl_t real_ioctl = NULL;
int ioctl(int fd, int request, void *data )
{
fprintf( stderr, "trying to wrap ioctl\n" );
void *handle = dlopen( "libc.so.6", RTLD_NOW );
if (!handle)
fprintf( stderr, "Error loading libc.so.6: %s\n", strerror(errno) );
real_ioctl = (ioctl_t) dlsym( handle, "ioctl" );
return real_ioctl( fd, request, data);
}
int entryfunctionFromSomelib( int par, int opt ) {
void *handle = dlopen( "/.../somelib.so", RTLD_NOW );
if (!handle)
fprintf( stderr, "Error loading somelib.so: %s\n", strerror(errno) );
real_entryfunctionFromSomelib = entryfunctionFromSomelib_t dlsym( handle, "entryfunctionFromSomelib" );
return real_entryfunctionFromSomelib( par, opt );
}
However, it does not in work in the sense that the calls to ioctl form somelib.so are not redirected to my custom ioctl implementation. How can I enforce that the wrapped somelib.so does so?
======================
Additional information added after #Nominal Animal post:
Here some information from mylib.so (somelib.so after edit) obtained via readelf -s | grep functionname:
246: 0000000000000000 121 FUNC GLOBAL DEFAULT UND dlsym#GLIBC_2.2.5 (11)
42427: 0000000000000000 121 FUNC GLOBAL DEFAULT UND dlsym##GLIBC_2.2.5
184: 0000000000000000 37 FUNC GLOBAL DEFAULT UND ioctl#GLIBC_2.2.5 (6)
42364: 0000000000000000 37 FUNC GLOBAL DEFAULT UND ioctl##GLIBC_2.2.5
After 'patching' mylib.so it also shows the new function as:
184: 0000000000000000 37 FUNC GLOBAL DEFAULT UND iqct1#GLIBC_2.2.5 (6)
I 'versioned' and exported the symbols from my wrap_mylib library for which readelf now shows:
25: 0000000000000d15 344 FUNC GLOBAL DEFAULT 12 iqct1#GLIBC_2.2.5
63: 0000000000000d15 344 FUNC GLOBAL DEFAULT 12 iqct1#GLIBC_2.2.5
However, when I try to dlopen wrap_mylib I get the following error:
symbol iqct1, version GLIBC_2.2.5 not defined in file libc.so.6 with link time reference
Is that maybe because mylib.so tries to dlsym iqct1 from libc.so.6 ?
If binutils' objcopy could modify dynamic symbols, and the mylib.so is an ELF dynamic library, we could use
mv mylib.so old.mylib.so
objcopy --redefine-sym ioctl=mylib_ioctl old.mylib.so mylib.so
to rename the symbol name in the library from ioctl to mylib_ioctl, so we could implement
int mylib_ioctl(int fd, int request, void *data);
in another library or object linked to the final binaries.
Unfortunately, this feature is not implemented (as of early 2017 at least).
We can solve this using an ugly hack, if the replacement symbol name is exactly the same length as the original name. The symbol name is a string (both preceded and followed by a nul byte) in the ELF file, so we can just replace it using e.g. GNU sed:
LANG=C LC_ALL=C sed -e 's|\x00ioctl\x00|\x00iqct1\x00|g' old.mylib.so > mylib.so
This replaces the name from ioctl() to iqct1(). It is obviously less than optimal, but it seems the simplest option here.
If you find you need to add version information to the iqct1() function you implement, with GCC you can simply add a line similar to
__asm__(".symver iqct1,iqct1#GLIBC_2.2.5");
where the version follows the # character.
Here is a practical example, showing how I tested this in practice.
First, let's create mylib.c, representing the sources for mylib.c (that the OP does not have -- otherwise just altering the sources and recompiling the library would solve the issue):
#include <unistd.h>
#include <errno.h>
int myfunc(const char *message)
{
int retval = 0;
if (message) {
const char *end = message;
int saved_errno;
ssize_t n;
while (*end)
end++;
saved_errno = errno;
while (message < end) {
n = write(STDERR_FILENO, message, (size_t)(end - message));
if (n > 0)
message += n;
else {
if (n == -1)
retval = errno;
else
retval = EIO;
break;
}
}
errno = saved_errno;
}
return retval;
}
The only function exported is myfunc(message), as declared in mylib.h:
#ifndef MYLIB_H
#define MYLIB_H
int myfunc(const char *message);
#endif /* MYLIB_H */
Let's compile the mylib.c into a dynamic shared library, mylib.so:
gcc -Wall -O2 -fPIC -shared mylib.c -Wl,-soname,libmylib.so -o mylib.so
Instead of write() from the C library (it's a POSIX function just like ioctl(), not a standard C one), we wish to use mywrt() of our own design in our own program. The above command saves the original library as mylib.so (while naming it internally as libmylib.so), so we can use
sed -e 's|\x00write\x00|\x00mywrt\x00|g' mylib.so > libmylib.so
to alter the symbol name, saving the modified library as libmylib.so.
Next, we need a test executable, that provides the ssize_t mywrt(int fd, const void *buf, size_t count); function (the prototype being the same as the write(2) function it replaces. test.c:
#include <stdlib.h>
#include <stdio.h>
#include "mylib.h"
ssize_t mywrt(int fd, const void *buffer, size_t bytes)
{
printf("write(%d, %p, %zu);\n", fd, buffer, bytes);
return bytes;
}
__asm__(".symver mywrt,mywrt#GLIBC_2.2.5");
int main(void)
{
myfunc("Hello, world!\n");
return EXIT_SUCCESS;
}
The .symver line specifies version GLIBC_2.2.5 for mywrt.
The version depends on the C library used. In this case, I ran objdump -T $(locate libc.so) 2>/dev/null | grep -e ' write$', which gave me
00000000000f66d0 w DF .text 000000000000005a GLIBC_2.2.5 write
the second to last field of which is the version needed.
Because the mywrt symbol needs to be exported for the dynamic library to use, I created test.syms:
{
mywrt;
};
To compile the test executable, I used
gcc -Wall -O2 test.c -Wl,-dynamic-list,test.syms -L. -lmylib -o test
Because libmylib.so is in the current working directory, we need to add current directory to the dynamic library search path:
export LD_LIBRARY_PATH=$PWD:$LD_LIBRARY_PATH
Then, we can run our test binary:
./test
It will output something like
write(2, 0xADDRESS, 14);
because that's what the mywrt() function does. If we want to check the unmodified output, we can run mv -f mylib.so libmylib.so and rerun ./test, which will then output just
Hello, world!
This shows that this approach, although depending on very crude binary modification of the shared library file (using sed -- but only because objcopy does not (yet) support --redefine-sym on dynamic symbols), should work just fine in practice.
This is also a perfect example of how open source is superior to proprietary libraries: the amount of effort already spent in trying to fix this minor issue is at least an order of magnitude higher than it would have been to rename the ioctl call in the library sources to e.g. mylib_ioctl(), and recompile it.
Interposing dlsym() (from <dlfcn.h>, as standardized in POSIX.1-2001) in the final binary seems necessary in OP's case.
Let's assume the original dynamic library is modified using
sed -e 's|\x00ioctl\x00|\x00iqct1\x00|g;
s|\x00dlsym\x00|\x00d15ym\x00|g;' mylib.so > libmylib.so
and we implement the two custom functions as something like
int iqct1(int fd, unsigned long request, void *data)
{
/* For OP to implement! */
}
__asm__(".symver iqct1,iqct1#GLIBC_2.2.5");
void *d15ym(void *handle, const char *symbol)
{
if (!strcmp(symbol, "ioctl"))
return iqct1;
else
if (!strcmp(symbol, "dlsym"))
return d15ym;
else
return dlsym(handle, symbol);
}
__asm__(".symver d15ym,d15ym#GLIBC_2.2.5");
Do check the versions correspond to the C library you use. The corresponding .syms file for the above would contain just
{
i1ct1;
d15ym;
};
otherwise the implementation should be as in the practical example shown earlier in this answer.
Because the actual prototype for ioctl() is int ioctl(int, unsigned long, ...);, there are no quarantees that this will work for all general uses of ioctl(). In Linux, the second parameter is of type unsigned long, and the third parameter is either a pointer or a long or unsigned long -- in all Linux architectures pointers and longs/unsigned longs have the same size --, so it should work, unless the driver implementing the ioctl() is also closed, in which case you are simply hosed, and limited to either hoping this works, or switching to other hardware with proper Linux support and open-source drivers.
The above special-cases both original symbols, and hard-wires them to the replaced functions. (I call these replaced instead of interposed symbols, because we really do replace the symbols the mylib.so calls with these ones, rather than interpose calls to ioctl() and dlsym().)
It is a rather brutish approach, but aside from using sed due to the lack of dynamic symbol redefinition support in objcopy, it is quite robust and clear as to what is done and what actually happens.

Running luajit object file from C

From the documentation: http://luajit.org/running.html
luajit -b test.lua test.obj # Generate object file
# Link test.obj with your application and load it with require("test")
But doesn't explain how to do these things. I guess they're assuming anyone using Lua is also a C programmer, not the case with me! Can I get some help? GCC as an example.
I would also like to do the same thing except from the C byte array header. I can't find documentation on this either.
luajit -bt h -n test test.lua test.h
This creates the header file but I don't know how to run it from C. Thanks.
main.lua
print("Hello from main.lua")
app.c
#include <stdio.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
int main(int argc, char **argv)
{
int status;
lua_State *L = luaL_newstate();
luaL_openlibs(L);
lua_getglobal(L, "require");
lua_pushliteral(L, "main");
status = lua_pcall(L, 1, 0, 0);
if (status) {
fprintf(stderr, "Error: %s\n", lua_tostring(L, -1));
return 1;
}
return 0;
}
Shell commands:
luajit -b main.lua main.o
gcc -O2 -Wall -Wl,-E -o app app.c main.o -Ixx -Lxx -lluajit-5.1 -lm -ldl
Replace -Ixx and -Lxx by the LuaJIT include and library directories. If you've installed it in /usr/local (the default), then most GCC installations will find it without these two options.
The first command compiles the Lua source code to bytecode and embeds it into the object file main.o.
The second command compiles and links the minimal C application code. Note that it links in the embedded bytecode, too. The -Wl,-E is mandatory (on Linux) to export all symbols from the executable.
Now move the original main.lua away (to ensure it's really running the embedded bytecode and not the Lua source code file) and then run your app:
mv main.lua main.lua.orig
./app
# Output: Hello from main.lua
The basic usage is as follows:
Generate the header file using luajit
#include that header in the source file(s) that's going to be referencing its symbols
Compile the source into a runnable executable or shared binary module for lua depending on your use-case.
Here's a minimal example to illustrate:
test.lua
return
{
fooprint = function (s) return print("from foo: "..s) end,
barprint = function (s) return print("from bar: "..s) end
}
test.h
// luajit -b test.lua test.h
#define luaJIT_BC_test_SIZE 155
static const char luaJIT_BC_test[] = {
27,76,74,1,2,44,0,1,4,0,2,0,5,52,1,0,0,37,2,1,0,16,3,0,0,36,2,3,2,64,1,2,0,15,
102,114,111,109,32,102,111,111,58,32,10,112,114,105,110,116,44,0,1,4,0,2,0,5,
52,1,0,0,37,2,1,0,16,3,0,0,36,2,3,2,64,1,2,0,15,102,114,111,109,32,98,97,114,
58,32,10,112,114,105,110,116,58,3,0,2,0,5,0,7,51,0,1,0,49,1,0,0,58,1,2,0,49,1,
3,0,58,1,4,0,48,0,0,128,72,0,2,0,13,98,97,114,112,114,105,110,116,0,13,102,
111,111,112,114,105,110,116,1,0,0,0,0
};
runtest.cpp
// g++ -Wall -pedantic -g runtest.cpp -o runtest.exe -llua51
#include <stdio.h>
#include <assert.h>
#include "lua.hpp"
#include "test.h"
static const char *runtest =
"test = require 'test'\n"
"test.fooprint('it works!')\n"
"test.barprint('it works!')\n";
int main()
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
lua_getglobal(L, "package");
lua_getfield(L, -1, "preload");
// package, preload, luaJIT_BC_test
bool err = luaL_loadbuffer(L, luaJIT_BC_test, luaJIT_BC_test_SIZE, NULL);
assert(!err);
// package.preload.test = luaJIT_BC_test
lua_setfield(L, -2, "test");
// check that 'test' lib is now available; run the embedded test script
lua_settop(L, 0);
err = luaL_dostring(L, runtest);
assert(!err);
lua_close(L);
}
This is pretty straight-forward. This example takes the byte-code and places it into the package.preload table for this program's lua environment. Other lua scripts can then use this by doing require 'test'. The embedded lua source in runtest does exactly this and outputs:
from foo: it works!
from bar: it works!

undefined reference to function declared in *.h file

I am a unskilled programmer and new to linux, I run into a problem when complining. I have two files 'ex_addinst.c' and 'lindo.h' in the same folder, I input command :
g++ -c ex_addinst.c
then, a object file ex_addinst.o is genetated with a warning:
ex_addinst.c: In function ‘int main()’:
ex_addinst.c:80: warning: deprecated conversion from string constant to ‘char*’
then I leak them with
g++ -Wall -o ex_addinst ex_addinst.o
and get the following info:
ex_addinst.o: In function `main':
ex_addinst.c:(.text+0x2b): undefined reference to `LSloadLicenseString'
ex_addinst.c:(.text+0x75): undefined reference to `LSgetVersionInfo'
ex_addinst.c:(.text+0xae): undefined reference to `LScreateEnv'
ex_addinst.c:(.text+0x10a): undefined reference to `LSgetErrorMessage'
...
...
ex_addinst.c:(.text+0x1163): undefined reference to `LSdeleteEnv'
collect2: ld returned 1 exit status
I guess that the header file 'lindo.h' is not complied into the .o file, but I have no idea what to do now. I have tried gcc, but get the same error. the version of my g++ and gcc is 4.4.5. I am using Ubuntu 10.10.
All the functions and structures have been declared in 'lindo.h'.
part of ex_addinst.c is as follows:
#include <stdio.h>
#include <stdlib.h>
/* LINDO API header file */
#include "lindo.h"
enter code here
int CALLTYPE LSwriteMPIFile(pLSmodel pModel,
char *pszFname);
/* Define a macro to declare variables for
error checking */
#define APIERRORSETUP \
int nErrorCode; \
char cErrorMessage[LS_MAX_ERROR_MESSAGE_LENGTH] \
/* Define a macro to do our error checking */
#define APIERRORCHECK \
if (nErrorCode) \
{ \
if ( pEnv) \
{ \
LSgetErrorMessage( pEnv, nErrorCode, \
cErrorMessage); \
printf("nErrorCode=%d: %s\n", nErrorCode, \
cErrorMessage); \
} else {\
printf( "Fatal Error\n"); \
} \
exit(1); \
} \
#define APIVERSION \
{\
char szVersion[255], szBuild[255];\
LSgetVersionInfo(szVersion,szBuild);\
printf("\nLINDO API Version %s built on %s\n",szVersion,szBuild);\
}\
/* main entry point */
int main()
{
APIERRORSETUP;
pLSenv pEnv;
pLSmodel pModel;
char MY_LICENSE_KEY[1024];
/*****************************************************************
* Step 1: Create a model in the environment.
*****************************************************************/
nErrorCode = LSloadLicenseString("home/li/work/tools/lindo/lindoapi/license/lndapi60.lic", MY_LICENSE_KEY);
if ( nErrorCode != LSERR_NO_ERROR)
{
printf( "Failed to load license key (error %d)\n",nErrorCode);
exit( 1);
}
......
......
......
APIERRORCHECK;
{
int nStatus;
double objval=0.0, primal[100];
/* Get the optimization result */
nErrorCode = LSgetInfo(pModel, LS_DINFO_GOP_OBJ, &objval);
APIERRORCHECK;
LSgetMIPPrimalSolution( pModel, primal) ;
APIERRORCHECK;
printf("\n\nObjective = %f \n",objval);
printf("x[0] = %f \n",primal[0]);
printf("x[1] = %f \n",primal[1]);
/* Get the linearity of the solved model */
nErrorCode = LSgetInfo (pModel, LS_IINFO_GOP_STATUS, &nStatus);
APIERRORCHECK;
/* Report the status of solution */
if (nStatus==LS_STATUS_OPTIMAL || nStatus==LS_STATUS_BASIC_OPTIMAL)
printf("\nSolution Status: Globally Optimal\n");
else if (nStatus==LS_STATUS_LOCAL_OPTIMAL)
printf("\nSolution Status: Locally Optimal\n\n");
else if (nStatus==LS_STATUS_INFEASIBLE)
printf("\nSolution Status: Infeasible\n\n");
}
/* >>> Step 7 <<< Delete the LINDO environment */
LSdeleteEnv(&pEnv);
/* Wait until user presses the Enter key */
printf("Press <Enter> ...");
getchar();
}
part of 'lindo.h' is:
/*********************************************************************
* Structure Creation and Deletion Routines (4) *
*********************************************************************/
pLSenv CALLTYPE LScreateEnv(int *pnErrorcode,
char *pszPassword);
pLSmodel CALLTYPE LScreateModel(pLSenv pEnv,
int *pnErrorcode);
int CALLTYPE LSdeleteEnv(pLSenv *pEnv);
int CALLTYPE LSdeleteModel(pLSmodel *pModel);
int CALLTYPE LSloadLicenseString(char *pszFname, char *pachLicense);
void CALLTYPE LSgetVersionInfo(char *pachVernum, char *pachBuildDate);
Thank you!
Thank you guys answering my problem. As you suggested, I need to link the library when complining. I have gotten the executable file with:
gcc -o ex_addinst ./ex_addinst.o -L/home/li/work/tools/lindo/lindoapi/bin/linux64 -m64 -llindo64 -lmosek64 -lconsub3 -lc -ldl -lm -lguide -lpthread -lsvml -limf -lirc
but there comes another problem when run the executable file ex_addinst: after run:
./ex_addinst
there comes:
./ex_addinst: error while loading shared libraries: liblindo64.so.6.0: cannot open shared object file: No such file or directory
The tricky thing is, liblindo64.so.6.0 is in the lib folder which contains:
libconsub3.so libirc.so liblindojni.so libmosek64.so.5.0 lindo.par
libguide.so liblindo64.so liblindojni.so.6.0.3 libsvml.so placeholder
libimf.so liblindo64.so.6.0 libmosek64.so lindoapivars.sh runlindo
I have created symbolic links between liblindo64.so.6.0 and liblindo64.so with
ln -sf liblindo64.so.6.0 liblindo64.so
but it doesn't help.
Can anyone tell me what is wrong here?
(I am not sure I should put this question in a new post, but I think currently it is better to follow the old one)
Ok, lindo.h contains the prototypes for those functions, but where are the functions actually defined? If they're in another C file you need to compile that one too, and link both the object files together.
If the functions are part of another static library, you need to tell the linker to link that library along with your object file.
If they're defined with a shared library, you can probably get g++ to still link to it at compile time, and take care of the library loading etc. Otherwise you'll need to load the library at runtime and reference the functions from the library. This Wikipedia article on dynamic loading of shared libraries contains some example code.
Try
g++ -Wall -o ex_addinst ex_addinst.c
instead of
g++ -Wall -o ex_addinst ex_addinst.o
You want to compile the .c file, not the .o file.
You need to tell gcc to link with the library or object file(s) that contain the LS... functions you're using. The header file tells the compiler how to call them, but the linker needs to know where to get the compiled code from.
undefined reference to ... is not a declaration problem. The compiler fails because it can't find symbols (objects) which are related to those declared functions.
In your case, you use the Limbo API, and include the header file, but you don't tell the compiler to link with the library : that's why it doesn't find symbols.
EDIT : I had forgotten the part when you say you're new to Linux. To link with the library, you need to use the -L/-l options of g++. man g++ is always a good read, and the Limbo's documentation should be, too.

Compiling a C program

i am trying to compile this code, but if i do using:
gcc prt.c portaudio.h -o prt
but i get this error:
main.c:47: undefined reference to `Pa_OpenDefaultStream'
main.c:62: undefined reference to `Pa_StartStream'
main.c:65: undefined reference to `Pa_Sleep'
main.c:66: undefined reference to `Pa_StopStream'
main.c:69: undefined reference to `Pa_CloseStream'
main.c:72: undefined reference to `Pa_Terminate'
main.c:78: undefined reference to `Pa_Terminate'
i don't know why, then i though it might be beacuse i don't have a rule (make file)
so i made one:
main: main.o
gcc main.o -o main
main.o: main.c portaudio.h
gcc -c main.c
but when i try to run it through cygwin: using "Make"
i get this message:
"make: *** No targets specified and no makefile found. Stop.
I don't understand the problem, please help me is something wrong with my makefile or is there something else wrong.
also this is the code:
main.c
#include <stdio.h>
#include "portaudio.h"
#define SAMPLE_RATE (44100)
typedef struct
{
float left_phase;
float right_phase;
}
paTestData;
static int patestCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
/* Cast data passed through stream to our structure. */
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned int i;
(void) inputBuffer; /* Prevent unused variable warning. */
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = data->left_phase; /* left */
*out++ = data->right_phase; /* right */
/* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */
data->left_phase += 0.01f;
/* When signal reaches top, drop back down. */
if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f;
/* higher pitch so we can distinguish left and right. */
data->right_phase += 0.03f;
if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f;
}
return 0;
}
static paTestData data;
int main (void) {
PaStream *stream;
PaError err;
err = Pa_OpenDefaultStream( &stream,
0, /* no input channels */
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
SAMPLE_RATE,
256, /* frames per buffer, i.e. the number
of sample frames that PortAudio will
request from the callback. Many apps
may want to use
paFramesPerBufferUnspecified, which
tells PortAudio to pick the best,
possibly changing, buffer size.*/
patestCallback, /* this is your callback function */
&data ); /*This is a pointer that will be passed to
your callback*/
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
Pa_Sleep(9*1000);
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
err = Pa_Terminate( );
if( err != paNoError ) goto error;
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
return err;
}
and the header file portaudio.h: Portaudio.h
if you want cleaner view of main.c: main.c
I am not so sure why these messages/errors/warning are coming, please help.
also this is my folder view:
You seem to be using functions from a library for the 'Port Audio' facility, but your link line does not tell the C compiler how to find that library - so the functions show up as undefined references.
Your link line should look something like:
gcc -o main main.o -lpa
That should be macroized, but the gist is correct.
This assumes the library is in 'libpa.a' or thereabouts. If it is in 'libportaudio.so', then use -lportaudio instead of -lpa.
Using macros in the makefile:
PROGRAM = main
SOURCE = main.c
OBJECT = $(SOURCE:.c=.o)
LIBDIR = /cygdrive/c/installdir/portaudio/lib
LIBRARY = $(LIBDIR)/portaudio_x86.lib
$(PROGRAM): $(OBJECT)
$(CC) $(CFLAGS) -o $# $(OBJECT) $(LDFLAGS) $(LIBRARY)
main.o: main.c portaudio.h
You should not need an explicit compilation command for main.o; make should be able to deduce that from its internal rules. Note that the character before $(CC) must be a TAB and not spaces.
The make command only looks for a file called makefile or Makefile, to use make with a differently named makefile, you need to do make -f otherfile target.
Rename your file Make file to Makefile to have make look at its contents. Also, verify that you use one tab character (no spaces) in all of the commands under a target. You might have done that, but your cut-and-paste of the contents in this posting doesn't let us know if that is really how it is.
It would also appear that you need the PortAudio library to link to or those functions will not be defined. That is, unless they're defined in the header (I haven't used that library before...)
Did portaudio come with a .lib or anything? The header file only contains the name of the functions, not the definitions. You'll need to link against the library to get the functionality for all of those functions
Your initial problem ("Undefined reference to...") is a message from the linker saying it cannot find a definition of the functions mentioned. This means you need to add a linker argument saying that you want to add the library providing these functions (lib portaudio?) to your program. GCC's command line parameter to do so is "-l"
It seems like you need to include the library (with a -l[library name] option.
A search of portaudio compile commands shows libportaudio.a included in the gcc options.
You are probably not linking to those libraries libs (.so or .a) look at the documentation and see what libs you need to link your program with.
The other thing is that when you run "make -f Makefile" you need to have a tab in your makefile before the "gcc..." lines i.e. the command lines.
I also need to add -lm, otherwise it throws an error.
So gcc -o test test.c -lm -lportaudio

Resources