Ubuntu 15.10, Çekirdek 4.2.0, x86-64, GCC 5.2.1 örneği
Yeterli standart, bir uygulamaya bakalım :-)
Yerel değişken
Standartlar: tanımlanmamış davranış.
Uygulama: Program yığın alanı ayırır ve hiçbir zaman bu adrese hiçbir şey taşımaz, bu nedenle daha önce ne varsa kullanılır.
#include <stdio.h>
int main() {
int i;
printf("%d\n", i);
}
derlemek:
gcc -O0 -std=c99 a.c
çıktılar:
0
ve aşağıdakilerle ayrışır:
objdump -dr a.out
için:
0000000000400536 <main>:
400536: 55 push %rbp
400537: 48 89 e5 mov %rsp,%rbp
40053a: 48 83 ec 10 sub $0x10,%rsp
40053e: 8b 45 fc mov -0x4(%rbp),%eax
400541: 89 c6 mov %eax,%esi
400543: bf e4 05 40 00 mov $0x4005e4,%edi
400548: b8 00 00 00 00 mov $0x0,%eax
40054d: e8 be fe ff ff callq 400410 <printf@plt>
400552: b8 00 00 00 00 mov $0x0,%eax
400557: c9 leaveq
400558: c3 retq
X86-64 çağrı sözleşmeleri bilgimizden:
%rdi
ilk printf argümanıdır, dolayısıyla "%d\n"
adresteki dize0x4005e4
%rsi
ikinci printf argümanıdır i
.
Bu gelir -0x4(%rbp)
ilk 4 baytlık lokal değişken olan.
Bu noktada, rbp
yığının ilk sayfasında çekirdek tarafından tahsis edilmiştir, bu yüzden bu değeri anlamak için çekirdek koduna bakıp neyi ayarladığını öğreneceğiz.
YAPILACAK Çekirdek, bir işlem öldüğünde diğer işlemler için yeniden kullanmadan önce bu belleği bir şeye ayarlıyor mu? Değilse, yeni süreç veri sızdıran diğer bitmiş programların belleğini okuyabilecektir. Bakınız: Başlatılmamış değerler bir güvenlik riski oluşturuyor mu?
Daha sonra kendi yığın değişikliklerimizle oynayabilir ve aşağıdaki gibi eğlenceli şeyler yazabiliriz:
#include <assert.h>
int f() {
int i = 13;
return i;
}
int g() {
int i;
return i;
}
int main() {
f();
assert(g() == 13);
}
Yerel değişken -O3
Uygulama analizi: gdb'de <optimize edilmiş değer> ne anlama geliyor?
Global değişkenler
Standartlar: 0
Uygulama: .bss
bölüm.
#include <stdio.h>
int i;
int main() {
printf("%d\n", i);
}
gcc -00 -std=c99 a.c
derler:
0000000000400536 <main>:
400536: 55 push %rbp
400537: 48 89 e5 mov %rsp,%rbp
40053a: 8b 05 04 0b 20 00 mov 0x200b04(%rip),%eax # 601044 <i>
400540: 89 c6 mov %eax,%esi
400542: bf e4 05 40 00 mov $0x4005e4,%edi
400547: b8 00 00 00 00 mov $0x0,%eax
40054c: e8 bf fe ff ff callq 400410 <printf@plt>
400551: b8 00 00 00 00 mov $0x0,%eax
400556: 5d pop %rbp
400557: c3 retq
400558: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
40055f: 00
# 601044 <i>
i
adreste olduğunu söylüyor 0x601044
ve:
readelf -SW a.out
içerir:
[25] .bss NOBITS 0000000000601040 001040 000008 00 WA 0 0 4
8 bayt uzunluğunda başlayan bölümün 0x601044
tam ortasında diyor ..bss
0x601040
ELF standart sonra adlandırılmış bölüm garanti .bss
tamamen sıfır ile doldurulur:
.bss
Bu bölüm, programın bellek görüntüsüne katkıda bulunan başlatılmamış verileri içerir. Tanım olarak, program çalışmaya başladığında sistem verileri sıfırlarla başlatır. Kesit, kesit tipinde belirtildiği gibi dosya alanı içermez SHT_NOBITS
.
Ayrıca, tür SHT_NOBITS
etkilidir ve yürütülebilir dosyada yer kaplamaz:
sh_size
Bu üye bölümün boyutunu bayt cinsinden verir. SHT_NOBITS
Bölüm türü olmadığı sürece, bölüm sh_size
dosyadaki baytları kaplar . Türün bir bölümü SHT_NOBITS
sıfır olmayan bir boyuta sahip olabilir, ancak dosyada yer kaplamaz.
Ardından, programı başlatıldığında belleğe yüklerken bu bellek bölgesini sıfırlamak Linux çekirdeğine bağlıdır.