Như đã đề cập bởi dasblinken, GCC 4.8 đặt các thống kê địa phương trên cùng một vị trí như hình cầu.
Chính xác hơn:
static int i = 0
đi trên .bss
static int i = 1
đi trên .data
Hãy phân tích một Linux x86-64 dụ ELF nhìn thấy nó mình:
#include <stdio.h>
int f() {
static int i = 1;
i++;
return i;
}
int main() {
printf("%d\n", f());
printf("%d\n", f());
return 0;
}
Để đạt được kết luận, chúng ta cần hiểu thông tin di dời. Nếu bạn chưa bao giờ chạm vào điều đó, hãy xem xét reading this post first.
Biên dịch nó:
gcc -ggdb -c main.c
Decompile mã với:
objdump -S main.o
f
chứa:
int f() {
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
static int i = 1;
i++;
4: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # a <f+0xa>
a: 83 c0 01 add $0x1,%eax
d: 89 05 00 00 00 00 mov %eax,0x0(%rip) # 13 <f+0x13>
return i;
13: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 19 <f+0x19>
}
19: 5d pop %rbp
1a: c3 retq
nào làm 3 truy cập để i
:
4
di chuyển đến eax
để chuẩn bị cho increment
d
di chuyển giá trị tăng lên trở lại vào bộ nhớ
13
di chuyển i
đến eax
cho giá trị trả về. Rõ ràng là không cần thiết vì eax
đã chứa nó và -O3
có thể xóa điều đó.
Vì vậy, hãy chỉ tập trung vào 4
:
4: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # a <f+0xa>
Hãy nhìn vào các dữ liệu di dời:
readelf -r main.o
mà nói cách các địa chỉ phần văn bản sẽ được điều chỉnh bởi các mối liên kết khi nó là làm cho thực thi.
Nó chứa:
Relocation section '.rela.text' at offset 0x660 contains 9 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000000006 000300000002 R_X86_64_PC32 0000000000000000 .data - 4
Chúng tôi nhìn vào .rela.text
và không phải là những người khác bởi vì chúng tôi quan tâm đến việc di dời của .text
.
Offset 6
rơi ngay vào hướng dẫn mà bắt đầu từ byte 4:
4: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # a <f+0xa>
^^
This is offset 6
Từ kiến thức của chúng ta về mã hóa lệnh x86-64:
8b 05
là phần mov
00 00 00 00
là phần địa chỉ, bắt đầu từ byte 6
AMD64 System V ABI Update cho chúng ta biết R_X86_64_PC32
hành vi trên 4 byte (00 00 00 00
) và tính toán các địa chỉ như:
S + A - P
có nghĩa là:
S
: phân khúc chỉ vào: .data
A
: số Added
: -4
P
: địa chỉ của byte 6 khi nạp
-P
là cần thiết vì GCC sử dụng RIP
thân giải quyết, vì vậy chúng tôi phải giảm giá vị trí trong .text
-4
là cần thiết vì RIP
điểm để hướng dẫn sau tại byte 0xA
nhưng P
là byte 0x6
, vì vậy chúng tôi cần giảm giá 4.
Kết luận: sau khi liên kết nó sẽ trỏ đến byte đầu tiên của đoạn .data
.
Tại sao bạn nghĩ rằng tên trình biên dịch sử dụng ở cấp liên kết là cùng tên mà bạn khai báo? – bmargulies
thể trùng lặp của một này: [gì “tĩnh” có nghĩa là trong một chương trình C] [1] [1]: http: // stackoverflow.com/questions/572547/what-does-static-mean-in-ac-program – tejas
có thể trùng lặp của [Các biến tĩnh được lưu trữ ở đâu (trong C/C++)?] (http://stackoverflow.com/questions/93039/ nơi-là-tĩnh-biến-được lưu trữ-in-cc) – jogojapan