shiyanlou:~/ $ cd Code [9:27:05] shiyanlou:Code/ $ vi hello.c [9:27:14] shiyanlou:Code/ $ gcc -E -o hello.cpp hello.c -m32 [9:34:55] shiyanlou:Code/ $ vi hello.cpp [9:35:04] shiyanlou:Code/ $ gcc -x cpp-output -S -o hello.s hello.cpp -m32 [9:35:21] shiyanlou:Code/ $ vi hello.s [9:35:28] shiyanlou:Code/ $ gcc -x assembler -c hello.s -o hello.o -m32 [9:35:58] shiyanlou:Code/ $ vi hello.o [9:38:44] shiyanlou:Code/ $ gcc -o hello hello.o -m32 [9:39:37] shiyanlou:Code/ $ vi hello [9:39:44] shiyanlou:Code/ $ gcc -o hello.static hello.o -m32 -static [9:40:21] shiyanlou:Code/ $ ls -l [9:41:13] -rwxrwxr-x 1 shiyanlou shiyanlou 7292 3\u6708 23 09:39 hello -rw-rw-r-- 1 shiyanlou shiyanlou 64 3\u6708 23 09:30 hello.c -rw-rw-r-- 1 shiyanlou shiyanlou 17302 3\u6708 23 09:35 hello.cpp -rw-rw-r-- 1 shiyanlou shiyanlou 1020 3\u6708 23 09:38 hello.o -rw-rw-r-- 1 shiyanlou shiyanlou 470 3\u6708 23 09:35 hello.s -rwxrwxr-x 1 shiyanlou shiyanlou 733254 3\u6708 23 09:41 hello.static
ELF文件格式http://www.muppetlabs.com/~breadbox/software/ELF.txtlinux
目標文件三種形式:shell
查看ELF文件的頭部bash
shiyanlou:Code/ $ readelf -h hello
shiyanlou:sharelib/ $ ldd main [21:25:56] linux-gate.so.1 => (0xf774e000) # 這個是vdso - virtual DSO:dynamically shared object,並不存在這個共享庫文件,它是內核的一部分,爲了解決libc與新版本內核的系統調用不一樣步的問題,linux-gate.so.1裏封裝的系統調用與內核支持的系統調用徹底匹配,由於它就是內核的一部分嘛。而libc裏封裝的系統調用與內核並不徹底一致,由於它們各自都在版本更新。 libshlibexample.so => /home/shiyanlou/LinuxKernel/sharelib/libshlibexample.so (0xf7749000) libdl.so.2 => /lib32/libdl.so.2 (0xf7734000) libc.so.6 => /lib32/libc.so.6 (0xf7588000) /lib/ld-linux.so.2 (0xf774f000) shiyanlou:sharelib/ $ ldd /lib32/libc.so.6 [21:37:00] /lib/ld-linux.so.2 (0xf779e000) linux-gate.so.1 => (0xf779d000) # readelf -d 也能夠看依賴的so文件 shiyanlou:sharelib/ $ readelf -d main [21:28:04] Dynamic section at offset 0xf04 contains 26 entries: 0x00000001 (NEEDED) 共享庫:[libshlibexample.so] 0x00000001 (NEEDED) 共享庫:[libdl.so.2] 0x00000001 (NEEDED) 共享庫:[libc.so.6] 0x0000000c (INIT) 0x80484f0 0x0000000d (FINI) 0x8048804 0x00000019 (INIT_ARRAY) 0x8049ef8
可執行程序的執行環境編輯器
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char * argv[]) { int pid; /* fork another process */ pid = fork(); if (pid<0) { /* error occurred */ fprintf(stderr,"Fork Failed!"); exit(-1); } else if (pid==0) { /* child process */ execlp("/bin/ls","ls",NULL); } else { /* parent process */ /* parent will wait for the child to complete*/ wait(NULL); printf("Child Complete!"); exit(0); } }
shlibexample.h (1.3 KB) - Interface of Shared Lib Example shlibexample.c (1.2 KB) - Implement of Shared Lib Example
$ gcc -shared shlibexample.c -o libshlibexample.so -m32 dllibexample.h (1.3 KB) - Interface of Dynamical Loading Lib Example dllibexample.c (1.3 KB) - Implement of Dynamical Loading Lib Example
$ gcc -shared dllibexample.c -o libdllibexample.so -m32
分別以共享庫和動態加載共享庫的方式使用libshlibexample.so文件和libdllibexample.so文件 函數
$ gcc main.c -o main -L/path/to/your/dir -lshlibexample -ldl -m32 $ export LD_LIBRARY_PATH=$PWD #將當前目錄加入默認路徑,不然main找不到依賴的庫文件,固然也能夠將庫文件copy到默認路徑下。 $ ./main This is a Main program! Calling SharedLibApi() function of libshlibexample.so! This is a shared libary! Calling DynamicalLoadingLibApi() function of libdllibexample.so! This is a Dynamical Loading libary!
1 int execve(const char * filename,char * const argv[ ],char * const envp[ ]);
sys_execve內部會解析可執行文件格式spa
doexecve -> doexecvecommon -> execbinprm命令行
searchbinaryhandler符合尋找文件格式對應的解析模塊,以下:3d
1369 list_for_each_entry(fmt, &formats, lh) { 1370 if (!try_module_get(fmt->module)) 1371 continue; 1372 read_unlock(&binfmt_lock); 1373 bprm->recursion_depth++; 1374 retval = fmt->load_binary(bprm); 1375 read_lock(&binfmt_lock);
對於ELF格式的可執行文件fmt->loadbinary(bprm);執行的應該是loadelf_binary其內部是和ELF文件格式解析的部分須要和ELF文件格式標準結合起來閱讀調試
Linux內核是如何支持多種不一樣的可執行文件格式的?code
82 static struct linux_binfmt elf_format = { 83 .module = THIS_MODULE, 84 .load_binary = load_elf_binary, 85 .load_shlib = load_elf_library, 86 .core_dump = elf_core_dump, 87 .min_coredump = ELF_EXEC_PAGESIZE, 88 };
2198 static int __init init_elf_binfmt(void) 2199 { 2200 register_binfmt(&elf_format); 2201 return 0; 2202 }
裝載和啓動一個可執行程序依次調用如下函數:
sysexecve() -> doexecve() -> doexecvecommon() -> execbinprm() -> searchbinaryhandler() -> loadelfbinary() -> startthread()
elfformat 和 initelf_binfmt,這裏是否是就是觀察者模式中的觀察者?
可執行文件開始執行的起點在哪裏?如何才能讓execve系統調用返回到用戶態時執行新程序?
當linux內核或程序(例如shell)用fork函數建立子進程後,子進程每每要調用一種exec函數以執行另外一個程序。當進程調用一種exec函數時,該進程執行的程序徹底替換爲新程序,而新程序則從其main函數開始執行。由於調用exec並不建立新進程,因此先後的進程ID並未改變。exec只是用一個全新的程序替換了當前進程的正文、數據、堆和棧段。