Mac OS X 如何執行應用程式

來源:互聯網
上載者:User

作為長期的UNIX使用者,我通常有一些排除系統故障的工具。最近,我正在開發軟體並新增了對蘋果公司的OSX系統的支援。但是和其他傳統UNIX變種不同,OSX不支援許多與載入、連結和執行程式相關的工具。

例如,當共用庫重定位出錯時,我所做的首要事情就是對可執行檔運行ldd。ldd工具列出了可執行檔所依賴的共用庫(包括所在路徑)。但是在OSX上,試圖運行ldd將報錯。

evil:~ mohit$ ldd /bin/ls-bash: ldd: command not found

沒找到?但在所有的類UNIX系統上基本上都有的啊!我想知道objdump是否可用。

evil:~ mohit$ objdump -x /bin/ls-bash: objdump: command not found

命令未找到!怎麼回事?

問題在於與Linux、Solaris、HP-UX和其他許多UNIX變種不同,OSX不使用ELF檔案格式。另外,OSX不屬於GNU項目的一部分。該項目包含像ldd和objdump這樣的工具。

為了在OSX獲得可執行檔所依賴的共用庫列表,需要使用otool工具。

evil:~ mohit$ otool /bin/lsotool: one of -fahlLtdoOrTMRIHScis must be specifiedUsage: otool [-fahlLDtdorSTMRIHvVcXm] <object file> ...        -f print the fat headers        -a print the archive header        -h print the mach header        -l print the load commands        -L print shared libraries used        -D print shared library id name        -t print the text section (disassemble with -v)        -p <routine name> start dissassemble from routine name        -s <segname> <sectname> print contents of section        -d print the data section        -o print the Objective-C segment        -r print the relocation entries        -S print the table of contents of a library        -T print the table of contents of a dynamic shared library        -M print the module table of a dynamic shared library        -R print the reference table of a dynamic shared library        -I print the indirect symbol table        -H print the two-level hints table        -v print verbosely (symbolicly) when possible        -V print disassembled operands symbolicly        -c print argument strings of a core file        -X print no leading addresses or headers        -m don't use archive(member) syntaxevil:~ mohit$ otool -L /bin/ls/bin/ls:        /usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.0.0)

好多了!我們可以看見/bin/ls引用了二個動態庫。儘管,副檔名我們根本不熟悉。

我相信許多UNIX、Linux使用者使用OSX系統時有過類似的經曆,所以我決定寫一點目前我所知道的關於 OSX可執行檔的知識。

OSX運行時架構運行時環境是OSX上代碼擴充的一個架構。它由一組定義代碼如何被載入、被管理、被執行的集合組成。一旦應用程式運行,合適的運行時環境就載入程式到記憶體,解決外部庫的引用,並為執行準備代碼。

OSX支援三種運行時環境:

  • Dyld運行時環境:基於Dyld庫管理器的推薦環境;
  • CFM運行時環境:OS9遺留環境。實際用來設計需要使用OSX新特色,但還沒完全移植到dyld的應用程式。
  • 經典環境:OS9(9.1 or 9.2)程式無需修改直接在OSX運行。

本文主要關注於Dyld運行時環境。

在OSX中幾乎所有的可執行檔都使用Mach-O檔案格式,如:應用程式、架構、庫、核心擴充……都是以Mach-O檔案實現。Mach-O是既一種檔案格式,也是一種描述可執行檔如何被核心載入並啟動並執行ABI(應用程式二進位介面)。專業一點講, 它告訴系統:使用哪個動態庫載入器;載入哪個共用庫;如何組織進程地址空間;函數進入點地址等。

Mach-O不是新事物。最初由開放軟體基金會(OSF)用於設計基於Mach微核心的OSF/1作業系統。後來被移植到x86系統OpenStep上。

為了支援Dyld 運行時環境,所有檔案應該編譯成Mach-O可執行檔格式。

Mach-O檔案的組織

Mach-O檔案分為三個地區:頭部、載入命令區段和原始段資料。頭部和載入命令區段描述檔案功能、布局和其他特性;原始段資料包含由載入命令引用的位元組序列。為了研究和檢查Mach-O檔案的各部分,OSX內建了一個很有用的程式otool,其位於/usr/bin目錄下。

接下來,將使用otool來瞭解Mach-O檔案如何組織的。

頭部查看檔案的Mach-O頭部,使用otool 命令的-h參數

evil:~ mohit$ otool -h /bin/ls/bin/ls:Mach header          magic cputype cpusubtype filetype ncmds sizeofcmds flags 0xfeedface           18                   0            2        11            1608 0x00000085

頭部首先指定的是魔數(magic number)。魔數標明檔案是32位還是64位的Mach-O檔案。也標明CPU位元組順序。魔數的解釋,參看/usr/include/mach-o/loader.h。

頭部也指定檔案的目標架構。這樣就允許核心確保該代碼不會在不是為此處理器編寫的CPU上運行。例如,在上面的輸出,cputype設成18,它代表CPU_TYPE_POWERPC,在/usr/include/mach/machine.h中定義。

從上兩項資訊,我們推斷出此二進位檔案用於基於32位PowerPC的系統。

有時二進位檔案可能包含不止一個體系的代碼。通常稱為Universal Binaries,通常以fat_header這額外的頭部開始。檢查fat_header內容, 使用otool命令的-f切換參數。

cpusubtype屬性制定了CPU確切模型,通常設成CPU_SUBTYPE_POWERPC_ALL或CPU_SUBTYPE_I386_ALL。

filetype指出檔案如何對齊如何使用。實際上它告訴你檔案是庫、靜態可執行檔、core file等。上面的filetype等於MH_EXECUTE,指出demand paged executable file。下面是從/usr/include/mach-o/loader.h截取的片段,列出了不同的檔案類型。

#define MH_OBJECT 0x1   /* relocatable object file */#define MH_EXECUTE  0x2   /* demand paged executable file */#define MH_FVMLIB 0x3   /* fixed VM shared library file */#define MH_CORE   0x4   /* core file */#define MH_PRELOAD  0x5   /* preloaded executable file */#define MH_DYLIB  0x6   /* dynamically bound shared library */#define MH_DYLINKER 0x7   /* dynamic link editor */#define MH_BUNDLE 0x8   /* dynamically bound bundle file */#define MH_DYLIB_STUB 0x9   /* shared library stub for static */          /*  linking only, no section contents */

接下來的兩個屬性涉及到載入命令區段,指定了命令的數目和大小。

最後, 獲得了狀態資訊, 這些可能在裝載和執行時被核心使用。

載入命令載入命令區段包含一個告知核心如何載入檔案中的各個原始段的命令列表。典型的描述如何對齊,保護每個段及各段在記憶體中的布局。

查看檔案中的載入命令列表,使用otool命令的-l切換參數。

evil:~/Temp mohit$ otool -l /bin/ls/bin/ls:Load command 0      cmd LC_SEGMENT  cmdsize 56  segname __PAGEZERO   vmaddr 0x00000000   vmsize 0x00001000  fileoff 0filesize 0  maxprot 0x00000000initprot 0x00000000   nsects 0    flags 0x4Load command 1      cmd LC_SEGMENT  cmdsize 600  segname __TEXT   vmaddr 0x00001000   vmsize 0x00006000  fileoff 0filesize 24576  maxprot 0x00000007initprot 0x00000005   nsects 8    flags 0x0Section  sectname __text   segname __TEXT      addr 0x00001ac4      size 0x000046e8    offset 2756     align 2^2 (4)    reloff 0    nreloc 0     flags 0x80000400reserved1 0reserved2 0[ ___SNIPPED FOR BREVITY___ ]Load command 4          cmd LC_LOAD_DYLINKER      cmdsize 28         name /usr/lib/dyld (offset 12)Load command 5          cmd LC_LOAD_DYLIB      cmdsize 56         name /usr/lib/libncurses.5.4.dylib (offset 24)   time stamp 1111407638 Mon Mar 21 07:20:38 2005      current version 5.4.0compatibility version 5.4.0Load command 6          cmd LC_LOAD_DYLIB      cmdsize 52         name /usr/lib/libSystem.B.dylib (offset 24)   time stamp 1111407267 Mon Mar 21 07:14:27 2005      current version 88.0.0compatibility version 1.0.0Load command 7     cmd LC_SYMTABcmdsize 24  symoff 28672   nsyms 101  stroff 31020strsize 1440Load command 8            cmd LC_DYSYMTAB        cmdsize 80      ilocalsym 0      nlocalsym 0     iextdefsym 0     nextdefsym 18      iundefsym 18      nundefsym 83         tocoff 0           ntoc 0      modtaboff 0        nmodtab 0   extrefsymoff 0    nextrefsyms 0indirectsymoff 30216  nindirectsyms 201      extreloff 0        nextrel 0      locreloff 0        nlocrel 0Load command 9     cmd LC_TWOLEVEL_HINTScmdsize 16  offset 29884  nhints 83Load command 10        cmd LC_UNIXTHREAD    cmdsize 176     flavor PPC_THREAD_STATE      count PPC_THREAD_STATE_COUNT    r0  0x00000000 r1  0x00000000 r2  0x00000000 r3   0x00000000 r4   0x00000000    r5  0x00000000 r6  0x00000000 r7  0x00000000 r8   0x00000000 r9   0x00000000    r10 0x00000000 r11 0x00000000 r12 0x00000000 r13  0x00000000 r14  0x00000000    r15 0x00000000 r16 0x00000000 r17 0x00000000 r18  0x00000000 r19  0x00000000    r20 0x00000000 r21 0x00000000 r22 0x00000000 r23  0x00000000 r24  0x00000000    r25 0x00000000 r26 0x00000000 r27 0x00000000 r28  0x00000000 r29  0x00000000    r30 0x00000000 r31 0x00000000 cr  0x00000000 xer  0x00000000 lr   0x00000000    ctr 0x00000000 mq  0x00000000 vrsave 0x00000000 srr0 0x00001ac4 srr1 0x00000000

上面的檔案在頭部下有11載入命令直接定位,從0到10。

Commands 0 and 3 (LC_SEGMENT) 從0到3,定義了檔案中的段如何映射到記憶體中去。段定義了Mach-O二進位檔案中的位元組序列,可以包含零個或更多的Sections。稍候我們談談段。

  • Command 4 (LC_LOAD_DYLINKER) 指定使用哪個動態連結器。幾乎總是設成OSX預設動態連結器/usr/lib/dyld。
  • Commands 5 and 6 (LC_LOAD_DYLIB) 指定檔案需要連結的共用庫。它們由command 4規定的動態連結器載入。
  • Commands 7 and 8 (LC_SYMTAB, LC_DYNSYMTAB) 指定由檔案和動態連結器分別使用的符號表。
  • Command 9 (LC_TWOLEVEL_HINTS) 包含兩級名稱空間的Hint Table。
  • Command 10 (LC_UNIXTHREAD) 定義進程主線程的初始狀態。該命令僅僅包含在可執行檔裡。

段(Segments)與區(Sections)

上面涉及到的大多數載入命令都引用了檔案中的段。段是Mach-O檔案直接被核心和動態連結器映射到虛擬記憶體中的一系列字元序列。頭部和載入命令地區認為是檔案的首段。一個典型的OSX可執行檔通常由下列五段:

  • __PAGEZERO 定位於虛擬位址0,無任何保護權利。此段在檔案中不佔用空間,訪問NULL導致立即崩潰。
  • __TEXT 包含唯讀資料和可執行代碼。
  • __DATA 包含可寫資料。這些section通常由核心標誌為copy-on-write。
  • __OBJC 包含Objective C語言運行時環境使用的資料。
  • __LINKEDIT 包含動態連結器用的未經處理資料。

__TEXT和 __DATA段可能包含0或更多的section。每個section由指定類型的資料,如,可執行代碼,常量,C字串等組成。

查看某section內容,使用otool命令-s選項。

evil:~/Temp mohit$ otool -sv __TEXT __cstring /bin/ls/bin/ls:Contents of (__TEXT,__cstring) section00006320 00000000 5f5f6479 6c645f6d 6f645f74 00006330 65726d5f 66756e63 73000000 5f5f6479 00006340 6c645f6d 616b655f 64656c61 7965645f 00006350 6d6f6475 6c655f69 6e697469 616c697a __SNIP__

反組譯碼__text section,使用 the -tv 切換參數。

evil:~/Temp mohit$ otool -tv /bin/ls/bin/ls:(__TEXT,__text) section00001ac4        or      r26,r1,r100001ac8        addi    r1,r1,0xfffc00001acc        rlwinm  r1,r1,0,0,2600001ad0        li      r0,0x000001ad4        stw     r0,0x0(r1)00001ad8        stwu    r1,0xffc0(r1)00001adc        lwz     r3,0x0(r26)00001ae0        addi    r4,r26,0x4__SNIP__

在 __TEXT段裡,存在四個主要的section:

  • __text 編譯後的機器碼。
  • __const 通用常量資料。
  • __cstring 字面量字串常量。
  • __picsymbol_stub 動態連結器使用的位置無關碼stub路由。

這樣保持了可執行檔和不可執行檔代碼在段裡的明顯隔離。

運行應用程式既然知道了Mach-O檔案的格式,接下來看看OSX如何載入並運行應用程式的。運行應用程式時,shell首先調用FORK(2)系統調用。fork建立調用進程(shell)邏輯拷貝並準備好執行。子進程然後調用EXECVE(2)系統調用,當然需要提供要執行的程式路徑。

核心載入指定的檔案,檢查其頭部驗證是否是合法的Mach-O檔案。然後開始解釋載入命令,將子進程地址空間替換成檔案中的各段。同時,核心也執行有二進位檔案指定的動態連結器,著手載入、連結所有依賴庫。在綁定了運行所必備的各個符號後,調用entry-point函數。

在build應用程式時entry-point函數通常從/usr/lib/crt1.o靜態連結(標準函數)。此函數初始化核心環境,調用可執行檔的main()函數。

應用程式現在運行了。

動態連結器

OSX動態連結器/usr/lib/dyld,負責載入依賴的共用庫,匯入變數符號和函數,與當前進程的綁定。進程首次運行時,連結器所做的就是把共用庫匯入到進程地址空間。取決於程式的build方式,實際綁定也足執行不同的方式。

  • 載入後立即綁定 Load-Time 綁定。
  • 當符號引用時 Just-In-Time 綁定。

預綁定:如未指定綁定類型,使用just-in-time綁定。

應用程式僅僅當所有需要的符號和段從不同的目標檔案解決是才能繼續運行。為了尋找庫和架構,標準動態連結器/usr/bin/dyld,將搜尋預定義的目錄集合。要修改目錄,或提供復原路徑,可以設定DYLD_LIBRARY_PATH或DYLD_FALLBACK_LIBRARY_PATH環境變數。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.