在编译c和c++文件的时候,每个c或cpp文件都会被编译器编译成obj(object)文件,所有的obj文件和资源文件经链接(Link)成为可执行文件,obj文件可称为目标文件或中间文件。
另外,obj文件只输出程序的相对地址,而exe文件是绝对地址。
dumpbin工具介绍
dumpbin是在Windows平台下用于显示COFF格式文件信息的一个命令行工具。你可以使用DUMPBIN去显示COFF格式的文件信息,比如像vc编译器生成的目标文件(obj),可执行文件(exe)和动态链接库(DLLs)等。
此工具只能在命令行下使用。
语法格式:
dumpbin [options] files
- 1
或者
dumpbin fiels [options]
- 1
例如:
dumpbin main.obj /all
- 1
或者
dumpbin main.obj /section:.bss
- 1
另外,可用使用/out:filename
选项将信息输出到文件中。如:
dumpbin main.obj /section:.data /out:new.txt
- 1
obj文件组成
使用/summary
选项,或者将不输入任何选项,将显示出每个段的基本信息(段名和大小)。
dumpbin main.obj /headers /out:head.txt
- 1
如下所示,可以得到obj的所有节(SECTION)的描述结构,即节头:
FILE HEADER VALUES
14C machine (x86)
19B number of sections
543BC5C0 time date stamp Mon Oct 13 20:29:52 2014
14D18 file pointer to symbol table
643 number of symbols
0 size of optional header
0 characteristics
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
当然,还有其他头的信息,太多了,这里就不一一列举。
各节的信息
使用下面的命令可用获得各节的基本信息。
dumpbin main.obj /out:head.txt
- 1
如下所示:
Summary
14 .CRT$XCU
E .bss
A7 .data
BB7C .debug$S
6C .debug$T
1C4 .drectve
333 .rdata
128 .rdata$r
4 .rtc$IMZ
4 .rtc$TMZ
14 .sxdata
20BD .text
CE .text$x
135 .text$yc
B4 .text$yd
13C .xdata$x
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
其中,左边是节的大小,右边是节名。
(.data)节
.data节用于存储已经初始化的静态(全局)变量。
// 如果是数组比如
int Array1[10];
int Array2[10] = {13};
那么在.data里有Array2,Array1应该放在了符号表等到运行时才创建并分配内存。
如下main.cpp函数函数:
#include <iostream>
int a = 1;
int b = 2;
int main()
{
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
编译后生成main.obj
文件,使用下述命令查看.data
节信息:
dumpbin main.obj /section:.data
- 1
输出如下:
SECTION HEADER #6B
.data name
0 physical address
0 virtual address
8 size of raw data
9062 file pointer to raw data (00009062 to 00009069)
0 file pointer to relocation table
0 file pointer to line numbers
0 number of relocations
0 number of line numbers
C0300040 flags
Initialized Data
4 byte align
Read Write
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
其中: 1. size of raw data : 表明为其预留的空间 2. physical address : 物理地址 3. virtual adderss : 虚拟地址 4. Initialized Data : 初始化的数据 5. 4 byte align : 4字节对齐 6. Read Write : 可读、可写
另外,从size of raw data
可以看出,预留来8Byte的空间,这是因为我们在函数中定义来两个初始化的int型全局变量。 再看下面的代码:
#include <iostream>
int a = 1;
int b = 2;
double c = 3;
int main()
{
static int d = 4;
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
此时的输出为:
SECTION HEADER #6B
.data name
...
18 size of raw data
...
8 byte align
Read Write
- 1
- 2
- 3
- 4
- 5
- 6
- 7
注意:前面的数组是十六进制,18(十六进制) = 24(十进制)。
此时的预留空间变成来24字节,而我们只定义来两个初始化的int型全局变量,一个初始化的int型静态变量,一个初始化的double型全局变量,应该占用20个字节猜对。看下面的8 byte align
,原来是因为double进行的8字节对齐。
再看下面的代码:
#include <iostream>
int a = 1;
char *p = "abc";
int main()
{
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
此时的输出为:
SECTION HEADER #6B
.data name
...
8 size of raw data
...
8 byte align
Read Write
- 1
- 2
- 3
- 4
- 5
- 6
- 7
这是因为,只预留来一个初始化的全局指针(32位程序中一个指针占4个字节),一个初始的int型全局变量的空间。而字符串”abc”是存在.rada
段的。
(.bss)节
.bss 节用于存储未初始化的静态或全局变量。
未定义未初始化的静态或全局变量时,输出如下所示:
SECTION HEADER #16E
.bss name
0 physical address
0 virtual address
2 size of raw data
0 file pointer to raw data
0 file pointer to relocation table
0 file pointer to line numbers
0 number of relocations
0 number of line numbers
C0100080 flags
Uninitialized Data
1 byte align
Read Write
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
可见,默认预留来2Byte的空间。Uninitialized Data
表示未初始化的数据。// 我个人测试的时候并没有看到预留,如果没有符合条件的变量,那么直接就没有这个SECTION.
看下面的代码:
#include <iostream>
int a ;
int main()
{
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
此时的输出为:
SECTION HEADER #6B
.bss name
...
6 size of raw data
...
4 byte align
Read Write
- 1
- 2
- 3
- 4
- 5
- 6
- 7
多了一个未初始化的全局变量,所以预留的空间就多了4字节。
(.rdata)节
字符常量保存在.rdata
中。
看下面的代码:
#include <iostream>
char *p = "abc";
int main()
{
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
此时,rdata的大小为337字节。
再看下面的代码:
#include <iostream>
char *p = "abcdefghi";
int main()
{
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
此时,rdata的大小为33D字节。
再看下面的代码:
#include <iostream>
int main()
{
return 0;
}
- 1
- 2
- 3
- 4
- 5
此时,rdata的大小为333字节。
“abc”字符串常量占四个字节 = 337 - 333; “abcdefghi”字符串常量占十个字节 = 33D - 333;
对于最初的代码,在.rdata
节中查找abc
可以找到下面的部份:
SECTION HEADER #17A
.rdata name
0 physical address
0 virtual address
4 size of raw data
14797 file pointer to raw data (00014797 to 0001479A)
0 file pointer to relocation table
0 file pointer to line numbers
0 number of relocations
0 number of line numbers
40301040 flags
Initialized Data
COMDAT; sym= "`string'" (??_C@_03FIKCJHKP@abc?$AA@)
4 byte align
Read Only
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
看的了abc
吧。// 同时在data区也会有一个relocation到这个常量
(.text)节
.text 节用于存储程序代码。
看下面的代码:
#include <iostream>
int main()
{
return 0;
}
- 1
- 2
- 3
- 4
- 5
此时,text的大小为20B6字节。
再看下面的代码:
#include <iostream>
int main()
{
int a = 1;
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
此时,text的大小为20BD字节,多了7个字节。
再看下面的代码:
#include <iostream>
int main()
{
int a = 1;
int b = 2;
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
此时,text的大小为20C4字节,又多了7个字节。
再看下面的代码:
#include <iostream>
int main()
{
int a = 1;
int b = 2;
double c = 3;
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
此时,text的大小为20D1字节,又多了13个字节。
// 如果函数如下,则会有两个单独的.rdata出来存储里面的常量。一个存储OOOOO,一个存储XXXXX。
void TTS()
{
printf("OOOOO");
printf("OOOOO");
printf("XXXXX");
}
const修饰的量确切说叫只读量,不是常量。
可能在栈区,可能在全局区,看作用域以及有没有static修饰了,如果没有对其取地址的话,也可能被编译器优化为字面常量。
①static无论是全局变量还是局部变量都存储在全局/静态区域,在编译期就为其分配内存,在程序结束时释放,例如:val_a、val_d、val_h、val_i。
②const全局变量存储在只读数据段,编译期最初将其保存在符号表中,第一次使用时为其分配内存,在程序结束时释放,例如:val_c;const局部变量存储在栈中,代码块结束时释放,例如:val_j。
③全局变量存储在全局/静态区域,在编译期为其分配内存,在程序结束时释放,例如:val_b、val_e。
④局部变量存储在栈中,代码块结束时释放,例如:val_h、val_i。
注:当全局变量和静态局部变量未赋初值时,系统自动置为0。
详细可参考:https://en.wikipedia.org/wiki/Object_file
- Header (descriptive and control information)
- Code segment ("text segment", executable code)
- Data segment (initialized static variables)
- Read-only data segment (rodata, initialized static constants)
- BSS segment (uninitialized static data, both variables and constants)
- External definitions and references for linking
- Relocation information
- Dynamic linking information
- Debugging information