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/cn.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灯, 水草

请输入看板名称,例如:Tech_Job站内搜寻

TOP