C_and_CPP 板


LINE

之前在《bss section (0) ( https://goo.gl/jlDiYt )》留下來的問題要在這篇解答。 先來看看 b.c, 這程式很簡單, b.c L6 int i; 會放在 bss, p() 會把 c=i+1 用 itoa() 轉成 string 印出。預期得到 1, 因為在 C 中, 教科書都告訴我們 int i; 雖然 沒有初值, 但 c 會初始為 0, 這不是憑空得來的, 裡頭有魔法, 讓我們來惡搞一下 bss 吧! 這是 bare-metal 程式, 和一般程式的編譯以及執行方式有些不同。 b.c 1 asm(".code16gcc\n"); 2 3 typedef unsigned char u8; 4 typedef unsigned int u32; 5 6 int i; 7 char* itoa(int n, char* str); 8 9 void print(const char *s) 10 { 11 while(*s) 12 { 13 __asm__ __volatile__ ("int $0x10" : : "a"(0x0E00 | *s), "b"(7)); 14 s++; 15 } 16 } 17 18 int p() 19 { 20 int c=i+1; 21 char arr[6]="a"; 22 char *s = arr; 23 24 //const char *str="test"; 25 //volatile u8 *video_addr = (u8*)0xB8000; 26 //asm("movw $0xb000 %gs"); 27 //*video_addr = 'a'; 28 s = itoa(c, s); 29 print(s); 30 return c; 31 } 32 33 #if 1 34 char* itoa(int n, char* str) 35 { 36 char digit[]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 37 char* p=str; 38 char* head=str; 39 int radix = 10; 40 41 // if(!p || radix < 2 || radix > 36) 42 // return p; 43 if (n==0) 44 { 45 *p++='0'; 46 *p=0; 47 return str; 48 } 50 if (radix == 10 && n < 0) 51 { 52 *p++='-'; 53 n= -n; 54 } 56 while(n) 57 { 58 *p++=digit[n%radix]; 59 n/=radix; 60 } 61 *p=0; 63 for (--p; head < p ; ++head, --p) 64 { 65 char temp=*head; 66 *head=*p; 67 *p=temp; 68 } 70 return str; 71 } 72 #endif 73 c_init.s 1 .code16 2 .extern __bss_start__ 3 .extern __bss_end__ 4 5 .text 6 .global _start 7 _start: 8 mov %cs, %ax 9 mov %ax, %ds 10 mov %ax, %es 11 12 # setup stack 13 mov %ax, %ss 14 mov $0xffff, %sp 15 16 # call disp_str 17 call init_bss_asm 18 call p 19 # call disp_str2 20 jmp . 41 42 43 # init bss 44 init_bss_asm: 45 movw $__bss_end__, %di /* Destination */ 46 movw $__bss_start__, %si /* Source */ 47 movw $0x0, %ax 48 movw %ax, %gs 49 jmp 2f 50 1: 51 movw %si, %ax 52 movb $0x1, %gs:(%eax) 53 add $1, %si 54 55 2: 56 cmpw %di, %si 57 jne 1b 58 ret 59 編譯/執行方式 as --32 -o c_init.o c_init.s gcc -std=c99 -fno-stack-protector -m32 -ffreestanding -fno-builtin -g -Iinclude -I../include -fomit-frame-pointer -fno-exceptions -fno-asynchronous-unwind-tables -fno-unwind-tables -Os -c b.c ld -m elf_i386 -nostdlib -g -o c_init.elf -Tbss.lds c_init.o b.o objcopy -R .pdr -R .comment -R.note -S -O binary c_init.elf c_init.bin c_init.bin 為要執行的 bin 檔 dd if=c_init.bin of=boot.img bs=512 count=1 dd if=/dev/zero of=boot.img skip=1 seek=1 bs=512 count=2879 透過 qemu 執行 qemu-system-i386 -fda boot.img 由 c_init.s _start 開始執行, 執行 init_bss_asm 呼叫 p(), p() 把 i+1 印出來。i 位於 bss, 所以應該看到 1 被印出來 (fig 1), 不過執行結果是 16843010, 怎麼不是 1, 我又搞錯了嗎? 非也, 這是刻意營造的結果, 我把 bss 初始為 0x01010101 , 加上 0x1 後為 0x01010102 = 十進位 16843010。 因為受限於開機磁區的 512 byte 限制, 所 以編譯這段程式碼需要加上 -Os, 讓它可以小於 512 byte。 我加入以下選項, 讓 gcc 不要產生 .eh_frame section, .eh_frame section 對 bare-metal 用處不大, 還佔了 0x8c 的空間。 -fomit-frame-pointer -fno-exceptions -fno-asynchronous-unwind-tables -fno-unwind-tables .eh_frame, .eh_frame_hdr - ref:how to remove that trash ( https://goo.gl/MLj0ql ) 之前用 gcc 4.x 好像不會產生 .eh_frame section, 這次用 gcc 5.4 就需要這些選項來 避免產生 .eh_frame section。 ( https://goo.gl/pxndrD ) fig 1 初始化 bss, c_init.s L17 call init_bss_asm 把 0x1 copy 到這個 bss 區域 (c_init.s L52), bss 開頭和結束可由 Linker Scripts 得知, bss.lds L18, L23 可以 看到 __bss_start__, __bss_end__, 這就是 c_init.s 用到的那兩個變數, 神奇吧!「 還可以這樣用的哦!」gnu toolchain 實在太 ... 厲害了。當然在 Linker Scripts 直 接指定一個位址也可以, 不過這樣用比較帥 (帥阿! 老皮), 也比較方便。 bss 區的變數不會存放在執行檔裡, 需要靠 c runtime library 來初始化, 我們能痛快 的使用這些變數, 便是這些初始化程式碼的功勞。 沒有初始值或是初始值為 0 的 static, global 變數, 就是放在 bss。 bss.lds 1 ENTRY(_start) 2 3 SECTIONS 4 { 5 . = 0x7c00; 6 .text : 7 { 8 *(.text) 9 } 10 .= ALIGN(32); 11 12 .data : 13 { 14 *(.data) 15 } 16 17 .= ALIGN(32); 18 __bss_start__ =.; 19 .bss : 20 { 21 *(.bss) 22 } 23 __bss_end__ = .; 24 25 .sig : AT(0x7DFE) 26 { 27 SHORT(0xaa55); 28 } 29 /* 30 .asig : AT(0x7e50) 31 { 32 SHORT(0xefab); 33 } 34 .bsig : AT(0x7f50) 35 { 36 SHORT(0xefab); 37 } 38 */ 39 /DISCARD/ : 40 { 41 *(.note*); 42 *(.iplt*); 43 *(.igot*); 44 *(.rel*); 45 *(.comment); 46 /* add any unwanted sections spewed out by your version of gcc and flags here */ 47 } 48 49 } list 5 L88 i 為於 0x7d60, 而 list 5 L27 指出的 .bss 從 0x7d60 開始, 總共 4 byte, 剛好就是 i 的位址。 0x7d60 位於 .bss 區, 長度4 byte, 自然就是 int i 佔據的大小。若你不相信, 把 bochs run 一次, 反組譯後就可得証。 list 5. readelf -a c_init.elf 1 ELF Header: 2 Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 3 Class: ELF32 4 Data: 2's complement, little endian 5 Version: 1 (current) 6 OS/ABI: UNIX - System V 7 ABI Version: 0 8 Type: EXEC (Executable file) 9 Machine: Intel 80386 10 Version: 0x1 11 Entry point address: 0x7c00 12 Start of program headers: 52 (bytes into file) 13 Start of section headers: 5692 (bytes into file) 14 Flags: 0x0 15 Size of this header: 52 (bytes) 16 Size of program headers: 32 (bytes) 17 Number of program headers: 3 18 Size of section headers: 40 (bytes) 19 Number of section headers: 16 20 Section header string table index: 13 21 22 Section Headers: 23 [Nr] Name Type Addr Off Size ES Flg Lk Inf Al 24 [ 0] NULL 00000000 000000 000000 00 0 0 0 25 [ 1] .text PROGBITS 00007c00 000c00 000138 00 AX 0 0 1 26 [ 2] .rodata.str1.1 PROGBITS 00007d38 000d38 000025 01 AMS 0 0 1 27 [ 3] .bss NOBITS 00007d60 000d5d 000004 00 WA 0 0 4 28 [ 4] .sig PROGBITS 00007d64 000d64 000002 00 WA 0 0 1 29 [ 5] .debug_info PROGBITS 00000000 000d66 00017e 00 0 0 1 30 [ 6] .debug_abbrev PROGBITS 00000000 000ee4 000128 00 0 0 1 31 [ 7] .debug_loc PROGBITS 00000000 00100c 000162 00 0 0 1 32 [ 8] .debug_aranges PROGBITS 00000000 00116e 000020 00 0 0 1 33 [ 9] .debug_ranges PROGBITS 00000000 00118e 000018 00 0 0 1 34 [10] .debug_line PROGBITS 00000000 0011a6 00007c 00 0 0 1 35 [11] .debug_str PROGBITS 00000000 001222 000131 01 MS 0 0 1 36 [12] .debug_frame PROGBITS 00000000 001354 00008c 00 0 0 4 37 [13] .shstrtab STRTAB 00000000 00159b 0000a0 00 0 0 1 38 [14] .symtab SYMTAB 00000000 0013e0 000170 10 15 16 4 39 [15] .strtab STRTAB 00000000 001550 00004b 00 0 0 1 40 Key to Flags: 41 W (write), A (alloc), X (execute), M (merge), S (strings), I (info), 42 L (link order), O (extra OS processing required), G (group), T (TLS), 43 C (compressed), x (unknown), o (OS specific), E (exclude), 44 p (processor specific) 45 46 There are no section groups in this file. 47 48 Program Headers: 49 Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align 50 LOAD 0x000000 0x00007000 0x00007000 0x00d5d 0x00d64 RWE 0x1000 51 LOAD 0x000d64 0x00007d64 0x00007dfe 0x00002 0x00002 RW 0x1000 52 GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10 53 54 Section to Segment mapping: 55 Segment Sections... 56 00 .text .rodata.str1.1 .bss 57 01 .sig 58 02 59 60 There is no dynamic section in this file. 61 62 There are no relocations in this file. 63 64 The decoding of unwind sections for machine type Intel 80386 is not currently supported. 65 66 Symbol table '.symtab' contains 23 entries: 67 Num: Value Size Type Bind Vis Ndx Name 68 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 69 1: 00007c00 0 SECTION LOCAL DEFAULT 1 70 2: 00007d38 0 SECTION LOCAL DEFAULT 2 71 3: 00007d60 0 SECTION LOCAL DEFAULT 3 72 4: 00007d64 0 SECTION LOCAL DEFAULT 4 73 5: 00000000 0 SECTION LOCAL DEFAULT 5 74 6: 00000000 0 SECTION LOCAL DEFAULT 6 75 7: 00000000 0 SECTION LOCAL DEFAULT 7 76 8: 00000000 0 SECTION LOCAL DEFAULT 8 77 9: 00000000 0 SECTION LOCAL DEFAULT 9 78 10: 00000000 0 SECTION LOCAL DEFAULT 10 79 11: 00000000 0 SECTION LOCAL DEFAULT 11 80 12: 00000000 0 SECTION LOCAL DEFAULT 12 81 13: 00000000 0 FILE LOCAL DEFAULT ABS c_init.o 82 14: 00007c13 0 NOTYPE LOCAL DEFAULT 1 init_bss_asm 83 15: 00000000 0 FILE LOCAL DEFAULT ABS b.c 84 16: 00007c2f 36 FUNC GLOBAL DEFAULT 1 print 85 17: 00007cf5 67 FUNC GLOBAL DEFAULT 1 p 86 18: 00007d60 0 NOTYPE GLOBAL DEFAULT 3 __bss_start__ 87 19: 00007c53 162 FUNC GLOBAL DEFAULT 1 itoa 88 20: 00007d60 4 OBJECT GLOBAL DEFAULT 3 i 89 21: 00007d64 0 NOTYPE GLOBAL DEFAULT 3 __bss_end__ 90 22: 00007c00 0 NOTYPE GLOBAL DEFAULT 1 _start 91 92 No version information found in this file. 以上為 x86 真實模式下的 bss 環境。那麼在 x86 保護模式下呢? 原理是一樣的, 就算 是 arm 也是一樣, 只是切入保護模式後 segment register 要改用 selector, 確認使用 的 selector:offset 是 bss 區域即可。這說來簡單, 我可花了不少時間才搞清楚: selector:offset 程式整個載入位址 整個程式的定址空間 用組合語言來初始化 bss 不太好, 我後來寫了 c 語言的版本, 可攜性較高。在 OS 環境 的庇護下寫程式, 真是很幸福, 不需要知道很多事情; 也很不幸, 因為有很多事情被遮蔽 起來, 讓我們無法了解細節。 ref: linux c 編程 - 一站式學習 ( https://goo.gl/idfRnI ) (p 237) 一步步寫嵌入式操作系統 - arm 編程的方法與實踐 ( https://goo.gl/SGSRnd ) (p 50) // 本文使用 Blog2BBS 自動將Blog文章轉成縮址的BBS純文字 http://goo.gl/TZ4E17 // blog 原文 https://goo.gl/Yr0rdc -- 紙上得來終覺淺,絕知此事要躬行。 --



※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 61.218.53.138
※ 文章網址: https://webptt.com/m.aspx?n=bbs/C_and_CPP/M.1481717799.A.5B7.html ※ 編輯: descent (61.218.53.138), 12/14/2016 20:16:49
1F:推 wtchen: 請問一下,上次看了你用sd開機的code,請問你SDRAM 12/14 20:20
2F:→ wtchen: initialization的部份放在哪?不然要怎麼從sd載入到SDRAM 12/14 20:21
3F:→ descent: stm32用sram,不需要初始化就可以使用 12/14 21:42
4F:→ wtchen: 感謝 12/14 22:01
5F:推 art1: 程式沒有眼睛,請問要怎麼看見全域變數或看不見區域變數? 12/14 22:30
不好意思, 不太懂你的問題? ※ 編輯: descent (180.204.64.97), 01/17/2017 21:36:52







like.gif 您可能會有興趣的文章
icon.png[問題/行為] 貓晚上進房間會不會有憋尿問題
icon.pngRe: [閒聊] 選了錯誤的女孩成為魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一張
icon.png[心得] EMS高領長版毛衣.墨小樓MC1002
icon.png[分享] 丹龍隔熱紙GE55+33+22
icon.png[問題] 清洗洗衣機
icon.png[尋物] 窗台下的空間
icon.png[閒聊] 双極の女神1 木魔爵
icon.png[售車] 新竹 1997 march 1297cc 白色 四門
icon.png[討論] 能從照片感受到攝影者心情嗎
icon.png[狂賀] 賀賀賀賀 賀!島村卯月!總選舉NO.1
icon.png[難過] 羨慕白皮膚的女生
icon.png閱讀文章
icon.png[黑特]
icon.png[問題] SBK S1安裝於安全帽位置
icon.png[分享] 舊woo100絕版開箱!!
icon.pngRe: [無言] 關於小包衛生紙
icon.png[開箱] E5-2683V3 RX480Strix 快睿C1 簡單測試
icon.png[心得] 蒼の海賊龍 地獄 執行者16PT
icon.png[售車] 1999年Virage iO 1.8EXi
icon.png[心得] 挑戰33 LV10 獅子座pt solo
icon.png[閒聊] 手把手教你不被桶之新手主購教學
icon.png[分享] Civic Type R 量產版官方照無預警流出
icon.png[售車] Golf 4 2.0 銀色 自排
icon.png[出售] Graco提籃汽座(有底座)2000元誠可議
icon.png[問題] 請問補牙材質掉了還能再補嗎?(台中半年內
icon.png[問題] 44th 單曲 生寫竟然都給重複的啊啊!
icon.png[心得] 華南紅卡/icash 核卡
icon.png[問題] 拔牙矯正這樣正常嗎
icon.png[贈送] 老莫高業 初業 102年版
icon.png[情報] 三大行動支付 本季掀戰火
icon.png[寶寶] 博客來Amos水蠟筆5/1特價五折
icon.pngRe: [心得] 新鮮人一些面試分享
icon.png[心得] 蒼の海賊龍 地獄 麒麟25PT
icon.pngRe: [閒聊] (君の名は。雷慎入) 君名二創漫畫翻譯
icon.pngRe: [閒聊] OGN中場影片:失蹤人口局 (英文字幕)
icon.png[問題] 台灣大哥大4G訊號差
icon.png[出售] [全國]全新千尋侘草LED燈, 水草

請輸入看板名稱,例如:Gossiping站內搜尋

TOP