minigui:静态编译连接mgncs库时遇到的xml2的问题

最近做一个基于minigui/mgncs的项目,在开发阶段因为是在ubuntu下基于minigui的模拟器开发,所以编译时都是标准的动态库连接。没啥问题,很顺序。
现在项目功能开发告一段落,要向嵌入式平台移植了,就要把编译改为全静态连接(--static)。问题就来了。

编译正常,连接时报了如下一大堆错误:

/usr/lib/x86_64-linux-gnu/libxml2.a(nanohttp.o): In function `xmlNanoHTTPConnectHost':
(.text+0x924): warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
/usr/lib/x86_64-linux-gnu/libxml2.a(nanohttp.o): In function `xmlNanoHTTPConnectHost':
(.text+0x9f4): warning: Using 'gethostbyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
/usr/lib/x86_64-linux-gnu/libxml2.a(xpath.o): In function `xmlXPathCastNumberToString':
(.text+0xa3d7): undefined reference to `log10'
/usr/lib/x86_64-linux-gnu/libxml2.a(xmlschemastypes.o): In function `xmlSchemaGetCanonValue':
(.text+0xb84e): undefined reference to `trunc'
/usr/lib/x86_64-linux-gnu/libxml2.a(xmlschemastypes.o): In function `xmlSchemaGetCanonValue':
(.text+0xb89a): undefined reference to `trunc'
/usr/lib/x86_64-linux-gnu/libxml2.a(xzlib.o): In function `xz_head':
(.text+0x69e): undefined reference to `lzma_auto_decoder'
/usr/lib/x86_64-linux-gnu/libxml2.a(xzlib.o): In function `xz_head':
(.text+0x76e): undefined reference to `lzma_properties_decode'
/usr/lib/x86_64-linux-gnu/libxml2.a(xzlib.o): In function `xz_decomp':
(.text+0x11a0): undefined reference to `lzma_code'
/usr/lib/x86_64-linux-gnu/libxml2.a(xzlib.o): In function `__libxml2_xzclose':
(.text+0x1f95): undefined reference to `lzma_end'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `openIcuConverter':
(.text+0x104b): undefined reference to `ucnv_open_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `openIcuConverter':
(.text+0x1077): undefined reference to `UCNV_FROM_U_CALLBACK_STOP_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `openIcuConverter':
(.text+0x107c): undefined reference to `ucnv_setFromUCallBack_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `openIcuConverter':
(.text+0x109b): undefined reference to `ucnv_open_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `openIcuConverter':
(.text+0x10b7): undefined reference to `ucnv_close_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `openIcuConverter':
(.text+0x10eb): undefined reference to `UCNV_TO_U_CALLBACK_STOP_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `openIcuConverter':
(.text+0x10f0): undefined reference to `ucnv_setToUCallBack_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `closeIcuConverter':
(.text+0x111d): undefined reference to `ucnv_close_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `closeIcuConverter':
(.text+0x1126): undefined reference to `ucnv_close_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `xmlFindCharEncodingHandler':
(.text+0x2793): undefined reference to `ucnv_close_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `xmlFindCharEncodingHandler':
(.text+0x279d): undefined reference to `ucnv_close_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `xmlFindCharEncodingHandler':
(.text+0x27b3): undefined reference to `ucnv_close_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o):(.text+0x27bc): more undefined references to `ucnv_close_55' follow
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `xmlCharEncFirstLineInt':
(.text+0x2f6e): undefined reference to `ucnv_convertEx_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `xmlCharEncFirstLine':
(.text+0x3270): undefined reference to `ucnv_convertEx_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `xmlCharEncFirstLineInput':
(.text+0x365a): undefined reference to `ucnv_convertEx_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `xmlCharEncInput':
(.text+0x3a47): undefined reference to `ucnv_convertEx_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `xmlCharEncInFunc':
(.text+0x3de7): undefined reference to `ucnv_convertEx_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o):(.text+0x4197): more undefined references to `ucnv_convertEx_55' follow
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `xmlCharEncCloseFunc':
(.text+0x4bb0): undefined reference to `ucnv_close_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `xmlCharEncCloseFunc':
(.text+0x4bba): undefined reference to `ucnv_close_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `xmlCharEncCloseFunc':
(.text+0x4bdd): undefined reference to `ucnv_close_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `xmlCharEncCloseFunc':
(.text+0x4be7): undefined reference to `ucnv_close_55'
/usr/lib/x86_64-linux-gnu/libxml2.a(encoding.o): In function `xmlByteConsumed':
(.text+0x4f2d): undefined reference to `ucnv_convertEx_55'
collect2: error: ld returned 1 exit status

可以发现,问题都来自于mgncs依赖的xml2这个库。分析问题原因花了好长时间,找到原因倒是很简单:

xml2这个库其实还依赖其他的库

用ldd命令查看libxml2.so的依赖库:

$ ldd /usr/lib/x86_64-linux-gnu/libxml2.so
	linux-vdso.so.1 =>  (0x00007ffd7fbaa000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007ffb86d88000)
	libicuuc.so.55 => /usr/lib/x86_64-linux-gnu/libicuuc.so.55 (0x00007ffb869f4000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007ffb867da000)
	liblzma.so.5 => /lib/x86_64-linux-gnu/liblzma.so.5 (0x00007ffb865b8000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007ffb862af000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffb85ee5000)
	/lib64/ld-linux-x86-64.so.2 (0x00007ffb87347000)
	libicudata.so.55 => /usr/lib/x86_64-linux-gnu/libicudata.so.55 (0x00007ffb8442e000)
	libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007ffb840ac000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007ffb83e96000)

所以虽然连接动态库时很简单,只需要加上-lxml2就可以了,但在静态连接时,就要把xml2所依赖的所有库都要加上,用pkg-config命令就可以查看xml2静态连接和动态连接所需要的参数,如下

# 动态库连接只需要-lxml2
$ pkg-config --libs libxml-2.0
-lxml2
# 静态库连接则需要一堆的库
$ pkg-config --static --libs libxml-2.0
-lxml2 -licui18n -licuuc -licudata -lz -llzma -lm

于是我按照上面pkg-config命令的输出加上静态连接相应的参数,再次编译,这下总该没事了吧?
然而又是一堆连接错误,如下

/usr/lib/x86_64-linux-gnu/libxml2.a(threads.o): In function `xmlRMutexLock':
(.text+0x1ff): undefined reference to `pthread_cond_wait'
/usr/lib/x86_64-linux-gnu/libxml2.a(threads.o): In function `xmlRMutexUnlock':
(.text+0x285): undefined reference to `pthread_cond_signal'
/usr/lib/x86_64-linux-gnu/libxml2.a(threads.o): In function `xmlIsMainThread':
(.text+0x4a8): undefined reference to `pthread_cond_wait'
/usr/lib/x86_64-linux-gnu/libxml2.a(threads.o): In function `xmlIsMainThread':
(.text+0x4d1): undefined reference to `pthread_cond_signal'
/usr/lib/x86_64-linux-gnu/libxml2.a(threads.o): In function `xmlInitThreads':
(.text+0x5e0): undefined reference to `pthread_cond_wait'
/usr/lib/x86_64-linux-gnu/libxml2.a(threads.o): In function `xmlInitThreads':
(.text+0x609): undefined reference to `pthread_cond_signal'
/usr/lib/x86_64-linux-gnu/libicuuc.a(putil.ao): In function `uprv_dl_open_55':
(.text+0x18c2): undefined reference to `dlopen'
/usr/lib/x86_64-linux-gnu/libicuuc.a(putil.ao): In function `uprv_dlsym_func_55':
(.text+0x190d): undefined reference to `dlsym'
/usr/lib/x86_64-linux-gnu/libicuuc.a(putil.ao): In function `uprv_dl_close_55':
(.text+0x18f1): undefined reference to `dlclose'
/usr/lib/x86_64-linux-gnu/libicuuc.a(umutex.ao): In function `icu_55::umtx_initImplPreInit(icu_55::UInitOnce&)':
(.text+0xaf): undefined reference to `pthread_cond_wait'
/usr/lib/x86_64-linux-gnu/libicuuc.a(umutex.ao): In function `icu_55::umtx_initImplPostInit(icu_55::UInitOnce&)':
(.text+0x10e): undefined reference to `pthread_cond_broadcast'
/usr/lib/x86_64-linux-gnu/libicuuc.a(umutex.ao): In function `umtx_condWait_55':
(.text+0x4f): undefined reference to `pthread_cond_wait'
/usr/lib/x86_64-linux-gnu/libicuuc.a(umutex.ao): In function `umtx_condBroadcast_55':
(.text+0x61): undefined reference to `pthread_cond_broadcast'
/usr/lib/x86_64-linux-gnu/libicuuc.a(umutex.ao): In function `umtx_condSignal_55':
(.text+0x71): undefined reference to `pthread_cond_signal'

可以看出原来的问题解决了,但是新问题来了,不过这次的问题倒简单,一看就明白,就是找不到pthread,dl这两个库(pthread_开头的引用都是pthread相关函数,dl开头的函数dlopen,dlclose都是dl库的函数)
其实前面用ldd命令查看libxml2.so的依赖库时,就显示有dl库。但不知道为什么没有显示pthread库。

于是再为xml2库加上-lpthread -ldl就可以编译通过了(-lpthread -ldl的先后顺序没有关系)
下面就是静态连接xml2的完整连接参数:

-lxml2 -licui18n -licuuc -licudata -lz -llzma -lm -lpthread -ldl
# 实际测试没有-licui18n也是可以连接通过的

你真的需要xml2码?

编译通过之后,满心欢喜,顺序查看了一下编译后的executable文件大小,吓我一大跳:44MB!!!

立即就崩溃了,目标平台上内存才32MB,这么大的可执行文件没法用呐。项目中用到的这么多连接库,到底哪个是最大的呢?幸运的是很快就找到了最大的连接库,就是-licudata

$ ll -h /usr/lib/x86_64-linux-gnu/libicudata.so.55.1 
-rw-r--r-- 1 root root 25M 3月  27  2018 /usr/lib/x86_64-linux-gnu/libicudata.so.55.1

ICU 是开源项目, 提供了最新的unicode标准,字符集转换, 以及超过300个国家的本地数据, 比如数字,时间和信息显示格式等,以及不同语言下的文本排序,日历相关的日期时间操作等。
详细内容可以访问: http://userguide.icu-project.org/

这个libicudata.so.55.1动态库就是ICU库用到的数据。

是不是可以通过自己编译减小icudata的大小?
如何编译ICU?
这又是一个要好一阵折腾的事儿,头大了。
这样被一个又一个出现的问题牵着鼻子走,何时是个头呢?
我打算跳出这个工作思路。
回头看mgncs的编译脚本,在${libmgncs-1.2.0}/configure.ac中找到下面的代码。
原来可以通过--enable-dbxml命令行参数控制是否使用xml2 !

AC_ARG_ENABLE(dbxml,
[ --enable-dbxml    enable libxml2 datasource support <default=yes>],
build_datasource_xml=$enableval)

if test "x$build_datasource_xml" = "xyes"; then
    AC_CHECK_LIB(xml2, xmlFree,
        DEP_LIBS="$DEP_LIBS -lxml2",
        build_datasource_xml=no)
fi

if test "x$build_datasource_xml" = "xyes"; then
    AC_DEFINE(_MGNCSDB_DATASOURCE, 1,
        [Define if support datasource])
    AC_DEFINE(_MGNCSDB_XML, 1,
		[Define if support xml datasource])
    CPPFLAGS="$CPPFLAGS -I/usr/include/libxml2"
fi

进一步查看_MGNCSDB_XML_MGNCSDB_DATASOURCE宏定义所涉及的代码,就搞明白了如果在mgncs中禁用xml2,就会禁用mxmlds.h这个接口文件定义的所有功能。

mxmlds.h会不会被应用项目用到呢?

mxmlds.h用于读写xml文件,MiniStudio中生成的资源文件就是xml格式,所以这个模块应该就是用于读写xml格式的资源文件,而在程序编译运行的时候,资源文件已经编译成.res的二进制文件了不再需要xml解析。
事实上,mgncs根本没有把这个mxmlds.h文件release出来,只是mgncs内部配合MiniStudio时使用的,所以编译目标平台的mgncs库时禁用它完全没问题。
于是如下在编译mgncs时加上--enable-dbxml=no,重新编译mgncs。

./configure \
	--host=$host \
	--prefix=$sh_folder/release/$mgncs_folder/$host \
	--enable-develmode 	\
	--enable-dbxml=no \
	MINIGUI_CFLAGS="-I$sh_folder/release/$minigui_folder/$host/include" \
	MGUTILS_CFLAGS="-I$sh_folder/release/$mgutils_folder/$host/include" \
	MINIGUI_LIBS="-L$sh_folder/release/$minigui_folder/$host/lib -l:libminigui_ths.a -ljpeg -lpng -lm -lfreetype -lz -lpng12 -lfreetype -lpthread" \
	MGUTILS_LIBS="-L$sh_folder/release/$mgutils_folder/$host/lib -l:libmgutils.a -ljpeg -lpng -lm -lfreetype -lz -lpng12 -lfreetype -lpthread" \
	MGPLUS_CFLAGS="-I$sh_folder/release/$mgplus_folder/$host/include" \
	MGPLUS_LIBS="-L$sh_folder/release/$mgplus_folder/$host/lib -l:libmgplus.a -lfreetype -lpthread -lstdc++" \
	|| exit -1

make -j8  || exit -1
make install

编译通过后再ldd查看libmgncs.so的信赖库,已经没有xml2了。

$ ldd release/libmgncs-1.2.0/x86_64-linux-gnu/lib/libmgncs-1.2.so.0
	linux-vdso.so.1 =>  (0x00007fff449c2000)
	libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fe8e8a22000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fe8e8719000)
	libfreetype.so.6 => /usr/lib/x86_64-linux-gnu/libfreetype.so.6 (0x00007fe8e846f000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fe8e8252000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fe8e803c000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe8e7c72000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fe8e9305000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007fe8e7a58000)
	libpng12.so.0 => /lib/x86_64-linux-gnu/libpng12.so.0 (0x00007fe8e7833000)

再用没有了xml2依赖的mgncs库作项目的静态编译。executable文件的体积变成了17MB,再经过strip后,只剩下8MB.虽然还是偏大,但这是Debug版,还没有优化,这个体积算是正常啦。

你可能感兴趣的:(embedded,minigui,MiniGUI)