Library Dependency 的議題要點

jollen 發表於 October 3, 2006 5:57 PM

在建立 embedded Linux 系統 (root filesystem) 時,程式庫相依 (library dependencies) 的議題是相當重要的一個題目。

當 root filesystem 缺少必要的 library 時,程式當然是無法執行的,甚致系統也會無法順利啟動。依據 Jollen 在「Embedded Linux / ARM9 開發實戰」的課程所提的幾個觀念,我們可以了解到在建構 embedded Linux 系統時,應具備的正確觀念與基本能力。

我們把「如何找出所需的 library」方法整理出 3 項的基本要點,依照這 3 種基本款來加入 library 將能解決幾乎所有的 library dependency 問題,這 3 種項基本要點為:

(1) 先利用 cross toolchain 的 objdump 觀察「NEEDED」的項目,加入 library。
(2) 再檢查這些 library 是否相依其它 library。
(3) 最後要檢視應用程式是否使用到需要特定 library 的「service」。

要點 1. 跟 2. 對大家來說沒有什麼問題,要點 3. 在我們的 training 課程裡,我們以建構 thttpd (embedded Web server) 的實際案例來做講解。

關於建構 thttpd 的案例

thttpd 使用到 NSS (Name Service Switch),因此若沒有將 libnss_SERVICE.so 加到 root filesystem,thttpd 在執行時可能會遇到一些奇怪的問題。舉個例子,當 thttpd 透過 /etc/passwd 去尋找 (查詢) UNIX 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() 函數裡。關於 libnss_SERVICE.so 的議題,Jollen 打算另外再做討論,因為還會與 libc 有關係。

在這裡我們由系統建構的角度來看這個問題。因為我們已經習慣用 objdump 來觀察程式的相依 library,所以當 objdump 的畫面跟我們預期的不同時,經常一時無法反應過來。例如,以下的訊息是我們所「預期」的:

# /opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu/bin/arm-9tdmi-linux-gnu-objdump -x thttpd|more
...
Dynamic Section:
  NEEDED      libcrypt.so.1
  NEEDED      libnss_files.so.2
  NEEDED      libc.so.6
...

但是實際的訊息卻是像這樣的:

# /opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu/bin/arm-9tdmi-linux-gnu-objdump -x thttpd|more
...
Dynamic Section:
  NEEDED      libcrypt.so.1
  NEEDED      libc.so.6
...

我們可以用一知半解的思考邏輯來解決問題:thttpd 呼叫到 getpwnam() 函數,此函數由 libnss_compat 提供,因此解決方案是把 libnss_files.so 加到 root filesystem 裡即可。

且慢!前面才講到 libnss_compat,怎麼後面是把 libnss_files 加到 root filesystem?是這樣的,libnss_compat 用來讀 /etc/shadow,但是現在我們只需要由 /etc/passwd 讀 Unix user,所以使用 libnss_files.so 就行了。

執行 thttpd 的話,再加上指定 username 的參數來執行:

# thttpd -p 80 -d /var/www -u root

libnss_SERVICE.so 是包含在 glibc 裡的程式庫,因此可以直接由 cross toolchain 裡取得,不必再另行建置。

有關 NSS (Name Service Switch) 可參考以下網頁:

Linux / Unix Command: nsswitch.conf - http://linux.about.com/od/commands/l/blcmdl5_nsswitc.htm

其它網路資源:

http://www.gnu.org/software/libc/manual/html_node/Name-Service-Switch.html
http://mirrors.usc.edu/pub/gnu/Manuals/glibc-2.2.3/html_chapter/libc_28.html

此處我們以「service」的角度來探討這個問題:因為 thttpd 使用到 Name Service Switch,所以需要加入 libnss_SERVICE.so。另外一種探討的角度是:由 programming 的角度來思考,大家可以試著去研究這個問題,還挺好玩的!

附帶一提,如果要讀 shadow passwd 的話,是使用 libnss_compat.so。

Jollen's Blog 使用 Github issues 與讀者交流討論。請點擊上方的文章專屬 issue,或 open a new issue

您可透過電子郵件 jollen@jollen.org,或是 Linkedin 與我連絡。更歡迎使用微信,請搜尋 WeChat ID:jollentw