歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux內核

Linux內核模塊的加載過程

前段時間為了解決內核模塊無法卸載的問題,對模塊的加載過程詳細地學習了一番。加載模塊時常用的命令是insmod和modprobe,這兩個命令主要是通過系統調用sys_init_module()來完成主要的工作,用戶層做的更多的是對參數的處理,以及將插入的模塊加入到內存中。系統調用sys_init_module()將大部分工作委托給load_module()函數來完成,load_module()中的操作,大部分是圍繞著ELF文件的格式來完成的,所以如果對ELF文件了解的話,看load_module()的過程很容易。 下面將我對load_module()的一些理解貼出來和大家分享一下,注釋比較詳細,就不多說了:

/* Allocate and load the module: note that size of section 0 is always
  zero, and we rely on this for optional sections. */
/*
 * load_module()負責最艱苦的模塊加載全過程。sys_init_module()調用load_module(),
 * 後者將在內核空間利用vmalloc分配一塊大小同樣為len的地址空間。然後通過
 * copy_from_user函數的調用將用戶空間的文件數據復制到內核空間中,從而在內核空間
 * 構造出內核模塊的一個ELF靜態的內存視圖。接下來的操作都將以此視圖為基礎,為使
 * 敘述簡單起見,我們稱該視圖為HDR視圖。HDR視圖所占用的內存空間在load_module結束時
 * 通過vfree予以釋放。
 */
static noinline struct module *load_module(void __user *umod,
      unsigned long len,
      const char __user *uargs)
{
 /*
  * ELF文件頭地址。
  */
 Elf_Ehdr *hdr;
 /*
  * 段首部表地址
  */
 Elf_Shdr *sechdrs;
 char *secstrings, *args, *modmagic, *strtab = NULL;
 char *staging;
 unsigned int i;
 unsigned int symindex = 0;
 unsigned int strindex = 0;
 unsigned int modindex, versindex, infoindex, pcpuindex;
 struct module *mod;
 long err = 0;
 void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */
 unsigned long symoffs, stroffs, *strmap;

 mm_segment_t old_fs;

 DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n",
        umod, len, uargs);
 /*
  * 如果len小於ELF文件首部長度,則返回ENOEXEC錯誤。
  */
 if (len < sizeof(*hdr))
  return ERR_PTR(-ENOEXEC);

 /* Suck in entire file: we'll want most of it. */
 /* vmalloc barfs on "unusual" numbers.  Check here */
 /*
  * 64 * 1024 * 1024應該是模塊文件的最大大小。
  */
 if (len > 64 * 1024 * 1024 || (hdr = vmalloc(len)) == NULL)
  return ERR_PTR(-ENOMEM);

 /*
  * 將模塊文件從用戶空間拷貝到分配的hdr中。
  */
 if (copy_from_user(hdr, umod, len) != 0) {
  err = -EFAULT;
  goto free_hdr;
 }

 /* Sanity checks against insmoding binaries or wrong arch,
          weird elf version */
 /*
  * 檢查文件標識是否是ELFMAG,檢查模塊目標文件是否是可重定向文件,
  * 檢查目標文件的體系結構類型,檢查ELF首部中段首部表中表項的大小,
  * 如果其中一項檢查失敗,則返回ENOEXEC。
  */
 if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0
    || hdr->e_type != ET_REL
    || !elf_check_arch(hdr)
    || hdr->e_shentsize != sizeof(*sechdrs)) {
  err = -ENOEXEC;
  goto free_hdr;
 }

 /*
  * hdr->e_shnum * sizeof(Elf_Shdr)計算的是ELF文件中段首部表的大小,
  * 加上偏移的值如果大於len,則說明模塊目標文件被截斷了,跳轉到
  * truncated標簽處處理
  */
 if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr))
  goto truncated;

 /* Convenience variables */
 /*
  * 計算段首部表的地址.
  */
 sechdrs = (void *)hdr + hdr->e_shoff;
 /*
  * 計算段名稱字符串表的地址,其中hdr->e_shstrndx是段名稱字符串表在段首部表中
  * 的索引,sh_offset是當前段相對於文件頭的偏移。
  */
 secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
 /*
  * 將第一個段在執行時的虛擬地址設為0,不使用段首部表中的第一個表項。
  */
 sechdrs[0].sh_addr = 0;

 /*
  * 開始遍歷段首部表, hdr->e_shnum是段首部表表項的數量
  */
 for (i = 1; i < hdr->e_shnum; i++) {
  /*
  * 如果索引為i的段需要在文件中占據空間,但是文件長度小於
  * 段的偏移加上段大小(也就是說文件長度不夠),則跳轉到
  * truncated標簽處處理
  */
  if (sechdrs[i].sh_type != SHT_NOBITS
      && len < sechdrs[i].sh_offset + sechdrs[i].sh_size)
   goto truncated;

  /* Mark all sections sh_addr with their address in the
    temporary image. */
  /*
  * 將段在執行時的虛擬地址設為他們在臨時內存映像中的地址.
  */
  sechdrs[i].sh_addr = (size_t)hdr + sechdrs[i].sh_offset;

  /* Internal symbols and strings. */
  /*
  * 如果索引為i的段是符號表,則做相應的處理.目前目標文件只能有一個符號表,
  * 這個限制以後可能會有變化,所以下面的語句只會執行一次。
  */
  if (sechdrs[i].sh_type == SHT_SYMTAB) {
   /*
    * 用來保存符號表在段首部表中的索引
    */
   symindex = i;
   /*
    * strindex存儲的是與當前段段相關的字符串表段的索引。
    */
   strindex = sechdrs[i].sh_link;
   /*
    * strtab存儲的是與當前段相關的字符串表段的地址。
    */
   strtab = (char *)hdr + sechdrs[strindex].sh_offset;
  }
#ifndef CONFIG_MODULE_UNLOAD
  /* Don't load .exit sections */
  /*
  * 如果當前段是".exit"段(前綴是".exit"),則在段的標志中移除SHF_ALLOC
  * 標志,意思是當前段在執行過程中不需要占用內存。
  */
  if (strstarts(secstrings+sechdrs[i].sh_name, ".exit"))
   sechdrs[i].sh_flags &= ~(unsigned long)SHF_ALLOC;
#endif
 }
 /*
  * 查找".gnu.linkonce.this_module"段在段首部表中的索引
  */
 modindex = find_sec(hdr, sechdrs, secstrings,
      ".gnu.linkonce.this_module");
 if (!modindex) {
  printk(KERN_WARNING "No module found in object\n");
  err = -ENOEXEC;
  goto free_hdr;
 }
 /* This is temporary: point mod into copy of data. */
 /*
  * 將模塊的地址暫時設為臨時映像中段給出的地址。
  */
 mod = (void *)sechdrs[modindex].sh_addr;

 /*
  * 如果沒有找到符號表段,則跳轉到free_hdr處處理
  */
 if (symindex == 0) {
  printk(KERN_WARNING "%s: module has no symbols (stripped?)\n",
        mod->name);
  err = -ENOEXEC;
  goto free_hdr;
 }

 /*
  * 查找__versions段在段首部表中的索引
  */
 versindex = find_sec(hdr, sechdrs, secstrings, "__versions");
    /*
  * 查找.modinfo段在段首部表中的索引
  */
 infoindex = find_sec(hdr, sechdrs, secstrings, ".modinfo");
 /*
  * 查找".data.percpu"段在段首部表中的索引
  */
 pcpuindex = find_pcpusec(hdr, sechdrs, secstrings);

 /* Don't keep modinfo and version sections. */
 /*
  * "__versions"和".modinfo"段在執行時不需要,因此移除SHF_ALLOC標志。
  */
 sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
 sechdrs[versindex].sh_flags &= ~(unsigned long)SHF_ALLOC;

 /* Check module struct version now, before we try to use module. */
 /*
  * 檢查模塊的版本信息。
  */
  *
 if (!check_modstruct_version(sechdrs, versindex, mod)) {
  err = -ENOEXEC;
  goto free_hdr;
 }

 /*
  * 在.modinfo段查找vermagic變量對應的值。
  */
 modmagic = get_modinfo(sechdrs, infoindex, "vermagic");
 /* This is allowed: modprobe --force will invalidate it. */
 if (!modmagic) {
  /*
  * 如果沒有找到vermagic變量,則嘗試強制加載模塊。
  * 但是try_to_force_load()函數的實現依賴於CONFIG_MODULE_FORCE_LOAD
  * 宏是否定義。而該宏默認是沒有定義的,所以這裡會
  * 返回失敗,看來內核並不推薦強制加載模塊。
  */
  err = try_to_force_load(mod, "bad vermagic");
  if (err)
   goto free_hdr;
 } else if (!same_magic(modmagic, vermagic, versindex)) {
  printk(KERN_ERR "%s: version magic '%s' should be '%s'\n",
        mod->name, modmagic, vermagic);
  err = -ENOEXEC;
  goto free_hdr;
 }

 /*
  * 在.modinfo段查找staging變量對應的值。
  */
 staging = get_modinfo(sechdrs, infoindex, "staging");
 if (staging) {
  /*
  * 從2.6.28版本起,內核代碼的drivers下增加了一個staging目錄,
  * 這個目錄也是用來存放驅動程序,只是這裡的驅動程序
  * 和上層目錄不同,加載的時候內核日志會打印如下的語句:
  * MODULE_NAME: module is from the staging directory, the quality is unknown, you have been warned.
  * Greg KH於2008年6月10號在Linux內核郵件列表裡發出一封信,宣布建
  * 立了另外一棵kernel tree,這就是Linux staging tree。Greg解釋到,staging tree
  * 建立之目的是用來放置一些未充分測試或者因為一些其他原因
  * 未能進入內核的新增驅動程序和新增文件系統。
  */
  add_taint_module(mod, TAINT_CRAP);
  printk(KERN_WARNING "%s: module is from the staging directory,"
        " the quality is unknown, you have been warned.\n",
        mod->name);
 }

 /* Now copy in args */
 /*
  * 將���入模塊時指定的參數從用於空間拷貝到args中。
  */
 args = strndup_user(uargs, ~0UL >> 1);
 if (IS_ERR(args)) {
  err = PTR_ERR(args);
  goto free_hdr;
 }

 /*
  * 為與符號表相關的字符串表段在內存中分配用於映射的空間。
  * sechdrs[strindex].sh_size是與符號表相關的字符串表段的大小。
  * 這裡分配的是一個位圖,用於符號表中的符號名稱的
  * 映射。
  */
 strmap = kzalloc(BITS_TO_LONGS(sechdrs[strindex].sh_size)
    * sizeof(long), GFP_KERNEL);
 if (!strmap) {
  err = -ENOMEM;
  goto free_mod;
 }

 /*
  * 查找當前要加載的模塊是否已經存在,如果存在,則
  * 跳轉到free_mod標簽處。
  */
 if (find_module(mod->name)) {
  err = -EEXIST;
  goto free_mod;
 }

 mod->state = MODULE_STATE_COMING;

 /* Allow arches to frob section contents and sizes.  */
 /*
  * err總是為0
  */
 err = module_frob_arch_sections(hdr, sechdrs, secstrings, mod);
 if (err < 0)
  goto free_mod;

 /*
  * 如果存在.data.percpu段,則為該段在內存中分配空間。
  * 分配成功後,移除SHF_ALLOC標志,並且初始化module實例
  * 的percpu成員。
  */
 if (pcpuindex) {
  /* We have a special allocation for this section. */
  percpu = percpu_modalloc(sechdrs[pcpuindex].sh_size,
      sechdrs[pcpuindex].sh_addralign,
      mod->name);
  if (!percpu) {
   err = -ENOMEM;
   goto free_mod;
  }
  sechdrs[pcpuindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
  mod->percpu = percpu;
 }

 /* Determine total sizes, and put offsets in sh_entsize.  For now
    this is done generically; there doesn't appear to be any
    special cases for the architectures. */
 /*
  * 對core section和init section中的大小及代碼段的信息進行
  * 統計
  */
 layout_sections(mod, hdr, sechdrs, secstrings);
 /*
  * 處理符號表中的符號,返回值是core section尾部的
  * 符號表的偏移。
  */
 symoffs = layout_symtab(mod, sechdrs, symindex, strindex, hdr,
    secstrings, &stroffs, strmap);

 /* Do the allocs. */
 /*
  * 為core section分配內存,初始化後存儲在module實例
  * 的module_core成員中。
  */
 ptr = module_alloc_update_bounds(mod->core_size);
 /*
  * The pointer to this block is stored in the module structure
  * which is inside the block. Just mark it as not being a
  * leak.
  */
 kmemleak_not_leak(ptr);
 if (!ptr) {
  err = -ENOMEM;
  goto free_percpu;
 }
 memset(ptr, 0, mod->core_size);
 mod->module_core = ptr;

 /*
  * 為init section分配內存,初始化後存儲在module實例
  * 的module_init成員中。
  */
 ptr = module_alloc_update_bounds(mod->init_size);
 /*
  * The pointer to this block is stored in the module structure
  * which is inside the block. This block doesn't need to be
  * scanned as it contains data and code that will be freed
  * after the module is initialized.
  */
 kmemleak_ignore(ptr);
 if (!ptr && mod->init_size) {
  err = -ENOMEM;
  goto free_core;
 }
 memset(ptr, 0, mod->init_size);
 mod->module_init = ptr;

 /* Transfer each section which specifies SHF_ALLOC */
 DEBUGP("final section addresses:\n");
 /*
  * 遍歷段首部表,拷貝需要占用內存的段到
  * init section 或core section,並且調整各個段的運行
  * 時地址。
  */
 for (i = 0; i < hdr->e_shnum; i++) {
  void *dest;

  /*
  * 如果當前段執行時不占用內存,
  * 則不處理
  */
  if (!(sechdrs[i].sh_flags & SHF_ALLOC))
   continue;

  /*
  * 如果段首部的sh_entsize的最高位設置的話,
  * 表示該段屬於init section,則從module_init開始的內存中獲取
  * 當前段應該存儲的地址,否則從module_core開始的內存
  * 中獲取當前段應該存儲的地址。
  */
  if (sechdrs[i].sh_entsize & INIT_OFFSET_MASK)
   dest = mod->module_init
    + (sechdrs[i].sh_entsize & ~INIT_OFFSET_MASK);
  else
   dest = mod->module_core + sechdrs[i].sh_entsize;

  /*
  * 將當前段的內容從ELF文件頭拷貝到指定的
  * 段(init section或core section)中
  */
  if (sechdrs[i].sh_type != SHT_NOBITS)
   memcpy(dest, (void *)sechdrs[i].sh_addr,
          sechdrs[i].sh_size);
  /* Update sh_addr to point to copy in image. */
  /*
  * 更改段的運行時地址,sh_addr原先存儲的地址是
  * 相對於ELF文件頭的地址
  */
  sechdrs[i].sh_addr = (unsigned long)dest;
  DEBUGP("\t0x%lx %s\n", sechdrs[i].sh_addr, secstrings + sechdrs[i].sh_name);
 }
 /* Module has been moved. */
 mod = (void *)sechdrs[modindex].sh_addr;
 kmemleak_load_module(mod, hdr, sechdrs, secstrings);

#if defined(CONFIG_MODULE_UNLOAD) && defined(CONFIG_SMP)
 /*
  * 初始化多處理下用於引用計數的refptr成員
  */
 mod->refptr = percpu_modalloc(sizeof(local_t), __alignof__(local_t),
          mod->name);
 if (!mod->refptr) {
  err = -ENOMEM;
  goto free_init;
 }
#endif
 /* Now we've moved module, initialize linked lists, etc. */
 /*
  * 初始化卸載模塊時的處理
  */
 module_unload_init(mod);

 /* add kobject, so we can reference it. */
 /*
  * 在sysfs中創建模塊對應的對象,可以在通過/sys/module/module_name
  * 查看。
  */
 err = mod_sysfs_init(mod);
 if (err)
  goto free_unload;

 /* Set up license info based on the info section */
 /*
  * 從.modinfo段獲取license對應的值,檢查是否兼容
  */
 set_license(mod, get_modinfo(sechdrs, infoindex, "license"));

 /*
  * ndiswrapper is under GPL by itself, but loads proprietary modules.
  * Don't use add_taint_module(), as it would prevent ndiswrapper from
  * using GPL-only symbols it needs.
  */
 if (strcmp(mod->name, "ndiswrapper") == 0)
  add_taint(TAINT_PROPRIETARY_MODULE);

 /* driverloader was caught wrongly pretending to be under GPL */
 if (strcmp(mod->name, "driverloader") == 0)
  add_taint_module(mod, TAINT_PROPRIETARY_MODULE);

 /* Set up MODINFO_ATTR fields */
 /*
  * 根據.modinfo段設置模塊信息。
  */
 setup_modinfo(mod, sechdrs, infoindex);

 /* Fix up syms, so that st_value is a pointer to location. */
 /*
  * 解決當前模塊對其他模塊的符號引用問題,
  * 並找到符號對應的值的地址
  */
 err = simplify_symbols(sechdrs, symindex, strtab, versindex, pcpuindex,
          mod);
 if (err < 0)
  goto cleanup;

 /* Now we've got everything in the final locations, we can
  * find optional sections. */
 /*
  * 獲取__param段的運行時地址,及其存儲的
  * 對象的個數。
  */
 mod->kp = section_objs(hdr, sechdrs, secstrings, "__param",
          sizeof(*mod->kp), &mod->num_kp);
 /*
  * 獲取__ksymtab段的運行時地址,及其存儲的
  * 對象的個數。
  */
 mod->syms = section_objs(hdr, sechdrs, secstrings, "__ksymtab",
    sizeof(*mod->syms), &mod->num_syms);
 /*
  * 獲取__kcrctab段的運行時地址。
  */
 mod->crcs = section_addr(hdr, sechdrs, secstrings, "__kcrctab");
 /*
  * 獲取__ksymtab_gpl段的運行時地址,及其存儲的
  * 對象的個數。
  */
 mod->gpl_syms = section_objs(hdr, sechdrs, secstrings, "__ksymtab_gpl",
        sizeof(*mod->gpl_syms),
        &mod->num_gpl_syms);
 /*
  * 獲取__kcrctab_gpl段的運行時地址。
  */     
 mod->gpl_crcs = section_addr(hdr, sechdrs, secstrings, "__kcrctab_gpl");
 /*
  * 獲取__ksymtab_gpl_future段的運行時地址,及其存儲的
  * 對象的個數。
  */
 mod->gpl_future_syms = section_objs(hdr, sechdrs, secstrings,
        "__ksymtab_gpl_future",
        sizeof(*mod->gpl_future_syms),
        &mod->num_gpl_future_syms);
 /*
  * 獲取__kcrctab_gpl_future段的運行時地址。
  */
 mod->gpl_future_crcs = section_addr(hdr, sechdrs, secstrings,
        "__kcrctab_gpl_future");

#ifdef CONFIG_UNUSED_SYMBOLS
      /*
  * 獲取__ksymtab_unused段的運行時地址,及其存儲的
  * 對象的個數。
  */
 mod->unused_syms = section_objs(hdr, sechdrs, secstrings,
     "__ksymtab_unused",
     sizeof(*mod->unused_syms),
     &mod->num_unused_syms);
 /*
  * 獲取__kcrctab_unused段的運行時地址。
  */
 mod->unused_crcs = section_addr(hdr, sechdrs, secstrings,
     "__kcrctab_unused");
      /*
  * 獲取__ksymtab_unused_gpl段的運行時地址,及其存儲的
  * 對象的個數。
  */
 mod->unused_gpl_syms = section_objs(hdr, sechdrs, secstrings,
        "__ksymtab_unused_gpl",
        sizeof(*mod->unused_gpl_syms),
        &mod->num_unused_gpl_syms);
 /*
  * 獲取__kcrctab_unused_gpl段的運行時地址。
  */
 mod->unused_gpl_crcs = section_addr(hdr, sechdrs, secstrings,
        "__kcrctab_unused_gpl");
#endif
#ifdef CONFIG_CONSTRUCTORS
      /*
  * 獲取.ctors段的運行時地址,及其存儲的
  * 對象的個數。
  */
 mod->ctors = section_objs(hdr, sechdrs, secstrings, ".ctors",
      sizeof(*mod->ctors), &mod->num_ctors);
#endif

#ifdef CONFIG_TRACEPOINTS
        /*
  * 獲取__tracepoints段的運行時地址,及其存儲的
  * 對象的個數。
  */
 mod->tracepoints = section_objs(hdr, sechdrs, secstrings,
     "__tracepoints",
     sizeof(*mod->tracepoints),
     &mod->num_tracepoints);
#endif
#ifdef CONFIG_EVENT_TRACING
      /*
  * 獲取_ftrace_events段的運行時地址,及其存儲的
  * 對象的個數。
  */
 mod->trace_events = section_objs(hdr, sechdrs, secstrings,
      "_ftrace_events",
      sizeof(*mod->trace_events),
      &mod->num_trace_events);
#endif
#ifdef CONFIG_FTRACE_MCOUNT_RECORD
 /* sechdrs[0].sh_size is always zero */
 /*
  * 獲取__mcount_loc段的運行時地址,及其存儲的
  * 對象的個數。
  */
 mod->ftrace_callsites = section_objs(hdr, sechdrs, secstrings,
          "__mcount_loc",
          sizeof(*mod->ftrace_callsites),
          &mod->num_ftrace_callsites);
#endif
#ifdef CONFIG_MODVERSIONS
 if ((mod->num_syms && !mod->crcs)
    || (mod->num_gpl_syms && !mod->gpl_crcs)
    || (mod->num_gpl_future_syms && !mod->gpl_future_crcs)
#ifdef CONFIG_UNUSED_SYMBOLS
    || (mod->num_unused_syms && !mod->unused_crcs)
    || (mod->num_unused_gpl_syms && !mod->unused_gpl_crcs)
#endif
  ) {
  err = try_to_force_load(mod,
     "no versions for exported symbols");
  if (err)
   goto cleanup;
 }
#endif

 /* Now do relocations. */
 for (i = 1; i < hdr->e_shnum; i++) {
  const char *strtab = (char *)sechdrs[strindex].sh_addr;
  unsigned int info = sechdrs[i].sh_info;

  /* Not a valid relocation section? */
  /*
  * 如果當前段附加的段的索引大於段的數目,
  * 則info不是一個有效的索引,不做處理。
  */
  if (info >= hdr->e_shnum)
   continue;

  /* Don't bother with non-allocated sections */
  /*
  * 如果段在執行過程中不占內存,則
  * 不需要進行處理。
  */
  if (!(sechdrs[info].sh_flags & SHF_ALLOC))
   continue;

  /*
  * 如果當前段包含重定向表項,但是沒有補齊內容
  * 則調用apply_relocate來處理。(只關心64位系統)。
  */
  if (sechdrs[i].sh_type == SHT_REL)
   err = apply_relocate(sechdrs, strtab, symindex, i,mod);
        /*
  * 如果當前段包含重定向表項,但是可能有補齊內容
  * 則調用apply_relocate_add來處理。
  */
  else if (sechdrs[i].sh_type == SHT_RELA)
   err = apply_relocate_add(sechdrs, strtab, symindex, i,
      mod);
  if (err < 0)
   goto cleanup;
 }

        /* Find duplicate symbols */
 /*
  * 檢查模塊導出的符號在內核導出的或其他模塊
  * 導出的符號是否有重復的。
  */
 err = verify_export_symbols(mod);
 if (err < 0)
  goto cleanup;

   /* Set up and sort exception table */
      /*
  * 獲取__ex_table段的運行時地址,及其存儲的
  * 對象的個數。
  */
 mod->extable = section_objs(hdr, sechdrs, secstrings, "__ex_table",
        sizeof(*mod->extable), &mod->num_exentries);
 sort_extable(mod->extable, mod->extable + mod->num_exentries);

 /* Finally, copy percpu area over. */
 percpu_modcopy(mod->percpu, (void *)sechdrs[pcpuindex].sh_addr,
        sechdrs[pcpuindex].sh_size);

 /*
  * 初始化模塊中字符串表、符號表相關的成員,
        * 初始化core section中的字符串表和符號表。
  */
 add_kallsyms(mod, sechdrs, hdr->e_shnum, symindex, strindex,
      symoffs, stroffs, secstrings, strmap);
 /*
  * 釋放用於字符串表名稱映射的位圖
  */
 kfree(strmap);
 strmap = NULL;

 if (!mod->taints) {
  /*
  * 處理用於debug的段,不關注這個。
  */
  struct _ddebug *debug;
  unsigned int num_debug;

  debug = section_objs(hdr, sechdrs, secstrings, "__verbose",
        sizeof(*debug), &num_debug);
  if (debug)
   dynamic_debug_setup(debug, num_debug);
 }

 err = module_finalize(hdr, sechdrs, mod);
 if (err < 0)
  goto cleanup;

 /* flush the icache in correct context */
 /*
  * get_fs是用來獲取當前進程的地址限制,當當前的限制是
  * KERNEL_DS時,內核不會檢查參數中的地址類型
  */
 old_fs = get_fs();
 set_fs(KERNEL_DS);

 /*
  * Flush the instruction cache, since we've played with text.
  * Do it before processing of module parameters, so the module
  * can provide parameter accessor functions of its own.
  */
 /*
  * flush_icache_range函數中沒有任何操作,不用考慮。
  */
 if (mod->module_init)
  flush_icache_range((unsigned long)mod->module_init,
      (unsigned long)mod->module_init
      + mod->init_size);
 flush_icache_range((unsigned long)mod->module_core,
      (unsigned long)mod->module_core + mod->core_size);

 set_fs(old_fs);

 mod->args = args;
 if (section_addr(hdr, sechdrs, secstrings, "__obsparm"))
  printk(KERN_WARNING "%s: Ignoring obsolete parameters\n",
        mod->name);

 /* Now sew it into the lists so we can get lockdep and oops
  * info during argument parsing.  Noone should access us, since
  * strong_try_module_get() will fail.
  * lockdep/oops can run asynchronous, so use the RCU list insertion
  * function to insert in a way safe to concurrent readers.
  * The mutex protects against concurrent writers.
  */
 list_add_rcu(&mod->list, &modules);

 /*
  * 解析插入模塊時指定的參數。
  */
 err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, NULL);
 if (err < 0)
  goto unlink;

 /*
  * 在sysfs中創建模塊相應的項
  */
 err = mod_sysfs_setup(mod, mod->kp, mod->num_kp);
 if (err < 0)
  goto unlink;
 /*
  * 添加段屬性
  */
 add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs);
 /*
  * 添加注解屬性
  */
 add_notes_attrs(mod, hdr->e_shnum, secstrings, sechdrs);

 /* Get rid of temporary copy */
 vfree(hdr);

 trace_module_load(mod);

 /* Done! */
 return mod;

 unlink:
 /* Unlink carefully: kallsyms could be walking list. */
 list_del_rcu(&mod->list);
 synchronize_sched();
 module_arch_cleanup(mod);
 cleanup:
 free_modinfo(mod);
 kobject_del(&mod->mkobj.kobj);
 kobject_put(&mod->mkobj.kobj);
 free_unload:
 module_unload_free(mod);
#if defined(CONFIG_MODULE_UNLOAD) && defined(CONFIG_SMP)
 percpu_modfree(mod->refptr);
 free_init:
#endif
 module_free(mod, mod->module_init);
 free_core:
 module_free(mod, mod->module_core);
 /* mod will be freed with core. Don't access it beyond this line! */
 free_percpu:
 if (percpu)
  percpu_modfree(percpu);
 free_mod:
 kfree(args);
 kfree(strmap);
 free_hdr:
 vfree(hdr);
 return ERR_PTR(err);

 truncated:
 printk(KERN_ERR "Module len %lu truncated\n", len);
 err = -ENOEXEC;
 goto free_hdr;
}

 

Copyright © Linux教程網 All Rights Reserved