<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          原來全局變量是這樣初始化的

          共 10350字,需瀏覽 21分鐘

           ·

          2024-04-11 01:53


          最近,有個好學的小伙子突然問了我一個問

          全局變量的初始值,是在哪里賦值的?

          這個問題雖然說不是很重要,但是我很好奇。

          為了給講清楚這個原理過程,我專門建立一個基于Renesas RH850的簡單工程,挖一挖里面的技術(shù)細節(jié)。

          我在main.c文件中定義了隨便這幾個變量

                

          int counter, accumulator = 0, limit_value = 1000000; unsigned char str_aa55[2] = {0xAA,0x55}; unsigned int int_1122334455667788 = 0x11223344; unsigned int int_55667788 = 0x55667788; int bss_val; void main(void) {     }

          然后,直接仿真查看,跟你我想的一樣,在main函數(shù)之前就初始化完成了,即這些變量都自動初始化賦值了。

          讓人好奇的是,它是怎么做到的?

          單片機的啟動程序一般都是很簡單的,即使匯編也沒多少行,直接翻出來看看也許會知道答案。

                

            -- Clear local RAM   mov ___ghs_ramstart, r6  -- start of local RAM   mov ___ghs_ramend, r7    -- end of local RAM   mov r0, r1 1:   st.dw   r0, 0[r6]   addi    8, r6, r6   cmp r7, r6   bl 1b   -- Jump to the HW initialisation function   jarl  ___lowinit, lp   -- Jump to the initialisation functions of the library   -- and from there to main()   jr __start

          以上這段匯編,根據(jù)旁邊的注釋其實很容易理解,前半部分就是將內(nèi)存Local RAM初始化清零,即這段匯編可以見到梳理成

          RAM清零--->執(zhí)行___lowinit--->執(zhí)行__start--->進入main函數(shù)

          既然前面給RAM清零了,那么此時的全局變量應該全是0值吧,那可以推測,給全局變量賦初始值應該是在___lowinit__start了,但是這兩個東西是編譯環(huán)境里某個庫的,暫時看不到源碼。

          但是,最終通過仿真查看變量值的方式,可以定位,給全局變量賦初始值是在__start里面。

          此時,雖然我知道了它在哪里給全局變量初始化了,但是并不知道是怎樣初始化的。

          我還是很好奇,本著刨根問底的精神繼續(xù)挖掘。

          但是我在這個RH850的代碼工程里面是找不到這個__start的源碼內(nèi)容的,仿真看匯編折騰了半天,突然想了下,為啥不換個其他工程試試,例如試試NXP S32K的?

          于是,我創(chuàng)建了一個NXP S32K1xx的代碼工程,仍然定義這幾個變量

                

          int counter, accumulator = 0, limit_value = 1000000; unsigned char str_aa55[2] = {0xAA,0x55}; unsigned int  int_1122334455667788 = 0x11223344; unsigned int  int_55667788 = 0x55667788; int bss_val;

          同樣的討論,直接翻啟動文件的匯編代碼

                

              /* Init .data and .bss sections */     ldr     r0,=init_data_bss     blx     r0

          還是歐美的芯片簡單粗暴,不像小日子做的初始化還有藏進庫里。這不是很明顯嘛,init_data_bss就是初始化全局變量的,以下截取了部分代碼,也很容易理解。

                

          void init_data_bss(void) { /* ...... */     /* Data */     data_ram        = (uint8_t *)__DATA_RAM;     data_rom        = (uint8_t *)__DATA_ROM;     data_rom_end    = (uint8_t *)__DATA_END;     /* ...... */     /* BSS */     bss_start       = (uint8_t *)__BSS_START;     bss_end         = (uint8_t *)__BSS_END;     /* ...... */         /* Copy initialized data from ROM to RAM */     while (data_rom_end != data_rom)     {         *data_ram = *data_rom;         data_ram++;         data_rom++;     }         /* ...... */     /* Clear the zero-initialized data section */     while(bss_end != bss_start)     {         *bss_start = 0;         bss_start++;     }     /* ...... */ }


          ?


          data段data_ram的初始化內(nèi)容就是從data_rom來,而data_rom是從__DATA_ROM來。那么,__DATA_ROM是什么東西,從哪里呢?搜一搜工程里面的代碼,很簡單,這是從ld文件來
                
                    
                      
          /* Specify the memory areas */ MEMORY {   /* … */   /* SRAM_L */ m_data   (RW)  : ORIGIN = 0x1FFF8000, LENGTH = 0x00008000  m_data_2  (RW)  : ORIGIN = 0x20000000, LENGTH = 0x00007000
            /* … */ .data : AT(__DATA_ROM)   {     . = ALIGN(4);     __DATA_RAM = .;     __data_start__ = .;      /* Create a global symbol at data start. */     *(.data)                 /* .data sections */     *(.data*)                /* .data* sections */     . = ALIGN(4);     __data_end__ = .;        /* Define a global symbol at data end. */   } > m_data
            __DATA_END = __DATA_ROM + (__data_end__ - __data_start__);   __CODE_ROM = __DATA_END; /* Symbol is used by code initialization. */

            /* Uninitialized data section. */   .bss :   {     /* This is used by the startup in order to initialize the .bss section. */     . = ALIGN(4);     __BSS_START = .;     __bss_start__ = .;     *(.bss)     *(.bss*)     *(COMMON)     . = ALIGN(4);     __bss_end__ = .;     __BSS_END = .;   } > m_data_2
          這里簡單介紹下,帶有初始化值(非0)全局變量(例如unsignedint  int_55667788 = 0x55667788;),都是定義在data段的,而未定義初始化值的全局變量,是分在bss段的(例如int bss_val;)。
          到底是不是我說的這樣子,直接查看map文件中的變量名和對應地址或段名就知道了
                
                  .data           0x1fff8400      0x42c load address 0x000009cc                0x1fff8400                . = ALIGN (0x4)                0x1fff8400                __DATA_RAM = .                0x1fff8400                __data_start__ = . *(.data) *(.data*) .data.limit_value                0x1fff8400        0x4 ./src/main.o                0x1fff8400                limit_value .data.str_aa55                0x1fff8404        0x2 ./src/main.o                0x1fff8404                str_aa55 *fill*         0x1fff8406        0x2  .data.int_11223344                0x1fff8408        0x4 ./src/main.o                0x1fff8408                int_11223344 .data.int_55667788                0x1fff840c        0x4 ./src/main.o                0x1fff840c                int_55667788.bss            0x20000000       0x28                0x20000000                . = ALIGN (0x4)                0x20000000                __BSS_START = .                0x20000000                __bss_start__ = . *(.bss) *(.bss*) .bss.accumulator                0x2000001c        0x4 ./src/main.o                0x2000001c                accumulator *(COMMON) COMMON         0x20000020        0x8 ./src/main.o                0x20000020                bss_val                0x20000024                counter                0x20000028                . = ALIGN (0x4)                0x20000028                __bss_end__ = .                0x20000028                __BSS_END = .                0x000009cc                __DATA_ROM = .
                
              
          另外,從這map文件里也可以看到,這個__DATA_ROM對應的地址是0x000009cc,也就是說,這些
                
                    int limit_value = 1000000;
                  
                  
                    unsigned char str_aa55[2] = {0xAA,0x55};
                  
                  
                    unsigned int  int_1122334455667788 = 0x11223344;
                  
                  
                    unsigned int  int_55667788 = 0x55667788;
                  
                
          等等變量的的初始值是來源于0x000009cc這里。 那就直接查看生成的hex文件


          54ec6079e5ed9240c1f19c2b40d35c0b.webp


          是不是很巧,是不是很妙?! 不過,還是很好奇,這是怎么做到的,怎么恰巧這些值就在這個地址呢?
          秘密就在于ld文件里的這個語句:.data : AT(__DATA_ROM)意思是,定義在data段的變量對應的初始化值,就放在__DATA_ROM中。就這么簡單,剩下的交給編譯器就行了。對這個問題一直記在心里,是一個化不開的結(jié)。終于某一天,我居然真的無意中發(fā)現(xiàn)了__start的源碼,感嘆小日子真的有一手。
          這里涉及到的內(nèi)容比較多也比較復雜,后續(xù)再寫個文章講解下GreenHills編譯器的啟動鏈接庫到底干了什么,敬請關(guān)注!
          如果你喜歡我的文章,請關(guān)注,并 轉(zhuǎn)發(fā) 點贊 在看 ,這是對我莫大的鼓勵!

          e4c29475167c12889b73f73c6588f025.webp


          瀏覽 42
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  韩日三级片电影在线观看 | 亚洲午夜久久久久久久久红桃 | 超碰免费视| 国产欧美综合一区二区三区 | 午夜精品久久久久久久久久久久 |