问题
First time poster. 2nd year CS student.
I am exploring the creation of static variables in the .data section of the Virtual Address Space in the context of a C source->GCC compilation->Linux execution environment.
C program is test.c
int main()
{
register int i = 0;
register int sum = 0;
static int staticVar[10] = {1,2,3,4,5,6,7,8,9,-1};
Loop:
sum = sum + staticVar[i]; //optimized away
i = i+1;
if(i != 10)
goto Loop;
return 0;
}
Asking GDB to 'disass /m
' reveals that there is no code for the staticVar[] creation because inspecting the .s file reveals the variable resides in the read/write .data segment of the virtual address space having been placed there at the time of process creation(this process is what I'm interested in).
Examining the output of (I though it was 'readelf -A test.o
') the object file contains assembly for what I assume is the creation of the array in the data segment. Here is the ELF output.
(Bonus if you can tell me what command generates this output. I can not duplicate it using readelf. I picked up the command off a website and saved output. I cant remember how generated)
[snip]
00000000 <staticVar.1359>:
0:01 00 add %eax,(%eax)
2:00 00 add %al,(%eax)
4:02 00 add (%eax),%al
6:00 00 add %al,(%eax)
8:03 00 add (%eax),%eax
a:00 00 add %al,(%eax)
c:04 00 add $0x0,%al
e:00 00 add %al,(%eax)
10:05 00 00 00 06 add $0x6000000,%eax
15:00 00 add %al,(%eax)
17:00 07 add %al,(%edi)
19:00 00 add %al,(%eax)
1b:00 08 add %cl,(%eax)
1d:00 00 add %al,(%eax)
1f:00 09 add %cl,(%ecx)
21:00 00 add %al,(%eax)
23:00 ff add %bh,%bh
25:ff (bad)
26:ff (bad)
27:ff .byte 0xff
[snip]
Assumptions(please correct): This assembly exists in the executable and is run by load_elf_binary(), or some part of the execve() initiated series of functions. I have no at&t (basic intel) syntax knowledge but even intuitively I dont see how these instructions can build an array. Looks like they are just adding register values together.
Bottom Line: I would like to know as much as possible about the life cycle of this static array especially where is the "missing code" that builds it and how can I look at it? Or better yet how can I debug (step through) the loader process? I have tried setting a breakpoint before main at the __start_libc entry (or something like that) but could not identify anything promising in this area.
Links to additional info are great! Thanks for your time!
回答1:
The initializers for staticVar
is stored in the .data
section of the executable. Using objdump
(e.g. How can I examine contents of a data section of an ELF file on Linux?) should reveal something like this for your file:
./test: file format elf64-x86-64
Contents of section .data:
00d2c0 00000000 00000000 00000000 00000000 ................
00d2d0 00000000 00000000 00000000 00000000 ................
00d2e0 01000000 02000000 03000000 04000000 ................
00d2f0 05000000 06000000 07000000 08000000 ................
00d300 09000000 ffffffff 00000000 00000000 ................
00d310 00000000 00000000 00000000 00000000 ................
That content from the executable is directly mapped into the address space of your process, so there is no need for any code to create the data. Codes that operate on staticVar
will refer to the content directly using memory pointers; e.g. for the loop you posted, gcc -S
gave me this:
18 .L5:
19 0013 90 nop
20 .L2:
21 0014 4863C3 movslq %ebx, %rax
22 0017 8B148500 movl staticVar.1707(,%rax,4), %edx
22 000000
23 001e 8B45F4 movl -12(%rbp), %eax
24 0021 01D0 addl %edx, %eax
25 0023 8945F4 movl %eax, -12(%rbp)
26 0026 83C301 addl $1, %ebx
27 0029 83FB0A cmpl $10, %ebx
28 002c 75E5 jne .L5
Lifetime of this static array would be the lifetime of your process, similar to a global variable. In any case, there is no code that builds it. It's just some data in memory.
P/S: You may need to add volatile to sum
like such: volatile int sum = 0;
Otherwise gcc
would probably optimize it away since the resulting value of sum is never used.
回答2:
My executable format is limited to old formats that aren't used anymore, but I'm pretty sure the code you're looking for does not exist in the ELF executable itself.
Executable files define sections that are copied/mapped to your process's memory verbatim. Just like your executable doesn't include code to populate the instruction stream of the functions it defines, it does not include code to populate static non-executable data. To that end, remember that there isn't any fundamental difference between data symbols and executable symbols: as far as storage is concerned, they're both data.
Some languages, like C++, will allow you to use dynamic initializers. Dynamic initializers are run before the entry point of your executable and populate data symbols with information that couldn't be inferred at compile-time. In that case, yes, there will be code for it. However, statically-initialized symbols don't need that, they can be copied or mapped straight to your process's address space.
Look at the data for staticVar
closer. Forget the instructions for a bit; what happens if you put all the bytes next to one another?
01 00 00 00
02 00 00 00
03 00 00 00
04 00 00 00
05 00 00 00
06 00 00 00
07 00 00 00
08 00 00 00
09 00 00 00
ff ff ff ff
This is the hexadecimal representation of the sequence of little-endian integers {1, 2, 3, 4, 5, 6, 7, 8, 9, -1}
, laid out in group of 4 bytes to make it easier to spot.
回答3:
As zneak answered in the object file there is no code initializing the staticVar
variable. The actual bytes of the .data
section are directly in the test.o
file. If you define a const
variable gcc
will put it into .rodata
section by default.
The disassembly you obtained most probably by objdump --disassemble-all file.o
. Maybe the disassembly of data confused you into thinking that it is a real code. For normal use I would recommend objdump --disassemble file.o
which disassembles just sections containing actual machine code.
You can get detailed relevant information by running: objdump -xdst test.o
Shortly about the life of staticVar
- gcc puts the variable (including its value) to the
.data
section as it is a static initialized variable. The size of the section is just for the variable, the address is not determined yet. Seeobjdump -xdst test.o
. - Linker determines the address of the
.data
section (and others) as the object files are known. In the.data
section other variables from other object files appears. Seeobjdump -xdst test
. - When the binary
test
is executed the contained segments are directly mapped (mmap()
) to the address space of the process so the value ofstaticVar
is directly read from thetest
binary (possibly when needed). Then the dynamic linkedld-linux
maps shared libraries likelibc
. Seecat /proc/$PID/maps
orpmap $PID
when the process with$PID
is loaded in memory.
There could be a variable initialization code in C
Just try to change the staticVar to a local non-static variable (i.e. stored on the stack by default):
int staticVar[10] = {1,2,3,4,5,6,7,8,9,-1};
By default gcc will generate an initialization code directly in the main()
function. Just see objdump -xdst test.o
. This is because the stack variables are allocated (so their addresses are determined) at run-time.
回答4:
Just to add that on bare-metal hardware (ie. microcontrollers) (where you don't have the benefits of binary loaders), you will see the code that zeroes the .bbs and copies .data section from RO FLASH into RAM.
That code will be something similar to http://repo.or.cz/w/cbaos.git/blob/HEAD:/kernel/init.c#l23 .
来源:https://stackoverflow.com/questions/18582582/where-do-i-find-the-assembly-that-creates-a-static-variable-in-the-data-section