x86 64 - पता विहित रूप और सूचक अंकगणित



x86-64 access-violation (1)

विहित पता नियमों का अर्थ है कि 64-बिट वर्चुअल एड्रेस स्पेस में एक विशालकाय छेद है। 2 ^ 47-1 इसके ऊपर के अगले वैध पते के साथ सन्निहित नहीं है, इसलिए एक एकल mmap में 64-बिट पतों में से कोई भी अनुपयोगी श्रेणी शामिल नहीं होगी।

+----------+
| 2^64-1   |   0xffffffffffffffff
| ...      |
| 2^64-2^47|   0xffff800000000000
+----------+
|          |
| unusable |
|          |
+----------+
| 2^47-1   |   0x00007fffffffffff
| ...      |
| 0        |   0x0000000000000000
+----------+

दूसरे शब्दों में:

क्या ओएस द्वारा गारंटी दी गई है कि आपको कभी भी मेमोरी आवंटित नहीं की जाएगी जिसकी पता सीमा 47 बिट से भिन्न नहीं है?

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

मजेदार तथ्य: लिनक्स वैध पते के निचले हिस्से के शीर्ष पर ढेर को मैप करने के लिए चूकता है।

जैसे

$ gdb /bin/ls
...
(gdb) b _start
Function "_start" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (_start) pending.
(gdb) r
Starting program: /bin/ls

Breakpoint 1, 0x00007ffff7dd9cd0 in _start () from /lib64/ld-linux-x86-64.so.2
(gdb) p $rsp
$1 = (void *) 0x7fffffffd850
(gdb) exit

$ calc
2^47-1
              0x7fffffffffff

AMD64 के अनुरूप आर्किटेक्चर पर, पतों को संक्षिप्त होने से पहले विहित रूप में होना चाहिए।

इंटेल मैनुअल से, खंड 3.3.7.1 :

64-बिट मोड में, एक पते को विहित रूप में माना जाता है यदि पता बिट्स 63 के माध्यम से सबसे महत्वपूर्ण कार्यान्वित बिट के माध्यम से माइक्रोआर्किटेक्चर द्वारा सभी या सभी शून्य पर सेट किया जाता है।

अब, सबसे महत्वपूर्ण वर्तमान ऑपरेटिंग सिस्टम और आर्किटेक्चर पर लागू बिट 47 वें बिट है। यह हमें 48-बिट एड्रेस स्पेस के साथ छोड़ता है।

विशेष रूप से जब ASLR सक्षम होता है, तो उपयोगकर्ता प्रोग्राम 47 वें बिट सेट के साथ एक पता प्राप्त करने की उम्मीद कर सकते हैं।

यदि पॉइंटर टैगिंग जैसे अनुकूलन का उपयोग किया जाता है और ऊपरी बिट्स का उपयोग सूचनाओं को संग्रहीत करने के लिए किया जाता है, तो प्रोग्राम को यह सुनिश्चित करना चाहिए कि एड्रेस को डीरेफर करने से पहले 47 वें बिट से पहले जो कुछ भी था, 48 वें से 63 वें बिट्स को वापस सेट किया जाए।

लेकिन इस कोड पर विचार करें:

int main()
{
    int* intArray = new int[100];

    int* it = intArray;

    // Fill the array with any value.
    for (int i = 0; i < 100; i++)
    {
        *it = 20;
        it++;   
    }

    delete [] intArray;
    return 0;
}

अब विचार करें कि intArray है, कहते हैं:

0000 0000 0000 0000 0 111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1100

it intArray सेट करने और it एक बार बढ़ाने के बाद, और sizeof(int) == 4 विचार करने के बाद, यह बन जाएगा:

0000 0000 0000 1 000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

47 वीं बिट बोल्ड में है। यहाँ क्या होता है कि सूचक अंकगणित द्वारा प्राप्त दूसरा सूचक अमान्य है क्योंकि विहित रूप में नहीं। सही पता होना चाहिए:

1111 1111 1111 1111 1 000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

कार्यक्रम इससे कैसे निपटते हैं? क्या ओएस द्वारा गारंटी दी गई है कि आपको कभी भी मेमोरी आवंटित नहीं की जाएगी जिसकी पता सीमा 47 बिट से भिन्न नहीं है?