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只是用一个全新的程序替换了当前进程的正文、数据、堆和栈段。