由于产品设计的需要,我要在Davinci6446平台移植thttpd-2.25b,在此过程中也出现一些问题,则在此记录之。
我们知道,当 rootfs缺少必要的library时,会导致应用程序无法正确运行,严重的话可能导致系统无法正常启动,解决库依赖(library dependency)问题可参考如下三个要点:
1、先使用交叉编译器的objdump观察目标项目,加入library。
2、检查这些library是不是依赖其他的library。
3、最后检查应用程序是否用到特定的library的service。
thttpd 使用到 NSS (Name Service Switch),因此若沒有将 libnss_SERVICE.so 加到 root filesystem,thttpd 在执行时可能会遇到一些奇怪的问题。例如,当 thttpd 依据 /etc/passwd 去寻找 linux user 時,会用到 libnss_files.so (不读 /etc/shadow),因此会看到以下错误信息:
unknown user - root
出现这个错误的原因是 thttpd 读不到 'root' 使用者,要深入探讨这个问题的原理,必须从以下的程序源代码中了解:
403 /* If we're root and we're going to become another user, get the uid /gid
404 ** now.
405 */
406 if ( getuid() == 0 )
407 {
408 pwd = getpwnam( user );
409 if ( pwd == (struct passwd*) 0 )
410 {
411 syslog( LOG_CRIT, "unknown user - '%.80s'", user );
412 (void) fprintf( stderr, "%s: unknown user - '%s'\n", argv0, user );
413 exit( 1 );
414 }
415 uid = pwd->pw_uid;
416 gid = pwd->pw_gid;
417 }
以上代码是 thttpd 2.25b 的程序源代码,位于 thttpd.c 的 main() 函数。
由系统构建的角度来看这个问题。我们已经习惯用 objdump 来观察程序library依赖,所以当 objdump 的输出跟我们預期的不同时,还比较惊奇。例如:
root@microtiger:/home/microtiger/task/thttpd/thttpd-2.25b# arm_v5t_le-objdump -x thttpd|more
thttpd: file format elf32-littlearm
thttpd
architecture: armv5t, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x00008180
Program Header:
0x70000001 off 0x00082d4c vaddr 0x0008ad4c paddr 0x0008ad4c align 2**2
filesz 0x000006e8 memsz 0x000006e8 flags r--
LOAD off 0x00000000 vaddr 0x00008000 paddr 0x00008000 align 2**15
filesz 0x000834b0 memsz 0x000834b0 flags r-x
LOAD off 0x000834b0 vaddr 0x000934b0 paddr 0x000934b0 align 2**15
filesz 0x00001ef8 memsz 0x0002a99c flags rw-
NOTE off 0x000000d4 vaddr 0x000080d4 paddr 0x000080d4 align 2**2
filesz 0x00000020 memsz 0x00000020 flags r--
NOTE off 0x000000f4 vaddr 0x000080f4 paddr 0x000080f4 align 2**2
filesz 0x00000074 memsz 0x00000074 flags r--
private flags = 4000002: [Version4 EABI] [has entry point]
Sections:
Idx Name Size VMA LMA File off Algn
0 .note.ABI-tag 00000020 000080d4 000080d4 000000d4 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA, LINK_ONCE_SAME_CONTENTS
1 .note.numapolicy 00000074 000080f4 000080f4 000000f4 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA, LINK_ONCE_SAME_CONTENTS
2 .init 00000018 00008168 00008168 00000168 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
3 .text 0006a3c4 00008180 00008180 00000180 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
4 __libc_freeres_fn 000009bc 00072544 00072544 0006a544 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
5 __libc_thread_freeres_fn 00000050 00072f00 00072f00 0006af00 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
6 .fini 00000018 00072f50 00072f50 0006af50 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
7 .rodata 00017cc4 00072f68 00072f68 0006af68 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 __libc_subfreeres 00000040 0008ac2c 0008ac2c 00082c2c 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 __libc_atexit 00000004 0008ac6c 0008ac6c 00082c6c 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
10 __libc_thread_subfreeres 00000004 0008ac70 0008ac70 00082c70 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
11 .ARM.extab 000000d8 0008ac74 0008ac74 00082c74 2**2
是相当的长,只要了这么一段。
追溯这个问题:thttpd 调用 getpwnam() 函数,此函数由 libnss_compat 提供,因此解決方案是把 libnss_files.so 加到 root filesystem 里面即可。
前面提到 libnss_compat,怎么后面是把 libnss_files 加到 root filesystem?原因在于libnss_compat 用來读 /etc/shadow,但是現在我们只需要由 /etc/passwd 读 linux user,所以使用 libnss_files.so 就行了。
执行 thttpd 的话,再加上指定 username 的参数即可:
# thttpd -p 80 -d www -u root
libnss_SERVICE.so 是包含在 glibc 里的程序库,因此可以直接由 cross toolchain 取得,不必再另行建置。
附带一提,如果要读 shadow passwd 的话,是使用 libnss_compat.so。