美文网首页
可执行程序的装载

可执行程序的装载

作者: virealer | 来源:发表于2016-04-10 22:02 被阅读0次

    曹朋辉
    原创作品转载请注明出处
    《Linux内核分析》MOOC课程

    可执行文件的创建——预处理、编译和链接

    shiyanlou:~/ $ cd Code                                                [9:27:05]
    shiyanlou:Code/ $ vi hello.c                                          [9:27:14]
    //预处理 -E
    shiyanlou:Code/ $ gcc -E -o hello.cpp hello.c -m32                    [9:34:55]
    shiyanlou:Code/ $ vi hello.cpp                                        [9:35:04]
    //-x 指定文件类型
    //将预处理的文件编译为汇编文件 -S
    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]
    //链接  hello为ELF格式的文件 以上语句调用共享库链接
    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
    

    静态链接和动态链接是怎么回事?
    可执行文件的内部是怎样的?
    ABI和目标文件什么关系?
    命令行参数和环境变量是如何保存和传递的?
    命令行参数和环境变量是如何进入新程序的堆栈的?

    参数传递

    ![Upload Paste_Image.png failed. Please try again.]

    目标文件及链接

    ELF目标文件格式
    ELF文件格式 -- (中文翻译版)

    目标文件的格式

    PE格式大多用在wondows上
    ELF格式大多用在Linux上
    ELF (Excutable and Linkble Format)

    .o 可重定位

    .o

    a.out

    a.out

    .so

    .so

    查看ELF文件的头部

    shiyanlou:Code/ $ readelf -h hello

    readelf

    查看该ELF文件依赖的共享库

    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
    

    命令行参数和shell环境,一般我们执行一个程序的Shell环境,我们的实验直接使用execve系统调用。
    $ ls -l /usr/bin 列出/usr/bin下的目录信息
    Shell本身不限制命令行参数的个数,�命令行参数的个数受限于命令自身
    例如,int main(int argc, char *argv[])
    又如, int main(int argc, char *argv[], char envp[])
    Shell会调用execve将命令行参数和环境参数传递给可执行程序的main函数
    int execve(const char * filename,char * const argv[ ],char * const envp[ ]);
    库函数exec
    都是execve的封装例程

    #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);
        }
    }
    

    命令行参数和环境串都放在用户态堆栈中

    用户态堆栈

    在linux中一个程序是如何加载运行的

    在shell中输入一个程序的名称时,shell会先fork一个子进程,然后在子进程中调用execlp函数来拉起我们执行的程序,

    exec系统调用

    首先,exec会调用sys_execve,然后调用do_execve,再调用do_execve_common,这个函数会把函数参数和系统环境传进来进行相应的处理。然后调用exec_binprm来执行相应的程序。而exec_binprm又会调用search_binary_handler,这个函数会调用各种不同的格式来识别相应的文件,直到识别为止,比如linux中可执行文件为ELF,它就会识别出elf文件。

    在sys_execve处设置断点

    sys_execve

    retval = fmt->load_binary(bprm)把对应的文件以对应的格式加载到内存里面。由于Linux是elf格式,故会执行对应的load_elf_binary,然后在这个函数里面有一个函数start_thread,这个函数会复制内核堆栈,同时会设置新的进程的执行位置,即会设置新的eip,使eip指向新程序的入口位置。

    retval

    相关文章

      网友评论

          本文标题:可执行程序的装载

          本文链接:https://www.haomeiwen.com/subject/vboblttx.html