क्या स्टैक चर GCC __attribute __ ((संरेखित (x))) द्वारा संरेखित किए गए हैं?


88

मेरे पास निम्नलिखित कोड हैं:

#include <stdio.h>

int
main(void)
{
        float a[4] __attribute__((aligned(0x1000))) = {1.0, 2.0, 3.0, 4.0};
        printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]);
}

और मेरे पास निम्न आउटपुट हैं:

0x7fffbfcd2da0 0x7fffbfcd2da4 0x7fffbfcd2da8 0x7fffbfcd2dac

का पता a[0]एक से अधिक क्यों नहीं है 0x1000?

वास्तव में क्या __attribute__((aligned(x)))करता है? मैंने इस स्पष्टीकरण को गलत समझा ?

मैं gcc का उपयोग कर रहा हूँ 4.1.2।

जवाबों:


98

मेरा मानना ​​है कि समस्या यह है कि आपका सरणी स्टैक पर है, और यह कि आपका कंपाइलर स्टैक वेरिएबल को ओवर-अलाइंड करने में पुराना है। जीसीसी 4.6 और बाद में उस बग को ठीक कर दिया

C11 / C ++ 11 alignas(64) float a[4];बस 2 संरेखण की किसी भी शक्ति के लिए काम करता है।
तो क्या जीएनयू सी __attribute__((aligned(x)))का उपयोग आप कर रहे हैं।

(C11 #include <stdalign.h>में #define alignas _Alignas: cppref के लिए )।


लेकिन आपके एक बहुत बड़े संरेखण के मामले में, एक 4k पृष्ठ सीमा के लिए, आप इसे स्टैक पर नहीं चाह सकते हैं।

क्योंकि स्टैक पॉइंटर कुछ भी हो सकता है जब फ़ंक्शन शुरू होता है, तो सरणी को संरेखित करने का कोई तरीका नहीं है जो आपको ज़रूरत से ज़्यादा आवंटित और समायोजित करने के बिना है। (कंपाइलर and rsp, -4096आवंटित किए गए 0 से 4088 बाइट्स में से किसी का भी उपयोग नहीं करेंगे या बराबर करेंगे ; उस स्थान पर ब्रांचिंग करना पर्याप्त है या नहीं, यह संभव नहीं है, लेकिन ऐसा नहीं किया जाता है क्योंकि विशाल संरेखण सरणी या अन्य स्थानीय लोगों के आकार की तुलना में बहुत बड़ा होता है। सामान्य मामला नहीं हैं।)

यदि आप फ़ंक्शन से बाहर और एक वैश्विक चर में सरणी को स्थानांतरित करते हैं, तो यह काम करना चाहिए। दूसरी चीज़ जो आप कर सकते हैं, उसे स्थानीय चर के रूप में रख सकते हैं (जो बहुत अच्छी बात है), लेकिन इसे बनाएं static। यह इसे स्टैक पर संग्रहीत होने से बचाएगा। सावधान रहें कि ये दोनों तरीके थ्रेड-सुरक्षित या पुनरावर्तन-सुरक्षित नहीं हैं, क्योंकि सरणी की केवल एक ही प्रति होगी।

इस कोड के साथ:

#include <stdio.h>

float a[4] __attribute__((aligned(0x1000))) = {1.0, 2.0, 3.0, 4.0};

int
main(void)
{
        printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]);
}

मैंने इसे प्राप्त किया:

0x804c000 0x804c004 0x804c008 0x804c00c

जो अपेक्षित है। आपके मूल कोड के साथ, मुझे आपके जैसे ही यादृच्छिक मूल्य मिलते हैं।


11
+1 सही उत्तर। एक वैकल्पिक समाधान स्थानीय सरणी को स्थिर बनाना है। स्टैक पर संरेखण हमेशा एक समस्या है और इसे टालने की आदत में आना सबसे अच्छा है।
दान ओल्सन

अरे हाँ, मैंने इसे स्थिर बनाने के बारे में नहीं सोचा था। यह एक अच्छा विचार है क्योंकि यह नाम टकराव को रोकता है। मैं अपने उत्तर को संपादित करूंगा।
जिफरे

3
ध्यान दें कि इसे स्थिर बनाने से यह गैर-रीएंन्ट्रेंट और गैर-थ्रेड-सुरक्षित भी हो जाता है।
ArchaeaSoftware

3
इसके अलावा gcc 4.6+ इसे स्टैक पर भी सही ढंग से हैंडल करें।
ग्रंथों में

1
यह उत्तर सही हुआ करता था, लेकिन अब ऐसा नहीं है। 4.6 के रूप में पुराने gcc, शायद पुराने, जानता है कि कैसे alignas(64)स्वचालित रूप से C11 / C ++ 11 को लागू करने के लिए या स्वचालित भंडारण के साथ वस्तुओं पर स्टैक पॉइंटर को संरेखित करना है । और निश्चित रूप से जीएनयू सी__attribute((aligned((64)))
पीटर कॉर्ड्स

41

जीसीसी में एक बग था जो स्टैक चर के साथ काम न करने के लिए विशेषता संरेखित करता था। यह नीचे दिए गए पैच के साथ तय किया गया प्रतीत होता है। नीचे दिए गए लिंक में समस्या के साथ-साथ चर्चा के बारे में भी काफी चर्चा है।

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=16660

मैंने आपके कोड को gcc के दो अलग-अलग संस्करणों के साथ ऊपर आज़माया है: RedHat 5.7 बॉक्स से 4.1.2, और यह आपकी समस्या के समान ही विफल हो गया (स्थानीय सरणियाँ किसी भी तरह से 0x1000 बाइट सीमा पर संरेखित नहीं हुई)। मैंने तब RedHat 6.3 पर gcc 4.4.6 के साथ आपके कोड की कोशिश की, और यह त्रुटिपूर्ण रूप से काम किया (स्थानीय सरणियों को संरेखित किया गया)। मिथक टीवी के लोगों को एक समान समस्या थी (कि ऊपर gcc पैच को ठीक करने के लिए लग रहा था):

http://code.mythtv.org/trac/ticket/6535

वैसे भी, ऐसा लगता है कि आपको जीसीसी में एक बग मिला, जो बाद के संस्करणों में तय किया गया प्रतीत होता है।


3
लिंक्ड बग gcc के अनुसार 4.6 इस समस्या के साथ पहली रिलीज़ थी जो पूरी तरह से सभी आर्किटेक्चर के लिए तय थी।
ग्रंथों में

इसके अलावा, स्टैक पर संरेखित चर बनाने के लिए gcc द्वारा उत्पन्न असेंबली कोड इतना भयानक और इतना अकल्पनीय है। तो, क्या कॉल के बजाय स्टैक पर संरेखित चर को आवंटित करना समझ में आता है memalign()?
जेरेम पुइलर

13

हाल ही में GCC (4.5.2-8ubuntu4 के साथ परीक्षण किया गया) ठीक से संरेखित सरणी के साथ अपेक्षा के अनुसार काम करता है।

#include <stdio.h>

int main(void)
{
    float a[4] = { 1.0, 2.0, 3.0, 4.0 };
    float b[4] __attribute__((aligned(0x1000))) = { 1.0, 2.0, 3.0, 4.0 };
    float c[4] __attribute__((aligned(0x10000))) = { 1.0, 2.0, 3.0, 4.0 };

    printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]);
    printf("%p %p %p %p\n", &b[0], &b[1], &b[2], &b[3]);
    printf("%p %p %p %p\n", &c[0], &c[1], &c[2], &c[3]);
}

मुझे मिला:

0x7ffffffefff0 0x7ffffffefff4 0x7ffffffefff8 0x7ffffffefffc
0x7ffffffef000 0x7ffffffef004 0x7ffffffef008 0x7ffffffef00c
0x7ffffffe0000 0x7ffffffe0004 0x7ffffffe0008 0x7ffffffe000c

यह थोड़ा आश्चर्य की बात है, यह देखते हुए कि सरणियों को स्टैक में आवंटित किया गया है - क्या इसका मतलब यह है कि स्टैक अब छेद से भरा है?
ysap

या उसका स्टैक 16-बाइट से जुड़ा हुआ है।
user7116

9

संरेखण सभी प्रकार के लिए प्रभावी नहीं है। कार्रवाई में विशेषताओं को देखने के लिए आपको एक संरचना का उपयोग करने पर विचार करना चाहिए:

#include <stdio.h>

struct my_float {
        float number;
}  __attribute__((aligned(0x1000)));

struct my_float a[4] = { {1.0}, {2.0}, {3.0}, {4.0} };

int
main(void)
{
        printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]);
}

और फिर, आप पढ़ेंगे:

0x603000 0x604000 0x605000 0x606000

जो आप उम्मीद कर रहे थे।

संपादित करें: @yzap द्वारा पुश किया गया और @ कैलेब केस टिप्पणी के बाद, प्रारंभिक समस्या केवल जीसीसी संस्करण के कारण है । मैंने GCC 3.4.6 बनाम GCC 4.4.1 पर आवश्यक स्रोत कोड के साथ जाँच की है:

$ ./test_orig-3.4.6
0x7fffe217d200 0x7fffe217d204 0x7fffe217d208 0x7fffe217d20c
$ ./test_orig-4.4.1
0x7fff81db9000 0x7fff81db9004 0x7fff81db9008 0x7fff81db900c

अब यह स्पष्ट है कि पुराने जीसीसी संस्करण (4.4.1 से पहले कहीं) संरेखण विकृति दिखाते हैं।

नोट 1: मेरा प्रस्तावित कोड उस प्रश्न का उत्तर नहीं देता है जिसे मैं "सरणी के प्रत्येक क्षेत्र को संरेखित करना" समझता हूं।

नोट 2: गैर-स्थैतिक [] मुख्य के अंदर लाना () और GCC 3.4.6 के साथ संकलन करना संरचना के सरणी के संरेखण निर्देश को तोड़ता है लेकिन संरचना के बीच 0x1000 की दूरी रखता है ... अभी भी बुरा है! (देखें वर्कअराउंड के लिए @zifre जवाब)


2
जैसा कि जीफ्रे ने उत्तर दिया, यह प्रकार नहीं है, लेकिन यह तथ्य कि आपने इसे अपने संस्करण में स्थिर बना दिया है।
ysap

@ysap, यह GCC संस्करण और वैश्विक परिभाषा दोनों था जिसने इसे काम किया। टिप्पणी के लिए धन्यवाद! मैंने इसे ठीक करने के लिए उत्तर संपादित किया। :)
लेविफ
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.