zoukankan      html  css  js  c++  java
  • 007:_main 和 main(转)

    不进入__main,一样能跑程序,因为__main实现的功能,我们自己也都能实现。至于怎么做,看我在最后转的帖子,看完你就了解了。
    所谓C库,就是一堆写好的C代码,比如你上面说的malloc,这些代码有的是封装好的(能调用但看不到源码),有的是没封装的(能调用能看源码)。

    __main() 和 main()(转载)

    因为我们通常在BOOTLOADER中都已做好了比较细致的初始化工作,包括代码的搬运,所以我们最好别再调用库函数__main(),因为__main()作为ADS集成好的库函数,会对系统进行初始化设置,可能会与我们的初始化发生冲突,故在我们做好初始化后最好别调用__main()。仿真时若调了__main()且没设置entry会报警告,__main()库函数代码不太了解,估计跟ADS初始化有关,库函数__main()要慎用。

        当所有的系统初始化工作完成之后,就需要把程序流程转入主应用程序,即呼叫主应用程序。最简单的一种情况是:

    IMPORT   main
    B        main

        直接从启动代码跳转到应用程序的主函数入口,当然主函数名字可以由用户随便定义。
        在ARM ADS环境中,还另外提供了一套系统级的呼叫机制。
    IMPORT   __main
    B        __main

        __main()是编译系统提供的一个函数,负责完成库函数的初始化和初始化应用程序执行环境,最后自动跳转到main()。所以说,前者是库函数,后者就是我们自己编写的main()主函数;

        因此我们用的B __main其实是执行库函数,然后该库函数再调用我们的main() 函数,因此在单步调试时会看到先要跑一段程序(其实是库函数),然后再单步到我们自己的main函数(这个同时也说明如果有B __main 则就对应必须有main函数,否则编译出错),如果我们用 B main来进入我们的主函数的话,那在单步调试时就看到直接进入到我们自己的main函数了,中间不会看到其他程序;

        那么用B __main和用B main 这两这进入我们的main函数方式有什么不同呢?
        如果采用前者则会由编译器加入一段"段拷贝"程序,即我们说的从加载域到执行域转化程序;而采用后者就没有这个了,因此如果要进行 "段拷贝"只能自己动手编写程序来实现了,完成段拷贝后就可以进入我们的主函数了,当然这个主函数不一定是叫做main(),可以起个其他好听的名字,这个有别于使用B __main方式;不管采用哪种方式进入我们的程序,都要有一段"段拷贝"程序,跑完了段拷贝后才能可以进入我们主程序了!(顺便提一下:startup.s这个文件并没有所谓的"段拷贝"功能,再看也无益!)

        对含有启动程序来说,"执行地址与加载地址相同"不容易实现:
        如果执行地址与加载地址相同哪当然不需要做"段拷贝",但是个人理解编译器还会加入"段拷贝"程序(如果用B __main的话),只是因为条件不满足而不执行而已;但是对含有启动程序来说,"执行地址与加载地址相同"就不容易了.因为启动程序是要烧到非易失存储器里,用来在上电执行的,而这个程序必定会有RW段,如果RW放在非易失存储器,如FLASH,那就不好实现RW功能了,因此要给RW移动到能够实现RW功能的存储器,如SRAM等.因此,对含有启动程序来说,"执行地址与加载地址相同"就不容易实现;程序的入口点在C 库中的__main 处,在该点,库代码执行以下操作:

    1. 将非零(只读和读写)运行区域从其载入地址复制到运行地址。
    2. 清零ZI 区域。
    3. 跳转到__rt_entry。   KEIL ARM的帮助文件里对__main的描述:
    程序的入口点在 C 库中的__main  处,在该点,库代码执行以下操作:
    将非根运行区(只读和读写)从其载入地址复制到运行地址。同如果任何区被压缩,将它们从载入地址解压到运行地址。更多信息,请参阅链接器用户指南 。
    清零 ZI 区域。
    跳转到 __rt_entry。
    如果不希望库执行这样的操作,可以如 例 2.1 中所示定义跳转到 __rt_entry的自有__main。
    例 2.1. __main 和 __rt_entry

    IMPORT __rt_entry
        EXPORT __main
        ENTRY
    __main
        B     __rt_entry
        END

    库函数__rt_entry() 运行程序步骤如下:
    调用 __rt_stackheap_init() 建立栈和堆。
    调用 __rt_lib_init() 初始化引用的库函数、初始化语言环境 (locale),如果必要,还将为main()函数建立argc 和 argv。对于 C++,为任何顶级对象调用构造函数。
    对于C++,为任何顶级对象调用构造函数作为 __cpp_initialize__aeabi_。更多信息,请参阅 C++ 初始化,建立和销毁 。
    调用 main()函数 ———— 应用程序的用户级根。
    从main()函数中,应用程序除了调用其他函数,还可调用库函数。有关详细信息,请参阅从 main()函数中调用库函数。从main()函数中调用库函数。
    使用main()函数返回的值调用exit()。
    从上面就可以看到__user_initial_stackheap的返回值到底是在哪里用到了,
    就是“调用 __rt_stackheap_init() 建立栈和堆”这里,实际上还有其它的C库函数有用到,但肯定都是在__main之后,因为__main是C库函数的入口。

    我说上面的代码没有调用__user_initial_stackheap,因为很明显,把STARTUP.S从头读到尾,根本没有调用__user_initial_stackheap的语句。那它在哪呢?“rt_misc.h”这个文件里有定义。

    /*
    * This can be defined to override the standard memory models' way
    * of determining where to put the initial stack and heap.
    *
    * The input parameters R0 and R2 contain nothing useful. The input
    * parameters SP and SL are the values that were in SP and SL when
    * the program began execution (so you can return them if you want
    * to keep that stack).
    *
    * The two `limit' fields in the return structure are ignored if
    * you are using the one-region memory model: the memory region is
    * taken to be all the space between heap_base and stack_base.
    */
    struct __initial_stackheap {
        unsigned heap_base;                /* low-address end of initial heap */
        unsigned stack_base;               /* high-address end of initial stack */
        unsigned heap_limit;               /* high-address end of initial heap */
        unsigned stack_limit;              /* low-address end of initial stack */
    };
    extern __value_in_regs struct __initial_stackheap
    __user_initial_stackheap(unsigned /*R0*/, unsigned /*SP*/,
                             unsigned /*R2*/, unsigned /*SL*/);

    由于ARM应用的灵活性,可以通过分散加载文件来定义代码和变量的位置,所以堆栈的地址并不固定,这样,在ARM C库里的函数用到堆栈的地址时候,就用上面定义的变量来代替。

    说它是不是多余,其实也不算,因为一般的STARTUP.S都只初始了栈,就是SP。没有初始堆,所以在这个函数里初始堆还是有必要的,而且这个函数的作用主要是返回堆栈的地址,这个地址除了初始化,还可以有其它的作用。毕竟我们不大方便在C语言里直接取SP的地址。

    如果没有 B __mian 的话,就没有调用C库,也就用不到初始化C库。而不调用__main,直接进入main()当然是可以实现的,具体方法网上也有,你可以查一下。

  • 相关阅读:
    NYOJ 625 笨蛋的难题(二)
    NYOJ 102 次方求模
    ZJU Least Common Multiple
    ZJUOJ 1073 Round and Round We Go
    NYOJ 709 异形卵
    HDU 1279 验证角谷猜想
    BNUOJ 1015 信息战(一)——加密程序
    HDU 1202 The calculation of GPA
    "蓝桥杯“基础练习:字母图形
    "蓝桥杯“基础练习:数列特征
  • 原文地址:https://www.cnblogs.com/afeibfp/p/2851152.html
Copyright © 2011-2022 走看看