Linux linker script analysis

Author: Q & A assistant lizuobin

original:

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

In the process of learning in the previous encounter when looking at the code arch_initcall (xxx) functions such as always stunned, for the most basic module_init (xxx) only make use of use, do not understand the principle behind the scenes, is the creation of a know MACHINE_START machine_desc, do not know when machine_desc-> map_io and other function is called.

This article, came to get them, when they met again, refusing to rip off ratio!

Tips: Full text word 9100, involving more code, followed by the establishment of source insight engineering linux source code reading. Oh, recommended collection.

First, look at the link script abbreviated version:

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 = .;        
}

Kernel file is one such organization, but something specific to each segment put, how into them, we do not know when to get out, one by one analysis below.

1, * (. Proc.info.init) segment

Kernel, several proc_info_list defined structure, which in the prototype structure include / asm-arm / procinfo.h in it supports, 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 architecture for the CPU, the source of these structures in the arch / arm / mm / directory, such as proc-arm920.S, proc_info_list structure is defined in the ".proc.info.init" section, when connecting the core, these structures bodies are grouped together, they began to address __proc_info_begin, end address _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

When the kernel starts, first read the chip ID, and then remove the proc_info_list in __proc_info_begin and _proc_info_end, to see whether the kernel supports the CPU.

2, * (. Arch.info.init) segment

* (. Arch.info.init) section, the information is stored in the board supported by the kernel, such as machine ID, the physical address IO fact that, MACHINE_END defined by MACHINE_START.

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

for example:

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

The macro expansion:

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,  
};

When connected to the core, all the structures are located machine_desc labeled ".arch.info.init" section, machine_desc different structures for different boards, u-boot when the kernel calls, gives boards in register r1 (machine ID), __lookup_machine_type in function, the taken ".arch.info.init" machine_desc structures each segment, and comparing r1 machine_desc-> nr kernel determines whether to support the board.

Look at the way the timing of calls and other functions 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);

Order:

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

map_io kernel partition function will also clock, serial port initialization, should pay attention to the time of transplantation kernel! (Different incoming machine ID, the initialization function called natural different)

3、*(.taglist.init)

* (. Taglist.init) segment is stored in the tag uboot passed to the kernel handler. In uboot, the definition of a tag structure, which is stored to be passed to the kernel of the information, the Uboot the tag sequentially discharged at a location kernel agreed as s3c2440 is at 0x30000100, the discharge order is required, must ATAG_CORE labeled the beginning of the tag, the tag marked with ATAG_NONE end.

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标志 */

In the kernel, the use of a function to handle __tagtable tag into a * (. Taglist.init) section

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

Macro definitions in 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 }

To __tagtable (ATAG_CORE, parse_tag_core), for example


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

The kernel boot process, will be used to process parse_tags Tag, it will eventually calls to parse_tag, each taken between __tagtable_begin tagtable and __tagtable_end, comparing their type, if the same tagtable handler is called in to deal with this 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 < &__tagtable_end; t++)
    if (tag->hdr.tag == t->tag) {
      t->parse(tag);
      break;
    }

  return t < &__tagtable_end;
}

4、*(.init.setup)


#define __setup(str, fn)          \
  __setup_param(str, fn, fn, 0)

#define early_param(str, fn)                    /
    __setup_param(str, fn, fn, 1)

struct obs_kernel_param {
  const char *str;
  int (*setup_func)(char *);
  int early;
};

#define __initdata  __attribute__ ((__section__ (".init.data")))
#define __setup_param(str, unique_id, fn, early)      \
  static char __setup_str_##unique_id[] __initdata = str;  \
  static struct obs_kernel_param __setup_##unique_id  \
    __attribute_used__        \
    __attribute__((__section__(".init.setup")))  \
    __attribute__((aligned((sizeof(long)))))  \
    = { __setup_str_##unique_id, fn, early }

for example:

__setup("init=", init_setup);
__setup_param("init=", init_setup, init_setup, 0)

static char __setup_str_init_setup[] __attribute__ ((__section__ (".init.data"))) = "init=";
static struct obs_kernel_param __setup_init_setup  
  __attribute_used__        
  __attribute__((__section__(".init.setup")))  
  __attribute__((aligned((sizeof(long)))))  
  = { __setup_str_init_setup, init_setup, 0 }

Early_param process parameters parse_early_param defined, parse_args defined processing parameters __setup

5、*(.early_param.init)

struct early_params {
  const char *arg;
  void (*fn)(char **p);
};

#define __early_param(name,fn)          \
static struct early_params __early_##fn __used      \
__attribute__((__section__(".early_param.init"))) = { name, fn }

E.g:

__early_param("initrd=", early_initrd);
static struct early_params __early_early_initrd __used  __attribute__((__section__(".early_param.init"))) = 
{
  "initrd=", 
  early_initrd 
}

parse_cmdline process parameter defined __early_param

6、INITCALLS

#define INITCALLS                                      
  *(.initcallearly.init)                        \        
  VMLINUX_SYMBOL(__early_initcall_end) = .;     \        
  *(.initcall0.init)                            \        
  *(.initcall0s.init)                           \        
  *(.initcall1.init)                            \        
  *(.initcall1s.init)                           \        
  *(.initcall2.init)                            \        
  *(.initcall2s.init)                           \        
  *(.initcall3.init)                            \        
  *(.initcall3s.init)                           \        
  *(.initcall4.init)                            \        
  *(.initcall4s.init)                           \        
  *(.initcall5.init)                            \        
  *(.initcall5s.init)                           \        
  *(.initcallrootfs.init)                       \        
  *(.initcall6.init)                            \        
  *(.initcall6s.init)                           \        
  *(.initcall7.init)                            \        
  *(.initcall7s.init)                           \


typedef int (*initcall_t)(void);
#define __define_initcall(level,fn,id) \
  static initcall_t __initcall_##fn##id __attribute_used__ \
  __attribute__((__section__(".initcall" level ".init"))) = fn

#define pure_initcall(fn)              __define_initcall("0",fn,0)
#define core_initcall(fn)              __define_initcall("1",fn,1)
#define core_initcall_sync(fn)          __define_initcall("1s",fn,1s)
#define postcore_initcall(fn)           __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn)     __define_initcall("2s",fn,2s)
#define arch_initcall(fn)             __define_initcall("3",fn,3)
#define arch_initcall_sync(fn)         __define_initcall("3s",fn,3s)
#define subsys_initcall(fn)             __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn)      __define_initcall("4s",fn,4s)
#define fs_initcall(fn)                 __define_initcall("5",fn,5)
#define fs_initcall_sync(fn)            __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn)            __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn)             __define_initcall("6",fn,6)
#define device_initcall_sync(fn)      __define_initcall("6s",fn,6s)
#define late_initcall(fn)             __define_initcall("7",fn,7)
#define late_initcall_sync(fn)          __define_initcall("7s",fn,7s)

To device_initcall (mac_hid_init) as an example:

__define_initcall("6",fn,6)
static initcall_t __initcall_mac_hid_init6 __attribute_used__ __attribute__((__section__(".initcall" 6 ".init"))) 
 = mac_hid_init

Look familiar module_init (xxx_init)

#define module_init(x)     __initcall(x);
#define __initcall(fn) device_initcall(fn)

It seems module_init (xxx_init) in ".initcall6.init" section to create a function pointer to xxx_init

So INITCALLS in a function where the call?

start_kernel

    rest_init()

        kernel_init

            do_basic_setup()

                do_initcalls()
static void __init do_initcalls(void)
{
    initcall_t *call;
    
  for (call = __early_initcall_end; call < __initcall_end; call++)
    do_one_initcall(*call);

  /* Make sure there is no pending stuff from the initcall sequence */
    flush_scheduled_work();
}

That INITCALLS segment of the things one by one in order to call the kernel to start, after the encounter xxx_initcall B will not be ignorant of.

                                           --

Public concern Pak asked Technologies (ID: baiwenkeji) to learn more knowledge embedded Linux dry.

Technical exchanges plus personal prestige 13,266,630,429, verify: CSDN

Published 135 original articles · won praise 401 · views 260 000 +

Guess you like

Origin blog.csdn.net/thisway_diy/article/details/94549928