___2017

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

一、引子

为了把编译好的bin和lib文件打包到镜像里面,新建了个package,在编译的时候,总是报库缺失的错误:
Package XXX is missing dependencies for the following libraries
这就有意思了:
1、它怎么知道我的bin和lib文件依赖哪些库?
2、我不需要编译器产生这些库依赖错误(已经确定系统里面这些库都是有的),怎么解决这个错误?

 

二、Demo

首先,创建一个测试用的Package:

$ tree package/pkg_test
package/pkg_test
├── Makefile
└── test_bin

Makefile:

include $(TOPDIR)/rules.mk
include $(BUILD_DIR)/kernel.mk

# Name and release number of this package
PKG_NAME:=pkg_test
PKG_VERSION:=0.0.1
PKG_RELEASE:=1

PKG_BUILD_DIR := $(COMPILE_DIR)/$(PKG_NAME)


include $(BUILD_DIR)/package.mk

define Package/$(PKG_NAME)
  SECTION:=utils
  CATEGORY:=Utilities
  TITLE:=pkg_test
endef

define Package/$(PKG_NAME)/description
endef

define Build/Prepare
    $(INSTALL_DIR) $(PKG_BUILD_DIR)/
    $(CP) test_bin $(PKG_BUILD_DIR)/
endef

define Build/Configure
endef

define Build/Compile
endef

define Package/$(PKG_NAME)/install
    $(INSTALL_DIR) $(1)/usr/bin

    $(INSTALL_BIN) $(PKG_BUILD_DIR)/test_bin $(1)/usr/bin
endef

$(eval $(call BuildPackage,$(PKG_NAME)))

编译之:

$ make package/pkg_test/install V=99

 

初步判断:

$ find ./out/ -name test_bin
.
/out/astar-parrot/compile_dir/target/pkg_test/ipkg-sunxi/pkg_test/usr/bin/test_bin ./out/astar-parrot/compile_dir/target/pkg_test/test_bin

我们发现,pkg_test其实已经编译成功了,只是在检查依赖库的时候出了差错。
目前可以确定:错误与编译无关。接下来就需要分析依赖库的检查脚本。

 

三、深入分析库依赖的检查流程及原理

”Package XXX is missing dependencies for the following libraries“的出处:TOPDIR/build/package-ipkg.mk

变量值:

PKG_INFO_DIR:/home/.../TOPDIR/out/astar-parrot/staging_dir/target/pkginfo
$(1):pkg_test

73~77行显示,$(PKG_INFO_DIR)目录下存在pkg_test.missing文件的时候,就打印库依赖错误,并把依赖的库显示出来。

$ cat out/astar-parrot/staging_dir/target/pkginfo/pkg_test.missing
libstdc++.so.6

显然,需要探究pkg_test.missing文件是怎么产生的。

TOPDIR/scripts/gen-dependencies.sh

第8行,SELF=${0##*/}:

$0:当前脚本的文件名;

${var##*str}语法:从左向右截取var,保留最后一个str后的字符串。

示例:

$ ./scripts/gen-dependencies.sh
则SELF的值为gen-dependencies.sh

 

第9行,READELF="${READELF:-readelf}":

${var:-DEFAULT}语法:如果var没有被声明, 或者其值为空, 那么就以$DEFAULT作为其值。

 

第12行,TARGETS=$*:

$*:传递给脚本或函数的所有参数。根据编译过程的Log,这里:
TARGETS=/home/.../TOPDIR/out/astar-parrot/compile_dir/target/pkg_test/ipkg-sunxi/pkg_test

 

第21行,find $TARGETS -type f -a -exec file {} \;:对$TARGETS目录下的文件执行"file"命令。

根据我们的demo简化该命令就是:

$ file $TARGETS/test_bin
$TARGETS/test_bin: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked (uses shared libs), not stripped

 

第22行,经过sed过滤后,结果:$TARGETS/test_bin

第23行,readelf -d:读取并显示test_bin这个bin文件的dynamic section

$ readelf -d $TARGETS/test_bin
Dynamic section at offset 0x4008 contains 23 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libstdc++.so.6]
 0x00000001 (NEEDED)                     Shared library: [libgcc_s.so.1]
 0x00000001 (NEEDED)                     Shared library: [libc.so]
 0x0000000c (INIT)                       0x10a20
 0x0000000d (FINI)                       0x13840
 0x00000019 (INIT_ARRAY)                 0x24000
 0x0000001b (INIT_ARRAYSZ)               4 (bytes)
 0x0000001a (FINI_ARRAY)                 0x24004
 0x0000001c (FINI_ARRAYSZ)               4 (bytes)
 0x00000004 (HASH)                       0x1012c
 0x00000005 (STRTAB)                     0x10628
 0x00000006 (SYMTAB)                     0x102a8
 0x0000000a (STRSZ)                      528 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000015 (DEBUG)                      0x0
 0x00000003 (PLTGOT)                     0x240e8
 0x00000002 (PLTRELSZ)                   344 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0x108c8
 0x6ffffffe (VERNEED)                    0x108a8
 0x6fffffff (VERNEEDNUM)                 1
 0x6ffffff0 (VERSYM)                     0x10838
 0x00000000 (NULL)                       0x0

 第24行以上结果经过awk过滤:

libstdc++.so.6
libgcc_s.so.1
libc.so

 第25行再经过sort排序(-u去除重复项):

libc.so
libgcc_s.so.1
libstdc++.so.6

可见,经过gen-dependencies.sh脚本,我们需要打包到镜像里的bin/lib文件的依赖库就获取到了。
至此,第一个问题解决。

 

继续分析TOPDIR/build/package-ipkg.mk脚本。

$ cat out/astar-parrot/staging_dir/target/pkginfo/pkg_test.provides 
libc.so
libgcc_s.so.1
libgomp.so
libgomp.so.1
libgomp.so.1.0.0

$ grep -xF "libc.so" out/astar-parrot/staging_dir/target/pkginfo/pkg_test.provides
libc.so
$ grep -xF "libgcc_s.so.1" out/astar-parrot/staging_dir/target/pkginfo/pkg_test.provides
libgcc_s.so.1
$ grep -xF "libstdc++.so.6" out/astar-parrot/staging_dir/target/pkginfo/pkg_test.provides <-- grep不到
$ echo $?
1

第70行,grep语句:在pkg_test.provides文件中搜索各个依赖库,如果不存在(grep命令返回非0)就把依赖库的名字写到pkg_test.missing文件中。
至此,CheckDependencies工作就做完了。pkg_test.provides这个文件是谁又是怎么产生的?

$(IDEPEND_$(1))的值:libc
第186~193行,patsubst:字符串替换函数,这里是把”libc“替换成”PKG_INFO_DIR/libc.provides“,即:
/home/.../TOPDIR/out/astar-parrot/staging_dir/target/pkginfo/libc.provides


pkg_test.provides文件内容来源:
1、/home/.../TOPDIR/out/astar-parrot/compile_dir/target/pkg_test/ipkg-sunxi/pkg_test目录下的lib*.so*和*.ko文件(第186行)
2、libc.provides文件(第187行)
3、pkg_test的Makefile中extra_provides配置项(第192行)

那么,我们只要在上述来源中任意一个添加缺失的库文件(名),即可解决问题。
下面验证一下。

第一种解决方式

$ touch TOPDIR/package/pkg_test/libstdc++.so.6
$ cat Makefile
define Build/Prepare
    $(CP) libstdc++.so.6 $(PKG_BUILD_DIR)/ <--- 新增
endef

define Package/$(PKG_NAME)/install
    $(CP) $(PKG_BUILD_DIR)/libstdc++.so.6 $(1) <--- 新增
endef

亲测有效。但是这种方式太“野”了,不够official。最官方的做法是在 Makefile 文件中加 DEPENDS 描述:

define Package/$(PKG_NAME)
    DEPENDS:+=libxxx
endef

 

第二种解决方式:libc.provides文件中添加libstdc++.so.6

亲测有效。缺点:libc.provides是动态生成的,下次clean编译镜像的时候就失效了,需要重新更改该文件。

 

第三种解决方式:在Makefile中添加extra_provides描述段

define Package/$(PKG_NAME)/extra_provides
    echo "libstdc++.so.6";
endef

亲测有效。对于我的应用环境,这种方式最好。

 

PS

在把自己的bin/lib文件编译到镜像之后,发现bin文件被更改了!如下图比较:

原因在第197行有答案:$(RSTRIP) $$(IDIR_$(1))

STRIP="/home/.../TOPDIR/out/host/bin/sstrip"
STRIP_KMOD="/home/.../TOPDIR/scripts/strip-kmod.sh"
PATCHELF="/.../TOPDIR/out/host/bin/patchelf"
/home/.../TOPDIR/scripts/rstrip.sh /home/.../TOPDIR/out/astar-parrot/compile_dir/target/pkg_test/ipkg-sunxi/pkg_test

strip的作用是删除掉目标文件中的符号及调试信息,从而达到给目标文件“瘦身”的目的。

如果需要禁用strip功能,参考“OpenWrt取消strip或者重新设置strip参数的方法”。

posted on 2019-01-09 19:54  yin'xiang  阅读(11213)  评论(1编辑  收藏  举报