我们先了解一下execve:
int execve(const char * filename,char * const argv[ ],char * const envp[ ]);
函数说明:
execve()用来执行参数filename字符串所代表的文件路径,第二个参数系利用数组指针来传递给执行文件,最后一个参数则为传递给执行文件的新环境变量数组。
返回值:
如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno 中。
这个代码会执行/bin/sh 使用参数-c 和 env 。 在当前的环境PATH中,运行env环境的程序,该环境变量包含了5个values。执行后的 结果为:
[test@localhost execve-test]$ ./execve-test USER=test PATH=/bin:/usr/bin PWD=/home/test/execve-test TZ=UTC0 SHLVL=1 HOME=/ LOGNAME=qfong _=/bin/env
该段也包含了”_”,”SHLVL”,”PWD”环境变量。
我们调试一下该程序运行的信息
[root@localhost execve]# cat test.c #include <unistd.h> #include <stdio.h> int main() { char *envp[] = { "PATH=/bin:/usr/bin", "/usr/bin/id=() { " "echo pwn me twice,shame on me; }; " "echo pwn me once,shame on you", NULL }; char *argv[] = {"/bin/bash",NULL}; execve(argv[0],argv,envp); perror("execve"); return 1 }
测试:
[root@localhost execve]# ./a.out pwn me once,shame on you bash: [: pwn me twice,shame on me: integer expression expected [root@localhost execve]# /usr/bin/id pwn me twice,shame on me
使用gdb进行分析一下
[test@localhost execve-test]$ gcc -static -o execve-test-static execve-test.c -O2 /bin/ld: cannot find -lc collect2: error: ld returned 1 exit status (gdb) disassemble main Dump of assembler code for function main: 0x0000000000400a60 <+0>: sub $0x58,%rsp 0x0000000000400a64 <+4>: mov $0x48e9b0,%edi 0x0000000000400a69 <+9>: lea 0x20(%rsp),%rdx 0x0000000000400a6e <+14>: mov %rsp,%rsi 0x0000000000400a71 <+17>: movq $0x48e9b0,(%rsp) 0x0000000000400a79 <+25>: movq $0x48e9b8,0x8(%rsp) 0x0000000000400a82 <+34>: movq $0x48e9bb,0x10(%rsp) 0x0000000000400a8b <+43>: movq $0x0,0x18(%rsp) 0x0000000000400a94 <+52>: movq $0x48e9bf,0x20(%rsp) 0x0000000000400a9d <+61>: movq $0x48e9c6,0x28(%rsp) 0x0000000000400aa6 <+70>: movq $0x48e9d9,0x30(%rsp) 0x0000000000400aaf <+79>: movq $0x48e9e1,0x38(%rsp) 0x0000000000400ab8 <+88>: movq $0x48e9eb,0x40(%rsp) 0x0000000000400ac1 <+97>: movq $0x0,0x48(%rsp) 0x0000000000400aca <+106>: callq 0x40ee50 <execve> 0x0000000000400acf <+111>: mov 0x2b75ca(%rip),%rcx # 0x6b80a0 <stderr> 0x0000000000400ad6 <+118>: mov $0x6,%edx 0x0000000000400adb <+123>: mov $0x1,%esi 0x0000000000400ae0 <+128>: mov $0x48e9f9,%edi 0x0000000000400ae5 <+133>: callq 0x401820 <fwrite> 0x0000000000400aea <+138>: mov $0xffffffff,%eax 0x0000000000400aef <+143>: add $0x58,%rsp 0x0000000000400af3 <+147>: retq End of assembler dump. (gdb) (gdb) printf "%s\n", 0x48e9b0 /bin/sh (gdb) printf "%s\n", 0x48e9b8 -c (gdb) printf "%s\n", 0x48e9bb env (gdb) printf "%s\n", 0x48e9bf HOME=/ (gdb) printf "%s\n", 0x48e9c6 PATH=/bin:/usr/bin (gdb) printf "%s\n", 0x48e9d9 TZ=UTC0 (gdb) printf "%s\n", 0x48e9e1 USER=test (gdb) printf "%s\n", 0x48e9eb LOGNAME=qfong (gdb) disassemble 0x40ee50 Dump of assembler code for function execve: 0x000000000040ee50 <+0>: mov $0x3b,%eax //0x3b中断调用号 0x000000000040ee55 <+5>: syscall 0x000000000040ee57 <+7>: cmp $0xfffffffffffff000,%rax 0x000000000040ee5d <+13>: ja 0x40ee61 <execve+17> 0x000000000040ee5f <+15>: repz retq 0x000000000040ee61 <+17>: mov $0xffffffffffffffc0,%rdx 0x000000000040ee68 <+24>: neg %eax 0x000000000040ee6a <+26>: mov %eax,%fs:(%rdx) 0x000000000040ee6d <+29>: or $0xffffffffffffffff,%rax 0x000000000040ee71 <+33>: retq End of assembler dump.
0x48 0 0x40 LOGNAME=qfong 0x38 USER=test 0x30 TZ=UTC0 0x28 PATH=/bin:/usr/bin 0x20 HOME=/ 0x18 0 0x10 env 0x8 -c %rsp 0x4006e0 /bin/sh
Execve()需要各种参数
将数据存放在堆栈中,表示要执行的命令/bin/sh在0x0.依次将参数存入堆栈中。第一个参数必须是该程序的名字mov $0x48e9b0,%edi。Edi为第一参数寄存器。Rsi指向第二寄存器。
在execve执行程序时,我们需要/bin/sh的字符串,一个指针指向这个字符串,和一个指向参数的指针。我们可以看到execve()调用之前的数据表示,一个指向/bin/sh字符串后跟一个指向参数的指针数组(堆栈所在的rsi寄存器),其他的ecx和edx分别为第三第四参数寄存器。
Execve(name[0],name, envp))
Execve 是怎么执行的呢?我们看一下内核里面的代码实现
sys_execve() → do_execve() → do_execve_common() versus compat_sys_execve() → compat_do_execve() → do_execve_common() 这个过程。
可以分析内核的一段代码:
Fs/exec.c 1674 SYSCALL_DEFINE3(execve, 1675 ▸ ▸ const char __user *, filename, 1676 ▸ ▸ const char __user *const __user *, argv, 1677 ▸ ▸ const char __user *const __user *, envp) 1678 { 1679 ▸ struct filename *path = getname(filename); //将一个可执行文件的名称装入到一个新分配的页面中。 1680 ▸ int error = PTR_ERR(path); 1681 ▸ if (!IS_ERR(path)) { 1682 ▸ ▸ error = do_execve(path->name, argv, envp); //执行可执行文件 1683 ▸ ▸ putname(path); 1684 ▸ } 1685 ▸ return error; 1686 } 在这里1674-1677这段代码的SYSCALL_DEFINE3(execve,filename,argv.envp)等同于: = SYSCALL_DEFINEx(3,## execve,__VA_ARGS) = asmlinkage long sys_execve(SC_DECL,(_VA_ARGS__))… = asmlinkage long sys_execve(filename,argv.envp) 参照include/linux/syscalls.h 176 #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__) 186 #define __SYSCALL_DEFINEx(x, name, ...)▸▸ ▸ ▸ ▸ \ 187 ▸ asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));▸ \ 188 ▸ static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__));▸ \ 189 ▸ asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__))▸ \ 190 ▸ {▸ ▸ ▸ ▸ ▸ ▸ ▸ ▸ \ 191 ▸ ▸ long ret = SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__));▸ \ 192 ▸ ▸ __MAP(x,__SC_TEST,__VA_ARGS__);▸▸ ▸ ▸ \ 193 ▸ ▸ __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));▸ \ 194 ▸ ▸ return ret;▸ ▸ ▸ ▸ ▸ ▸ \ 195 ▸ }▸ ▸ ▸ ▸ ▸ ▸ ▸ ▸ \ 196 ▸ SYSCALL_ALIAS(sys##name, SyS##name);▸ ▸ ▸ ▸ \ 197 ▸ static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)) 198 => 99 #define __SC_DECL(t, a)▸t a do_execve code: 1580 int do_execve(const char *filename, 1581 ▸ const char __user *const __user *__argv, 1582 ▸ const char __user *const __user *__envp) 1583 { 1584 ▸ struct user_arg_ptr argv = { .ptr.native = __argv }; //封装结构 1585 ▸ struct user_arg_ptr envp = { .ptr.native = __envp }; 1586 ▸ return do_execve_common(filename, argv, envp); //调用do_execve_common 1587 } 1588 1450 /* 1451 * sys_execve() executes a new program. 1452 */ 1453 static int do_execve_common(const char *filename, 1454 ▸ ▸ ▸ ▸ struct user_arg_ptr argv, 1455 ▸ ▸ ▸ ▸ struct user_arg_ptr envp) 1456 { 1457 ▸ struct linux_binprm *bprm; //保存和要执行的文件相关数据 1458 ▸ struct file *file; 1459 ▸ struct files_struct *displaced; 1460 ▸ bool clear_in_exec; 1461 ▸ int retval; 1462 ▸ const struct cred *cred = current_cred(); 1463 1464 ▸ /* 1465 ▸ * We move the actual failure in case of RLIMIT_NPROC excess from 1466 ▸ * set*uid() to execve() because too many poorly written programs 1467 ▸ * don't check setuid() return code. Here we additionally recheck 1468 ▸ * whether NPROC limit is still exceeded. 1469 ▸ */ 1470 ▸ if ((current->flags & PF_NPROC_EXCEEDED) && 1471 ▸ atomic_read(&cred->user->processes) > rlimit(RLIMIT_NPROC)) { 1472 ▸ ▸ retval = -EAGAIN; 1473 ▸ ▸ goto out_ret; 1474 ▸ } 1475 1476 ▸ /* We're below the limit (still or again), so we don't want to make 1477 ▸ * further execve() calls fail. */ 1478 ▸ current->flags &= ~PF_NPROC_EXCEEDED; 1479 1480 ▸ retval = unshare_files(&displaced); 1481 ▸ if (retval) 1482 ▸ ▸ goto out_ret; 1483 1484 ▸ retval = -ENOMEM; 1485 ▸ bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); //在堆上分配一个linux_binprm结构 1486 ▸ if (!bprm) 1487 ▸ ▸ goto out_files; 1488 1489 ▸ retval = prepare_bprm_creds(bprm); 1490 ▸ if (retval) 1491 ▸ ▸ goto out_free; 1492 1493 ▸ retval = check_unsafe_exec(bprm); 1494 ▸ if (retval < 0) 1495 ▸ ▸ goto out_free; 1496 ▸ clear_in_exec = retval; 1497 ▸ current->in_execve = 1; 1498 1499 ▸ file = open_exec(filename); //调用open_exec 读取可执行文件 1500 ▸ retval = PTR_ERR(file); 1501 ▸ if (IS_ERR(file)) 1502 ▸ ▸ goto out_unmark; 1503 1504 ▸ sched_exec(); //选择确定最小负载的cpu执行,并把当前process转移过去 1506 ▸ bprm->file = file; 1507 ▸ bprm->filename = filename; 1508 ▸ bprm->interp = filename; 1509 1510 ▸ retval = bprm_mm_init(bprm); //调用bprm_mm_init 为新程序初始化内存 1511 ▸ if (retval) 1512 ▸ ▸ goto out_file; 1513 1514 ▸ bprm->argc = count(argv, MAX_ARG_STRINGS); 1515 ▸ if ((retval = bprm->argc) < 0) 1516 ▸ ▸ goto out; 1517 1518 ▸ bprm->envc = count(envp, MAX_ARG_STRINGS); 1519 ▸ if ((retval = bprm->envc) < 0) 1520 ▸ ▸ goto out; 1521 1522 ▸ retval = prepare_binprm(bprm); 填充bprm结构 1523 ▸ if (retval < 0) 1524 ▸ ▸ goto out; 1525 1526 ▸ retval = copy_strings_kernel(1, &bprm->filename, bprm); //拷贝filename到新进程中 1527 ▸ if (retval < 0) 1528 ▸ ▸ goto out; 1529 1530 ▸ bprm->exec = bprm->p; 1531 ▸ retval = copy_strings(bprm->envc, envp, bprm); //拷贝环境变量envp到新进程中 1532 ▸ if (retval < 0) 1533 ▸ ▸ goto out; 1534 1535 ▸ retval = copy_strings(bprm->argc, argv, bprm); //拷贝参数argv到新进程中 1536 ▸ if (retval < 0) 1537 ▸ ▸ goto out; 1538 1539 ▸ retval = search_binary_handler(bprm); //该函数式对formats链表进行扫描,并尝试每个load_binary函数,如果成功加载了文件的执行格式,对formats的扫描终止 1540 ▸ if (retval < 0) 1541 ▸ ▸ goto out; 1542 1543 ▸ /* execve succeeded */ 1544 ▸ current->fs->in_exec = 0; 1545 ▸ current->in_execve = 0; 1546 ▸ acct_update_integrals(current); 1547 ▸ task_numa_free(current); 1548 ▸ free_bprm(bprm); //释放bprm 结构 1549 ▸ if (displaced) 1550 ▸ ▸ put_files_struct(displaced); 1551 ▸ return retval; 1552 1553 out: 1554 ▸ if (bprm->mm) { 1555 ▸ ▸ acct_arg_size(bprm, 0); 1556 ▸ ▸ mmput(bprm->mm); 1557 ▸ } 1558 1559 out_file: 1560 ▸ if (bprm->file) { 1561 ▸ ▸ allow_write_access(bprm->file); 1562 ▸ ▸ fput(bprm->file); 1563 ▸ } 1564 1565 out_unmark: 1566 ▸ if (clear_in_exec) 1567 ▸ ▸ current->fs->in_exec = 0; 1568 ▸ current->in_execve = 0; 1569 1570 out_free: 1571 ▸ free_bprm(bprm); 1572 1573 out_files: 1574 ▸ if (displaced) 1575 ▸ ▸ reset_files_struct(displaced); 1576 out_ret: 1577 ▸ return retval; 1578 } 1579 load_elf_binary函数,该函数位于fs/binfmt_elf.c中 569 static int load_elf_binary(struct linux_binprm *bprm) 570 { 571 ▸ struct file *interpreter = NULL; /* to shut gcc up */ 572 ▸ unsigned long load_addr = 0, load_bias = 0; 573 ▸ int load_addr_set = 0; 574 ▸ char * elf_interpreter = NULL; 575 ▸ unsigned long error; 576 ▸ struct elf_phdr *elf_ppnt, *elf_phdata; 577 ▸ unsigned long elf_bss, elf_brk; 578 ▸ int retval, i; 579 ▸ unsigned int size; 580 ▸ unsigned long elf_entry; 581 ▸ unsigned long interp_load_addr = 0; 582 ▸ unsigned long start_code, end_code, start_data, end_data; 583 ▸ unsigned long reloc_func_desc __maybe_unused = 0; 584 ▸ int executable_stack = EXSTACK_DEFAULT; 585 ▸ unsigned long def_flags = 0; 586 ▸ struct pt_regs *regs = current_pt_regs(); 587 ▸ struct { 588 ▸ ▸ struct elfhdr elf_ex; 589 ▸ ▸ struct elfhdr interp_elf_ex; 590 ▸ } *loc; 591 592 ▸ loc = kmalloc(sizeof(*loc), GFP_KERNEL); 593 ▸ if (!loc) { 594 ▸ ▸ retval = -ENOMEM; 595 ▸ ▸ goto out_ret; 596 ▸ } 597 ▸ 598 ▸ /* Get the exec-header */ //读取可执行文件的首部,首部描述程序的段和所需 的共享库 599 ▸ loc->elf_ex = *((struct elfhdr *)bprm->buf); 600 600 601 ▸ retval = -ENOEXEC; 602 ▸ /* First of all, some simple consistency checks */ //一致性检测 603 ▸ if (memcmp(loc->elf_ex.e_ident, ELFMAG, SELFMAG) != 0) 604 ▸ ▸ goto out; 605 606 ▸ if (loc->elf_ex.e_type != ET_EXEC && loc->elf_ex.e_type != ET_DYN) 607 ▸ ▸ goto out; 608 ▸ if (!elf_check_arch(&loc->elf_ex)) 609 ▸ ▸ goto out; 610 ▸ if (!bprm->file->f_op || !bprm->file->f_op->mmap) 611 ▸ ▸ goto out; 612 613 ▸ /* Now read in all of the header information */ //读取所有的首部信息 614 ▸ if (loc->elf_ex.e_phentsize != sizeof(struct elf_phdr)) 615 ▸ ▸ goto out; 616 ▸ if (loc->elf_ex.e_phnum < 1 || 617 ▸ ▸ loc->elf_ex.e_phnum > 65536U / sizeof(struct elf_phdr)) 618 ▸ ▸ goto out; 619 ▸ size = loc->elf_ex.e_phnum * sizeof(struct elf_phdr); 620 ▸ retval = -ENOMEM; 621 ▸ elf_phdata = kmalloc(size, GFP_KERNEL); 622 ▸ if (!elf_phdata) 623 ▸ ▸ goto out; 624 625 ▸ retval = kernel_read(bprm->file, loc->elf_ex.e_phoff, 626 ▸ ▸ ▸ (char *)elf_phdata, size); 627 ▸ if (retval != size) { 628 ▸ ▸ if (retval >= 0) 629 ▸ ▸ ▸ retval = -EIO; 630 ▸ ▸ goto out_free_ph; 631 ▸ } 632 633 ▸ elf_ppnt = elf_phdata; 634 ▸ elf_bss = 0; 635 ▸ elf_brk = 0; 636 637 ▸ start_code = ~0UL; 638 ▸ end_code = 0; 639 ▸ start_data = 0; 640 ▸ end_data = 0; 641 //从可执行文件中确定动态链接程序的路径名。并用它来确定共享库的位置,并把他们映射到内存。 642 ▸ for (i = 0; i < loc->elf_ex.e_phnum; i++) { 643 ▸ ▸ if (elf_ppnt->p_type == PT_INTERP) { 644 ▸ ▸ ▸ /* This is the program interpreter used for 645 ▸ ▸ ▸ * shared libraries - for now assume that this 646 ▸ ▸ ▸ * is an a.out format binary 647 ▸ ▸ ▸ */ 648 ▸ ▸ ▸ retval = -ENOEXEC; 649 ▸ ▸ ▸ if (elf_ppnt->p_filesz > PATH_MAX ||▫ 650 ▸ ▸ ▸ elf_ppnt->p_filesz < 2) 651 ▸ ▸ ▸ ▸ goto out_free_ph; 652 653 ▸ ▸ ▸ retval = -ENOMEM; 654 ▸ ▸ ▸ elf_interpreter = kmalloc(elf_ppnt->p_filesz, 655 ▸ ▸ ▸ ▸ ▸ ▸ GFP_KERNEL); 656 ▸ ▸ ▸ if (!elf_interpreter) 657 ▸ ▸ ▸ ▸ goto out_free_ph; 658 659 ▸ ▸ ▸ retval = kernel_read(bprm->file, elf_ppnt->p_offset, 660 ▸ ▸ ▸ ▸ ▸ elf_interpreter, 661 ▸ ▸ ▸ ▸ ▸ elf_ppnt->p_filesz); 662 ▸ ▸ ▸ if (retval != elf_ppnt->p_filesz) { 663 ▸ ▸ ▸ ▸ if (retval >= 0) 664 ▸ ▸ ▸ ▸ ▸ retval = -EIO; 665 ▸ ▸ ▸ ▸ goto out_free_interp; 666 ▸ ▸ ▸ } 667 ▸ ▸ ▸ /* make sure path is NULL terminated */ 668 ▸ ▸ ▸ retval = -ENOEXEC; 669 ▸ ▸ ▸ if (elf_interpreter[elf_ppnt->p_filesz - 1] != '\0') 670 ▸ ▸ ▸ ▸ goto out_free_interp; 671 672 ▸ ▸ ▸ interpreter = open_exec(elf_interpreter); 673 ▸ ▸ ▸ retval = PTR_ERR(interpreter); 674 ▸ ▸ ▸ if (IS_ERR(interpreter)) 675 ▸ ▸ ▸ ▸ goto out_free_interp; 676 677 ▸ ▸ ▸ /* 678 ▸ ▸ ▸ * If the binary is not readable then enforce 679 ▸ ▸ ▸ * mm->dumpable = 0 regardless of the interpreter's 680 ▸ ▸ ▸ * permissions. 681 ▸ ▸ ▸ */ 682 ▸ ▸ ▸ would_dump(bprm, interpreter); 683 684 ▸ ▸ ▸ retval = kernel_read(interpreter, 0, bprm->buf, 685 ▸ ▸ ▸ ▸ ▸ BINPRM_BUF_SIZE); 686 ▸ ▸ ▸ if (retval != BINPRM_BUF_SIZE) { 687 ▸ ▸ ▸ ▸ if (retval >= 0) 688 ▸ ▸ ▸ ▸ ▸ retval = -EIO; 689 ▸ ▸ ▸ ▸ goto out_free_dentry; 690 ▸ ▸ ▸ } 691 692 ▸ ▸ ▸ /* Get the exec headers */ 693 ▸ ▸ ▸ loc->interp_elf_ex = *((struct elfhdr *)bprm->buf); 694 ▸ ▸ ▸ break; 695 ▸ ▸ } 696 ▸ ▸ elf_ppnt++; 697 ▸ } 698 699 ▸ elf_ppnt = elf_phdata; 700 ▸ for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++) 701 ▸ ▸ if (elf_ppnt->p_type == PT_GNU_STACK) { 702 ▸ ▸ ▸ if (elf_ppnt->p_flags & PF_X) 703 ▸ ▸ ▸ ▸ executable_stack = EXSTACK_ENABLE_X; 704 ▸ ▸ ▸ else 705 ▸ ▸ ▸ ▸ executable_stack = EXSTACK_DISABLE_X; 706 ▸ ▸ ▸ break; 707 ▸ ▸ } 708 709 ▸ /* Some simple consistency checks for the interpreter */ //对动态链接库执行一致性检测 710 ▸ if (elf_interpreter) { 711 ▸ ▸ retval = -ELIBBAD; 712 ▸ ▸ /* Not an ELF interpreter */ 713 ▸ ▸ if (memcmp(loc->interp_elf_ex.e_ident, ELFMAG, SELFMAG) != 0) 714 ▸ ▸ ▸ goto out_free_dentry; 715 ▸ ▸ /* Verify the interpreter has a valid arch */ 716 ▸ ▸ if (!elf_check_arch(&loc->interp_elf_ex)) 717 ▸ ▸ ▸ goto out_free_dentry; 718 ▸ } 719 720 ▸ /* Flush all traces of the currently running executable */ //释放当前运行的可执行文件占用的所有资源 721 ▸ retval = flush_old_exec(bprm); 722 ▸ if (retval) 723 ▸ ▸ goto out_free_dentry; 724 725 ▸ /* OK, This is the point of no return */ 726 ▸ current->mm->def_flags = def_flags; 727 728 ▸ /* Do this immediately, since STACK_TOP as used in setup_arg_pages 729 ▸ may depend on the personality. */ 730 ▸ SET_PERSONALITY(loc->elf_ex); 731 ▸ if (elf_read_implies_exec(loc->elf_ex, executable_stack)) 732 ▸ ▸ current->personality |= READ_IMPLIES_EXEC; 733 734 ▸ if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space) 735 ▸ ▸ current->flags |= PF_RANDOMIZE; 736 737 ▸ setup_new_exec(bprm); 738 739 ▸ /* Do this so that we can load the interpreter, if need be. We will 740 ▸ change some of these later */ 741 ▸ current->mm->free_area_cache = current->mm->mmap_base; 742 ▸ current->mm->cached_hole_size = 0; //为进程的用户态堆栈分配一个新的线性区描述符,并把那个线性区插入到进程的地址空间。 743 ▸ retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP), 744 ▸ ▸ ▸ ▸ executable_stack); 745 ▸ if (retval < 0) { 746 ▸ ▸ send_sig(SIGKILL, current, 0); 747 ▸ ▸ goto out_free_dentry; 748 ▸ } 749 ▸ 750 ▸ current->mm->start_stack = bprm->p; 751 752 ▸ /* Now we do a little grungy work by mmapping the ELF image into 753 ▸ the correct location in memory. */ //将ELF镜像文件映射到内存中正确的位置 754 ▸ for(i = 0, elf_ppnt = elf_phdata; 755 ▸ i < loc->elf_ex.e_phnum; i++, elf_ppnt++) { 756 ▸ ▸ int elf_prot = 0, elf_flags; 757 ▸ ▸ unsigned long k, vaddr; 758 759 ▸ ▸ if (elf_ppnt->p_type != PT_LOAD) 760 ▸ ▸ ▸ continue; 761 762 ▸ ▸ if (unlikely (elf_brk > elf_bss)) { 763 ▸ ▸ ▸ unsigned long nbyte; 764 ▸ ▫▫▫▫▫▫▫▫▫▫▫▫ 765 ▸ ▸ ▸ /* There was a PT_LOAD segment with p_memsz > p_filesz 766 ▸ ▸ ▸ before this one. Map anonymous pages, if needed, 767 ▸ ▸ ▸ and clear the area. */ 768 ▸ ▸ ▸ retval = set_brk(elf_bss + load_bias, 769 ▸ ▸ ▸ ▸ ▸ elf_brk + load_bias); 770 ▸ ▸ ▸ if (retval) { 771 ▸ ▸ ▸ ▸ send_sig(SIGKILL, current, 0); 772 ▸ ▸ ▸ ▸ goto out_free_dentry; 773 ▸ ▸ ▸ } 774 ▸ ▸ ▸ nbyte = ELF_PAGEOFFSET(elf_bss); 775 ▸ ▸ ▸ if (nbyte) { 776 ▸ ▸ ▸ ▸ nbyte = ELF_MIN_ALIGN - nbyte; 777 ▸ ▸ ▸ ▸ if (nbyte > elf_brk - elf_bss) 778 ▸ ▸ ▸ ▸ ▸ nbyte = elf_brk - elf_bss; 779 ▸ ▸ ▸ ▸ if (clear_user((void __user *)elf_bss + 780 ▸ ▸ ▸ ▸ ▸ ▸ ▸ load_bias, nbyte)) { 781 ▸ ▸ ▸ ▸ ▸ /* 782 ▸ ▸ ▸ ▸ ▸ * This bss-zeroing can fail if the ELF 783 ▸ ▸ ▸ ▸ ▸ * file specifies odd protections. So 784 ▸ ▸ ▸ ▸ ▸ * we don't check the return value 785 ▸ ▸ ▸ ▸ ▸ */ 786 ▸ ▸ ▸ ▸ } 787 ▸ ▸ ▸ } 788 ▸ ▸ } 789 790 ▸ ▸ if (elf_ppnt->p_flags & PF_R) 791 ▸ ▸ ▸ elf_prot |= PROT_READ; 792 ▸ ▸ if (elf_ppnt->p_flags & PF_W) 793 ▸ ▸ ▸ elf_prot |= PROT_WRITE; 794 ▸ ▸ if (elf_ppnt->p_flags & PF_X) 795 ▸ ▸ ▸ elf_prot |= PROT_EXEC; 796 797 ▸ ▸ elf_flags = MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE; 798 799 ▸ ▸ vaddr = elf_ppnt->p_vaddr; 800 ▸ ▸ if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) { 801 ▸ ▸ ▸ elf_flags |= MAP_FIXED; 802 ▸ ▸ } else if (loc->elf_ex.e_type == ET_DYN) { 803 ▸ ▸ ▸ /* Try and get dynamic programs out of the way of the 804 ▸ ▸ ▸ * default mmap base, as well as whatever program they 805 ▸ ▸ ▸ * might try to exec. This is because the brk will 806 ▸ ▸ ▸ * follow the loader, and is not movable. */ 807 #ifdef CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE 808 ▸ ▸ ▸ /* Memory randomization might have been switched off 809 ▸ ▸ ▸ * in runtime via sysctl or explicit setting of 810 ▸ ▸ ▸ * personality flags. 811 ▸ ▸ ▸ * If that is the case, retain the original non-zero 812 ▸ ▸ ▸ * load_bias value in order to establish proper 813 ▸ ▸ ▸ * non-randomized mappings. 814 ▸ ▸ ▸ */ 815 ▸ ▸ ▸ if (current->flags & PF_RANDOMIZE) 816 ▸ ▸ ▸ ▸ load_bias = 0; 817 ▸ ▸ ▸ else 818 ▸ ▸ ▸ ▸ load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr); 819 #else 820 ▸ ▸ ▸ load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr); 821 #endif 822 ▸ ▸ } 823 824 ▸ ▸ error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, 825 ▸ ▸ ▸ ▸ elf_prot, elf_flags, 0); 826 ▸ ▸ if (BAD_ADDR(error)) { 827 ▸ ▸ ▸ send_sig(SIGKILL, current, 0); 828 ▸ ▸ ▸ retval = IS_ERR((void *)error) ? 829 ▸ ▸ ▸ ▸ PTR_ERR((void*)error) : -EINVAL; 830 ▸ ▸ ▸ goto out_free_dentry; 831 ▸ ▸ } 832 833 ▸ ▸ if (!load_addr_set) { 834 ▸ ▸ ▸ load_addr_set = 1; 835 ▸ ▸ ▸ load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset); 836 ▸ ▸ ▸ if (loc->elf_ex.e_type == ET_DYN) { 837 ▸ ▸ ▸ ▸ load_bias += error - 838 ▸ ▸ ▸ ▸ ELF_PAGESTART(load_bias + vaddr); 839 ▸ ▸ ▸ ▸ load_addr += load_bias; 840 ▸ ▸ ▸ ▸ reloc_func_desc = load_bias; 841 ▸ ▸ ▸ } 842 ▸ ▸ } 843 ▸ ▸ k = elf_ppnt->p_vaddr; 844 ▸ ▸ if (k < start_code) 845 ▸ ▸ ▸ start_code = k; 846 ▸ ▸ if (start_data < k) 847 ▸ ▸ ▸ start_data = k; 848 849 ▸ ▸ /* 850 ▸ ▸ * Check to see if the section's size will overflow the 851 ▸ ▸ * allowed task size. Note that p_filesz must always be 852 ▸ ▸ * <= p_memsz so it is only necessary to check p_memsz. 853 ▸ ▸ */ 854 ▸ ▸ if (BAD_ADDR(k) || elf_ppnt->p_filesz > elf_ppnt->p_memsz || 855 ▸ ▸ elf_ppnt->p_memsz > TASK_SIZE || 856 ▸ ▸ TASK_SIZE - elf_ppnt->p_memsz < k) { 857 ▸ ▸ ▸ /* set_brk can never work. Avoid overflows. */ 858 ▸ ▸ ▸ send_sig(SIGKILL, current, 0); 859 ▸ ▸ ▸ retval = -EINVAL; 860 ▸ ▸ ▸ goto out_free_dentry; 861 ▸ ▸ } 862 863 ▸ ▸ k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz; 864 865 ▸ ▸ if (k > elf_bss) 866 ▸ ▸ ▸ elf_bss = k; 867 ▸ ▸ if ((elf_ppnt->p_flags & PF_X) && end_code < k) 868 ▸ ▸ ▸ end_code = k; 869 ▸ ▸ if (end_data < k) 870 ▸ ▸ ▸ end_data = k; 871 ▸ ▸ k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz; 872 ▸ ▸ if (k > elf_brk) 873 ▸ ▸ ▸ elf_brk = k; 874 ▸ } 875 876 ▸ loc->elf_ex.e_entry += load_bias; 877 ▸ elf_bss += load_bias; 878 ▸ elf_brk += load_bias; 879 ▸ start_code += load_bias; 880 ▸ end_code += load_bias; 881 ▸ start_data += load_bias; 882 ▸ end_data += load_bias; 883 884 ▸ /* Calling set_brk effectively mmaps the pages that we need 885 ▸ * for the bss and break sections. We must do this before 886 ▸ * mapping in the interpreter, to make sure it doesn't wind 887 ▸ * up getting placed where the bss needs to go. 888 ▸ */ 890 ▸ if (retval) { 891 ▸ ▸ send_sig(SIGKILL, current, 0); 892 ▸ ▸ goto out_free_dentry; 893 ▸ } 894 ▸ if (likely(elf_bss != elf_brk) && unlikely(padzero(elf_bss))) { 895 ▸ ▸ send_sig(SIGSEGV, current, 0); 896 ▸ ▸ retval = -EFAULT; /* Nobody gets to see this, but.. */ 897 ▸ ▸ goto out_free_dentry; 898 ▸ } 899 //调用一个动态链接程序的函数,如果动态链接程序是elf可执行的,调用load_elf_interp(动态链接程序) 900 ▸ if (elf_interpreter) { 901 ▸ ▸ unsigned long interp_map_addr = 0; 902 903 ▸ ▸ elf_entry = load_elf_interp(&loc->interp_elf_ex, 904 ▸ ▸ ▸ ▸ ▸ interpreter, 905 ▸ ▸ ▸ ▸ ▸ &interp_map_addr, 906 ▸ ▸ ▸ ▸ ▸ load_bias); 907 ▸ ▸ if (!IS_ERR((void *)elf_entry)) { 908 ▸ ▸ ▸ /* 909 ▸ ▸ ▸ * load_elf_interp() returns relocation 910 ▸ ▸ ▸ * adjustment 911 ▸ ▸ ▸ */ 912 ▸ ▸ ▸ interp_load_addr = elf_entry; 913 ▸ ▸ ▸ elf_entry += loc->interp_elf_ex.e_entry; 914 ▸ ▸ } 915 ▸ ▸ if (BAD_ADDR(elf_entry)) { 916 ▸ ▸ ▸ force_sig(SIGSEGV, current); 917 ▸ ▸ ▸ retval = IS_ERR((void *)elf_entry) ? 918 ▸ ▸ ▸ ▸ ▸ (int)elf_entry : -EINVAL; 919 ▸ ▸ ▸ goto out_free_dentry; 920 ▸ ▸ } 921 ▸ ▸ reloc_func_desc = interp_load_addr; 922 923 ▸ ▸ allow_write_access(interpreter); 924 ▸ ▸ fput(interpreter); 925 ▸ ▸ kfree(elf_interpreter); 926 ▸ } else { 927 ▸ ▸ elf_entry = loc->elf_ex.e_entry; 928 ▸ ▸ if (BAD_ADDR(elf_entry)) { 929 ▸ ▸ ▸ force_sig(SIGSEGV, current); 930 ▸ ▸ ▸ retval = -EINVAL; 931 ▸ ▸ ▸ goto out_free_dentry; 932 ▸ ▸ } 933 ▸ } 934 935 ▸ kfree(elf_phdata); 936 //把可执行格式的linux_binfmt对象地址存放在进程描述符binfmt字段中 937 ▸ set_binfmt(&elf_format); 938 939 #ifdef ARCH_HAS_SETUP_ADDITIONAL_PAGES 940 ▸ retval = arch_setup_additional_pages(bprm, !!elf_interpreter); 941 ▸ if (retval < 0) { 942 ▸ ▸ send_sig(SIGKILL, current, 0); 943 ▸ ▸ goto out; 944 ▸ } 945 #endif /* ARCH_HAS_SETUP_ADDITIONAL_PAGES */ 946 947 ▸ install_exec_creds(bprm); 948 ▸ retval = create_elf_tables(bprm, &loc->elf_ex, 949 ▸ ▸ ▸ load_addr, interp_load_addr); 950 ▸ if (retval < 0) { 951 ▸ ▸ send_sig(SIGKILL, current, 0); 952 ▸ ▸ goto out; 953 ▸ } 954 ▸ /* N.B. passed_fileno might not be initialized? */ 955 ▸ current->mm->end_code = end_code; 956 ▸ current->mm->start_code = start_code; 957 ▸ current->mm->start_data = start_data; 958 ▸ current->mm->end_data = end_data; 959 ▸ current->mm->start_stack = bprm->p; 960 961 #ifdef arch_randomize_brk 962 ▸ if ((current->flags & PF_RANDOMIZE) && (randomize_va_space > 1)) { 963 ▸ ▸ current->mm->brk = current->mm->start_brk = 964 ▸ ▸ ▸ arch_randomize_brk(current->mm); 965 #ifdef CONFIG_COMPAT_BRK 966 ▸ ▸ current->brk_randomized = 1; 967 #endif 968 ▸ } 969 #endif 970 971 ▸ if (current->personality & MMAP_PAGE_ZERO) { 972 ▸ ▸ /* Why this, you ask??? Well SVr4 maps page 0 as read-only, 973 ▸ ▸ and some applications "depend" upon this behavior. 974 ▸ ▸ Since we do not have the power to recompile these, we 975 ▸ ▸ emulate the SVr4 behavior. Sigh. */ 976 ▸ ▸ error = vm_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_EXEC, 977 ▸ ▸ ▸ ▸ MAP_FIXED | MAP_PRIVATE, 0); 978 ▸ } 979 980 #ifdef ELF_PLAT_INIT 981 ▸ /* 982 ▸ * The ABI may specify that certain registers be set up in special 983 ▸ * ways (on i386 %edx is the address of a DT_FINI function, for 984 ▸ * example. In addition, it may also specify (eg, PowerPC64 ELF) 985 ▸ * that the e_entry field is the address of the function descriptor 986 ▸ * for the startup routine, rather than the address of the startup 987 ▸ * routine itself. This macro performs whatever initialization to 988 ▸ * the regs structure is required as well as any relocations to the 989 ▸ * function descriptor entries when executing dynamically links apps. 990 ▸ */ 991 ▸ ELF_PLAT_INIT(regs, reloc_func_desc); 992 #endif 993 994 ▸ start_thread(regs, elf_entry, bprm->p); 995 ▸ retval = 0; 996 out: 997 ▸ kfree(loc); 998 out_ret: 999 ▸ return retval; 1000 1001 ▸ /* error cleanup */ 1002 out_free_dentry: 1003 ▸ allow_write_access(interpreter); 1004 ▸ if (interpreter) 1005 ▸ ▸ fput(interpreter); 1006 out_free_interp: 1007 ▸ kfree(elf_interpreter); 1008 out_free_ph: 1009 ▸ kfree(elf_phdata); 1010 ▸ goto out; 1011 }
附录:
X86-64中,所有寄存器都是64位,相对32位的x86来说,标识符发生了变化,比如:从原来的%ebp变成了%rbp。为了向后兼容性,%ebp依然可以使用,不过指向了%rbp的低32位。
X86-64寄存器的变化,不仅体现在位数上,更加体现在寄存器数量上。新增加寄存器%r8到%r15。加上x86的原有8个,一共16个寄存器。
刚刚说到,寄存器集成在CPU上,存取速度比存储器快好几个数量级,寄存器多了,GCC就可以更多的使用寄存器,替换之前的存储器堆栈使用,从而大大提升性能。
让寄存器为己所用,就得了解它们的用途,这些用途都涉及函数调用,X86-64有16个64位寄存器,分别是:%rax,%rbx,%rcx,%rdx,%esi,%edi,%rbp,%rsp,%r8,%r9,%r10,%r11,%r12,%r13,%r14,%r15。其中:
• %rax 作为函数返回值使用。
• %rsp 栈指针寄存器,指向栈顶
• %rdi,%rsi,%rdx,%rcx,%r8,%r9 用作函数参数,依次对应第1参数,第2参数。。。
• %rbx,%rbp,%r12,%r13,%14,%15 用作数据存储,遵循被调用者使用规则,简单说就是随便用,调用子函数之前要备份它,以防他被修改
• %r10,%r11 用作数据存储,遵循调用者使用规则,简单说就是使用之前要先保存原值