可执行程序的生成过程

1.可执行程序的生成过程

以一个简单的输出hello,world的程序作为示例

#include <stdio.h>
int main()
{
    printf("hello,world");
}

1)首先进行预处理:在命令行中输入gcc -E -o hello.cpp hello.c -m32,

  • -m32 -m64 生成32位/64位机器上的代码
  • -E 仅作预处理,不进行编译、汇编和链接

2)编译到汇编语言 gcc -x cpp-output -S -o hello.s hello.cpp -m32

    -S 仅编译到汇编语言,不进行汇编和链接

3)汇编成目标文件:gcc -x assembler -c hello.s -o hello.o -m32,这时输出文件hello.o已经是一个二进制文件了

   -x language 指明使用的编程语言。允许的语言包括:c c++ assembler none 。 ‘none’意味着恢复默认行为,即根据文件的扩展名猜测源文件的语     言。

   -c 编译、汇编到目标代码,不进行链接    

4) 链接成可执行文件 gcc -o hello hello.o,接着输入./hello可看到输出结果

    -o outfile 指定输出文件

5) 若使用静态编译可添加选项-static , gcc -o hello.static hello.o -static
    -static 在支持动态链接的系统上,阻止连接共享库。该选项在其它系统上 无效
二.可执行文件内部的结构

  ABI:应用程序二进制接口

     二进制兼容:某个程序(或库)有依赖其他库A,当库A升级时可以做到向后兼容,则程序(或库)功能不受影响。

ELF格式有三种目标文件:

     可重定位文件(relocatable)保存着代码和适当的数据,用来和其他的object文件一起来创建一个可执行文件或者是一个共享文件,主要值.o文件

     可执行文件(executable)保存着一个用来执行的程序,该文件指出了exec(BA_OS)如何来闯进啊程序进程映像

     共享object文件:保存有代码和合适的数据,用来被下面的两个链接器链接。第一个是链接编辑器,可以和其他的可重定位和共享object文件来创建其他 的object。第二个是动态链接器,联合一个可执行文件和其他共享object文件来创建一个进程映像

 

目标文件:参与程序的链接(创建一个程序)和程序的执行(运行一个程序  )  

下图为ELF文件的两个视角:Linking View和Execution View

ELF header:保存了路线图(road map),描述了该文件的组织情况

程序头表(program header table):告诉系统如何闯进啊一个进程的内存映像

Section 头表(section header table):包含描述文件section的信息。每个section在这个表中有一个入口,每个入口给出了该section的名字,大小

可以使用 readelf -h 选项来查看ELF文件的头部信息,如下图所示  

当创建或增加一个进程映像的时候,系统在理论上将拷贝一个文件的段到虚拟内存段。

如上图所示,Text segment将拷贝到虚拟内存的0x8048100地址,可执行程序的格式和进程的地址空间有映射关系。

进程加载一个可执行文件的时候从虚拟地址空间的0x8048000处加载,首先是ELF头,程序开始执行的第一行代码的地址有ELF头里的enter point (程序入口点地址给出),一般静态链接文件只有一个代码段,动态链接有多个代码段。

三.可执行程序的装载

1.执行环境

命令行参数和shell环境,一般我们执行一个程序的Shell环境

  1. Shell本身不限制命令行参数的个数, 命令行参数的个数受限于命令自身

              例如,int main(int argc, char *argv[])

              又如, int main(int argc, char *argv[], char *envp[]),envp 是shell的执行环境,一帮由shell设置。下面是一段示例程序

2.Shell会调用execve将命令行参数和环境参数传递给可执行程序的main函数

            int execve(const char * filename,char * const argv[ ],char * const envp[ ]);

            库函数exec*都是execve的封装例程

 

调用execve时先是通过函数调用参数传递的方式把参数传递给execve,接着execve再构造一个main函数的参数传递栈。

3.可执行程序装载时动态链接和运行时动态链接

动态链接分为可执行程序装载时动态链接和运行时动态链接,如下代码演示了这两种动态链接。

 

  • 准备.so文件

 

shlibexample.h (1.3 KB) - Interface of Shared Lib Example

/********************************************************************/
/* Copyright (C) SSE@USTCSZ, 2012                                   */
/*                                                                  */
/*  FILE NAME             :  shlibexample.h                         */
/*  PRINCIPAL AUTHOR      :  Mengning                               */
/*  SUBSYSTEM NAME        :                                         */
/*  MODULE NAME           :                                         */
/*  LANGUAGE              :  C                                      */
/*  TARGET ENVIRONMENT    :  ANY                                    */
/*  DATE OF FIRST RELEASE :  2012/5/3                               */
/*  DESCRIPTION           :  Interface of Shared Lib Example        */
/********************************************************************/

/*
 * Revision log:
 *
 * Created by Mengning,2012/5/3
 *
 */

#ifndef _SH_LIB_EXAMPLE_H_
#define _SH_LIB_EXAMPLE_H_

#define SUCCESS 0
#define FAILURE (-1)

#ifdef __cplusplus
extern "C" {
#endif
/*
 * Shared Lib API Example
 * input    : none
 * output    : none
 * return    : SUCCESS(0)/FAILURE(-1)
 *
 */
int SharedLibApi();


#ifdef __cplusplus
}
#endif
#endif /* _SH_LIB_EXAMPLE_H_ */

 

shlibexample.c (1.2 KB) - Implement of Shared Lib Example

 

/********************************************************************/
/* Copyright (C) SSE@USTCSZ, 2012                                   */
/*                                                                  */
/*  FILE NAME             :  shlibexample.c                         */
/*  PRINCIPAL AUTHOR      :  Mengning                               */
/*  SUBSYSTEM NAME        :                                         */
/*  MODULE NAME           :                                         */
/*  LANGUAGE              :  C                                      */
/*  TARGET ENVIRONMENT    :  ANY                                    */
/*  DATE OF FIRST RELEASE :  2012/5/3                               */
/*  DESCRIPTION           :  Implement of Shared Lib Example        */
/********************************************************************/

/*
 * Revision log:
 *
 * Created by Mengning,2012/5/3
 *
 */


#include <stdio.h>
#include "shlibexample.h"

/*
 * Shared Lib API Example
 * input    : none
 * output    : none
 * return    : SUCCESS(0)/FAILURE(-1)
 *
 */
int SharedLibApi()
{
    printf("This is a shared libary!\n");
    return SUCCESS;
}

 

编译成libshlibexample.so文件

 

  1. $ gcc -shared shlibexample.c -o libshlibexample.so -m32

dllibexample.h (1.3 KB) - Interface of Dynamical Loading Lib Example

/********************************************************************/
/* Copyright (C) SSE@USTCSZ, 2012                                   */
/*                                                                  */
/*  FILE NAME             :  dllibexample.h                         */
/*  PRINCIPAL AUTHOR      :  Mengning                               */
/*  SUBSYSTEM NAME        :                                         */
/*  MODULE NAME           :                                         */
/*  LANGUAGE              :  C                                      */
/*  TARGET ENVIRONMENT    :  ANY                                    */
/*  DATE OF FIRST RELEASE :  2012/5/3                               */
/*  DESCRIPTION           :  Interface of Dynamical Loading         */
/*                           Lib Example                            */
/********************************************************************/

/*
 * Revision log:
 *
 * Created by Mengning,2012/5/3
 *
 */

#ifndef _DL_LIB_EXAMPLE_H_
#define _DL_LIB_EXAMPLE_H_



#ifdef __cplusplus
extern "C" {
#endif
/*
 * Dynamical Loading Lib API Example
 * input    : none
 * output    : none
 * return    : SUCCESS(0)/FAILURE(-1)
 *
 */
int DynamicalLoadingLibApi();


#ifdef __cplusplus
}
#endif
#endif /* _DL_LIB_EXAMPLE_H_ */

 

dllibexample.c (1.3 KB) - Implement of Dynamical Loading Lib Example

/********************************************************************/
/* Copyright (C) SSE@USTCSZ, 2012                                   */
/*                                                                  */
/*  FILE NAME             :  dllibexample.c                         */
/*  PRINCIPAL AUTHOR      :  Mengning                               */
/*  SUBSYSTEM NAME        :                                         */
/*  MODULE NAME           :                                         */
/*  LANGUAGE              :  C                                      */
/*  TARGET ENVIRONMENT    :  ANY                                    */
/*  DATE OF FIRST RELEASE :  2012/5/3                               */
/*  DESCRIPTION           :  Implement of Dynamical Loading         */
/*                           Lib Example                            */
/********************************************************************/

/*
 * Revision log:
 *
 * Created by Mengning,2012/5/3
 *
 */

#include <stdio.h>
#include "dllibexample.h"

#define SUCCESS 0
#define FAILURE (-1)

/*
 * Dynamical Loading Lib API Example
 * input    : none
 * output    : none
 * return    : SUCCESS(0)/FAILURE(-1)
 *
 */
int DynamicalLoadingLibApi()
{
    printf("This is a Dynamical Loading libary!\n");
    return SUCCESS;
}

 

 

编译成libdllibexample.so文件

 

  1. $ gcc -shared dllibexample.c -o libdllibexample.so -m32

 

  • 分别以共享库和动态加载共享库的方式使用libshlibexample.so文件和libdllibexample.so文件

 

main.c  (1.9 KB) - Main program

/********************************************************************/
/* Copyright (C) SSE@USTCSZ, 2012                                   */
/*                                                                  */
/*  FILE NAME             :  main.c                                 */
/*  PRINCIPAL AUTHOR      :  Mengning                               */
/*  SUBSYSTEM NAME        :                                         */
/*  MODULE NAME           :                                         */
/*  LANGUAGE              :  C                                      */
/*  TARGET ENVIRONMENT    :  ANY                                    */
/*  DATE OF FIRST RELEASE :  2012/5/3                               */
/*  DESCRIPTION           :  Main program                           */
/********************************************************************/

/*
 * Revision log:
 *
 * Created by Mengning,2012/5/3
 *
 */


#include <stdio.h>

#include "shlibexample.h" 

#include <dlfcn.h>

/*
 * Main program
 * input    : none
 * output    : none
 * return    : SUCCESS(0)/FAILURE(-1)
 *
 */
int main()
{
    printf("This is a Main program!\n");
    /* Use Shared Lib */
    printf("Calling SharedLibApi() function of libshlibexample.so!\n");
    SharedLibApi();
    /* Use Dynamical Loading Lib */
    void * handle = dlopen("libdllibexample.so",RTLD_NOW);
    if(handle == NULL)
    {
        printf("Open Lib libdllibexample.so Error:%s\n",dlerror());
        return   FAILURE;
    }
    int (*func)(void);
    char * error;
    func = dlsym(handle,"DynamicalLoadingLibApi");
    if((error = dlerror()) != NULL)
    {
        printf("DynamicalLoadingLibApi not found:%s\n",error);
        return   FAILURE;
    }    
    printf("Calling DynamicalLoadingLibApi() function of libdllibexample.so!\n");
    func();  
    dlclose(handle);       
    return SUCCESS;
}

 

 

编译main,注意这里只提供shlibexample的-L(库对应的接口头文件所在目录)和-l(库名,如libshlibexample.so去掉lib和.so的部分),并没有提供dllibexample的相关信息,只是指明了-ldl

  1. $ gcc main.c -o main -L/path/to/your/dir -lshlibexample -ldl -m32
  2. export LD_LIBRARY_PATH=$PWD #将当前目录加入默认路径,否则main找不到依赖的库文件,当然也可以将库文件copy到默认路径下。
  3. $ ./main
  4. This is a Main program!
  5. Calling SharedLibApi() function of libshlibexample.so!
  6. This is a shared libary!
  7. Calling DynamicalLoadingLibApi() function of libdllibexample.so!
  8. This is a Dynamical Loading libary!

 

    • sys_execve内部会解析可执行文件格式

      • do_execve -> do_execve_common ->  exec_binprm
      • search_binary_handler符合寻找文件格式对应的解析模块,如下:

        1. 1369    list_for_each_entry(fmt, &formats, lh) {
        2. 1370        if (!try_module_get(fmt->module))
        3. 1371            continue;
        4. 1372        read_unlock(&binfmt_lock);
        5. 1373        bprm->recursion_depth++;
        6. 1374        retval = fmt->load_binary(bprm);
        7. 1375        read_lock(&binfmt_lock);
      • 对于ELF格式的可执行文件fmt->load_binary(bprm);执行的应该是load_elf_binary 其内部是和ELF文件格式解析的部分需要和ELF文件格式标准结合起来阅读

      • Linux内核是如何支持多种不同的可执行文件格式的? 

        1. 82static struct linux_binfmt elf_format = {
        2. 83  .module     = THIS_MODULE,
        3. 84  .load_binary    = load_elf_binary,
        4. 85  .load_shlib = load_elf_library,
        5. 86  .core_dump  = elf_core_dump,
        6. 87  .min_coredump   = ELF_EXEC_PAGESIZE,
        7. 88};
        1. 2198static int __init init_elf_binfmt(void)
        2. 2199{
        3. 2200    register_binfmt(&elf_format);
        4. 2201    return 0;
        5. 2202}

 

         elf_format和 init_elf_binfmt,就是观察者模式中的观察者

 

         execve系统调用返回到用户态

                修改int 0x80压入内核堆栈的EIP

                load_elf_binary->start_thread

                start_thread的new_ip从ELF文件中获得

      动态加载时将elf_entry的值设为动态库加载程序的入口地址。

SYS_execve的执行过程

 

 

 

 

 

 

 

 

 

      

 

 

 

三.GDB跟踪execve系统调用



 

 

posted @ 2016-04-10 21:55  ceibaf  阅读(543)  评论(0编辑  收藏  举报