最近项目c应用程序嵌入lua脚本,在lua中用到了第三方库luasocket,程序启动报错:
Undefined symbol "lua_insert"
问题困扰我2天直到找到一片文章
链接lua的正确方式
这几天,我们遇到一个问题:在xy2.5引擎里面,使用posix库或者socket库时,主
程序会crash。,改变代码的顺序或加入 print代码,主程序又可能不会再crash了。检
查产生的core文件,发现内存已经被破坏掉了,无法从中得知调用堆栈的信息。经过后
来用多种手段检查(在另一篇文章中介绍),终于发现问题所在:在垃圾回收过程中,
free了dummynode_,而dummynode_实际上是一个静态变量,不是通过malloc分配的。
进一步分析,才发现是由于链接参数不正确导致的。
正确的编译和链接方式是:
1. lua解释器
按官方的做法,lua解释器被编译成静态链接库。其Makefile也未写明编译为动态链接
库的方式。因此,假定大家都用静态链接的方式使用lua。
2. 第三方库
如posix和socket,以socket库为例,连接生成socket.so时的指令为:
ld -o socket.so $(SOCKET_OBJS)
注意:这里并没有写成
ld -o socket.so $(SOCKET_OBJS) -llua
即没有把lua解释器也链接进来,如果加了-llua,就会在socket.so中,有一份自己的
dummynode_。
3. 应用程序
应用程序内嵌lua解释器,又会在lua脚本中使用第三方库,所以,首先要-llua,将lua
链接进应用程序,同时又要用-Wl,-E, 将lua中的符号导出,让第三方库能访问这些符
号。
/usr/local/bin/g++ -g -O0 -Wl,-E -o run -L/usr/local/lib
-I/usr/local/include -include
stdlib.h mytest.cpp -llua
-lexecinfo
可能的错误做法:
在编译链接应用程序时,未指明-Wl,-E
/usr/local/bin/g++ -g -O0 -o run -L/usr/local/lib
-I/usr/local/include -include stdlib.h mytest.cpp -llua -lexecinfo
这样会导致在使用socket.so时提示:
Undefined symbol "lua_insert"
于是,就在编译socket.so时,加上-llua。
这样做以后,没有编译链接错误,程序也能够跑起来。但问题被隐藏起来了。
我们来看看创建对象和释放对象时的代码,以发现问题:
a) 创建对象时
lua会给对象的node属性设置初始值:
t->node = cast(Node*, dummynode)
b) 释放对象时
会判断是否应该释放节点:
if (t->node != &dummynode_)
lua_free(t->node)
按照上面错误的链接方式,socket.so中有一份dummynode_,在应用程序中也有一份
dummynode_。设置
t->node时,可能使用的是应用程序内的dummynode_的地址;而释放
阶段比较地址时,使用的是socket.so中的 dummynode_的地址,这样,就调用了free释
放了不是malloc分配出来的地址空间。 于是内存空间被破坏,程序crash。
--
邹光先
-Wl,-E和-llua这两个编译选项以后不能忘记了