ตัวอย่าง Ubuntu 15.10, เคอร์เนล 4.2.0, x86-64, GCC 5.2.1
มาตรฐานที่เพียงพอมาดูการใช้งาน :-)
ตัวแปรท้องถิ่น
มาตรฐาน: พฤติกรรมที่ไม่ได้กำหนด
การติดตั้งใช้งาน: โปรแกรมจัดสรรพื้นที่สแต็กและไม่ย้ายสิ่งใดไปยังที่อยู่นั้น
#include <stdio.h>
int main() {
int i;
printf("%d\n", i);
}
รวบรวมกับ:
gcc -O0 -std=c99 a.c
เอาท์พุท:
0
และถอดรหัสกับ:
objdump -dr a.out
ถึง:
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:
%rdi
เป็นอาร์กิวเมนต์ printf แรกดังนั้นสตริง"%d\n"
ที่แอดเดรส0x4005e4
%rsi
เป็นอาร์กิวเมนต์ printf i
สองจึง
มันมาจาก-0x4(%rbp)
ซึ่งเป็นตัวแปรท้องถิ่น 4 ไบต์แรก
ณ จุดนี้rbp
อยู่ในหน้าแรกของสแต็คที่ได้รับการจัดสรรโดยเคอร์เนลเพื่อให้เข้าใจถึงคุณค่าที่เราจะมองเข้าไปในรหัสเคอร์เนลและหาสิ่งที่มันตั้งให้
สิ่งที่ต้องทำเคอร์เนลตั้งค่าหน่วยความจำที่เป็นสิ่งที่ก่อนที่จะนำมาใช้ใหม่สำหรับกระบวนการอื่น ๆ เมื่อกระบวนการตาย? หากไม่เป็นเช่นนั้นกระบวนการใหม่จะสามารถอ่านหน่วยความจำของโปรแกรมที่เสร็จแล้วอื่น ๆ ซึ่งเป็นการรั่วข้อมูล ดู: ค่าที่ไม่ได้กำหนดค่าเริ่มต้นนั้นมีความเสี่ยงด้านความปลอดภัยหรือไม่?
จากนั้นเราสามารถเล่นกับการดัดแปลงแบบกองซ้อนของเราเองและเขียนสิ่งที่สนุกสนานเช่น:
#include <assert.h>
int f() {
int i = 13;
return i;
}
int g() {
int i;
return i;
}
int main() {
f();
assert(g() == 13);
}
ตัวแปรท้องถิ่นใน -O3
การวิเคราะห์การใช้งานที่: <value Optimization out> หมายถึงอะไรใน gdb?
ตัวแปรทั่วโลก
มาตรฐาน: 0
การใช้งาน: .bss
ส่วน
#include <stdio.h>
int i;
int main() {
printf("%d\n", i);
}
gcc -00 -std=c99 a.c
รวบรวมเพื่อ:
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
เป็นที่อยู่0x601044
และ:
readelf -SW a.out
ประกอบด้วย:
[25] .bss NOBITS 0000000000601040 001040 000008 00 WA 0 0 4
ซึ่งระบุว่า0x601044
อยู่ตรงกลางของ.bss
ส่วนซึ่งเริ่มต้นที่0x601040
และมีความยาว 8 ไบต์
มาตรฐานเอลฟ์แล้วรับประกันว่าส่วนที่มีชื่อ.bss
จะสมบูรณ์เต็มไปด้วยของศูนย์:
.bss
ส่วนนี้เก็บข้อมูลที่ไม่ได้กำหนดค่าเริ่มต้นซึ่งนำไปสู่อิมเมจหน่วยความจำของโปรแกรม ตามคำนิยามระบบจะเริ่มต้นข้อมูลด้วยค่าศูนย์เมื่อโปรแกรมเริ่มทำงาน ส่วน occu- SHT_NOBITS
พายพื้นที่ไม่มีแฟ้มตามที่ระบุไว้ตามประเภทส่วน
นอกจากนี้ชนิดSHT_NOBITS
ยังมีประสิทธิภาพและไม่มีที่ว่างบนไฟล์เรียกทำงาน:
sh_size
สมาชิกนี้ให้ขนาดของส่วนเป็นไบต์ ยกเว้นว่าเป็นSHT_NOBITS
ส่วนประเภทส่วนที่ครอบครองsh_size
ไบต์ในไฟล์ ส่วนของประเภทSHT_NOBITS
อาจมีขนาดที่ไม่เป็นศูนย์ แต่จะไม่มีพื้นที่ในไฟล์
จากนั้นจะขึ้นอยู่กับเคอร์เนลของ Linux เพื่อให้ไม่มีพื้นที่หน่วยความจำนั้นเมื่อโหลดโปรแกรมลงในหน่วยความจำเมื่อเริ่มต้น