閱讀272 返回首頁    go 阿裏雲 go 技術社區[雲棲]


[翻譯] getauxval() and the auxiliary vector

前言

英文原文:getauxval() and the auxiliary vector

該文章在2012年10月發表。


翻譯

用戶空間應用程序與內核之間有許多交流機製。係統調用和偽文件係統(諸如:/proc和/sys)廣為人知。信號也同樣廣為人知;內核利用信號通知進程的各種同步或異步事件——例子:當進程嚐試寫一個破碎的管道或子進程終止時。


內核和用戶空間交流還有許多複雜的機製。包括Linux專用的netlink socketsuser-mode helper功能。Netlink套接字為內核交換信息提供了一套套接字風格的API。user-mode helper功能允許內核自動調用用戶空間的可執行文件;這個機製被用於許多地方,包括控製組的實現和piping core dumps to a user-space application


輔助向量(auxiliary vector),一個從內核到用戶空間的信息交流機製,它一直保持透明。然而,在GNU C庫(glibc)2.16發布版中添加了一個新的庫函數”getauxval()”,這似乎在六月底,現在它已經變得更加可見。曆史上,許多UNIX係統實現過輔助向量功能。本質上,它是一個鍵值對列表,當一個新的可執行映像被加載到進程中時被內核的ELF二進製加載器(fs/binfmt_elf.c文件,在內核源碼中)構造。這個列表被放在進程地址空間的特定位置;在Linux係統上它存在於用戶地址空間的高位(high end),在棧,命令行參數(argv)和環境變量(environ)的正上方(向下生長)。


輸入圖片說明

從描述和圖表中我們可以看到,雖然在輔助向量在一定程度上被隱藏,對它的訪問需要費點功夫。即使不使用新的庫函數,一個應用程序想要訪問輔助向量隻需要獲得挨著環境變量列表結尾的NULL指針的地址。而且,在shell級別(level),我們可以通過設置LD_SHOW_AUXV環境變量來查明當執行一個應用程序時提供給可執行文件的輔助向量。


$ LD_SHOW_AUXV=1 sleep 1000
AT_SYSINFO_EHDR: 0x7fff35d0d000
AT_HWCAP:        bfebfbff
AT_PAGESZ:       4096
AT_CLKTCK:       100
AT_PHDR:         0x400040
AT_PHENT:        56
AT_PHNUM:        9
AT_BASE:         0x0
AT_FLAGS:        0x0
AT_ENTRY:        0x40164c
AT_UID:          1000
AT_EUID:         1000
AT_GID:          1000
AT_EGID:         1000
AT_SECURE:       0
AT_RANDOM:       0x7fff35c2a209
AT_EXECFN:       /usr/bin/sleep
AT_PLATFORM:     x86_64


在係統中每個進程的輔助向量可以通過/proc/PID/auxv文件看到。dump的文件內容與上麵命令的一致(作為八字節十進製數,用於該例子的鍵和值的尺寸在64位係統上),我們可以看到在向量中的鍵值對,下麵內容中鍵值對均為0的行表示向量的結尾:

$ od -t d8 /proc/15558/auxv
0000000                   33      140734096265216
0000020                   16           3219913727
0000040                    6                 4096
0000060                   17                  100
0000100                    3              4194368
0000120                    4                   56
0000140                    5                    9
0000160                    7                    0
0000200                    8                    0
0000220                    9              4200012
0000240                   11                 1000
0000260                   12                 1000
0000300                   13                 1000
0000320                   14                 1000
0000340                   23                    0
0000360                   25      140734095335945
0000400                   31      140734095347689
0000420                   15      140734095335961
0000440                    0                    0
0000460


掃描用戶空間內存的高位或讀取/proc/PID/auxv的方法從輔助向量中取值是不優雅的。新的庫函數提供了從列表中取出單個值的簡單機製:



函數僅有一個參數,並返回對應的值。glibc頭文件定義一個符號常量集,它們均以”AT_*”這種格式定義,將這些符號作為鍵傳入getauxval;這些名稱與執行帶LD_SHOW_AUXV=1的命令時顯示的字符串完全一樣。


當然,現在顯而易見的問題是:什麼樣的信息被放在輔助向量中,和誰需要這些信息?輔助向量的主要訪問者是動態鏈接器(ld-linux.so)。在正常的方案中,內核的ELF二進製加載器通過加載可執行文件到進程的內存構造一個進程映像,同樣的加載linker到內存。此時,動態鏈接器(dynamic linker)準備好接管加載程序需要的動態鏈接庫的任務,移交控製程序本身的準備工作。然而,它缺少對這些任務某些至關重要的信息:程序在虛擬地址空間中的位置,和程序執行的起始地址。


理論上,內核可以提供一個係統調用動態鏈接器可以獲得所需的信息。然而,這是一種低效的做事方式:內核的程序加載器已經擁有信息(因為它掃描了ELF二進製文件並構建了進程映像)並知道動態鏈接程序會需要它。而不是保留這個信息的記錄,直到動態鏈接程序要求,內核可以簡單的讓它在進程映像的某個動態鏈接器已知的位置被獲得。該位置當然保存的是輔助向量。


事實證明,內核的程序加載器擁有這些信息並且是動態鏈接器所需要的。By placing all of this information in the auxiliary vector, the kernel either saves the programming overhead of making this information available in some other way (e.g., by implementing a dedicated system call), or saves the dynamic linker the cost of making a system call, or both.輔助向量中的值可通過getauxval()獲得,下麵是該函數可傳入的參數:


AT_PHDR和AT_ENTRY:這些鍵的值是可執行文件的ELF程序頭的地址和可執行文件的入口地址。動態鏈接器使用這些信息執行鏈接並將控製權交給可執行文件。

AT_SECURE:如果這個可執行文件應該被安全地對待,內核會給這個鍵分配一個非零值。這個設置可以被Linux Security Module觸發,but the common reason is that the kernel recognizes that the process is executing a set-user-ID or set-group-ID program.在這種情況下,動態鏈接器禁用某些環境變量(就如ld-linux.so(8)手冊頁中的描述)並且C庫改變其行為的其他方麵。

AT_UID, AT_EUID, AT_GID, 和 AT_EGID:這些是進程的真實和有效的用戶ID和組ID。Making these values available in the vector saves the dynamic linker the cost of making system calls to determine the values.如果AT_SECURE值不可獲得,動態鏈接器使用這些值來做出是否安全地處理可執行的決定。

AT_PAGESZ:這個值是係統頁尺寸。動態鏈接器在鏈接階段需要這個信息,並且C庫在malloc函數族的實現中使用它。

AT_PLATFORM:這個值指向一個字符串,用於辨認程序運行在哪個硬件平台。在某些情況下,the dynamic linker uses this value in the interpretation of rpath values. (The ld-linux.so(8) man page describes rpath values.)

AT_SYSINFO_EHDR:這個值指向包含有Virtual Dynamic Shared Object (VDSO)的頁,這是內核創建的以提供某些係統調用的快速實現。(一些VDSO的文檔可在內核源文件Documentation/ABI/stable/vdso中找到)

AT_HWCAP:這個值指向一個多字節位掩碼,設置指示詳細的處理器能力。這個信息可以被用來提供某些庫函數優化的行為。位掩碼是硬件相關(例如:內核源碼文件”arch/x86/include/asm/cpufeature.h”詳細的講述了Intel x86架構)。

AT_RANDOM:The value is a pointer to sixteen random bytes provided by the kernel. The dynamic linker uses this to implement a stack canary.


The precise reasons why the GNU C library developers have chosen to add the getauxval() function now are a little unclear. The commit message and NEWS file entry for the change were merely brief explanations of what the change was, rather than why it was made. The only clue provided by the implementer on the libc-alpha mailing list suggested that doing so was useful to allow for “future enhancements to the AT_ values, especially target-specific ones.” That comment, plus the observation that the glibc developers tend to be rather conservative about adding new interfaces to the ABI, suggest that that they have some interesting new user-space uses of the auxiliary vector in mind.

最後更新:2017-04-10 21:30:28

  上一篇:go [實踐] Android5.1.1源碼 - 在Framework中添加自定義係統服務
  下一篇:go 互聯網業務安全之通用安全風險模型