2011-12-14 33 views
14

मैं वर्तमान में लिनक्स पर असेंबली भाषा सीख रहा हूं। मैं 'प्रोग्रामिंग फ्रॉम द ग्राउंड अप' पुस्तक का उपयोग कर रहा हूं और सभी उदाहरण 32-बिट हैं। मेरा ओएस 64-बिट है और मैं 64-बिट में सभी उदाहरण करने की कोशिश कर रहा हूं। मुझे परेशानी हो रही है:x86_64 असेंबली लिनक्स सिस्टम कॉल भ्रम

.section .data 

.section .text 
.global _start 
_start: 
movq $60, %rax 
movq $2, %rbx 
int $0x80 

यह केवल लिनक्स निकास सिस्टम कॉल या इसे चाहिए। इसके बजाय यह एक एसईजी FAULT का कारण बनता है और जब मैं इसे

.section .data 

.section .text 
.global _start 
_start: 
movq $1, %rax 
movq $2, %rbx 
int $0x80 

यह काम करता है। स्पष्ट रूप से समस्या वह मान है जो मैं% रैक्स पर जाता हूं। दूसरे उदाहरण में उपयोग किए जाने वाले मूल्य $ 1 में 'ग्राउंड अप से प्रोग्रामिंग' का उपयोग करने के लिए कहा गया है, हालांकि इंटरनेट पर कई स्रोतों ने कहा है कि 64-बिट सिस्टम कॉल नंबर $ 60 है। Reference मैं क्या गलत कर रहा हूँ? इसके अलावा मुझे अन्य मुद्दों के बारे में क्या देखना चाहिए और संदर्भ के लिए मुझे क्या उपयोग करना चाहिए? बस अगर आपको पता होना चाहिए, तो मैं ग्राउंड अप से प्रोग्रामिंग में अध्याय 5 पर हूं।

उत्तर

15

आप i386 और x86_64 के बीच एक आश्चर्यजनक अंतर में भाग रहे हैं: वे उसी सिस्टम कॉल तंत्र का उपयोग नहीं करते हैं। सही कोड है:

movq $60, %rax 
movq $2, %rdi ; not %rbx! 
syscall 

इंटरप्ट 0x80 हमेशा 32-बिट सिस्टम कॉल invokes। इसका उपयोग 32-बिट अनुप्रयोगों को 64-बिट सिस्टम पर चलाने की अनुमति देने के लिए किया जाता है।

सीखने के प्रयोजनों के लिए, आपको शायद फ्लाई पर 64-बिट पर अनुवाद करने के बजाए ट्यूटोरियल का पालन करने का प्रयास करना चाहिए - कुछ अन्य महत्वपूर्ण व्यवहारिक मतभेद हैं जिन्हें आप चलाने की संभावना रखते हैं। एक बार जब आप i386, से परिचित हो जाते हैं तो आप अलग से x86_64 चुन सकते हैं।

+0

मैं शायद ऐसा करने जा रहा हूं। आपकी प्रतिक्रिया के लिए धन्यवाद। –

+0

किसी को पहले सिस्टम कॉल तर्क के लिए '% आरडीआई' का उपयोग करना चाहिए, न कि '% आरबीएक्स'। –

+0

इसे पकड़ने के लिए धन्यवाद - निश्चित। – duskwuff

10

कृपया इस What are the calling conventions for UNIX & Linux system calls on x86-64

पढ़ सकते हैं और ध्यान दें कि 64 सिस्टम पर syscall के लिए int 0x80 का उपयोग कर एक पुराने संगतता परत है। आपको x64 सिस्टम पर syscall निर्देश का उपयोग करना चाहिए।

आप अभी भी इस पुरानी विधि का उपयोग कर सकते हैं, लेकिन आपको अपनी बाइनरी को x86 मोड में संकलित करने की आवश्यकता है, विवरण के लिए अपने कंपाइलर/असेंबलर मैनुअल को देखें।

+0

किसी को यह इंगित करने में खुशी हुई कि x86_64 लिनक्स वास्तव में 'syscall'' sysenter' का उपयोग नहीं करता है! मैंने [दोनों के बीच भ्रम की व्याख्या करने के लिए एक लंबा जवाब लिखा] (// stackoverflow.com/a/31510342/20789)। –

4

i386 और x86_64 के बीच काफी कुछ बदल गया है जिसमें कर्नेल और सिस्टम कॉल तर्क लेने के लिए उपयोग किए जाने वाले रजिस्टरों दोनों में शामिल निर्देश शामिल हैं।

.section .data 

.section .text 
.global _start 
_start: 
movq $60, %rax 
movq $2, %rdi 
syscall 

एक संबंधित सवाल का this answer से हवाला देते हुए:: यहाँ तुम्हारा करने के लिए कोड बराबर है

syscall संख्या चाप/86/शामिल/ASM/unistd_64.h तहत लिनक्स स्रोत कोड में हैं। Syscall संख्या रैक्स रजिस्टर में पारित किया जाता है। पैरामीटर आरडीआई, आरएसआई, rdx, आर 10, आर 8, आर 9 में हैं। कॉल "syscall" निर्देश के साथ बुलाया जाता है। Syscall आरसीएक्स रजिस्टर ओवरराइट करता है। वापसी रैक्स में है।

+0

मैं शेलकोडर की हैंडबुक में भी सामान कर रहा हूं 2. हालांकि, मेरा ओएस भी 64 बिट है। मैं nasm-option -f elf32 के साथ इकट्ठा हूं और मैं -melf_i386 के साथ भी लिंक करता हूं।मैं जीडीबी का उपयोग करता हूं और डीबगर भी करता हूं (हालांकि, इसे 64 के लिए संकलित किया गया है और यह बाइनरी लोड करने के बाद संकेत देता है, लेकिन एडीबी के अंदर, जीडीबी के अंदर और निष्पादन में, त्रुटि समान होती है): int 0x80 के बाद एक निर्देश के बाद segfault .And मैं केवल बाहर निकलना चाहता हूं। मैंने देखा है, (आर) कुल्हाड़ी रजिस्टर में 0x01 के बजाय 0x3c होना चाहिए (क्योंकि 64 बिट के लिए syscall फ़ाइल का उपयोग किया जाता है), लेकिन मैं कल्पना नहीं कर सकता, int 0x80 के बाद निर्देश क्यों कहा जाता है। कोई भी जानता है , क्यूं कर ? – icbytes

2

आप की जाँच /usr/include/asm/unistd_32.h बाहर निकलने 1 से मेल खाती है, लेकिन /usr/include/asm/unistd_64.h बाहर निकलने में 60 से मेल खाती है।

4

duskwuff का answer सही ढंग से बताता है कि सिस्टम कॉल के लिए तंत्र 64-बिट x86 लिनक्स बनाम 32-बिट लिनक्स के लिए अलग है।

हालांकि, इस सवाल का जवाब अधूरा और कुछ कारण के लिए भ्रामक है:

  • परिवर्तन वास्तव में पेश किया गया था से पहले 64-बिट सिस्टम, लोकप्रिय बन observation that int 0x80 was very slow on Pentium 4 से प्रेरित। लिनस टोरवाल्ड्स coded up a solution using the SYSENTER/SYSEXIT instructions (जिसे इंटेल द्वारा पेंटियम प्रो युग के आसपास पेश किया गया था, लेकिन जो छोटी थीं और कोई व्यावहारिक लाभ नहीं दिया गया था)। तो आधुनिक 32-बिट लिनक्स सिस्टम वास्तव में SYSENTER का उपयोग करते हैं, int 0x80 नहीं।
  • 64-बिट x86 लिनक्स कर्नेल वास्तव में SYSENTER और SYSEXIT का उपयोग नहीं करते हैं। वे actually use बहुत समान SYSCALL/SYSRET निर्देशों के समान हैं।

pointed out in the comments के रूप में, SYSENTERवास्तव में कई 64-बिट Linux सिस्टम -namely 64-बिट एएमडी सिस्टम पर काम नहीं करता।

यह एक स्वीकार्य रूप से भ्रमित स्थिति है। gory details are here, लेकिन क्या यह करने के लिए नीचे आता है यह है:

एक 32bit कर्नेल के लिए, SYSENTER/SYSEXIT ही संगत जोड़ी हैं

लांग में एक 64 बिट कर्नेल के लिए [एएमडी और इंटेल सीपीयू के बीच] मोड केवल ... syscall/SYSRET ही संगत जोड़ी Awa [के बीच एएमडी और इंटेल सीपीयू]

यह 64-बिट मोड में एक इंटेल CPU पर दिखाई देने वाले हैं, तो आप प्राप्त कर सकते हैं y SYSENTER का उपयोग करने के साथ क्योंकि यह SYSCALL जैसा ही है, हालांकि यह एएमडी सिस्टम के मामले में नहीं है।

नीचे पंक्ति: हमेशा 64-बिट x86 सिस्टम पर लिनक्स पर SYSCALL का उपयोग करें। यह x86-64 एबीआई वास्तव में निर्दिष्ट करता है। (और भी अधिक जानकारी के लिए इस महान wiki answer देखें।)