อะไรคืออนุสัญญาการเรียกสำหรับระบบ UNIX & Linux ที่เรียกใช้ i386 และ x86-64


147

ลิงก์ต่อไปนี้อธิบายข้อตกลงการเรียกใช้ระบบ x86-32 สำหรับทั้ง UNIX (รสชาติ BSD) และ Linux:

แต่อนุสัญญาการเรียกระบบ x86-64 บนทั้ง UNIX และ Linux คืออะไร?


ไม่มี "มาตรฐาน" สำหรับการประชุม Unix สำหรับ linux แน่นอน แต่ฉันแน่ใจว่า Solaris, OpenBSD, Linux และ Minix อาจมีรูปแบบการโทรที่แตกต่างกันอย่างน้อยแตกต่างกันเล็กน้อยและพวกมันทั้งหมดเป็นยูนิกซ์
Earlz

2
ไม่เป็นความจริงเลย - มีชุดของ UNIX ABIs สำหรับประเภทเครื่องส่วนใหญ่ซึ่งทำให้คอมไพเลอร์ C สามารถทำงานร่วมกันได้ คอมไพเลอร์ C ++ มีปัญหาที่ใหญ่กว่า
Jonathan Leffler

1
คุณทั้งคู่ถูกต้องแล้ว ฉันกำลังมองหา FreeBSD & Linux
กรงเล็บ

ฉันจะขอบคุณถ้าคำตอบมีข้อมูลเกี่ยวกับสิ่งที่ลงทะเบียนจะถูกเก็บไว้ในสายระบบ แน่นอนว่าตัวชี้สแต็กคือ (เว้นแต่จะมีการเปลี่ยนแปลงในวิธีการควบคุมในการโทร __NR_clone) แต่เป็นของคนอื่น ๆ ?
Albert van der Horst

@AlbertvanderHorst: ใช่ฉันเพิ่งอัปเดตคำตอบของ wiki พร้อมรายละเอียดสำหรับ 32 บิต 64 บิตมีความถูกต้องอยู่แล้ว: rcx และ r11 ถูกทำลายเนื่องจากวิธีการsysretทำงานพร้อมกับ rax ที่ถูกแทนที่ด้วยค่าส่งคืน การลงทะเบียนอื่น ๆ ทั้งหมดจะถูกเก็บรักษาไว้ที่ amd64
Peter Cordes

คำตอบ:


231

อ่านเพิ่มเติมสำหรับหัวข้อใด ๆ ที่นี่: คู่มือการเรียกระบบ Linux ที่ชัดเจน


ฉันตรวจสอบสิ่งเหล่านี้โดยใช้ GNU Assembler (gas) บน Linux

ส่วนต่อประสานเคอร์เนล

x86-32 aka i386 Linux System Call Convention:

ในพารามิเตอร์ x86-32 สำหรับการเรียกระบบ Linux ถูกส่งผ่านโดยใช้รีจิสเตอร์ %eaxสำหรับ syscall_number % ebx,% ecx,% edx,% esi,% edi,% ebp ใช้สำหรับส่งพารามิเตอร์ 6 ตัวไปยังการเรียกของระบบ

%eaxค่าตอบแทนอยู่ใน ทั้งหมดลงทะเบียนอื่น ๆ (รวมธง) int $0x80จะถูกเก็บไว้ใน

ฉันติดตามตัวอย่างจากLinux Assembly Tutorialแต่ฉันสงสัยเกี่ยวกับสิ่งนี้ หากใครสามารถแสดงตัวอย่างมันจะดี

หากมีข้อโต้แย้งมากกว่าหกข้อ %ebxต้องมีตำแหน่งหน่วยความจำที่จัดเก็บรายการข้อโต้แย้ง - แต่ไม่ต้องกังวลเกี่ยวกับสิ่งนี้เพราะมันไม่น่าเป็นไปได้ที่คุณจะใช้ syscall ที่มีข้อโต้แย้งมากกว่าหกข้อ

สำหรับตัวอย่างเล็ก ๆ น้อย ๆ และอ่านเพิ่มเติมโปรดดูที่http://www.int80h.org/bsdasm/#alternate-calling-convention อีกตัวอย่างหนึ่งของ Hello World สำหรับ i386 Linux ที่ใช้int 0x80: Hello, world ในภาษาแอสเซมบลีด้วยการเรียกระบบ Linux?

sysenterใช้: มีวิธีที่เร็วกว่าการโทรระบบ 32 บิตเป็น เคอร์เนลแมปหน้าหน่วยความจำในทุกกระบวนการ (the vDSO) กับด้านผู้ใช้พื้นที่ของการsysenterเต้นรำซึ่งจะต้องร่วมมือกับเคอร์เนลเพื่อให้สามารถค้นหาที่อยู่ผู้ส่ง int $0x80หาเรื่องที่จะลงทะเบียนการทำแผนที่เป็นเช่นเดียวกับ ปกติแล้วคุณควรโทรเข้าสู่ vDSO แทนที่จะใช้sysenterโดยตรง (ดูคู่มือขั้นสูงสำหรับการโทรระบบ Linuxสำหรับข้อมูลเกี่ยวกับการเชื่อมโยงและการโทรเข้าสู่ vDSO และสำหรับข้อมูลเพิ่มเติมเกี่ยวกับsysenterและทุกสิ่งอื่นที่เกี่ยวข้องกับการโทรของระบบ)

x86-32 [ฟรี | เปิด | สุทธิ | DragonFly] การประชุมการเรียกระบบ BSD UNIX:

พารามิเตอร์ถูกส่งผ่านบนสแต็ก ผลักดันพารามิเตอร์ (พารามิเตอร์สุดท้ายผลักก่อน) ไปยังสแต็ก จากนั้นดันข้อมูลจำลอง 32 บิตเพิ่มเติม (ไม่ใช่ข้อมูลจำลองจริงอ้างถึงลิงก์ต่อไปนี้สำหรับข้อมูลเพิ่มเติม) จากนั้นให้คำแนะนำการเรียกระบบint $0x80

http://www.int80h.org/bsdasm/#default-calling-convention


x86-64 การประชุมการเรียกระบบ Linux:

x86-64 Mac OS X คล้ายกัน แต่ที่แตกต่างกัน สิ่งที่ต้องทำ: ตรวจสอบสิ่งที่ * BSD ทำ

ดูส่วน "A.2 AMD64 ลินุกซ์อนุสัญญา kernel" ของระบบวี Application Binary Interface AMD64 สถาปัตยกรรมเสริมประมวลผล รุ่นล่าสุดของ i386 และ x86-64 System V psABIs สามารถพบได้เชื่อมโยงจากหน้านี้ใน repo (ดูเพิ่มเติมที่ ติดแท็ก wiki สำหรับลิงก์ ABI ที่ทันสมัยและสิ่งดีๆอื่น ๆ อีกมากมายเกี่ยวกับ x86 asm)

นี่คือตัวอย่างจากส่วนนี้:

  1. แอปพลิเคชันระดับผู้ใช้ใช้เป็นทะเบียนจำนวนเต็มสำหรับการส่งลำดับ% rdi,% rsi,% rdx,% rcx,% r8 และ% r9 เคอร์เนลอินเตอร์เฟสใช้% rdi,% rsi,% rdx,% r10,% r8 และ% r9
  2. ระบบโทรจะทำผ่านการเรียนการสอนsyscall การอุดตันนี้% rcx และ% r11รวมถึงค่าที่คืนกลับมา% rax แต่การลงทะเบียนอื่น ๆ จะถูกเก็บไว้
  3. จำนวน syscall ต้องถูกส่งผ่านใน register% rax
  4. การเรียกระบบถูก จำกัด ไว้ที่หกอาร์กิวเมนต์เท่านั้นไม่มีการส่งผ่านอาร์กิวเมนต์บนสแต็กโดยตรง
  5. เมื่อกลับมาจาก syscall register% rax จะมีผลลัพธ์ของการเรียกระบบ ค่าอยู่ในช่วงระหว่าง -4095 และ -1 -errnoบ่งชี้ข้อผิดพลาดก็คือ
  6. เฉพาะค่าของคลาส INTEGER หรือคลาส MEMORY เท่านั้นที่ถูกส่งไปยังเคอร์เนล

โปรดจำไว้ว่านี่คือจากภาคผนวกลินุกซ์ที่เฉพาะเจาะจงไปยัง ABI และสำหรับลินุกซ์มันเป็นข้อมูลที่ไม่เชิงบรรทัดฐาน (แต่จริงๆแล้วมันถูกต้อง)

int $0x80ABI 32 บิตนี้สามารถใช้งานได้ในรหัส 64 บิต (แต่ไม่แนะนำอย่างยิ่ง) จะเกิดอะไรขึ้นถ้าคุณใช้ 32-bit int 0x80 Linux ABI ในรหัส 64- บิต มันยังตัดทอนอินพุตเป็น 32- บิตดังนั้นจึงไม่เหมาะสำหรับพอยน์เตอร์และค่าศูนย์ r8-r11

ส่วนต่อประสานผู้ใช้: การเรียกใช้ฟังก์ชัน

x86-32 การประชุมการเรียกใช้ฟังก์ชัน:

ในพารามิเตอร์ x86-32 ถูกส่งผ่านบนสแต็ก พารามิเตอร์สุดท้ายถูกผลักไปที่สแต็กเป็นอันดับแรกจนกระทั่งพารามิเตอร์ทั้งหมดเสร็จสิ้นจากนั้นcallคำสั่งถูกเรียกใช้ ใช้สำหรับเรียกฟังก์ชัน C library (libc) บน Linux จากชุดประกอบ

เวอร์ชันล่าสุดของ i386 System V ABI (ใช้บน Linux) จำเป็นต้องมีการจัดตำแหน่งแบบ 16 ไบต์%espก่อน a callเช่น x86-64 System V ABI จำเป็นต้องมีเสมอ Callees ได้รับอนุญาตให้สันนิษฐานได้และใช้ SSE 16-byte load / stores แต่ในอดีตลินุกซ์ต้องการการจัดตำแหน่งสแต็ก 4 ไบต์เท่านั้นดังนั้นจึงต้องทำงานพิเศษเพื่อสงวนพื้นที่ที่จัดชิดตามธรรมชาติแม้กระทั่งสำหรับ 8 ไบต์doubleหรือบางอย่าง

ระบบ 32 บิตสมัยใหม่อื่น ๆ บางระบบยังไม่ต้องการการจัดแนวสแต็กมากกว่า 4 ไบต์


x86-64 System V พื้นที่ผู้ใช้แบบแผน

x86-64 System V ผ่าน args ในการลงทะเบียนซึ่งมีประสิทธิภาพมากกว่าการประชุม stack args ของ i386 System V มันหลีกเลี่ยงเวลาแฝงและคำแนะนำพิเศษในการจัดเก็บ args ไปยังหน่วยความจำ (แคช) แล้วโหลดมันกลับมาอีกครั้งใน callee สิ่งนี้ใช้ได้ดีเพราะมีการลงทะเบียนมากขึ้นและดีกว่าสำหรับซีพียูประสิทธิภาพสูงรุ่นใหม่ที่มีความหน่วงแฝงและการดำเนินการที่ไม่เป็นไปตามคำสั่ง (i386 ABI นั้นเก่ามาก)

ในกลไกใหม่นี้: อันดับแรกพารามิเตอร์จะแบ่งออกเป็นคลาส คลาสของแต่ละพารามิเตอร์กำหนดลักษณะที่มันจะถูกส่งผ่านไปยังฟังก์ชั่นที่เรียกว่า

สำหรับข้อมูลที่สมบูรณ์ดูที่: "3.2 ลำดับการเรียกใช้ฟังก์ชัน" ของSystem V Application Binary Interface AMD64 Architecture Processor Supplementซึ่งอ่านส่วนหนึ่ง:

เมื่อมีการจำแนกข้อโต้แย้งการลงทะเบียนจะได้รับมอบหมาย (ตามลำดับจากซ้ายไปขวา) สำหรับการส่งผ่านดังนี้:

  1. ถ้าคลาสคือ MEMORY ให้ส่งอาร์กิวเมนต์ไปยังสแต็ก
  2. ถ้าคลาสนั้นเป็น INTEGER จะใช้การลงทะเบียนลำดับถัดไปของลำดับ% rdi,% rsi,% rdx,% rcx,% r8 และ% r9

ดังนั้น%rdi, %rsi, %rdx, %rcx, %r8 and %r9การลงทะเบียนเพื่อใช้ในการส่งผ่านพารามิเตอร์จำนวนเต็ม / ตัวชี้ (เช่นชั้น INTEGER) ไปยังฟังก์ชั่น libc ใด ๆ จากการชุมนุม % rdi ใช้สำหรับพารามิเตอร์ INTEGER ตัวแรก % rsi สำหรับที่ 2,% rdx สำหรับที่ 3 เป็นต้นไป จากนั้นcallควรได้รับคำแนะนำ สแต็ก ( %rsp) ต้องอยู่ในแนว 16B เมื่อcallดำเนินการ

หากมีพารามิเตอร์ INTEGER มากกว่า 6 ตัวพารามิเตอร์ INTEGER ที่ 7 และต่อมาจะถูกส่งผ่านไปยังสแต็ก (ผู้โทรปรากฏเช่นเดียวกับ x86-32)

8 จำนวนจุดลอยตัวแรกจะถูกส่งผ่านใน% xmm0-7 ต่อมาบนสแต็ก ไม่มีการบันทึกการลงทะเบียนของเวกเตอร์ (ฟังก์ชั่นที่มีการผสมผสานของ FP และอาร์กิวเมนต์จำนวนเต็มสามารถมีอาร์กิวเมนต์การลงทะเบียนทั้งหมดมากกว่า 8 ข้อ)

ฟังก์ชั่น Variadic ( เช่นprintf ) ต้องการ%al= จำนวนของการลงทะเบียน FP เสมอ

มีกฎสำหรับการที่จะแพ็ค structs เข้าสู่รีจิสเตอร์ ( rdx:raxเมื่อคืน) เทียบกับในหน่วยความจำ ดู ABI สำหรับรายละเอียดและตรวจสอบผลลัพธ์คอมไพเลอร์เพื่อให้แน่ใจว่าโค้ดของคุณเห็นด้วยกับคอมไพเลอร์เกี่ยวกับวิธีส่งผ่าน / ส่งคืนบางสิ่งบางอย่าง


โปรดทราบว่าการเรียกใช้ฟังก์ชัน Windows x64มีความแตกต่างที่สำคัญหลายอย่างจาก x86-64 System V เช่นพื้นที่เงาที่ต้องสำรองไว้โดยผู้โทร (แทนที่จะเป็นโซนสีแดง) และ xmm6-xmm15 ที่สงวนการโทรไว้ และกฎที่แตกต่างกันมากสำหรับ arg ซึ่งไปในการลงทะเบียน


1
ใน linux 32 "รีจิสเตอร์ทั้งหมดยกเว้น ax bx cd dx si di bp จะถูกเก็บไว้" ฉันไม่สามารถคิดใด ๆ ...
อัลเบิร์แวนเดอร์ฮอร์ส

ใน amd64 หากมีพารามิเตอร์มากกว่า 6 พารามิเตอร์และส่งผ่านไปยังสแต็กใครจะเป็นผู้รับผิดชอบในการล้างสแต็คหลังจากการโทรผู้โทรหรือผู้ถูกเรียก
Nicolás

1
@ Nicolás: ผู้เรียกชำระล้างสแต็ก ฉันอัพเดตคำตอบพร้อมรายละเอียดเพิ่มเติมเกี่ยวกับการประชุมการเรียกใช้ฟังก์ชัน
Peter Cordes

1
ถ้าคุณใช้ลินุกซ์int 0x80ABI ในรหัส 64 บิตตรงนี้เป็นสิ่งที่เกิดขึ้น: stackoverflow.com/questions/46087730/... มันเป็นศูนย์ r8-r11 และทำงานเหมือนกันเมื่อทำงานในกระบวนการ 32 บิต ในคำถาม & คำตอบนั้นฉันมีตัวอย่างที่แสดงว่าทำงานได้หรือไม่สามารถตัดทอนตัวชี้ได้ และฉันก็ขุดเข้าไปในเคอร์เนลเพื่อแสดงว่าทำไมมันถึงทำงานแบบนั้น
Peter Cordes

1
@EvanCarroll: ตัวอย่าง (ข้อความที่ยกมา) เป็นลิงค์ที่ให้ลินุกซ์การสอนประกอบในส่วนที่4.3 การโทรของระบบลินุกซ์
Michael Petch

14

บางทีคุณกำลังมองหา x86_64 ABI

หากนั่นไม่ใช่สิ่งที่คุณต้องการอย่างแน่นอนให้ใช้ 'x86_64 abi' ในเครื่องมือค้นหาที่คุณต้องการเพื่อค้นหาข้อมูลอ้างอิงอื่น ๆ


5
จริง ๆ แล้วฉันต้องการเพียงแค่การประชุม System Call esp สำหรับ UNIX (FreeBSD)
กรงเล็บ

3
@claws: การเรียกการประชุมระบบเป็นส่วนหนึ่งของ ABI
Jonathan Leffler

1
ใช่. ฉันได้ไปที่การพัฒนาเคอร์เนลของแต่ละระบบปฏิบัติการและถามพวกเขาเกี่ยวกับเรื่องนี้ พวกเขาบอกให้ฉันดูแหล่งที่มาและคิดออก ฉันไม่เข้าใจถ้าไม่มีการบันทึกสิ่งที่จะเริ่มพัฒนาได้อย่างไร ดังนั้นฉันได้เพิ่มคำตอบจากข้อมูลที่ฉันเก็บรวบรวมโดยหวังให้ผู้อื่นกรอกรายละเอียดที่เหลือ
กรงเล็บ

@JanathanLeffler ดูเหมือนว่าลิงก์จะไม่ทำงานในขณะนี้ หากคุณพบปัญหาในการเข้าชมลิงค์คุณสามารถอัพเดทได้ไหม?
Ajay Brahmakshatriya

@AjayBrahmakshatriya: ขอบคุณสำหรับหัวขึ้น; ฉันได้เพิ่มลิงก์ไปยังบันทึกของ Wayback Machine เว็บไซต์x86-64.orgทั้งหมดไม่ตอบสนองกับข้อมูลใด ๆ
Jonathan Leffler

11

ระเบียบการโทรจะกำหนดวิธีการส่งผ่านพารามิเตอร์ในรีจิสเตอร์เมื่อเรียกหรือถูกเรียกโดยโปรแกรมอื่น และแหล่งที่มาที่ดีที่สุดของการประชุมเหล่านี้อยู่ในรูปแบบของมาตรฐาน ABI ที่กำหนดไว้สำหรับฮาร์ดแวร์เหล่านี้ เพื่อความสะดวกในการรวบรวม ABI เดียวกันนี้ยังถูกใช้โดย userspace และเคอร์เนลโปรแกรม Linux / Freebsd ทำตาม ABI เดียวกันสำหรับ x86-64 และอีก 32 บิต แต่ x86-64 ABI สำหรับ Windows แตกต่างจาก Linux / FreeBSD และโดยทั่วไป ABI จะไม่แยกความแตกต่างระหว่างการโทรของระบบและ "การเรียกใช้ฟังก์ชัน" ปกติ คือนี่คือตัวอย่างหนึ่งของการเรียกประชุม x86_64 และมันก็เหมือนกันสำหรับทั้ง userspace Linux และ kernel: http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64 / (สังเกตลำดับ a, b, c, d, e, f ของพารามิเตอร์):

การเรนเดอร์ที่ดีของการเรียกระเบียบ vs

ประสิทธิภาพเป็นหนึ่งในเหตุผลสำหรับ ABI เหล่านี้ (เช่นการส่งพารามิเตอร์ผ่านการลงทะเบียนแทนที่จะบันทึกลงในสแต็คหน่วยความจำ)

สำหรับ ARM มี ABI หลากหลาย:

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.subset.swdev.abi/index.html

https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/iPhoneOSABIReference.pdf

การประชุม ARM64:

http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf

สำหรับ Linux บน PowerPC:

http://refspecs.freestandards.org/elf/elfspec_ppc.pdf

http://www.0x04.net/doc/elf/psABI-ppc64.pdf

และสำหรับการฝังตัวมี PPC EABI คือ:

http://www.freescale.com/files/32bit/doc/app_note/PPCEABI.pdf

เอกสารนี้เป็นภาพรวมที่ดีของการประชุมที่แตกต่างกันทั้งหมด:

http://www.agner.org/optimize/calling_conventions.pdf


โดยสิ้นเชิงนอกเหนือจากจุด ผู้โพสต์ของคำถามจะไม่ขอให้เรียกประชุมแบบ 64 บิต syscall ใน linux หากเป็นแบบเดียวกันกับการแปลงแบบ ABI ทั่วไป
Albert van der Horst

6

ข้อคิดเห็นเกี่ยวกับ Linux kernel 5.0

ผมรู้ว่าเฉพาะ x86 อยู่ภายใต้การarch/x86และสิ่งที่ syscall arch/x86/entryไปตาม ดังนั้นอย่างรวดเร็วgit grep rdiในไดเรกทอรีนั้นทำให้ฉันไปarch / x86 / entry / entry_64.S :

/*
 * 64-bit SYSCALL instruction entry. Up to 6 arguments in registers.
 *
 * This is the only entry point used for 64-bit system calls.  The
 * hardware interface is reasonably well designed and the register to
 * argument mapping Linux uses fits well with the registers that are
 * available when SYSCALL is used.
 *
 * SYSCALL instructions can be found inlined in libc implementations as
 * well as some other programs and libraries.  There are also a handful
 * of SYSCALL instructions in the vDSO used, for example, as a
 * clock_gettimeofday fallback.
 *
 * 64-bit SYSCALL saves rip to rcx, clears rflags.RF, then saves rflags to r11,
 * then loads new ss, cs, and rip from previously programmed MSRs.
 * rflags gets masked by a value from another MSR (so CLD and CLAC
 * are not needed). SYSCALL does not save anything on the stack
 * and does not change rsp.
 *
 * Registers on entry:
 * rax  system call number
 * rcx  return address
 * r11  saved rflags (note: r11 is callee-clobbered register in C ABI)
 * rdi  arg0
 * rsi  arg1
 * rdx  arg2
 * r10  arg3 (needs to be moved to rcx to conform to C ABI)
 * r8   arg4
 * r9   arg5
 * (note: r12-r15, rbp, rbx are callee-preserved in C ABI)
 *
 * Only called from user space.
 *
 * When user can change pt_regs->foo always force IRET. That is because
 * it deals with uncanonical addresses better. SYSRET has trouble
 * with them due to bugs in both AMD and Intel CPUs.
 */

และสำหรับ 32- บิตที่arch / x86 / entry / entry_32.S :

/*
 * 32-bit SYSENTER entry.
 *
 * 32-bit system calls through the vDSO's __kernel_vsyscall enter here
 * if X86_FEATURE_SEP is available.  This is the preferred system call
 * entry on 32-bit systems.
 *
 * The SYSENTER instruction, in principle, should *only* occur in the
 * vDSO.  In practice, a small number of Android devices were shipped
 * with a copy of Bionic that inlined a SYSENTER instruction.  This
 * never happened in any of Google's Bionic versions -- it only happened
 * in a narrow range of Intel-provided versions.
 *
 * SYSENTER loads SS, ESP, CS, and EIP from previously programmed MSRs.
 * IF and VM in RFLAGS are cleared (IOW: interrupts are off).
 * SYSENTER does not save anything on the stack,
 * and does not save old EIP (!!!), ESP, or EFLAGS.
 *
 * To avoid losing track of EFLAGS.VM (and thus potentially corrupting
 * user and/or vm86 state), we explicitly disable the SYSENTER
 * instruction in vm86 mode by reprogramming the MSRs.
 *
 * Arguments:
 * eax  system call number
 * ebx  arg1
 * ecx  arg2
 * edx  arg3
 * esi  arg4
 * edi  arg5
 * ebp  user stack
 * 0(%ebp) arg6
 */

glibc 2.29 Linux x86_64 การติดตั้งใช้งานระบบ

ตอนนี้เรามาโกงโดยดูที่การใช้งาน libc ที่สำคัญและดูว่าพวกเขากำลังทำอะไร

จะมีอะไรดีไปกว่าการดู glibc ที่ฉันใช้อยู่ตอนนี้เมื่อฉันเขียนคำตอบนี้? :-)

glibc 2.29 กำหนด x86_64 syscalls ที่sysdeps/unix/sysv/linux/x86_64/sysdep.hและมีรหัสที่น่าสนใจเช่น:

/* The Linux/x86-64 kernel expects the system call parameters in
   registers according to the following table:

    syscall number  rax
    arg 1       rdi
    arg 2       rsi
    arg 3       rdx
    arg 4       r10
    arg 5       r8
    arg 6       r9

    The Linux kernel uses and destroys internally these registers:
    return address from
    syscall     rcx
    eflags from syscall r11

    Normal function call, including calls to the system call stub
    functions in the libc, get the first six parameters passed in
    registers and the seventh parameter and later on the stack.  The
    register use is as follows:

     system call number in the DO_CALL macro
     arg 1      rdi
     arg 2      rsi
     arg 3      rdx
     arg 4      rcx
     arg 5      r8
     arg 6      r9

    We have to take care that the stack is aligned to 16 bytes.  When
    called the stack is not aligned since the return address has just
    been pushed.


    Syscalls of more than 6 arguments are not supported.  */

และ:

/* Registers clobbered by syscall.  */
# define REGISTERS_CLOBBERED_BY_SYSCALL "cc", "r11", "cx"

#undef internal_syscall6
#define internal_syscall6(number, err, arg1, arg2, arg3, arg4, arg5, arg6) \
({                                  \
    unsigned long int resultvar;                    \
    TYPEFY (arg6, __arg6) = ARGIFY (arg6);              \
    TYPEFY (arg5, __arg5) = ARGIFY (arg5);              \
    TYPEFY (arg4, __arg4) = ARGIFY (arg4);              \
    TYPEFY (arg3, __arg3) = ARGIFY (arg3);              \
    TYPEFY (arg2, __arg2) = ARGIFY (arg2);              \
    TYPEFY (arg1, __arg1) = ARGIFY (arg1);              \
    register TYPEFY (arg6, _a6) asm ("r9") = __arg6;            \
    register TYPEFY (arg5, _a5) asm ("r8") = __arg5;            \
    register TYPEFY (arg4, _a4) asm ("r10") = __arg4;           \
    register TYPEFY (arg3, _a3) asm ("rdx") = __arg3;           \
    register TYPEFY (arg2, _a2) asm ("rsi") = __arg2;           \
    register TYPEFY (arg1, _a1) asm ("rdi") = __arg1;           \
    asm volatile (                          \
    "syscall\n\t"                           \
    : "=a" (resultvar)                          \
    : "0" (number), "r" (_a1), "r" (_a2), "r" (_a3), "r" (_a4),     \
      "r" (_a5), "r" (_a6)                      \
    : "memory", REGISTERS_CLOBBERED_BY_SYSCALL);            \
    (long int) resultvar;                       \
})

ซึ่งฉันรู้สึกว่าตนเองอธิบายได้สวย โปรดทราบว่าสิ่งนี้ดูเหมือนว่าได้รับการออกแบบให้ตรงกับรูปแบบการเรียกของฟังก์ชั่น System V AMD64 ABI ปกติ: https://en.wikipedia.org/wiki/X86_calling_conventions#List_of_x86_calling_conventions

เตือนความจำอย่างรวดเร็วของ clobbers:

  • ccหมายถึงการลงทะเบียนธง แต่ความเห็นของ Peter Cordesว่าสิ่งนี้ไม่จำเป็นที่นี่
  • memory หมายความว่าตัวชี้อาจถูกส่งผ่านในแอสเซมบลีและใช้ในการเข้าถึงหน่วยความจำ

สำหรับตัวอย่างที่รันได้น้อยที่สุดอย่างชัดเจนตั้งแต่เริ่มต้นให้ดูคำตอบนี้: วิธีการเรียกการเรียกใช้ระบบผ่าน sysenter ในชุดประกอบแบบอินไลน์ได้อย่างไร

สร้าง syscalls บางส่วนด้วยตนเอง

ไม่ใช่ทางวิทยาศาสตร์ แต่สนุก:

  • x86_64.S

    .text
    .global _start
    _start:
    asm_main_after_prologue:
        /* write */
        mov $1, %rax    /* syscall number */
        mov $1, %rdi    /* stdout */
        mov $msg, %rsi  /* buffer */
        mov $len, %rdx  /* len */
        syscall
    
        /* exit */
        mov $60, %rax   /* syscall number */
        mov $0, %rdi    /* exit status */
        syscall
    msg:
        .ascii "hello\n"
    len = . - msg
    

    GitHub ต้นน้ำ

aarch64

ฉันได้แสดงตัวอย่าง userland ที่รันได้น้อยที่สุดที่: /reverseengineering/16917/arm64-syscalls-table/18834#18834สิ่งที่ต้องทำ grep kernel ที่นี่ควรจะง่าย


1
"cc"ข่มขี่ไม่จำเป็น: syscalls ลินุกซ์บันทึก / เรียกคืน RFLAGS (ในsyscall/ sysretคำแนะนำทำว่าการใช้ R11 และเคอร์เนลไม่ได้ปรับเปลี่ยนที่บันทึกไว้ R11 / RFLAGS อื่น ๆ กว่าผ่านptraceดีบักสายระบบ.) ไม่ว่าจะเคยมีความสำคัญเพราะ"cc"ข่มขี่คือ โดยปริยายสำหรับ x86 / x86-64 ใน GNU C Extended asm ดังนั้นคุณจะไม่สามารถได้อะไรจากการออกไป
Peter Cordes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.