首页
关于
友链
其它
统计
壁纸
Search
1
修改Linux Kernel defconfig的标准方法
4,786 阅读
2
Android系统之VINTF(1)manifests&compatibility matrices
4,455 阅读
3
cgroup--(4)cgroup v1和cgroup v2的详细介绍
4,113 阅读
4
c语言的__attribute__
2,812 阅读
5
Android系统之VINTF(3)Matching Rules
2,220 阅读
默认分类
文章收集
学习总结
每天一刷
环境配置
知识点
入门系列
vim
shell
Git
Make
Android
Linux
Linux命令
内存管理
Linux驱动
Language
C++
C
工具
软件工具
Bug
登录
Search
标签搜索
shell
Linux
c
uboot
Vim
vintf
Linux驱动
Android
device_tree
git
DEBUG
链表
数据结构
IDR
内核
ELF
gcc
网址
内存管理
进程管理
adtxl
累计撰写
332
篇文章
累计收到
8
条评论
首页
栏目
默认分类
文章收集
学习总结
每天一刷
环境配置
知识点
入门系列
vim
shell
Git
Make
Android
Linux
Linux命令
内存管理
Linux驱动
Language
C++
C
工具
软件工具
Bug
页面
关于
友链
其它
统计
壁纸
搜索到
6
篇与
c
的结果
2020-10-12
C库函数--memset()函数
描述在定义变量时一定要进行初始化,尤其是数组和结构体这种占用内存大的数据结构。在使用数组的时候经常因为没有初始化而产生“烫烫烫烫烫烫”这样的野值,俗称“乱码”。每种类型的变量都有各自的初始化方法,memset() 函数可以说是初始化内存的“万能函数”,通常为新申请的内存进行初始化工作。它是直接操作内存空间,mem即“内存”(memory)的意思。原型该函数的原型为:# include <string.h> void *memset(void *str, int c, size_t n);参数:str -- 指向要填充的内存块。c -- 要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式。n -- 要被设置为该值的字符数。函数的功能是:将指针变量 s 所指向的前 n 字节的内存单元用一个“整数” c 替换,注意 c 是 int 型。str 是 void* 型的指针变量,所以它可以为任何类型的数据进行初始化。返回值:该值返回一个指向存储区 str 的指针。memset() 的作用是在一段内存块中填充某个给定的值。因为它只能填充一个值,所以该函数的初始化为原始初始化,无法将变量初始化为程序中需要的数据。用memset初始化完后,后面程序中再向该内存空间中存放需要的数据。memset 一般使用“0”初始化内存单元,而且通常是给数组或结构体进行初始化。一般的变量如 char、int、float、double 等类型的变量直接初始化即可,没有必要用 memset。如果用 memset 的话反而显得麻烦。当然,数组也可以直接进行初始化,但 memset 是对较大的数组或结构体进行清零初始化的最快方法,因为它是直接对内存进行操作的。这时有人会问:“字符串数组不是最好用'0'进行初始化吗?那么可以用 memset 给字符串数组进行初始化吗?也就是说参数 c 可以赋值为'0'吗?”可以的。虽然参数 c 要求是一个整数,但是整型和字符型是互通的。但是赋值为 '0' 和 0 是等价的,因为字符 '0' 在内存中就是 0。所以在 memset 中初始化为 0 也具有结束标志符 '0' 的作用,所以通常我们就写“0”。memset 函数的第三个参数 n 的值一般用 sizeof() 获取,这样比较专业。注意,如果是对指针变量所指向的内存单元进行清零初始化,那么一定要先对这个指针变量进行初始化,即一定要先让它指向某个有效的地址。而且用memset给指针变量如p所指向的内存单元进行初始化时,n 千万别写成 sizeof(p),这是新手经常会犯的错误。因为 p 是指针变量,不管 p 指向什么类型的变量,sizeof(p) 的值都是 4。注意,C语言中的指针和数组名不完全等价,不能将它们混为一谈。下面写一个程序:# include <stdio.h> # include <string.h> int main(void) { int i; //循环变量 char str[10]; char *p = str; memset(str, 0, sizeof(str)); //只能写sizeof(str), 不能写sizeof(p) for (i=0; i<10; ++i) { printf("%d\x20", str[i]); } printf("\n"); return 0; }根据memset函数的不同,输出结果也不同,分为以下几种情况:memset(p, 0, sizeof(p)); //地址的大小都是4字节 0 0 0 0 -52 -52 -52 -52 -52 -52 memset(p, 0, sizeof(*p)); //*p表示的是一个字符变量, 只有一字节 0 -52 -52 -52 -52 -52 -52 -52 -52 -52 memset(p, 0, sizeof(str)); 0 0 0 0 0 0 0 0 0 0 memset(str, 0, sizeof(str)); 0 0 0 0 0 0 0 0 0 0 memset(p, 0, 10); //直接写10也行, 但不专业 0 0 0 0 0 0 0 0 0 0
2020年10月12日
6 阅读
0 评论
0 点赞
2020-09-24
c语言的左移和右移
https://www.cnblogs.com/lyggqm/p/7826938.html
2020年09月24日
795 阅读
0 评论
0 点赞
2020-09-15
c语言的__attribute__
[TOC]转载:https://blog.csdn.net/qlexcel/article/details/92656797https://www.cnblogs.com/embedded-linux/p/5801999.html一、介绍GNU C 的一大特色就是__attribute__ 机制。__attribute__ 可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute )。__attribute__ 书写特征是:__attribute__ 前后都有两个下划线,并切后面会紧跟一对原括弧,__attribute__ 参数。__attribute__ 语法格式为:__attribute__ ((attribute-list))__attribute__ 也可以对结构体(struct )或共用体(union )进行属性设置。大致有六个参数值可以被设定,即:aligned, packed, transparent_union, unused, deprecated 和 may_alias 。__attribute__ 参数时,你也可以在参数的前后都加上“__” (两个下划线),例如,使用__aligned__而不是aligned ,这样,你就可以在相应的头文件里使用它而不用关心头文件里是否有重名的宏定义。二、__attribute__参数介绍1.aligned该属性设定一个指定大小的对齐格式(以字节 为单位),例如:struct S { short b[3]; } __attribute__ ((aligned (8))); typedef int int32_t __attribute__ ((aligned (8)));该声明将强制编译器确保(尽它所能)变量类 型为struct S 或者int32_t 的变量在分配空间时采用8 字节对齐方式。如上所述,你可以手动指定对齐的格式,同 样,你也可以使用默认的对齐方式。如果aligned 后面不紧跟一个指定的数字值,那么编译器将依据你的目标机器情况使用最大最有益的对齐方式。例如:struct S { short b[3]; } __attribute__ ((aligned));这里,如果sizeof (short )的大小为2 (byte ),那么,S 的大小就为6 。取一个2 的次方值,使得该值大于等于6 ,则该值为8 ,所以编译器将设置S 类型的对齐方式为8 字节。ligned 属性使被设置的对象占用更多的空间,相反的,使用packed 可以减小对象占用的空间。需要注意的是,attribute 属性的效力与你的连接器也有关,如果你的连接器最大只支持16 字节对齐,那么你此时定义32 字节对齐也是无济于事的。2.packed使用该属性对struct 或者union 类型进行定义,设定其类型的每一个变量的内存约束。当用在enum 类型 定义时,暗示了应该使用最小完整的类型(it indicates that the smallest integral type should be used)。下面的例子中,packed_struct 类型的变量数组中的值将会紧紧的靠在一起,但内部的成员变量s 不会被“pack” ,如果希望内部的成员变量也被packed 的话,unpacked-struct 也需要使用packed 进行相应的约束。struct unpacked_struct { char c; int i; }; struct packed_struct { char c; int i; struct unpacked_struct s; }__attribute__ ((__packed__));下面的例子中使用__attribute__ 属性定义了一些结构体及其变量,并给出了输出结果和对结果的分析。struct p { int a; char b; short c; }__attribute__((aligned(4))) pp; struct m { char a; int b; short c; }__attribute__((aligned(4))) mm; struct o { int a; char b; short c; }oo; struct x { int a; char b; struct p px; short c; }__attribute__((aligned(8))) xx; int main() { printf("sizeof(int)=%d,sizeof(short)=%d.sizeof(char)=%d\n",sizeof(int),sizeof(short),sizeof(char)); printf("pp=%d,mm=%d \n", sizeof(pp),sizeof(mm)); printf("oo=%d,xx=%d \n", sizeof(oo),sizeof(xx)); return 0; }输出结 果:sizeof(int)=4,sizeof(short)=2.sizeof(char)=1pp=8,mm=12oo=8,xx=24分析:sizeof(pp):sizeof(a)+sizeof(b)+sizeof(c)=4+1+1=6<8 所以sizeof(pp)=8sizeof(mm):sizeof(a)+sizeof(b)+sizeof(c)=1+4+2=7但是 a 后面需要用 3 个字节填充,但是 b 是 4 个字节,所以 a 占用 4 字节, b 占用 4 个字节,而 c 又要占用 4 个字节。所以 sizeof(mm)=12sizeof(oo):sizeof(a)+sizeof(b)+sizeof(c)=4+1+2=7因为默 认是以4 字节对齐,所以sizeof(oo)=8sizeof(xx):sizeof(a)+ sizeof(b)=4+1=5sizeof(pp)=8; 即xx 是采用8 字节对齐的,所以要在a ,b 后面添3 个空余字节,然后才能存储px ,4+1+ (3 )+8+1=17因为xx 采用的对齐是8 字节对齐,所以xx 的大小必定是8 的整数倍,即xx 的大小是一个比17 大又是8 的倍数的一个最小值,由此得到17<24 ,所以sizeof(xx)=243.函数属性(Function Attribute)函数属性可以帮助开发者把一些特性添加到函数声明中,从而可以使编译器在错误检查方面的功能更强大。__attribute__机制也很容易同非GNU应用程序做到兼容之功效。GNU CC需要使用 –Wall编译器来击活该功能,这是控制警告信息的一个很好的方式。下面介绍几个常见的属性参数。__attribute__ format该__attribute__属性可以给被声明的函数加上类似printf或者scanf的特征,它可以使编译器检查函数声明和函数实际调用参数之间的格式化字符串是否匹配。该功能十分有用,尤其是处理一些很难发现的bug。format的语法格式为:format (archetype, string-index, first-to-check)format属性告诉编译器,按照printf, scanf, strftime或strfmon的参数表格式规则对该函数的参数进行检查。“archetype”指定是哪种风格;“string-index”指定传入函数的第几个参数是格式化字符串;“first-to-check”指定从函数的第几个参数开始按上述规则进行检查。具体使用格式如下:__attribute__((format(printf,m,n)))__attribute__((format(scanf,m,n)))其中参数m与n的含义为:m:第几个参数为格式化字符串(format string);n:参数集合中的第一个,即参数“…”里的第一个参数在函数参数总数排在第几,注意,有时函数参数里还有“隐身”的呢,后面会提到;在使用上,__attribute__((format(printf,m,n)))是常用的,而另一种却很少见到。下面举例说明,其中myprint为自己定义的一个带有可变参数的函数,其功能类似于printf://m=1;n=2 extern void myprint(const char *format,...) \_\_attribute\_\_((format(printf,1,2))); //m=2;n=3 extern void myprint(int l,const char *format,...) \_\_attribute\_\_((format(printf,2,3)));需要特别注意的是,如果myprint是一个函数的成员函数,那么m和n的值可有点“悬乎”了,例如://m=3;n=4 extern void myprint(int l,const char *format,...) \_\_attribute\_\_((format(printf,3,4)));其原因是,类成员函数的第一个参数实际上一个“隐身”的“this”指针。(有点C++基础的都知道点this指针,不知道你在这里还知道吗?)这里给出测试用例:attribute.c,代码如下:1: 2:extern void myprint(const char *format,...) __attribute__((format(printf,1,2))); 3: 4:void test() 5:{ 6: myprint("i=%d\n",6); 7: myprint("i=%s\n",6); 8: myprint("i=%s\n","abc"); 9: myprint("%s,%d,%d\n",1,2); 10:}运行$gcc –Wall –c attribute.c attribute后,输出结果为:attribute.c: In function 'test':attribute.c: 7: warning: format argument is not a pointer (arg 2)attribute.c: 9: warning: format argument is not a pointer (arg 2)attribute.c: 9: warning: too few arguments for format如果在attribute.c中的函数声明去掉__attribute__((format(printf,1,2))),再重新编译,既运行$gcc –Wall –c attribute.c attribute后,则并不会输出任何警告信息。注意,默认情况下,编译器是能识别类似printf的“标准”库函数。__attribute__ noreturn该属性通知编译器函数从不返回值,当遇到类似函数需要返回值而却不可能运行到返回值处就已经退出来的情况,该属性可以避免出现错误信息。C库函数中的abort()和exit()的声明格式就采用了这种格式,如下所示:extern void exit(int) __attribute__((noreturn));extern void abort(void) __attribute__((noreturn)); 为了方便理解,大家可以参考如下的例子://name: noreturn.c ;测试__attribute__((noreturn)) extern void myexit(); int test(int n) { if ( n > 0 ) { myexit(); /* 程序不可能到达这里*/ } else return 0; }编译显示的输出信息为:gcc –Wall –c noreturn.cnoreturn.c: In function 'test':noreturn.c: 12: warning: control reaches end of non-void function警告信息也很好理解,因为你定义了一个有返回值的函数test却有可能没有返回值,程序当然不知道怎么办了!加上__attribute__((noreturn))则可以很好的处理类似这种问题。把extern void myexit();修改为:extern void myexit() __attribute__((noreturn));之后,编译不会再出现警告信息。__attribute__ const该属性只能用于带有数值类型参数的函数上。当重复调用带有数值参数的函数时,由于返回值是相同的,所以此时编译器可以进行优化处理,除第一次需要运算外, 其它只需要返回第一次的结果就可以了,进而可以提高效率。该属性主要适用于没有静态状态(static state)和副作用的一些函数,并且返回值仅仅依赖输入的参数。为了说明问题,下面举个非常“糟糕”的例子,该例子将重复调用一个带有相同参数值的函数,具体如下:extern int square(int n) __attribute__ ((const)); ... for (i = 0; i < 100; i++ ) { total += square (5) + i; }通过添加__attribute__((const))声明,编译器只调用了函数一次,以后只是直接得到了相同的一个返回值。事实上,const参数不能用在带有指针类型参数的函数中,因为该属性不但影响函数的参数值,同样也影响到了参数指向的数据,它可能会对代码本身产生严重甚至是不可恢复的严重后果。并且,带有该属性的函数不能有任何副作用或者是静态的状态,所以,类似getchar()或time()的函数是不适合使用该属性的。关于linux内核中的"__attribute__ ((packed))"引用:__attribute__ ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。#define __u8 unsigned char #define __u16 unsigned short /* __attribute__ ((packed)) 的位置约束是放于声明的尾部“;”之前 */ struct str_struct{ __u8 a; __u8 b; __u8 c; __u16 d; } __attribute__ ((packed)); /* 当用到typedef时,要特别注意__attribute__ ((packed))放置的位置,相当于: * typedef struct str_stuct str; * 而struct str_struct 就是上面的那个结构。 */ typedef struct { __u8 a; __u8 b; __u8 c; __u16 d; } __attribute__ ((packed)) str; /* 在下面这个typedef结构中,__attribute__ ((packed))放在结构名str_temp之后,其作用是被忽略的,注意与结构str的区别。*/ typedef struct { __u8 a; __u8 b; __u8 c; __u16 d; }str_temp __attribute__ ((packed)); typedef struct { __u8 a; __u8 b; __u8 c; __u16 d; }str_nopacked; int main(void) { printf("sizeof str = %d\n", sizeof(str)); printf("sizeof str_struct = %d\n", sizeof(struct str_struct)); printf("sizeof str_temp = %d\n", sizeof(str_temp)); printf("sizeof str_nopacked = %d\n", sizeof(str_nopacked)); return 0; }编译运行:引用:[root@localhost root]# ./packedtest sizeof str = 5sizeof str_struct = 5sizeof str_temp = 6sizeof str_nopacked = 6packed属性:使用该属性可以使得变量或者结构体成员使用最小的对齐方式,即对变量是一字节对齐,对域(field)是位对齐。4.at绝对定位,可以把变量或函数绝对定位到Flash中,或者定位到RAM。1)、定位到flash中,一般用于固化的信息,如出厂设置的参数,上位机配置的参数,ID卡的ID号,flash标记等等const u16 gFlashDefValue[512] __attribute__((at(0x0800F000))) = {0x1111,0x1111,0x1111,0x0111,0x0111,0x0111};//定位在flash中,其他flash补充为00 const u16 gflashdata__attribute__((at(0x0800F000))) = 0xFFFF;2)、定位到RAM中,一般用于数据量比较大的缓存,如串口的接收缓存,再就是某个位置的特定变量u8 USART2_RX_BUF[USART2_REC_LEN] __attribute__ ((at(0X20001000)));//接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20001000.注意:1)、绝对定位不能在函数中定义,局部变量是定义在栈区的,栈区由MDK自动分配、释放,不能定义为绝对地址,只能放在函数外定义。2)、定义的长度不能超过栈或Flash的大小,否则,造成栈、Flash溢出。5.section提到section,就得说RO RI ZI了,在ARM编译器编译之后,代码被划分为不同的段,RO Section(ReadOnly)中存放代码段和常量,RW Section(ReadWrite)中存放可读写静态变量和全局变量,ZI Section(ZeroInit)是存放在RW段中初始化为0的变量。于是本文的大体意思就清晰了,__attribute__((section("section_name"))),其作用是将作用的函数或数据放入指定名为"section_name"对应的段中。1)、编译时为变量指定段:__attribute__((section("name"))) RealView Compilation Tools for µVision Compiler Reference Guide Version 4.0 Home > Compiler-specific Features > Variable attributes > __attribute__((section("name"))) 4.5.6. __attribute__((section("name"))) Normally, the ARM compiler places the objects it generates in sections like data and bss. However, you might require additional data sections or you might want a variable to appear in a special section, for example, to map to special hardware. The section attribute specifies that a variable must be placed in a particular data section. If you use the section attribute, read-only variables are placed in RO data sections, read-write variables are placed in RW data sections unless you use the zero_init attribute. In this case, the variable is placed in a ZI section. Note This variable attribute is a GNU compiler extension supported by the ARM compiler. Example /* in RO section */ const int descriptor[3] __attribute__ ((section ("descr"))) = { 1,2,3 }; /* in RW section */ long long rw[10] __attribute__ ((section ("RW"))); /* in ZI section * long long altstack[10] __attribute__ ((section ("STACK"), zero_init));/2)、编译时为函数指定段__attribute__((section("name"))) RealView Compilation Tools for µVision Compiler Reference Guide Version 4.0 Home > Compiler-specific Features > Function attributes > __attribute__((section("name"))) 4.3.13. __attribute__((section("name"))) The section function attribute enables you to place code in different sections of the image. Note This function attribute is a GNU compiler extension that is supported by the ARM compiler. Example In the following example, Function_Attributes_section_0 is placed into the RO section new_section rather than .text. void Function_Attributes_section_0 (void) __attribute__ ((section ("new_section"))); void Function_Attributes_section_0 (void) { static int aStatic =0; aStatic++; } In the following example, section function attribute overrides the #pragma arm section setting. #pragma arm section code="foo" int f2() { return 1; } // into the 'foo' area __attribute__ ((section ("bar"))) int f3() { return 1; } // into the 'bar' area int f4() { return 1; } // into the 'foo' area #pragma arm section6.多个属性组合使用u8 FileAddr[100] __attribute__ ((section ("FILE_RAM"), zero_init,aligned(4)));
2020年09月15日
2,812 阅读
0 评论
0 点赞
2020-09-07
动态内存分配
目录:[TOC]1.malloc和free函数原型如下,在头文件stdlib.h中声明:void *malloc(size_t size); void free(void *pointer);malloc的参数是需要分配的内存字节数。如果内存池中的可用内存可以满足这个需求,malloc就返回一个指向被分配的内存块起始位置的指针。malloc分配的是一块连续的内存,并且不对这块内存进行任何的初始化。如果操作系统无法向malloc提供更多的内存,malloc就返回一个NULL指针。因此,对每个从malloc返回的指针都进行检查,确保它并非NULL是非常重要的。free的参数必须要么是NULL,要么是一个先前从malloc、calloc或realloc返回的值。向free传递一个NULL参数不会产生任何效果。malloc返回一个类型为void*的指针,一个void*类型的指针可以转换成其它任何类型的指针2.calloc和realloccalloc和realloc的函数原型如下所示:void *calloc(size_t num_elements, size_t element_size); void realloc(void *ptr, size_t new_size);malloc和calloc之间的主要区别是calloc在返回指向内存的指针之前把它初始化为0。calloc和malloc之间另一个较小的区别是它们请求内存数量的方式不同。calloc的参数包括所需元素的数量和每个元素的字节数。根据这些值,它能够计算出总共需要分配的内存。realloc函数用于修改一个原先已经分配的内存块的大小。使用这个函数,可以使一块内存扩大或缩小。如果它用于扩大一个内存块,那么这块内存原先的内容依然保留,新增加的内存添加到原先内存块的后面,新内存并未以任何方法进行初始化。如果它用于缩小内存块,该内存块尾部的部分内存便被拿掉,剩余部分内存的原先内容依然保留。如果原先的内存块无法改变大小,realloc将分配另一块正确大小的内存,并把原先那块内存的内容复制到新块上。因此,在使用realloc之后,你就不能再使用指向旧内存的指针,而是应该用realloc所返回的新指针。3. 常见的动态内存错误在使用动态内存分配的程序中,常常会出现许多错误。这些错误包括对NULL指针进行解引用操作、对分配的内存进行操作时越过边界、释放并非动态分配的内存、试图释放一块动态分配的内存以及一块动态内存被释放之后被继续使用。动态内存分配最常见的错误就是忘记检查所请求的内存是否成功分配// 记得对分配的内存进行检查 if (new_mem == NULL) printf( "Out of memory" ); exit(1);动态内存分配的第二大错误来源是操作内存时超出了分配内存的边界在malloc和free的有些实现中,它们以链表的形式维护可用的内存池。对分配的内存之外的区域进行访问可能破坏这个链表,这有可能产生异常,从而终止程序。
2020年09月07日
730 阅读
0 评论
0 点赞
2020-09-07
gcc和objdump
1. gcc-ansi 关闭 gnu c中与ansi c不兼容的特性,激活ansi c的专有特性 ( 包括禁止一些 asm inline typeof 关键字 , 以及 UNIX,vax 等预处理宏-lxx 表示动态加载libxx.so库-Lxx 表示增加目录xx,让编译器可以在xx下寻找库文件-Ixx 表示增加目录xx,让编译器可以在xx下寻找头文件优化选项-shared 生成共享目标文件。通常用在建立共享库时-Wall 生成所有警告信息。一下是具体的选项,可以单独使用简单的GCC语法:如果你只有一个文件(或者只有几个文件),那么就可以不写Makefile文件(当然有Makefile更加方便),用gcc直接编译就行了。在这里我们只介绍几个我经常用的几个参数,第一是 “-o”,它后面的参数表示要输出的目标文件,再一个是 “-c”,表示仅编译(Compile),不链接(Make),如果没有”-c”参数,那么就表示链接,如下面的几个命令:gcc –c test.c,表示只编译test.c文件,成功时输出目标文件test.ogcc –c test.c –o test.o ,与上一条命令完全相同gcc –o test test.o,将test.o连接成可执行的二进制文件testgcc –o test test.c,将test.c编译并连接成可执行的二进制文件testgcc test.c –o test,与上一条命令相同gcc –c test1.c,只编译test1.c,成功时输出目标文件test1.ogcc –c test2.c,只编译test2.c,成功时输出目标文件test2.ogcc –o test test1.o test2.o,将test1.o和test2.o连接为可执行的二进制文件testgcc –c test test1.c test2.c,将test1.o和test2.o编译并连接为可执行的二进制文件test注:如果你想编译cpp文件,那么请用g++,否则会有类似如下莫名其妙的错误:cc3r3i2U.o(.eh_frame+0x12): undefined reference to `__gxx_personality_v0’......还有一个参数是”-l”参数,与之紧紧相连的是表示连接时所要的链接库,比如多线程,如果你使用了pthread_create函数,那么你就应该在编译语句的最后加上”-lpthread”,”-l”表示连接,”pthread”表示要连接的库,注意他们在这里要连在一起写,还有比如你使用了光标库curses,那么呢就应该在后面加上”-lcurses”,比如下面的写法:gcc –o test test1.o test2.o –lpthread –lcurses例如: 在ubuntu 环境下编译基于course库函数的程序时,如果不带 -lncurses时,会出现screen1.c:(.text+0x12):对‘initscr’未定义的引用screen1.c:(.text+0x24):对‘wmove’未定义的引用screen1.c:(.text+0x39):对‘printw’未定义的引用screen1.c:(.text+0x4a):对‘wrefresh’未定义的引用screen1.c:(.text+0x5f):对‘endwin’未定义的引用需使用 gcc -o screen1 screen1.c -lncurses2. objdumpgcc命令之 objdump ---------------objdump是用查看目标文件或者可执行的目标文件的构成的GCC工具----------以下3条命令足够那些喜欢探索目标文件与源代码之间的丝丝的关系的朋友。objdump -x obj 以某种分类信息的形式把目标文件的数据组织(被分为几大块)输出 <可查到该文件的所有动态库> objdump -t obj 输出目标文件的符号表()objdump -h obj 输出目标文件的所有段概括()objdump -j .text/.data -S obj 输出指定段的信息,大概就是反汇编源代码把objdump -S obj C语言与汇编语言同时显示 以下为网上摘录文章。关于nm -s的显示请自己man nm查看objdump命令的man手册objdump - 显示二进制文件信息objdump [-a] [-b bfdname | --target=bfdname] [-C] [--debugging] [-d] [-D] [--disassemble-zeroes] [-EB|-EL|--endian={big|little}] [-f] [-h] [-i|--info] [-j section | --section=section] [-l] [-m machine ] [--prefix-addresses] [-r] [-R] [-s|--full-contents] [-S|--source] [--[no-]show-raw-insn] [--stabs] [-t] [-T] [-x] [--start-address=address] [--stop-address=address] [--adjust-vma=offset] [--version] [--help] objfile... --archive-headers-a 显示档案库的成员信息,与 ar tv 类似objdump -a libpcap.a 和 ar -tv libpcap.a 显示结果比较比较 显然这个选项没有什么意思。--adjust-vma=offsetWhen dumping information, first add offset to all the section addresses. This is useful if the sec- tion addresses do not correspond to the symbol table, which can happen when putting sections at particular addresses when using a format which can not represent section addresses, such as a.out.-b bfdname--target=bfdname指定目标码格式。这不是必须的,objdump能自动识别许多格式, 比如:objdump -b oasys -m vax -h fu.o 显示fu.o的头部摘要信息,明确指出该文件是Vax系统下用Oasys 编译器生成的目标文件。objdump -i将给出这里可以指定的 目标码格式列表--demangle-C 将底层的符号名解码成用户级名字,除了去掉所有开头 的下划线之外,还使得C++函数名以可理解的方式显示出来。--debugging显示调试信息。企图解析保存在文件中的调试信息并以C语言 的语法显示出来。仅仅支持某些类型的调试信息。--disassemble-d 反汇编那些应该还有指令机器码的section--disassemble-all-D 与 -d 类似,但反汇编所有section--prefix-addresses反汇编的时候,显示每一行的完整地址。这是一种比较老的反汇编格式。 显示效果并不理想,但可能会用到其中的某些显示,自己可以对比。--disassemble-zeroes一般反汇编输出将省略大块的零,该选项使得这些零块也被反汇编。-EB-EL--endian={big|little}这个选项将影响反汇编出来的指令。 little-endian就是我们当年在dos下玩汇编的时候常说的高位在高地址, x86都是这种。 --file-headers-f 显示objfile中每个文件的整体头部摘要信息。--section-headers--headers-h 显示目标文件各个section的头部摘要信息。--help 简短的帮助信息。--info-i 显示对于 -b 或者 -m 选项可用的架构和目标格式列表。--section=name-j name 仅仅显示指定section的信息--line-numbers-l 用文件名和行号标注相应的目标代码,仅仅和-d、-D或者-r一起使用 使用-ld和使用-d的区别不是很大,在源码级调试的时候有用,要求 编译时使用了-g之类的调试编译选项。--architecture=machine-m machine指定反汇编目标文件时使用的架构,当待反汇编文件本身没有描述 架构信息的时候(比如S-records),这个选项很有用。可以用-i选项 列出这里能够指定的架构 --reloc-r 显示文件的重定位入口。如果和-d或者-D一起使用,重定位部分以反汇 编后的格式显示出来。--dynamic-reloc-R 显示文件的动态重定位入口,仅仅对于动态目标文件有意义,比如某些 共享库。--full-contents-s 显示指定section的完整内容。objdump --section=.text -s inet.o | more --source-S 尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时, 效果比较明显。隐含了-d参数。--show-raw-insn反汇编的时候,显示每条汇编指令对应的机器码,除非指定了 --prefix-addresses,这将是缺省选项。 --no-show-raw-insn反汇编时,不显示汇编指令的机器码,这是指定 --prefix-addresses 选项时的缺省设置。 --stabsDisplay the contents of the .stab, .stab.index, and .stab.excl sections from an ELF file. This is only useful on systems (such as Solaris 2.0) in which .stab debugging symbol-table entries are carried in an ELF section. In most other file formats, debug- ging symbol-table entries are interleaved with linkage symbols, and are visible in the --syms output. --start-address=address从指定地址开始显示数据,该选项影响-d、-r和-s选项的输出。 --stop-address=address显示数据直到指定地址为止,该选项影响-d、-r和-s选项的输出。 --syms-t 显示文件的符号表入口。类似于nm -s提供的信息--dynamic-syms-T 显示文件的动态符号表入口,仅仅对动态目标文件有意义,比如某些 共享库。它显示的信息类似于 nm -D|--dynamic 显示的信息。--version 版本信息objdump --version --all-headers-x 显示所有可用的头信息,包括符号表、重定位入口。-x 等价于 -a -f -h -r -t 同时指定。objdump -x inet.o 参看 nm(1)★ objdump应用举例(待增加)/*g++ -g -Wstrict-prototypes -Wall -Wunused -o objtest objtest.c*/includeincludeint main ( int argc, char * argv[] ){execl( "/bin/sh", "/bin/sh", "-i", 0 ); return 0;}g++ -g -Wstrict-prototypes -Wall -Wunused -o objtest objtest.cobjdump -j .text -Sl objtest | more/main(查找)08048750:main():/home/scz/src/objtest.c:7*/includeincludeint main ( int argc, char * argv[] ){8048750: 55 pushl %ebp8048751: 89 e5 movl %esp,%ebp/home/scz/src/objtest.c:8 execl( "/bin/sh", "/bin/sh", "-i", 0 );8048753: 6a 00 pushl $0x08048755: 68 d0 87 04 08 pushl $0x80487d0804875a: 68 d3 87 04 08 pushl $0x80487d3804875f: 68 d3 87 04 08 pushl $0x80487d38048764: e8 db fe ff ff call 8048644 <_init+0x40>8048769: 83 c4 10 addl $0x10,%esp/home/scz/src/objtest.c:9 return 0;804876c: 31 c0 xorl %eax,%eax804876e: eb 04 jmp 8048774 8048770: 31 c0 xorl %eax,%eax8048772: eb 00 jmp 8048774 /home/scz/src/objtest.c:10}8048774: c9 leave 8048775: c3 ret 8048776: 90 nop如果说上面还不够清楚,可以用下面的命令辅助一下:objdump -j .text -Sl objtest --prefix-addresses | moreobjdump -j .text -Dl objtest | more用以上不同的命令去试会得到惊喜!
2020年09月07日
855 阅读
0 评论
0 点赞
2020-09-07
目标文件格式
编译器编译源代码后生成的文件叫做目标文件。目标文件从结构上讲,它是已经编译后的可执行文件格式,只是还没有经过链接的过程,其中可能有些符号或有些地址还没有被调整。其实它本身就是按照可执行文件格式存储的,只是跟真正的可执行文件在结构上稍有不同。1.1 目标文件的格式Linux下的可执行文件格式是ELF(Executable Linkable Format)。ELF文件标准把系统中采用的ELF格式的文件分为四类,分别是:可重定位文件(Relocatable File)如Linux下的.o可执行文件(Executable File)共享目标文件(Shared Object File)如Linux下的.so核心转储文件(Core Dump File)当进程意外终止时,系统可以将该进程的地址空间的内容及终止时的一些其它信息转储到核心转储文件如Linux下的core dumpLinux下使用file命令可以查看相应的文件格式,file test.o1.2 目标文件时什么样的section(节)与segment(段)二者唯一的区别是在ELF的链接视图和装载视图的时候code section(代码段)存放编译后的机器指令,如".code"、".text"data section(数据段)存放编译后的全局变量和局部静态变量,如.data文件头ELF文件的开头是一个“文件头”,它描述了整个文件的文件属性,包括文件是否可执行、是静态链接还是动态链接及入口地址(如果是可执行文件)、目标硬件、目标操作系统等信息,文件头还包括一个段表。section table(段表)段表是一个描述文件中各个段的数组。段表描述了文件中各个段在文件中的偏移位置及段的属性等,从段表里面可以得到各个段的所有信息。.bss段未初始化的全局变量和局部静态变量一般放在.bss段。程序运行的时候这些变量确是要占内存空间的,并且可执行文件必须记录所有未初始化的全局变量和局部静态变量的大小总和。所以.bss段只是为位初始化的全局变量和局部静态变量预留位置而已,它并没有内容,所以在文件中也不占据空间。总体来说,程序源代码被编译以后主要分成两种段:程序指令和程序数据。代码段属于程序指令,而数据段和.bss段属于程序数据。1.3 一个实例:simple_section.osimple_section.c代码如下:/* * Linux: gcc -c simple-section.c */ int printf( const char* format, ... ); int global_int_var = 84; int global_uninit_var; void func1(int i) { printf( "%d\n", i); } int main(void) { static int static_var = 85; static int static_var2; int a = 1; int b; func1( static_var + static_var2 + a + b ); return a; }使用gcc编译文件(-c参数表示只编译不链接)gcc -c simple_section.c 使用objdump查看目标文件的结构和内容,Linux下还有一个工具readelf,是专门针对elf文件格式的解析器参数-h把各个段的信息打印出来,-x打印更多的信息objdump -h simple_section.o显示如下:user@user-HP-ProDesk-600-G5-MT:~/txl/code/c/simlesection$ objdump -h simple_section.o simple_section.o: 文件格式 elf64-x86-64 节: Idx Name Size VMA LMA File off Algn 0 .text 00000057 0000000000000000 0000000000000000 00000040 2**0 CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE 1 .data 00000008 0000000000000000 0000000000000000 00000098 2**2 CONTENTS, ALLOC, LOAD, DATA 2 .bss 00000004 0000000000000000 0000000000000000 000000a0 2**2 ALLOC 3 .rodata 00000004 0000000000000000 0000000000000000 000000a0 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 4 .comment 0000002a 0000000000000000 0000000000000000 000000a4 2**0 CONTENTS, READONLY 5 .note.GNU-stack 00000000 0000000000000000 0000000000000000 000000ce 2**0 CONTENTS, READONLY 6 .eh_frame 00000058 0000000000000000 0000000000000000 000000d0 2**3 CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA 其中Size表示段的长度,File off表示段所在的位置,每个段的第二行CONTENTS, ALLOC等表示段的属性,“CONTENTS”表示段在文件中存在。使用size命令,可以用来查看ELF文件代码段、数据段和bss段的长度(dec表示3个段长度和的十进制,hex表示长度和的十六进制)user@user-HP-ProDesk-600-G5-MT:~/txl/code/c/simlesection$ size simple_section.o text data bss dec hex filename 179 8 4 191 bf simple_section.o objdump的“-s”参数可以将所有段的内容以十六进制的方式打印出来,“-d”参数可以将所有包含指令的段反汇编objdump -s -d simple_section.o显示内容如下:simple_section.o: 文件格式 elf64-x86-64 Contents of section .text: 0000 554889e5 4883ec10 897dfc8b 45fc89c6 UH..H....}..E... 0010 488d3d00 000000b8 00000000 e8000000 H.=............. 0020 0090c9c3 554889e5 4883ec10 c745f801 ....UH..H....E.. 0030 0000008b 15000000 008b0500 00000001 ................ 0040 c28b45f8 01c28b45 fc01d089 c7e80000 ..E....E........ 0050 00008b45 f8c9c3 ...E... Contents of section .data: 0000 54000000 55000000 T...U... Contents of section .rodata: 0000 25640a00 %d.. Contents of section .comment: 0000 00474343 3a202855 62756e74 7520372e .GCC: (Ubuntu 7. 0010 352e302d 33756275 6e747531 7e31382e 5.0-3ubuntu1~18. 0020 30342920 372e352e 3000 04) 7.5.0. Contents of section .eh_frame: 0000 14000000 00000000 017a5200 01781001 .........zR..x.. 0010 1b0c0708 90010000 1c000000 1c000000 ................ 0020 00000000 24000000 00410e10 8602430d ....$....A....C. 0030 065f0c07 08000000 1c000000 3c000000 ._..........<... 0040 00000000 33000000 00410e10 8602430d ....3....A....C. 0050 066e0c07 08000000 .n...... Disassembly of section .text: 0000000000000000 <func1>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: 48 83 ec 10 sub $0x10,%rsp 8: 89 7d fc mov %edi,-0x4(%rbp) b: 8b 45 fc mov -0x4(%rbp),%eax e: 89 c6 mov %eax,%esi 10: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 17 <func1+0x17> 17: b8 00 00 00 00 mov $0x0,%eax 1c: e8 00 00 00 00 callq 21 <func1+0x21> 21: 90 nop 22: c9 leaveq 23: c3 retq 0000000000000024 <main>: 24: 55 push %rbp 25: 48 89 e5 mov %rsp,%rbp 28: 48 83 ec 10 sub $0x10,%rsp 2c: c7 45 f8 01 00 00 00 movl $0x1,-0x8(%rbp) 33: 8b 15 00 00 00 00 mov 0x0(%rip),%edx # 39 <main+0x15> 39: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 3f <main+0x1b> 3f: 01 c2 add %eax,%edx 41: 8b 45 f8 mov -0x8(%rbp),%eax 44: 01 c2 add %eax,%edx 46: 8b 45 fc mov -0x4(%rbp),%eax 49: 01 d0 add %edx,%eax 4b: 89 c7 mov %eax,%edi 4d: e8 00 00 00 00 callq 52 <main+0x2e> 52: 8b 45 f8 mov -0x8(%rbp),%eax 55: c9 leaveq 56: c3 retq 在.text段,最左面一列是偏移量,中间四列是十六进制内容,最右面一列是.text段的ASCII码形式。.data段保存的是那些已经初始化了的全局静态变量和局部静态变量。字符串常量"%dn",它是一种只读数据,所以它被放到了“.rodata”段,我们可以从输出结果看到".rodata"这个段的4个字节刚好是这个字符串常量的ASCII字节序,最后以0结尾。.bss段存放的是未初始化的全局变量和局部静态变量。还有一些其它的常用段如下:常用的段名说明.rodata跟.rodata一样.interp/lib64/ld-linux.so.2(动态链接器的路径,有入口函数,装载的时候启动).dynamic动态链接信息.symtab用于静态链接和调试(符号表保留在文件中,不加载进内存).dynsym用于动态链接(符号表会被加载进内存).init和.finit1 共享对象可能会持有这两个段,做为共享对象的入口和出口函数 2 c++中的全局对象,或者static对象的构造函数和析构函数.comment存放的是编译器版本信息.debug调试信息.plt和.got动态链接的跳转表和全局入口表1.4 ELF文件结构描述ELF目标文件格式的最前部是ELF文件头,它描述了整个文件的基本属性,比如ELF文件版本、目标机器型号、程序入口地址等。紧接着就是各个段。其中ELF文件中与段有关的重要结构就是段表,该表描述了ELF文件包含的所有段的信息,比如每个段的段名、段的长度、在文件中的偏移、读写权限及段的其他属性。1.4.1 文件头使用readelf命令来详细查看ELF文件,readelf -h simple_section.o输出如下:ELF 头: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 类别: ELF64 数据: 2 补码,小端序 (little endian) 版本: 1 (current) OS/ABI: UNIX - System V ABI 版本: 0 类型: REL (可重定位文件) 系统架构: Advanced Micro Devices X86-64 版本: 0x1 入口点地址: 0x0 程序头起点: 0 (bytes into file) Start of section headers: 1104 (bytes into file) 标志: 0x0 本头的大小: 64 (字节) 程序头大小: 0 (字节) Number of program headers: 0 节头大小: 64 (字节) 节头数量: 13 字符串表索引节头: 12 ELF魔数:最前面的16个字节刚好对应“Elf32_Ehdr”的e_ident这个成员。1.4.2 段表使用readelf工具来查看文件的段,它显示出来的结果才是真正的段表结构readelf -S simple_section.o输出如下:There are 13 section headers, starting at offset 0x450: 节头: [号] 名称 类型 地址 偏移量 大小 全体大小 旗标 链接 信息 对齐 [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .text PROGBITS 0000000000000000 00000040 0000000000000057 0000000000000000 AX 0 0 1 [ 2] .rela.text RELA 0000000000000000 00000340 0000000000000078 0000000000000018 I 10 1 8 [ 3] .data PROGBITS 0000000000000000 00000098 0000000000000008 0000000000000000 WA 0 0 4 [ 4] .bss NOBITS 0000000000000000 000000a0 0000000000000004 0000000000000000 WA 0 0 4 [ 5] .rodata PROGBITS 0000000000000000 000000a0 0000000000000004 0000000000000000 A 0 0 1 [ 6] .comment PROGBITS 0000000000000000 000000a4 000000000000002a 0000000000000001 MS 0 0 1 [ 7] .note.GNU-stack PROGBITS 0000000000000000 000000ce 0000000000000000 0000000000000000 0 0 1 [ 8] .eh_frame PROGBITS 0000000000000000 000000d0 0000000000000058 0000000000000000 A 0 0 8 [ 9] .rela.eh_frame RELA 0000000000000000 000003b8 0000000000000030 0000000000000018 I 10 8 8 [10] .symtab SYMTAB 0000000000000000 00000128 0000000000000198 0000000000000018 11 11 8 [11] .strtab STRTAB 0000000000000000 000002c0 000000000000007c 0000000000000000 0 0 1 [12] .shstrtab STRTAB 0000000000000000 000003e8 0000000000000061 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) 1.4.3 重定位表.rela.text类型位RELA,是一个重定位表。链接器在处理目标文件时,需要对目标文件中某些部位进行重定位,即代码段和数据段中那些绝对地址的引用的位置。这些重定位信息都记录在重定位表里面。.rela.text段就是.text段的重定位表,因为.text段中至少有一个绝对地址的引用,那就是对printf函数的调用。而.data段则没有对绝对地址的引用,它只包含了几个常量,所以没有针对data段的重定位表。1.4.4 字符串表.strtab(string table): 字符串表,用来保存普通的字符串 .shstrtab(section header string table):用来保存段表中用到的字符串,最常见的就是段名
2020年09月07日
1,014 阅读
0 评论
0 点赞