您当前的位置: 首页 >  linux

韦东山

暂无认证

  • 0浏览

    0关注

    506博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Linux 链接脚本分析

韦东山 发布时间:2019-07-03 10:09:36 ,浏览量:0

作者:答疑助手lizuobin

原文:

https://blog.csdn.net/lizuobin2/article/details/51779064

在前面学习的过程中,看代码时遇到 arch_initcall(xxx) 等函数总是愣的,对于最基础的module_init(xxx) 也只是拿来用用,不知道幕后的原理,知道 MACHINE_START 是创建了一个machine_desc ,却不知道machine_desc->map_io 等函数何时被调用。

这篇文章,就来搞定它们,再遇到它们时,拒绝懵比!

友情提示:全文9100字,涉及代码较多,请建立source insight工程跟着linux源码阅读。建议收藏哦。

首先,来看链接脚本的缩略版:

SECTIONS
{
    .init : {            /* Init code and data        */
            INIT_TEXT
        _einittext = .;
        __proc_info_begin = .;
            *(.proc.info.init)
        __proc_info_end = .;
        __arch_info_begin = .;
            *(.arch.info.init)
        __arch_info_end = .;
        __tagtable_begin = .;
            *(.taglist.init)
        __tagtable_end = .;
        . = ALIGN(16);
        __setup_start = .;
            *(.init.setup)
        __setup_end = .;
        __early_begin = .;
            *(.early_param.init)
        __early_end = .;
        __initcall_start = .;
            INITCALLS
        __initcall_end = .;        
}

内核的文件就是这样组织的,但是具体每个段放的什么东西,怎么放进去,何时取出来我们不知道,下面一个一个分析。

1、*(.proc.info.init) 段

内核中,定义了若干个proc_info_list 结构,它的结构原形在include/asm-arm/procinfo.h 中,表示它所支持的CPU。

struct proc_info_list {
  unsigned int    cpu_val;
  unsigned int    cpu_mask;
  unsigned long    __cpu_mm_mmu_flags;  /* used by head.S */
  unsigned long    __cpu_io_mmu_flags;  /* used by head.S */
  unsigned long    __cpu_flush;    /* used by head.S */
  const char    *arch_name;
  const char    *elf_name;
  unsigned int    elf_hwcap;
  const char    *cpu_name;
  struct processor  *proc;
  struct cpu_tlb_fns  *tlb;
  struct cpu_user_fns  *user;
  struct cpu_cache_fns  *cache;
};

对于ARM架构的CPU,这些结构体的源码在arch/arm/mm/目录下,比如 proc-arm920.S , proc_info_list 结构被定义在 “.proc.info.init” 段,在连接内核时,这些结构体被组织在一起,开始地址 __proc_info_begin ,结束地址 _proc_info_end 。

.section ".proc.info.init", #alloc, #execinstr
  .type  __arm920_proc_info,#object
__arm920_proc_info:
  .long  0x41009200
  .long  0xff00fff0
  .long   PMD_TYPE_SECT | \
    PMD_SECT_BUFFERABLE | \
    PMD_SECT_CACHEABLE | \
    PMD_BIT4 | \
    PMD_SECT_AP_WRITE | \
    PMD_SECT_AP_READ
  .long   PMD_TYPE_SECT | \
    PMD_BIT4 | \
    PMD_SECT_AP_WRITE | \
    PMD_SECT_AP_READ
  b  __arm920_setup
  .long  cpu_arch_name
  .long  cpu_elf_name
  .long  HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB
  .long  cpu_arm920_name
  .long  arm920_processor_functions
  .long  v4wbi_tlb_fns
  .long  v4wb_user_fns
#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
  .long  arm920_cache_fns
#else
  .long  v4wt_cache_fns
#endif
  .size  __arm920_proc_info, . - __arm920_proc_info

在内核启动时,首先读取出芯片ID,然后就在__proc_info_begin 和 _proc_info_end 取出 proc_info_list ,看内核是否支持这个CPU。

2, *(.arch.info.init) 段

*(.arch.info.init) 段,存放的是内核所支持的单板信息如机器ID、其实IO物理地址等,它由 MACHINE_START、MACHINE_END 定义。

#define MACHINE_START(_type,_name)                      \   
static const struct machine_desc __mach_desc_##_type    \  
__used                                                  \  
__attribute__((__section__(".arch.info.init")))= {      \  
        .nr             = MACH_TYPE_##_type,            \  
        .name           = _name,  
   
#define MACHINE_END                                     \   
};

举个例子:

MACHINE_START(HALIBUT,"Halibut Board (QCT SURF7200A)")  
  .boot_params      = 0x10000100,  
  .map_io           = halibut_map_io,  
  .init_irq         = halibut_init_irq,  
  .init_machine     = halibut_init,  
  .timer            = &msm_timer,  
MACHINE_END

将宏展开:

struct machine_desc __mach_desc_HALIBUT{  
__used                                                            
__attribute__((__section__(".arch.info.init")))= {  
  .nr               = MACH_TYPE_HALIBUT,                
  .name             = "HalibutBoard (QCT SURF7200A)",  
  .boot_params      = 0x10000100,  
  .map_io           = halibut_map_io,  
  .init_irq         = halibut_init_irq,  
  .init_machine     = halibut_init,  
  .timer            = &msm_timer,  
};

内核连接时,所有的 machine_desc 结构都会位于 “.arch.info.init” 段,不同的 machine_desc 结构体用于不同的单板,u-boot 调用内核时,会在 r1 寄存器中给出开发板的标记(机器ID),在__lookup_machine_type 函数中,将取出 “.arch.info.init” 段中的每一个 machine_desc 结构,比较 r1 与 machine_desc->nr 判断内核是否支持该单板。

顺便看一下 map_io 等函数的调用时机:

start_kernel
  setup_arch(&command_line);
    init_arch_irq = mdesc->init_irq;
    system_timer = mdesc->timer;
    init_machine = mdesc->init_machine;
    paging_init(mdesc)
      devicemaps_init(mdesc);
        mdesc->map_io()
init_IRQ()
   init_arch_irq();
time_init()
 system_timer->init();
rest_init();
 kernel_init
   do_basic_setup()
     do_initcalls()
       init_machine()
static int __init customize_machine(void)
{
  /* customizes platform devices, or adds new ones */
  if (init_machine)
    init_machine();
  return 0;
}
arch_initcall(customize_machine);

先后顺序:

start_kernel -》setup_arch -》 map_io -》 init_irq -》 timer -》 init_machine

map_io 函数中 会对内核进行分区还有时钟、串口的初始化,移植内核时需注意!(传入的机器ID不同,调用的初始化函数自然不同)

3、*(.taglist.init)

*(.taglist.init) 段存放的是 uboot 传递到内核的 tag 的处理函数。在 uboot 中,定义了一个 tag 结构体,里面存放要传递给内核的信息,Uboot 将 tag 依次排放在和内核约定的地点,如s3c2440是 0x30000100 处,排放顺序是有要求的,必须以 ATAG_CORE 标记的 tag 开头,以 ATAG_NONE 为标记的 tag 结尾。

struct tag {

  struct tag_header {
    u32 size;       /* 表示tag数据结构的联合u实质存放的数据的大小*/
    u32 tag;        /* 表示标记的类型 */
  }hdr;

  union {
    struct tag_core           core;
    struct tag_mem32      mem;
    struct tag_videotext   videotext;
    struct tag_ramdisk     ramdisk;
    struct tag_initrd  initrd;
    struct tag_serialnr       serialnr;
    struct tag_revision      revision;
    struct tag_videolfb     videolfb;
    struct tag_cmdline     cmdline;
    /*
    * Acorn specific
    */
    struct tag_acorn  acorn;
    /*
    * DC21285 specific
    */
    struct tag_memclk      memclk;
  } u;
};


setup_start_tag (bd);    /*设置ATAG_CORE标志*/
setup_memory_tags (bd);                     /*设置内存标记*/
setup_commandline_tag (bd, commandline);  /*设置命令行标记*/
...
setup_end_tag (bd);      /*设置ATAG_NONE标志 */

在内核中,使用 __tagtable 来将处理 tag 的函数放到 *(.taglist.init) 段

arch\arm\kernel\setup.c

__tagtable(ATAG_CORE, parse_tag_core);
__tagtable(ATAG_MEM, parse_tag_mem32);
__tagtable(ATAG_VIDEOTEXT, parse_tag_videotext);
__tagtable(ATAG_CMDLINE, parse_tag_cmdline);
__tagtable(ATAG_REVISION, parse_tag_revision);

宏定义在 setup.h (include\asm-arm)

struct tagtable {
  __u32 tag;
  int (*parse)(const struct tag *);
};


#define __tag __used __attribute__((__section__(".taglist.init")))
#define __tagtable(tag, fn) \
static struct tagtable __tagtable_##fn __tag = { tag, fn }

以 __tagtable(ATAG_CORE, parse_tag_core)为例


static struct tagtable __tagtable_parse_tag_core __used __attribute__((__section__(".taglist.init"))) = {
  ATAG_CORE,
  parse_tag_core
}

在内核启动过程中,会使用 parse_tags 来处理 tag ,它最终会调用到 parse_tag ,取出 __tagtable_begin 和 __tagtable_end 之间的每一个 tagtable ,比较它们的类型,如果相同则调用 tagtable 里的处理函数来处理这个tag 。

if (mdesc->boot_params)
    tags = phys_to_virt(mdesc->boot_params);
  parse_tags(tags);

static void __init parse_tags(const struct tag *t)
{
  for (; t->hdr.size; t = tag_next(t))
    if (!parse_tag(t))
      printk(KERN_WARNING
        "Ignoring unrecognised tag 0x%08x\n",
        t->hdr.tag);
}
static int __init parse_tag(const struct tag *tag)
{
  extern struct tagtable __tagtable_begin, __tagtable_end;
  struct tagtable *t;


  for (t = &__tagtable_begin; t hdr.tag == t->tag) {
      t->parse(tag);
      break;
    }

  return t             
关注
打赏
1658827356
查看更多评论
0.0477s