【嵌入式】C语言高级编程-attribute和section(06)
00. 目录
文章目录
- 00. 目录
- 01. 扩展关键字: attribute
- 02. 属性声明: section
- 03. 属性在Uboot中应用
- 04. 附录
01. 扩展关键字: attribute
GNU C 增加一个 atttribute 关键字用来声明一个函数、变量或类型的特殊属性。声明这个特殊属性有什么用呢?主要用途就是指导编译器在编译程序时进行特定方面的优化或代码检查。比如,我们可以通过使用属性声明指定某个变量的数据边界对齐方式。
__attribute__的使用非常简单,当我们定义一个函数、变量或类型时,直接在它们名字旁边添加下面的属性声明即可:
__atttribute__((ATTRIBUTE))
注意:attribute 后面是两对小括号,不能图方便只写一对,否则编译可能通不过。括号里面的 ATTRIBUTE 代表的就是要声明的属性。现在 attribute 支持十几种属性:
- section
- aligned
- packed
- format
- weak
- alias
- noinline
- always_inline
- ……
在这些属性中,aligned 和 packed 用来显式指定一个变量的存储边界对齐方式。一般来讲,我们定义一个变量,编译器会根据变量类型,按照默认的规则来给这个变量分配大小、按照默认的边界对齐方式分配一个地址。而使用 atttribute 这个属性声明,就相当于告诉编译器:按照我们指定的边界地址对齐去给这个变量分配存储空间。
char c2 __attribute__((aligned(8)) = 4;
int global_val __attribute__((section(".data")));
有些属性可能还有自己的参数。比如 aligned(8) 表示这个变量按8字节地址对齐,参数也要使用小括号括起来。如果属性的参数是一个字符串,小括号里的参数还要用双引号引起来。
当然,我们也可以对一个变量同时添加多个属性说明。在定义时,各个属性之间用逗号隔开就可以了。
char c1 __attribute__((packed,aligned(4)));
char c1 __attribute__((packed,aligned(4))) = 4;
__attribute__((packed,aligned(4))) char c1 = 4;
在上面的示例中,我们对一个变量添加2个属性声明,这两个属性都放在 atttribute(()) 的2对小括号里面,属性之间用逗号隔开。这里还有一个细节,就是属性声明要紧挨着变量,上面的三种定义方式都是没有问题的,但下面的定义方式在编译的时候可能就通不过。
char c2 = 4 __attribute__((packed,aligned(4)));
02. 属性声明: section
首先我们先讲一下 section 这个属性。使用atttribute 来声明一个 section 属性,主要用途是在程序编译时,将一个函数或变量放到指定的段,即 section 中。
程序的编译、链接过程
一个可执行目标文件,它主要由代码段、数据段、BSS 段构成。代码段主要存放编译生成的可执行指令代码,数据段和 BSS 段用来存放全局变量、未初始化的全局变量。代码段、数据段和 BSS 段构成了一个可执行文件的主要部分。
除了这三个段,可执行文件中还包含其它一些段。用编译器的专业术语讲,还会包含其它一些 section,比如只读数据段、符号表等等。我们可以使用下面的 readelf 命令,去查看一个可执行文件中各个 section 的信息。
deng@itcast:~/share$ gcc test.c -o test
deng@itcast:~/share$ readelf -S test
There are 31 section headers, starting at offset 0x3970:节头:[号] 名称 类型 地址 偏移量大小 全体大小 旗标 链接 信息 对齐[ 0] NULL 0000000000000000 000000000000000000000000 0000000000000000 0 0 0[ 1] .interp PROGBITS 0000000000000318 00000318000000000000001c 0000000000000000 A 0 0 1[ 2] .note.gnu.propert NOTE 0000000000000338 000003380000000000000020 0000000000000000 A 0 0 8[ 3] .note.gnu.build-i NOTE 0000000000000358 000003580000000000000024 0000000000000000 A 0 0 4[ 4] .note.ABI-tag NOTE 000000000000037c 0000037c0000000000000020 0000000000000000 A 0 0 4[ 5] .gnu.hash GNU_HASH 00000000000003a0 000003a00000000000000024 0000000000000000 A 6 0 8[ 6] .dynsym DYNSYM 00000000000003c8 000003c800000000000000a8 0000000000000018 A 7 1 8[ 7] .dynstr STRTAB 0000000000000470 000004700000000000000082 0000000000000000 A 0 0 1[ 8] .gnu.version VERSYM 00000000000004f2 000004f2000000000000000e 0000000000000002 A 6 0 2[ 9] .gnu.version_r VERNEED 0000000000000500 000005000000000000000020 0000000000000000 A 7 1 8[10] .rela.dyn RELA 0000000000000520 0000052000000000000000c0 0000000000000018 A 6 0 8[11] .rela.plt RELA 00000000000005e0 000005e00000000000000018 0000000000000018 AI 6 24 8[12] .init PROGBITS 0000000000001000 00001000000000000000001b 0000000000000000 AX 0 0 4[13] .plt PROGBITS 0000000000001020 000010200000000000000020 0000000000000010 AX 0 0 16[14] .plt.got PROGBITS 0000000000001040 000010400000000000000010 0000000000000010 AX 0 0 16[15] .plt.sec PROGBITS 0000000000001050 000010500000000000000010 0000000000000010 AX 0 0 16[16] .text PROGBITS 0000000000001060 000010600000000000000185 0000000000000000 AX 0 0 16[17] .fini PROGBITS 00000000000011e8 000011e8000000000000000d 0000000000000000 AX 0 0 4[18] .rodata PROGBITS 0000000000002000 000020000000000000000010 0000000000000000 A 0 0 4[19] .eh_frame_hdr PROGBITS 0000000000002010 000020100000000000000044 0000000000000000 A 0 0 4[20] .eh_frame PROGBITS 0000000000002058 000020580000000000000108 0000000000000000 A 0 0 8[21] .init_array INIT_ARRAY 0000000000003db8 00002db80000000000000008 0000000000000008 WA 0 0 8[22] .fini_array FINI_ARRAY 0000000000003dc0 00002dc00000000000000008 0000000000000008 WA 0 0 8[23] .dynamic DYNAMIC 0000000000003dc8 00002dc800000000000001f0 0000000000000010 WA 7 0 8[24] .got PROGBITS 0000000000003fb8 00002fb80000000000000048 0000000000000008 WA 0 0 8[25] .data PROGBITS 0000000000004000 000030000000000000000010 0000000000000000 WA 0 0 8[26] .bss NOBITS 0000000000004010 000030100000000000000008 0000000000000000 WA 0 0 1[27] .comment PROGBITS 0000000000000000 000030100000000000000024 0000000000000001 MS 0 0 1[28] .symtab SYMTAB 0000000000000000 000030380000000000000618 0000000000000018 29 46 8[29] .strtab STRTAB 0000000000000000 000036500000000000000202 0000000000000000 0 0 1[30] .shstrtab STRTAB 0000000000000000 00003852000000000000011a 0000000000000000 0 0 1
Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings), I (info),L (link order), O (extra OS processing required), G (group), T (TLS),C (compressed), x (unknown), o (OS specific), E (exclude),l (large), p (processor specific)
deng@itcast:~/share$
在 Linux 环境下,使用 GCC 编译生成一个可执行文件 test,使用上面的 readelf 命令,就可以查看这个可执行文件中各个 section 的基本信息,比如大小、起始地址等等。在这些 section 中,其中 .text section 就是我们常说的代码段,.data section 是数据段,.bss section 是 BSS 段。
我们知道一段源程序代码在编译生成可执行文件的过程中,函数和变量是放在不同段中的。一般默认的规则如下。
段 | 组成 |
---|---|
代码段( .text) | 函数定义、程序语句 |
数据段( .data) | 初始化的全局变量、初始化的静态局部变量 |
BSS段( .bss) | 未初始化的全局变量、未初始化的静态局部变量 |
在下面的程序中,我们分别定义一个函数、一个全局变量和一个未初始化的全局变量。
#include <stdio.h>int num = 8;
int var;void fun(void)
{printf("hello fun\n");
}int main(void)
{fun();printf("hello world\n");return 0;
}
查看结果
deng@itcast:~/share$ gcc test.c -o test
deng@itcast:~/share$ readelf -S test
There are 31 section headers, starting at offset 0x39c0:节头:[号] 名称 类型 地址 偏移量大小 全体大小 旗标 链接 信息 对齐[ 0] NULL 0000000000000000 000000000000000000000000 0000000000000000 0 0 0[ 1] .interp PROGBITS 0000000000000318 00000318000000000000001c 0000000000000000 A 0 0 1[ 2] .note.gnu.propert NOTE 0000000000000338 000003380000000000000020 0000000000000000 A 0 0 8[ 3] .note.gnu.build-i NOTE 0000000000000358 000003580000000000000024 0000000000000000 A 0 0 4[ 4] .note.ABI-tag NOTE 000000000000037c 0000037c0000000000000020 0000000000000000 A 0 0 4[ 5] .gnu.hash GNU_HASH 00000000000003a0 000003a00000000000000024 0000000000000000 A 6 0 8[ 6] .dynsym DYNSYM 00000000000003c8 000003c800000000000000a8 0000000000000018 A 7 1 8[ 7] .dynstr STRTAB 0000000000000470 000004700000000000000082 0000000000000000 A 0 0 1[ 8] .gnu.version VERSYM 00000000000004f2 000004f2000000000000000e 0000000000000002 A 6 0 2[ 9] .gnu.version_r VERNEED 0000000000000500 000005000000000000000020 0000000000000000 A 7 1 8[10] .rela.dyn RELA 0000000000000520 0000052000000000000000c0 0000000000000018 A 6 0 8[11] .rela.plt RELA 00000000000005e0 000005e00000000000000018 0000000000000018 AI 6 24 8[12] .init PROGBITS 0000000000001000 00001000000000000000001b 0000000000000000 AX 0 0 4[13] .plt PROGBITS 0000000000001020 000010200000000000000020 0000000000000010 AX 0 0 16[14] .plt.got PROGBITS 0000000000001040 000010400000000000000010 0000000000000010 AX 0 0 16[15] .plt.sec PROGBITS 0000000000001050 000010500000000000000010 0000000000000010 AX 0 0 16[16] .text PROGBITS 0000000000001060 000010600000000000000195 0000000000000000 AX 0 0 16[17] .fini PROGBITS 00000000000011f8 000011f8000000000000000d 0000000000000000 AX 0 0 4[18] .rodata PROGBITS 0000000000002000 00002000000000000000001a 0000000000000000 A 0 0 4[19] .eh_frame_hdr PROGBITS 000000000000201c 0000201c000000000000004c 0000000000000000 A 0 0 4[20] .eh_frame PROGBITS 0000000000002068 000020680000000000000128 0000000000000000 A 0 0 8[21] .init_array INIT_ARRAY 0000000000003db8 00002db80000000000000008 0000000000000008 WA 0 0 8[22] .fini_array FINI_ARRAY 0000000000003dc0 00002dc00000000000000008 0000000000000008 WA 0 0 8[23] .dynamic DYNAMIC 0000000000003dc8 00002dc800000000000001f0 0000000000000010 WA 7 0 8[24] .got PROGBITS 0000000000003fb8 00002fb80000000000000048 0000000000000008 WA 0 0 8[25] .data PROGBITS 0000000000004000 000030000000000000000014 0000000000000000 WA 0 0 8[26] .bss NOBITS 0000000000004014 00003014000000000000000c 0000000000000000 WA 0 0 4[27] .comment PROGBITS 0000000000000000 000030140000000000000024 0000000000000001 MS 0 0 1[28] .symtab SYMTAB 0000000000000000 000030380000000000000660 0000000000000018 29 46 8[29] .strtab STRTAB 0000000000000000 00003698000000000000020e 0000000000000000 0 0 1[30] .shstrtab STRTAB 0000000000000000 000038a6000000000000011a 0000000000000000 0 0 1
Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings), I (info),L (link order), O (extra OS processing required), G (group), T (TLS),C (compressed), x (unknown), o (OS specific), E (exclude),l (large), p (processor specific)deng@itcast:~/share$ readelf -s testSymbol table '.dynsym' contains 7 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.2.5 (2)3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)4: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__5: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable6: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@GLIBC_2.2.5 (2)Symbol table '.symtab' contains 68 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000318 0 SECTION LOCAL DEFAULT 1 2: 0000000000000338 0 SECTION LOCAL DEFAULT 2 3: 0000000000000358 0 SECTION LOCAL DEFAULT 3 4: 000000000000037c 0 SECTION LOCAL DEFAULT 4 5: 00000000000003a0 0 SECTION LOCAL DEFAULT 5 6: 00000000000003c8 0 SECTION LOCAL DEFAULT 6 7: 0000000000000470 0 SECTION LOCAL DEFAULT 7 8: 00000000000004f2 0 SECTION LOCAL DEFAULT 8 9: 0000000000000500 0 SECTION LOCAL DEFAULT 9 10: 0000000000000520 0 SECTION LOCAL DEFAULT 10 11: 00000000000005e0 0 SECTION LOCAL DEFAULT 11 12: 0000000000001000 0 SECTION LOCAL DEFAULT 12 13: 0000000000001020 0 SECTION LOCAL DEFAULT 13 14: 0000000000001040 0 SECTION LOCAL DEFAULT 14 15: 0000000000001050 0 SECTION LOCAL DEFAULT 15 16: 0000000000001060 0 SECTION LOCAL DEFAULT 16 17: 00000000000011f8 0 SECTION LOCAL DEFAULT 17 18: 0000000000002000 0 SECTION LOCAL DEFAULT 18 19: 000000000000201c 0 SECTION LOCAL DEFAULT 19 20: 0000000000002068 0 SECTION LOCAL DEFAULT 20 21: 0000000000003db8 0 SECTION LOCAL DEFAULT 21 22: 0000000000003dc0 0 SECTION LOCAL DEFAULT 22 23: 0000000000003dc8 0 SECTION LOCAL DEFAULT 23 24: 0000000000003fb8 0 SECTION LOCAL DEFAULT 24 25: 0000000000004000 0 SECTION LOCAL DEFAULT 25 26: 0000000000004014 0 SECTION LOCAL DEFAULT 26 27: 0000000000000000 0 SECTION LOCAL DEFAULT 27 28: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c29: 0000000000001090 0 FUNC LOCAL DEFAULT 16 deregister_tm_clones30: 00000000000010c0 0 FUNC LOCAL DEFAULT 16 register_tm_clones31: 0000000000001100 0 FUNC LOCAL DEFAULT 16 __do_global_dtors_aux32: 0000000000004014 1 OBJECT LOCAL DEFAULT 26 completed.805933: 0000000000003dc0 0 OBJECT LOCAL DEFAULT 22 __do_global_dtors_aux_fin34: 0000000000001140 0 FUNC LOCAL DEFAULT 16 frame_dummy35: 0000000000003db8 0 OBJECT LOCAL DEFAULT 21 __frame_dummy_init_array_36: 0000000000000000 0 FILE LOCAL DEFAULT ABS test.c37: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c38: 000000000000218c 0 OBJECT LOCAL DEFAULT 20 __FRAME_END__39: 0000000000000000 0 FILE LOCAL DEFAULT ABS 40: 0000000000003dc0 0 NOTYPE LOCAL DEFAULT 21 __init_array_end41: 0000000000003dc8 0 OBJECT LOCAL DEFAULT 23 _DYNAMIC42: 0000000000003db8 0 NOTYPE LOCAL DEFAULT 21 __init_array_start43: 000000000000201c 0 NOTYPE LOCAL DEFAULT 19 __GNU_EH_FRAME_HDR44: 0000000000003fb8 0 OBJECT LOCAL DEFAULT 24 _GLOBAL_OFFSET_TABLE_45: 0000000000001000 0 FUNC LOCAL DEFAULT 12 _init46: 00000000000011f0 5 FUNC GLOBAL DEFAULT 16 __libc_csu_fini47: 0000000000004010 4 OBJECT GLOBAL DEFAULT 25 num48: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab49: 0000000000004000 0 NOTYPE WEAK DEFAULT 25 data_start50: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.2.551: 0000000000004014 0 NOTYPE GLOBAL DEFAULT 25 _edata52: 00000000000011f8 0 FUNC GLOBAL HIDDEN 17 _fini53: 0000000000001149 23 FUNC GLOBAL DEFAULT 16 fun54: 0000000000004018 4 OBJECT GLOBAL DEFAULT 26 var55: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_56: 0000000000004000 0 NOTYPE GLOBAL DEFAULT 25 __data_start57: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__58: 0000000000004008 0 OBJECT GLOBAL HIDDEN 25 __dso_handle59: 0000000000002000 4 OBJECT GLOBAL DEFAULT 18 _IO_stdin_used60: 0000000000001180 101 FUNC GLOBAL DEFAULT 16 __libc_csu_init61: 0000000000004020 0 NOTYPE GLOBAL DEFAULT 26 _end62: 0000000000001060 47 FUNC GLOBAL DEFAULT 16 _start63: 0000000000004014 0 NOTYPE GLOBAL DEFAULT 26 __bss_start64: 0000000000001160 32 FUNC GLOBAL DEFAULT 16 main65: 0000000000004018 0 OBJECT GLOBAL HIDDEN 25 __TMC_END__66: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable67: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@@GLIBC_2.2
deng@itcast:~/share$
通过符号表和节头表 section header table 信息,我们可以看到,函数 print_star 被放在可执行文件中的 .text section,即代码段;初始化的全局变量 global_val 被放在了 .data section,即数据段;而未初始化的全局变量 uninit_val 则被放在了.bss section,即 BSS 段。
编译器在编译程序时,是以源文件为单位,将一个个源文件编译生成一个个目标文件。在编译过程中,编译器都会按照这个默认规则,将函数、变量分别放在不同的 section 中,最后将各个 section 组成一个目标文件。编译过程结束后,链接器接着会将各个目标文件组装合并、重定位,生成一个可执行文件。
链接器是如何将各个目标文件组装成一个可执行文件的呢?很简单,链接器首先会分别将各个目标文件的代码段整合,组装成一个大的代码段;将各个目标文件中的数据段整合,合并成一个大的数据段;接着将合并后的新代码段、数据段再合并为一个文件;最后经过重定位,就生成了一个可以运行的可执行文件了。
现在又有一个疑问来了,链接器在将各个不同的 section 段组装成一个可执行文件的过程中,各个 section 的顺序如何排放呢?比如代码段、数据段、BSS 段、符号表等,谁放在前面?谁放在后面?
链接器在链接过程中,会将不同的 section,按照链接脚本中指定的各个 section 的排放顺序,组装成一个可执行文件。一般在 Ubuntu 等 PC 版本的系统中,系统会有默认的链接脚本,不需要程序员操心。
deng@itcast:~/share$ ld --version
GNU ld (GNU Binutils for Ubuntu) 2.34
Copyright (C) 2020 Free Software Foundation, Inc.
这个程序是自由软件;您可以遵循GNU 通用公共授权版本 3 或
(您自行选择的) 稍后版本以再次散布它。
这个程序完全没有任何担保。
我们使用上面命令,就可以查看编译当前程序时,链接器使用的默认链接脚本。在嵌入式系统中,因为是交叉编译,所以软件源码一般会自带一个链接脚本。比如在 U-boot 源码的根目录下面,你会看到一个 u-boot.lds 的文件,这个文件就是编译 U-boot 时,链接器要使用的链接脚本。在 Linux 内核中,同样会有 vmlinux.lds 这样一个链接脚本。
在 GNU C 中,我们可以通过 attribute 的 section 属性,显式指定一个函数或变量,在编译时放到指定的 section 里面。通过上面的程序我们知道,未初始化的全局变量是放在 .data section 中的,即放在 BSS 段中。现在我们就可以通过 section 属性,把这个未初始化的全局变量放到数据段 .data 中。
使用示例
deng@itcast:~/share$ gcc test.c -o test
执行结果
deng@itcast:~/share$ readelf -s testSymbol table '.dynsym' contains 7 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.2.5 (2)3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)4: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__5: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable6: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@GLIBC_2.2.5 (2)Symbol table '.symtab' contains 66 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000318 0 SECTION LOCAL DEFAULT 1 2: 0000000000000338 0 SECTION LOCAL DEFAULT 2 3: 0000000000000358 0 SECTION LOCAL DEFAULT 3 4: 000000000000037c 0 SECTION LOCAL DEFAULT 4 5: 00000000000003a0 0 SECTION LOCAL DEFAULT 5 6: 00000000000003c8 0 SECTION LOCAL DEFAULT 6 7: 0000000000000470 0 SECTION LOCAL DEFAULT 7 8: 00000000000004f2 0 SECTION LOCAL DEFAULT 8 9: 0000000000000500 0 SECTION LOCAL DEFAULT 9 10: 0000000000000520 0 SECTION LOCAL DEFAULT 10 11: 00000000000005e0 0 SECTION LOCAL DEFAULT 11 12: 0000000000001000 0 SECTION LOCAL DEFAULT 12 13: 0000000000001020 0 SECTION LOCAL DEFAULT 13 14: 0000000000001040 0 SECTION LOCAL DEFAULT 14 15: 0000000000001050 0 SECTION LOCAL DEFAULT 15 16: 0000000000001060 0 SECTION LOCAL DEFAULT 16 17: 00000000000011e8 0 SECTION LOCAL DEFAULT 17 18: 0000000000002000 0 SECTION LOCAL DEFAULT 18 19: 0000000000002010 0 SECTION LOCAL DEFAULT 19 20: 0000000000002058 0 SECTION LOCAL DEFAULT 20 21: 0000000000003db8 0 SECTION LOCAL DEFAULT 21 22: 0000000000003dc0 0 SECTION LOCAL DEFAULT 22 23: 0000000000003dc8 0 SECTION LOCAL DEFAULT 23 24: 0000000000003fb8 0 SECTION LOCAL DEFAULT 24 25: 0000000000004000 0 SECTION LOCAL DEFAULT 25 26: 0000000000004014 0 SECTION LOCAL DEFAULT 26 27: 0000000000000000 0 SECTION LOCAL DEFAULT 27 28: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c29: 0000000000001090 0 FUNC LOCAL DEFAULT 16 deregister_tm_clones30: 00000000000010c0 0 FUNC LOCAL DEFAULT 16 register_tm_clones31: 0000000000001100 0 FUNC LOCAL DEFAULT 16 __do_global_dtors_aux32: 0000000000004014 1 OBJECT LOCAL DEFAULT 26 completed.805933: 0000000000003dc0 0 OBJECT LOCAL DEFAULT 22 __do_global_dtors_aux_fin34: 0000000000001140 0 FUNC LOCAL DEFAULT 16 frame_dummy35: 0000000000003db8 0 OBJECT LOCAL DEFAULT 21 __frame_dummy_init_array_36: 0000000000000000 0 FILE LOCAL DEFAULT ABS test.c37: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c38: 000000000000215c 0 OBJECT LOCAL DEFAULT 20 __FRAME_END__39: 0000000000000000 0 FILE LOCAL DEFAULT ABS 40: 0000000000003dc0 0 NOTYPE LOCAL DEFAULT 21 __init_array_end41: 0000000000003dc8 0 OBJECT LOCAL DEFAULT 23 _DYNAMIC42: 0000000000003db8 0 NOTYPE LOCAL DEFAULT 21 __init_array_start43: 0000000000002010 0 NOTYPE LOCAL DEFAULT 19 __GNU_EH_FRAME_HDR44: 0000000000003fb8 0 OBJECT LOCAL DEFAULT 24 _GLOBAL_OFFSET_TABLE_45: 0000000000001000 0 FUNC LOCAL DEFAULT 12 _init46: 00000000000011e0 5 FUNC GLOBAL DEFAULT 16 __libc_csu_fini47: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab48: 0000000000004000 0 NOTYPE WEAK DEFAULT 25 data_start49: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.2.550: 0000000000004014 0 NOTYPE GLOBAL DEFAULT 25 _edata51: 00000000000011e8 0 FUNC GLOBAL HIDDEN 17 _fini52: 0000000000004010 4 OBJECT GLOBAL DEFAULT 25 val53: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_54: 0000000000004000 0 NOTYPE GLOBAL DEFAULT 25 __data_start55: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__56: 0000000000004008 0 OBJECT GLOBAL HIDDEN 25 __dso_handle57: 0000000000002000 4 OBJECT GLOBAL DEFAULT 18 _IO_stdin_used58: 0000000000001170 101 FUNC GLOBAL DEFAULT 16 __libc_csu_init59: 0000000000004018 0 NOTYPE GLOBAL DEFAULT 26 _end60: 0000000000001060 47 FUNC GLOBAL DEFAULT 16 _start61: 0000000000004014 0 NOTYPE GLOBAL DEFAULT 26 __bss_start62: 0000000000001149 27 FUNC GLOBAL DEFAULT 16 main63: 0000000000004018 0 OBJECT GLOBAL HIDDEN 25 __TMC_END__64: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable65: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@@GLIBC_2.2
deng@itcast:~/share$
通过上面的 readelf 命令查看符号表,我们可以看到,val这个未初始化的全局变量,通过__attribute__((section(".data")))
属性声明,就被编译器放在了数据段 .data section 中。
03. 属性在Uboot中应用
有了 section 这个属性,我们接下来就可以试着分析,U-boot 在启动过程中,是如何将自身代码加载的 RAM 中的。
搞嵌入式的都知道 U-boot,U-boot 的用途主要是加载 Linux 内核镜像到内存、给内核传递启动参数、然后引导 Linux 操作系统启动。
U-boot 一般存储在 Nor flash 或 NAND Flash 上。无论从 Nor Flash 还是从 Nand Flash 启动,U-boot 其本身在启动过程中,也会从 Flash 存储介质上加载自身代码到内存,然后进行重定位,跳到内存 RAM 中去执行。这个功能一般叫做“自举。我们的主要任务是去看看 U-boot 是怎么完成自拷贝的,或者说它是怎样将自身代码从 Flash 拷贝到内存 RAM 中的。
在拷贝自身代码的过程中,一个主要的疑问就是,U-boot 是如何识别自身代码的?是如何知道从哪里拷贝代码的?是如何知道拷贝到哪里停止的?这个时候我们不得不说起 U-boot 源码中的一个零长度数组。
char __image_copy_start[0] __attribute__((section(".__image_copy_start")));
char __image_copy_end[0] __attribute__((section(".__image_copy_end")));
这两行代码定义在 U-boot-2016.09 中的 arch/arm/lib/section.c 文件中。在其它版本中可能路径不同或者没有定义,为了分析这个功能,建议大家可以下载 U-boot-2016.09 这个版本的U-boot源码。
这两行代码的作用是分别定义一个零长度数组,并告诉编译器要分别放在 .imagecopystart
和 .image_copy_end
这两个 section 中。
链接器在链接各个目标文件时,会按照链接脚本里各个 section 的排列顺序,将各个 section 组装成一个可执行文件。U-boot 的链接脚本 u-boot.lds 在 U-boot 源码的根目录下面。
OUTPUT_FORMAT("elf32-littlearm","elf32-littlearm","elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{. = 0x00000000;. = ALIGN(4);.text :{*(.__image_copy_start)*(.vectors)arch/arm/cpu/armv7/start.o (.text*)*(.text*)}. = ALIGN(4);.data : {*(.data*)}....... = ALIGN(4);.image_copy_end :{*(.__image_copy_end)}.end :{*(.__end)}_image_binary_end = .;. = ALIGN(4096);.mmutable : {*(.mmutable)}.bss_start __rel_dyn_start (OVERLAY) : {KEEP(*(.__bss_start));__bss_base = .;}.bss __bss_base (OVERLAY) : {*(.bss*). = ALIGN(4);__bss_limit = .;}.bss_end __bss_limit (OVERLAY) : {KEEP(*(.__bss_end));}
}
通过链接脚本我们可以看到,__image_copy_start 和 __image_copy_end 这两个 section,在链接的时候分别放在了代码段 .text 的前面、数据段 .data 的后面,作为 U-boot 拷贝自身代码的起始地址和结束地址。而在这两个 section 中,我们除了放2个零长度数组外,并没有再放其它变量。根据前面的学习我们知道,零长度数组是不占用存储空间的,所以上面定义的两个零长度数组,其实就分别代表了 U-boot 镜像要拷贝自身镜像的起始地址和结束地址。
char __image_copy_start[0] __attribute__((section(".__image_copy_start")));
char __image_copy_end[0] __attribute__((section(".__image_copy_end")));
无论 U-boot 自身镜像是存储在 Nor Flash,还是 Nand Flash 上,我们只要知道了这两个地址,就可以直接调用相关代码拷贝。
接着在 arch/arm/lib/relocate.S 中,ENTRY(relocate_code) 汇编代码主要完成代码拷贝的功能。
ENTRY(relocate_code)ldr r1, =__image_copy_start /* r1 <- SRC &__image_copy_start */subs r4, r0, r1 /* r4 <- relocation offset */beq relocate_done /* skip relocation */ldr r2, =__image_copy_end /* r2 <- SRC &__image_copy_end */copy_loop:ldmia r1!, {r10-r11} /* copy from source address [r1] */stmia r0!, {r10-r11} /* copy to target address [r0] */cmp r1, r2 /* until source end address [r2] */blo copy_loop
在这段汇编代码中,寄存器 R1、R2 分别表示要拷贝镜像的起始地址和结束地址,R0 表示要拷贝到 RAM 中的地址,R4 存放的是源地址和目的地址之间的偏移,在后面重定位过程中会用到这个偏移值。
ldr r1, =__image_copy_start
见上面指令,在汇编代码中,ARM的 ldr 指令立即寻址,直接对数组名进行引用,获取要拷贝镜像的首地址,并保存在 R1 寄存器中。数组名本身其实就代表一个地址。通过这种方式,U-boot 在嵌入式启动的初始阶段,就完成了自身代码的拷贝工作:从 Flash 上拷贝自身镜像到 RAM 中,然后再进行重定位,最后跳到 RAM 中执行。
04. 附录
参考:C语言嵌入式Linux高级编程
【嵌入式】C语言高级编程-attribute和section(06)相关推荐
- Linux·C语言高级编程·attribute和section详解
目录 01. 扩展关键字: attribute 02. 属性声明: section 03. 属性在Uboot中应用 01. 扩展关键字: attribute GNU C 增加一个 atttribute ...
- c语言高级程序设计第五版PDF,C语言高级编程.pdf
C语言高级编程 概述 由几个测试程序说开去 预编译与宏 高级预编译介绍 宏的高级用法 变量 变量分类详细解析 我的变量去哪儿了? 大小端对变量的影响 内存与指针 常见内存使用错误大观 指针,又是指针! ...
- Go 学习推荐 —(Go by example 中文版、Go 构建 Web 应用、Go 学习笔记、Golang常见错误、Go 语言四十二章经、Go 语言高级编程)
Go by example 中文版 Go 构建 Web 应用 Go 学习笔记:无痕 Go 标准库中文文档 Golang开发新手常犯的50个错误 50 Shades of Go: Traps, Gotc ...
- 鼠标绘图 c语言,c语言高级编程技术教程 图形显示方式与鼠标输入.doc
c语言高级编程技术教程 图形显示方式与鼠标输入 c语言高级编程技术教程 图形显示方式和鼠标输入 图形显示方式和鼠标输入 问题的提出编写程序,使用鼠标进行如下操作:按住鼠标器的任意键并移动,十字光 标将 ...
- 高级编程中C语言属于,c语言高级编程
c语言高级编程 C高级编程 责任编辑:admin 更新日期:2005-8-6 深入了解C语言(函数的参数传递和函数使用参数的方法) tangl_99(原作) 关键字 C语言,汇编,代码生成,编译器 C ...
- 《go语言圣经》+《Mastering.GO-cn》+《go语言高级编程》PDF下载
公众号[爱吃橙子的搬砖小徐]开通啦,后续将会同步更新,欢迎订阅 回复[java面试]获得两套面试宝典 回复[golang]获得go语言学习三部曲 <go语言圣经>+<Masterin ...
- matlab高级教程教材,MATLAB语言高级编程 PDF_IT教程网
资源名称:MATLAB语言高级编程 PDF 本书共分8章,主要介绍了matlab的概述.matlab安装与工作桌面:matlab的编程基础,包括matlab的变量.matlab的运算符.矩阵的创建及运 ...
- 【嵌入式】C语言高级编程-强符号和弱符号(09)
00. 目录 文章目录 00. 目录 01. weak属性 02. 变量强符号和弱符号 03. 函数强符号和弱符号 04. 弱符号的作用 05. alias属性 06. 附录 01. weak属性 G ...
- 【嵌入式】C语言高级编程-内联函数(10)
00. 目录 文章目录 00. 目录 01. 属性声明 02. 内联函数概述 03. 内联函数与宏 04. 编译器对内联函数的处理 05. static修饰内联函数 06. 附录 01. 属性声明 a ...
最新文章
- Linux运行cat进程,linux下如何使用某个用户启动某个进程?
- unity实战 实现鼠标选择对象前置显示
- Arrays.copyOf()、Arrays.copyOfRange()与System.arraycopy()用法
- 一个基于用户的API限流策略 Rate Limit
- 戴尔集群监控与管理系统_监控与管理
- 【BZOJ】1649: [Usaco2006 Dec]Cow Roller Coaster(dp)
- 【2019牛客暑期多校训练营(第二场) - H】Second Large Rectangle(单调栈,全1子矩阵变形)
- java案例代码19--二分查找排序
- 2020年天津市二级分类土地利用数据(矢量)
- 【matlab】 GMSK的调制与解调【附详尽注释】
- Github 汉化插件教程
- Speedoffice(Excel)怎么把边框线条加粗
- Jmeter取样器设置
- 计算机rankeq函数,Excel中的rank函数与rank.eq函数有什么区别
- JavaWeb宿舍管理系统环境搭建运行教程
- 广东英语高考怎么计算机,2019广东高考英语听说考试大纲出炉!附三大题型得分套路!...
- 打印机乱码故障解决办法
- 法里昂第一大学一座大楼楼顶爆炸起火 致至少3人伤
- 更改C盘用户目录下的用户名(真实有效)
- 语法分析--自上而下分析的基本问题