可执行文件的创建
- 预处理阶段
这一过程,主要针对#include和#define进行处理,具体过程如下:对于cpp文件中经常会出现#include来包含某个头文件,在进行预处理之后,所有的#include命令都将替换成该头文件中的详细内容,如果该头文件中还包含另外的头文件,采用同样的方法进行递归处理。同样#define亦是如此,对用它进行宏定义的字符进行替换,这里就是简单的替换。所以整个预处理过程就是进行简单的替换。
c
#include<stdio.h>
void main(void)
{
printf("hello world\n");
return 0;
}
对hello.c文件进行预处理:
gcc -E hello.c -o hello.i
使用vim hello.i
,部分代码如下
# 1 "hello.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
.....
typedef unsigned char __u_char;
typedef unsigned short int __u_short;
....
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# 943 "/usr/include/stdio.h" 3 4
# 2 "hello.c" 2
int main(void)
{
printf("hello world\n");
return 0;
}
- 编译阶段
编译器进行词法分析、语法分析,把源代码翻译为汇编语言
gcc -S hello.i -o hello.s
.file "hello.c"
.section .rodata
.LC0:
.string "hello world"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $.LC0, %edi
call puts
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4"
.section .note.GNU-stack,"",@progbits
- 汇编阶段
汇编阶段是把编译阶段生成的”.s”文件转成二进制目标代码.
gcc –c hello.s –o hello.o
使用vim hello.o
^@UH<89>å¿^@^@^@^@è^@^@^@^@¸^@^@^@^@]Ãhello world^@^@GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4^@^@^@^@^T^@^@^@^@^@^@^@^AzR^@^Ax^P^A^[^L^G^H<90>^A^@^@^\^@^@^@^\^@^@^@^@^@^@^@^U^@^@^@^@A^N^P<86>^BC^M^FP^L^G^H^@^@^@^@.symtab^@.strtab^@.shstrtab^@.rela.text^@.data^@.bss^@.rodata^@.comment^@.note.GNU-stack^@.rela.eh_frame^@
- 链接阶段
通过链接器将一个个目标文件(或许还会有库文件)链接在一起生成一个完整的可执行程序。链接程序的主要工作就是将有关的目标文件彼此相连接,也就是将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。
gcc hello.o -o hello
文件的格式
- 一个可重定位(relocatable)文件保存着代码和适当的数据,用来和其他的
object文件一起来创建一个可执行文件或者是一个共享文件。 - 一个可执行(executable)文件保存着一个用来执行的程序;该文件指出了
exec(BA_OS)如何来创建程序进程映象。 - 一个共享object文件保存着代码和合适的数据,用来被下面的两个链接器
链接。第一个是连接编辑器[请参看ld(SD_CMD)],可以和其他的可重定位和
共享object文件来创建其他的object。第二个是动态链接器,联合一个
可执行文件和其他的共享object文件来创建一个进程映象。
execve系统调用
do_execve:
1549int do_execve(struct filename *filename,
1550 const char __user *const __user *__argv,
1551 const char __user *const __user *__envp)
1552{
1553 struct user_arg_ptr argv = { .ptr.native = __argv };
1554 struct user_arg_ptr envp = { .ptr.native = __envp };
1555 return do_execve_common(filename, argv, envp);
1556}
do_execve_common
1430 static int do_execve_common(struct filename *filename,
1431 struct user_arg_ptr argv,
1432 struct user_arg_ptr envp)
1433{
1434 struct linux_binprm *bprm;
1435 struct file *file;
....
1458 retval = unshare_files(&displaced);
....
1467 retval = prepare_bprm_creds(bprm);
....
1474 file = do_open_exec(filename);
....
1484 retval = bprm_mm_init(bprm);
....
1504 bprm->exec = bprm->p;
1505 retval = copy_strings(bprm->envc, envp, bprm);
1506 if (retval < 0)
1507 goto out;
1508
1509 retval = copy_strings(bprm->argc, argv, bprm);
1510 if (retval < 0)
1511 goto out;
1512
1513 retval = exec_binprm(bprm);
....
1547}
exec_binprm:
1405static int exec_binprm(struct linux_binprm *bprm)
1406{
1407 pid_t old_pid, old_vpid;
1408 int ret;
1409
1410 /* Need to fetch pid before load_binary changes it */
1411 old_pid = current->pid;
1412 rcu_read_lock();
1413 old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent));
1414 rcu_read_unlock();
1415
1416 ret = search_binary_handler(bprm);
1417 if (ret >= 0) {
1418 audit_bprm(bprm);
1419 trace_sched_process_exec(current, old_pid, bprm);
1420 ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
1421 proc_exec_connector(current);
1422 }
1423
1424 return ret;
1425}
search_binary_headler:
1352int search_binary_handler(struct linux_binprm *bprm)
1353{
1355 struct linux_binfmt *fmt;
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);
1376 put_binfmt(fmt);
1377 bprm->recursion_depth--;
1378 if (retval < 0 && !bprm->mm) {
1379 /* we got to flush_old_exec() and failed after it */
1380 read_unlock(&binfmt_lock);
1381 force_sigsegv(SIGSEGV, current);
1382 return retval;
1383 }
1384 if (retval != -ENOEXEC || !bprm->file) {
1385 read_unlock(&binfmt_lock);
1386 return retval;
1387 }
1388 }
load_elf_binary:
571 static int load_elf_binary(struct linux_binprm *bprm)
572{
.....
975 start_thread(regs, elf_entry, bprm->p);
992}