ทำไมฉันไม่ลองเปลี่ยนมันล่ะ?
เพราะมันเป็นพฤติกรรมที่ไม่ได้กำหนด อ้างอิงจากC99 N1256 ฉบับร่าง 6.7.8 / 32 "การเริ่มต้น" :
ตัวอย่างที่ 8: การประกาศ
char s[] = "abc", t[3] = "abc";
กำหนดวัตถุอาร์เรย์ถ่าน "ธรรมดา" s
และt
องค์ประกอบที่จะเริ่มต้นด้วยตัวอักษรสตริงตัวอักษร
คำประกาศนี้เหมือนกัน
char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };
เนื้อหาของอาร์เรย์สามารถแก้ไขได้ ในทางกลับกันการประกาศ
char *p = "abc";
กำหนดp
ด้วยประเภท "ตัวชี้ไปที่ถ่าน" และเริ่มต้นให้ชี้ไปที่วัตถุที่มีประเภท "อาร์เรย์ของถ่าน" ที่มีความยาว 4 ซึ่งองค์ประกอบจะเริ่มต้นด้วยตัวอักษรสตริงตัวอักษร หากมีความพยายามในการใช้p
เพื่อปรับเปลี่ยนเนื้อหาของอาร์เรย์พฤติกรรมจะไม่ได้กำหนด
พวกเขาไปไหน?
GCC 4.8 x86-64 ELF Ubuntu 14.04:
char s[]
: ซ้อนกัน
char *s
:
.rodata
ส่วนของไฟล์วัตถุ
- เซ็กเมนต์เดียวกันที่
.text
ส่วนของไฟล์อ็อบเจ็กต์ได้รับการดัมพ์ซึ่งมีสิทธิ์ Read และ Exec แต่ไม่ใช่ Write
โปรแกรม:
#include <stdio.h>
int main() {
char *s = "abc";
printf("%s\n", s);
return 0;
}
รวบรวมและถอดรหัส:
gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o
เอาท์พุทประกอบด้วย:
char *s = "abc";
8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
f: 00
c: R_X86_64_32S .rodata
ดังนั้นสตริงจะถูกเก็บไว้ใน.rodata
ส่วน
แล้ว:
readelf -l a.out
มี (ประยุกต์):
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000000704 0x0000000000000704 R E 200000
Section to Segment mapping:
Segment Sections...
02 .text .rodata
ซึ่งหมายความว่าสคริปต์ตัวเชื่อมโยงค่าเริ่มต้นจะทิ้งทั้งสองอย่าง.text
และ.rodata
ไปยังส่วนที่สามารถดำเนินการได้ แต่ไม่ได้แก้ไข ( Flags = R E
) ความพยายามที่จะแก้ไขส่วนดังกล่าวนำไปสู่ segfault ใน Linux
ถ้าเราทำเช่นเดียวกันสำหรับchar[]
:
char s[] = "abc";
เราได้รับ:
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
ดังนั้นมันจึงถูกเก็บไว้ในสแต็ก (สัมพันธ์กับ%rbp
) และแน่นอนว่าเราสามารถแก้ไขได้