• __attribute__ ((__section__ (".init.text")))


    在kernel中有很多__init,这个东东到底是何方神圣捏?且听小生我一一道来。
    下面是其定义:
    file:/include/linux/init.h
     43 #define __init      __attribute__ ((__section__ (".init.text"))) __cold
     44 #define __initdata  __attribute__ ((__section__ (".init.data")))
     45 #define __exitdata  __attribute__ ((__section__(".exit.data")))
     46 #define __exit_call __attribute_used__ __attribute__ ((__section__ (".exitcall.exit")))

    也许你会问那 __attribute__ ((__section__ (".init.text"))) __cold是什么东东阿?
    且看 info gcc C Extensions Attribute Syntax
    section ("SECTION-NAME")'
         Normally, the compiler places the objects it generates in sections
         like `data' and `bss'.  Sometimes, however, you need additional
         sections, or you need certain particular variables to appear in
         special sections, for example to map to special hardware.  The
         `section' attribute specifies that a variable (or function) lives
         in a particular section.  For example, this small program uses
         several specific section names:
              struct duart a __attribute__ ((section ("DUART_A"))) = { 0 };
              struct duart b __attribute__ ((section ("DUART_B"))) = { 0 };
              char stack[10000] __attribute__ ((section ("STACK"))) = { 0 };
              int init_data __attribute__ ((section ("INITDATA"))) = 0;

              main()
              {
                /* Initialize stack pointer */
                init_sp (stack + sizeof (stack));

                /* Initialize initialized data */
                memcpy (&init_data, &data, &edata - &data);

                /* Turn on the serial ports */
                init_duart (&a);
                init_duart (&b);
              }

         Use the `section' attribute with an _initialized_ definition of a
         _global_ variable, as shown in the example.  GCC issues a warning
         and otherwise ignores the `section' attribute in uninitialized
         variable declarations.

         You may only use the `section' attribute with a fully initialized
         global definition because of the way linkers work.  The linker
         requires each object be defined once, with the exception that
         uninitialized variables tentatively go in the `common' (or `bss')
         section and can be multiply "defined".  You can force a variable
         to be initialized with the `-fno-common' flag or the `nocommon'
         attribute.

         Some file formats do not support arbitrary sections so the
         `section' attribute is not available on all platforms.  If you
         need to map the entire contents of a module to a particular
         section, consider using the facilities of the linker instead.

    简单来说是指示gcc把标记的数据或者函数放到指定sector。
    linux中把一些启动及初始化时候用的数据用__init标识,然后在适当的时候把它们释放,回收内存。
    说到这个__init,就不能不说module_init,subsys_initcall。
    在init.h中我们能够找到 #define subsys_initcall(fn)     __define_initcall("4",fn,4)
    又是一个宏定义,简直是无极中的圆环套圆环之城阿。
    file:/include/linux/init.h
    100 /* initcalls are now grouped by functionality into separate
    101  * subsections. Ordering inside the subsections is determined
    102  * by link order.
    103  * For backwards compatibility, initcall() puts the call in
    104  * the device init subsection.
    105  *
    106  * The `id' arg to __define_initcall() is needed so that multiple initcalls
    107  * can point at the same handler without causing duplicate-symbol build errors.
    108  */
    109

    110 #define __define_initcall(level,fn,id)
    111     static initcall_t __initcall_##fn##id __attribute_used__
    112     __attribute__((__section__(".initcall" level ".init"))) = fn


    subsys_initcall(usb_init)转换后就变成了 static initcall_t  __initcall_usbinit4   __attribute_used__
    __attribute__((__section__(".initcall 4.init"))) = usb_init
    就是把usb_init的函数入口指针存放在.initcall4.init中。
    file:/include/asm-generic/vmlinux.lds.h
    239 #define INITCALLS                          
    240     *(.initcall0.init)                     
    241     *(.initcall0s.init)                    
    242     *(.initcall1.init)                     
    243     *(.initcall1s.init)                    
    244     *(.initcall2.init)                     
    245     *(.initcall2s.init)                    
    246     *(.initcall3.init)                     
    247     *(.initcall3s.init)                    
    248     *(.initcall4.init)                     
    249     *(.initcall4s.init)                    
    250     *(.initcall5.init)                     
    251     *(.initcall5s.init)                    
    252     *(.initcallrootfs.init)                    
    253     *(.initcall6.init)                     
    254     *(.initcall6s.init)                    
    255     *(.initcall7.init)                     
    256     *(.initcall7s.init)


    file:/arch/kernel/vmlinux_32.lds.S
    144   .initcall.init : AT(ADDR(.initcall.init) - LOAD_OFFSET) {
    145     __initcall_start = .;
    146     INITCALLS
    147     __initcall_end = .;
    148   }

    展开
       .initcall.init : AT(ADDR(.initcall.init) - LOAD_OFFSET) {
         __initcall_start = .;
         *(.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)
         __initcall_end = .;
       }


    那么系统是如何执行这些函数呢?
    此话就长了阿~ 话说盘古开天芙蓉姐姐补天后我们来到了main.c这个linux中举足轻重的文件
    进入start_kernel
    start_kernel  -->rest_init() -->kernel_init()  --> do_basic_setup()  -->do_initcalls()

    这个do_initcalls()就是调用这些函数的地方。
    file:/init/main.c
    662 static void __init do_initcalls(void)
    663 {
    664     initcall_t *call;
    665     int count = preempt_count();
    666
    667     for (call = __initcall_start; call < __initcall_end; call++) {
    668         ktime_t t0, t1, delta;
    669         char *msg = NULL;
    670         char msgbuf[40];
    671         int result;
    672
    673         if (initcall_debug) {
    674             printk("Calling initcall 0x%p", *call);
    675             print_fn_descriptor_symbol(": %s()",
    676                     (unsigned long) *call);
    677             printk(" ");
    678             t0 = ktime_get();
    679         }
    680
    681         result = (*call)();
    682
    683         if (initcall_debug) {
    684             t1 = ktime_get();
    685             delta = ktime_sub(t1, t0);
    686
    687             printk("initcall 0x%p", *call);
    688             print_fn_descriptor_symbol(": %s()",
    689                     (unsigned long) *call);
    690             printk(" returned %d. ", result);
    691
    692             printk("initcall 0x%p ran for %Ld msecs: ",
    693                 *call, (unsigned long long)delta.tv64 >> 20);
    694             print_fn_descriptor_symbol("%s() ",
    695                 (unsigned long) *call);
    696         }
    697
    698         if (result && result != -ENODEV && initcall_debug) {
    699             sprintf(msgbuf, "error code %d", result);
    700             msg = msgbuf;
    701         }
    702         if (preempt_count() != count) {
    703             msg = "preemption imbalance";
    704             preempt_count() = count;
    705         }
    706         if (irqs_disabled()) {
    707             msg = "disabled interrupts";
    708             local_irq_enable();
    709         }
    710         if (msg) {
    711             printk(KERN_WARNING "initcall at 0x%p", *call);
    712             print_fn_descriptor_symbol(": %s()",
    713                     (unsigned long) *call);
    714             printk(": returned with %s ", msg);
    715         }
    716     }
    717
    718     /* Make sure there is no pending stuff from the initcall sequence */
    719     flush_scheduled_work();
    720 }

    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    我想把某一个函数放入data段中(放到ram里面去)。
    在这个函数前加了前缀:

    1. void __attribute__((.section ".data.mydata")))myfunc(){
    2.   ……
    3. ……
    4.   ……
    5. }


    编译成功后通过dump文件可以看到myfunc这个函数放在了从0x110 - 0x180这一段(0-0x3ffff是sram)
    所以说_myfunc这个标号的地址应该是0x110,但是通过nm出来的结果却是0x1ad??在0x110处的标号是__gnu__compiled_c。
    所有我的程序在执行这个函数的时候总是跑飞了。请问为什么会出现这样的问题。是不是编译器有错?还是我需要更改参数呢?请各位指教!

    我是直接在data段中写的:

    1. SECTIONS
    2. {
    3. ……
    4. .data 0x100:{

    5. ……
    6. *(.data)
    7. ……
    8. *(.data.mydata)
    9. ……
    10. }

    11. ……
    12. }
    复制代码


    我是这样写的阿。。。会出现这样的情况吗?

  • 相关阅读:
    delphi TreeView 从数据库添加节点的四种方法
    mac攻略(3) -- brew使用
    mac攻略(2) -- apache站点配置
    mac攻略(1) -- 简单配置php开发环境
    Mac通过brew安装reds、memcached
    golang urlencode
    golang GET 出现 x509: certificate signed by unknown authority
    git取消文件跟踪
    golang使用http client发起get和post请求示例
    PHP判断SQL语句是否合法:mysqli_error()
  • 原文地址:https://www.cnblogs.com/lixiaofei1987/p/3198638.html
Copyright © 2020-2023  润新知