71v5 发表于 2014-06-26 15:00

freebsd9.2-内核支持的可执行文件格式

在freebsd上,执行一个程序都是通过execve系统调用来实现的,但是在execve能够正确工作之前,内核需要进行
一系列的初始化并创建几个数据结构来支持execve系统调用的实现,比较重要的两个数据结构就是数组execsw和
数组elf_brand_list。在execve的实现中,数组execsw元素指向的对象包含的方法对具体的可执行文件格式进行解释,
而数组elf_brand_list用来设置struct proc(进程描述符)中的p_sysent成员,p_sysent成员和系统调用紧密联系,
相信在了解了execve的实现后,对系统调用的实现将有一个更深刻的理解。

freebsd支持多种可执行文件的格式,这意味着不同格式的可执行文件都可以在freebsd上执行,虽然内核不关心
普通文件的内容,但是对于可执行文件来说,内核必须能理解其文件格式。

对于支持的每种可执行文件格式,相应的实现都给内核提供一个接口,这个接口用来对相应的可执行文件格式进行
解释,该接口实际上是一个类型为struct execsw的结构体,如下所示:/*********************************************
* ex_imgact:
   一个函数,该函数负责对相应的可执行文件格式
   进行解释。

   ex_name:描述name的字符串。
***********************************/
    64        struct execsw {
    65                int (*ex_imgact)(struct image_params *);
    66                const char *ex_name;
    67        };

/************************************************************************
* Each of the items is a pointer to a `const struct execsw', hence the
* double pointer here.
   非常重要的execsw数组,在execve系统调用中使用,用来解释相应的
   可执行文件,数组元素的类型为指向struct execsw数据对象的指针
**************************************/
   185        static const struct execsw **execsw;freebsd9.2现阶段支持的文件格式及其对应的struct execsw数据对象如下所示,由函数exec_register将
这些数据对象添加到execsw指针数组中:-解释linux上的a.out文件格式:
   235        /*
   236       * Tell kern_execve.c about it, with a little help from the linker.
   237       */
   238        static struct execsw linux_execsw = { exec_linux_imgact, "linux a.out" };
   239        EXEC_SET(linuxaout, linux_execsw);


-解释通用文件对象格式:
   489        /*
   490       * Tell kern_execve.c about it, with a little help from the linker.
   491       */
   492        static struct execsw coff_execsw = { exec_coff_imgact, "coff" };
   493        EXEC_SET(coff, coff_execsw);


-解释gzip文件格式:
   389        /*
   390       * Tell kern_execve.c about it, with a little help from the linker.
   391       */
   392        static struct execsw gzip_execsw = {exec_gzip_imgact, "gzip"};
   393        EXEC_SET(execgzip, gzip_execsw);

-解释a.out文件格式:
   339        /*
   340       * Tell kern_execve.c about it, with a little help from the linker.
   341       */
   342        static struct execsw aout_execsw = { exec_aout_imgact, "a.out" };
   343        EXEC_SET(aout, aout_execsw);

-解释shell脚本:
   254        /*
   255       * Tell kern_execve.c about it, with a little help from the linker.
   256       */
   257        static struct execsw shell_execsw = { exec_shell_imgact, "#!" };
   258        EXEC_SET(shell, shell_execsw);

-解释svr4上的可执行文件格式:
   233        /*
   234       * Tell kern_execve.c about it, with a little help from the linker.
   235       */
   236        struct execsw svr4_execsw = { exec_svr4_imgact, "svr4 ELF" };
   237        EXEC_SET(execsw_set, svr4_execsw);

-解释elf文件格式:
2043/*
2044   * Tell kern_execve.c about it, with a little help from the linker.
2045   */
/***************************************************************************
* static struct execsw elf32_execsw = {
          exec_elf32_imgact,
          "ELF32"
   };
   EXEC_SET(elf32,elf32_execsw);
*************/
2046static struct execsw __elfN(execsw) = {
2047          __CONCAT(exec_, __elfN(imgact)),
2048          __XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE))
2049};
2050EXEC_SET(__CONCAT(elf, __ELF_WORD_SIZE), __elfN(execsw));下面以最通用的elf文件,来看看EXEC_SET的工作流程:
-展开EXEC_SET宏:static int elf32_modevent(module_t mod, int type,void *data)
{
   struct execsw *exec = (struct execsw *)data;
   int error = 0;
   switch (type) {
      case MOD_LOAD:
      error = exec_register(exec);
      if (error)
         printf("elf32 register failed\n");
      break;

      case MOD_UNLOAD:
      error = exec_unregister(exec);
      if (error)
         printf("elf32 unregister failed\n");
      break;

      default:
      error = EOPNOTSUPP;
      break;
   }

   return error;
}
static moduledata_t elf32_mod = {
         "elf32",
         elf32_modevent,
         (void *)&elf32_execsw
};
DECLARE_MODULE_TIED(elf32, elf32_mod, SI_SUB_EXEC, SI_ORDER_ANY);[展开宏DECLARE_MODULE_TIED]:static int elf32_modevent(module_t mod, int type,void *data)
{
   struct execsw *exec = (struct execsw *)data;
   int error = 0;
   switch (type) {
      case MOD_LOAD:
      error = exec_register(exec);
      if (error)
         printf("elf32 register failed\n");
      break;

      case MOD_UNLOAD:
      error = exec_unregister(exec);
      if (error)
         printf("elf32 unregister failed\n");
      break;

      default:
      error = EOPNOTSUPP;
      break;
   }

   return error;
}
static moduledata_t elf32_mod = {
         "elf32",
         elf32_modevent,
         (void *)&elf32_execsw
};

static struct mod_depend _elf32_depend_on_kernel = {       
      __FreeBSD_version,                                               
      __FreeBSD_version,                                               
      __FreeBSD_version                                               
};
                                                               
static struct mod_metadata _mod_metadata_md_elf32_on_kernel = {       
       MDT_STRUCT_VERSION,                                       
       MDT_DEPEND,                       
       &_elf32_depend_on_kernel,                       
       "kernel"                                                       
};                                                               
DATA_SET(modmetadata_set, _mod_metadata_md_elf32_on_kernel);

static struct mod_metadata _mod_metadata_md_elf32 = {       
       MDT_STRUCT_VERSION,                                       
       MDT_MODULE,                       
       &elf32_mod,                               
       "elf32"                                                       
};                               
DATA_SET(modmetadata_set, _mod_metadata_md_elf32);
       
SYSINIT(elf32module, SI_SUB_EXEC, SI_ORDER_ANY, module_register_init, &elf32_mod);
struct __hack;从上面的展开过程可以得出:
1:内核对负责解释相应可执行文件格式这部分源代码是以模块的方式管理。

2:定义了一个对该模块具体事件进行响应的回调函数。

3:定义并初始化了几个描述模块的数据结构。

4:将类型为struct mod_metadata数据对象的地址保存到name为"set_modmetadata_set"的section中,
   在SI_SUB_KLD对应的子系统中由初始化函数linker_init_kernel_modules处理name为"set_modmetadata_set"
   的section,并且创建描述可执行文件格式的struct module对象,并将其链接到相应的链表中,
   这个处理过程在SI_SUB_EXEC对应的子系统初始化之前执行。

5:通过SYSINIT机制来对模块进行相应的初始化,由初始化函数module_register_init来完成,同时用
   有意义的值初始化execsw数组。   104                SI_SUB_KLD                = 0x2000000,        /* KLD and module setup */
   143                SI_SUB_EXEC                = 0x7400000,        /* execve() handlers */
页: [1]
查看完整版本: freebsd9.2-内核支持的可执行文件格式