Evet, __attribute__((packed))bazı sistemlerde potansiyel olarak güvenli değildir. Semptom muhtemelen x86'da görünmeyecek, bu da sorunu daha sinsi hale getiriyor; x86 sistemlerinde test yapmak sorunu ortaya çıkarmaz. (X86'da, yanlış hizalanmış erişimler donanımda işlenir; int*tek bir adrese işaret eden bir işaretçiyi kaldırırsanız , düzgün hizalanmış olduğundan biraz daha yavaş olur, ancak doğru sonucu alırsınız.)
SPARC gibi diğer bazı sistemlerde, yanlış hizalanmış bir intnesneye erişmeye çalışmak , programın çökmesine neden olan bir veriyolu hatasına neden olur.
Yanlış hizalanmış bir erişimin adresin düşük dereceli bitlerini sessizce göz ardı ettiği ve yanlış bellek yığınına erişmesine neden olan sistemler de vardır.
Aşağıdaki programı düşünün:
#include <stdio.h>
#include <stddef.h>
int main(void)
{
struct foo {
char c;
int x;
} __attribute__((packed));
struct foo arr[2] = { { 'a', 10 }, {'b', 20 } };
int *p0 = &arr[0].x;
int *p1 = &arr[1].x;
printf("sizeof(struct foo) = %d\n", (int)sizeof(struct foo));
printf("offsetof(struct foo, c) = %d\n", (int)offsetof(struct foo, c));
printf("offsetof(struct foo, x) = %d\n", (int)offsetof(struct foo, x));
printf("arr[0].x = %d\n", arr[0].x);
printf("arr[1].x = %d\n", arr[1].x);
printf("p0 = %p\n", (void*)p0);
printf("p1 = %p\n", (void*)p1);
printf("*p0 = %d\n", *p0);
printf("*p1 = %d\n", *p1);
return 0;
}
Gcc 4.5.2 ile x86 Ubuntu'da aşağıdaki çıktıyı üretir:
sizeof(struct foo) = 5
offsetof(struct foo, c) = 0
offsetof(struct foo, x) = 1
arr[0].x = 10
arr[1].x = 20
p0 = 0xbffc104f
p1 = 0xbffc1054
*p0 = 10
*p1 = 20
Gcc 4.5.1 ile SPARC Solaris 9'da aşağıdakileri üretir:
sizeof(struct foo) = 5
offsetof(struct foo, c) = 0
offsetof(struct foo, x) = 1
arr[0].x = 10
arr[1].x = 20
p0 = ffbff317
p1 = ffbff31c
Bus error
Her iki durumda da, program sadece ekstra seçeneklerle derlenir gcc packed.c -o packed.
(Bu nedenle derleyici tek adresine yapı tahsis çünkü, dizi güvenilir bir sorunu gösteren yerine tek bir yapı kullanan bir program xiki Bir dizi elemanı uygun şekilde hizalanır. struct fooNesneler, en az bir ya da diğer yanlış hizalanmış bir xüyesi olacaktır.)
(Bu durumda, p0yanlış hizalanmış bir adrese işaret eder, çünkü bir intüyeyi izleyen paketlenmiş bir charüyeye p1işaret eder. Dizinin ikinci öğesinde aynı üyeye işaret ettiğinden, doğru hizalanmış olur, bu nedenle ondan charönce iki nesne vardır - ve SPARC Solaris'te dizi arreşit olan, ancak 4'ün katı olmayan bir adede tahsis edilmiş gibi görünür.)
Üyesi olduğu ifade edilirken xa struct fooadıyla, derleyici bilir xpotansiyel yanlış hizalanmış olup, doğru şekilde erişmek için ek kod üretecektir.
İşaretçi nesnesinin adresi arr[0].xveya arr[1].xbir nesne içinde saklandıktan sonra, ne derleyici ne de çalışan program yanlış hizalanmış bir intnesneyi gösterdiğini bilmez . Sadece düzgün bir şekilde hizalandığını varsayar (bazı sistemlerde) bir veri yolu hatası veya benzeri başka bir arızaya neden olur.
Bunu gcc'de düzeltmek, pratik olmadığına inanıyorum. Genel bir çözüm, her bir işaretçiyi önemsiz hizalama gerekliliklerine sahip herhangi bir türden vazgeçirmek için ya (a) derleyicinin paketlenmiş bir yapının yanlış hizalanmış bir üyesine işaret etmediğini kanıtlaması veya (b) hizalanmış veya yanlış hizalanmış nesneleri işleyebilen daha büyük ve yavaş kod üretme.
Bir gcc hata raporu gönderdim . Dediğim gibi, düzeltmenin pratik olduğuna inanmıyorum, ancak belgeler bundan bahsetmelidir (şu anda değil).
GÜNCELLEME : 2018-12-20 itibariyle bu hata DÜZELTİL olarak işaretlenir. Yama, -Waddress-of-packed-membervarsayılan olarak etkin olan yeni bir seçenek eklenerek gcc 9'da görünecektir .
Paketlenmiş yapı veya birleşim üyesinin adresi alındığında, hizalanmamış işaretçi değeri ile sonuçlanabilir. Bu düzeltme eki, işaretçi atamasındaki hizalamayı denetlemek ve hizalanmamış adresin yanı sıra hizalanmamış işaretçiyi uyarmak için paketlenmiş üyeye eklenir.
Gcc'nin bu sürümünü kaynağından oluşturdum. Yukarıdaki program için şu teşhisleri üretir:
c.c: In function ‘main’:
c.c:10:15: warning: taking address of packed member of ‘struct foo’ may result in an unaligned pointer value [-Waddress-of-packed-member]
10 | int *p0 = &arr[0].x;
| ^~~~~~~~~
c.c:11:15: warning: taking address of packed member of ‘struct foo’ may result in an unaligned pointer value [-Waddress-of-packed-member]
11 | int *p1 = &arr[1].x;
| ^~~~~~~~~