การส่งผ่านตัวชี้ไปยังอาร์เรย์ของสนิมไปยัง x86-64 Asm Pointer Off ทีละตัว

เมื่อฉันส่งพอยน์เตอร์ไปยังอาร์เรย์จาก Rust ไปยัง x86-64 Asm รีจิสเตอร์ที่เกี่ยวข้อง (rdi, rsi) ดูเหมือนจะปิดไปทีละอัน โดยชี้ไปที่องค์ประกอบ 1 ของอาร์เรย์แทนที่จะเป็นองค์ประกอบ 0 ฉันสามารถลดค่ารีจิสเตอร์เพื่อเข้าถึงค่าที่ต้องการได้ สถานที่ แต่ฉันกังวลเกี่ยวกับพฤติกรรมที่ไม่คาดคิด มีคำอธิบายที่เป็นไปได้สำหรับสิ่งนี้ที่ฉันมองข้ามไปหรือไม่?

ส่วนที่เกี่ยวข้องมากที่สุดของโปรแกรมง่ายๆ เพื่ออธิบายสิ่งนี้มีดังนี้

main.rs

extern crate utilities;

fn main() {
    let input: [u8;8] = [0;8];
    let output: [u64; 1] = [0;1];

    let input_ptr = input.as_ptr();
    let output_ptr = output.as_ptr();

    utilities::u8tou64(input_ptr,output_ptr);

    for i in 0..8 {print!("{:02X}", input[i]);} // byte 1 will be 0xEE
    println!();
    println!("{:016X}", output[0].swap_bytes());  /* byte 1 position of the u64
    will be 0xFF */

    println!("{:02X}",  unsafe{*input_ptr.offset(1)}); /* modifying byte at address
    passed into rdi in Asm function modifies input_ptr.offset(1) when expected
    behavior was modification of input_ptr with no offset, e.g. input[0] */
}

u8_to_u64.S

.globl u8_to_u64
.intel_syntax noprefix
u8_to_u64:
    mov rax, 0xff
    mov byte [rsi], rax
    mov rax, 0xee
    mov byte [rdi], rax
    xor rax, rax
retq

person WDS    schedule 16.12.2018    source แหล่งที่มา
comment
เหตุใดคุณจึงใช้ 7-byte mov r64, sign_extended_imm32 สำหรับค่าคงที่ 1 ไบต์ mov byte ptr [rsi], 0xff / mov byte ptr [rdi], 0xee / xor eax,eax สั้นกว่าและมีประสิทธิภาพมากกว่ามาก เป็นโบนัสพิเศษ มันจะประกอบกันจริง ๆ ซึ่งแตกต่างจาก mov byte [rdi], rax ซึ่งมีขนาดตัวถูกดำเนินการไบต์และ qword ไม่ตรงกัน (al คือไบต์ต่ำของ RAX) นอกจากนี้ .intel_syntax ของ GAS ก็เหมือนกับ MASM ดังนั้นคุณจึงต้องใช้ byte ptr ไม่ใช่ byte แบบ NASM เว้นแต่ว่า Rust จะใช้แอสเซมเบลอร์อื่นที่ดูเหมือน GAS เท่านั้น นี่ไม่ใช่รหัส asm ที่แท้จริงของคุณ   -  person Peter Cordes    schedule 16.12.2018
comment
@PeterCordes โปรแกรมที่ฉันโพสต์นั้นทำให้ง่ายขึ้นจากสิ่งที่ฉันพยายามทำ โปรแกรมจริงของฉันใช้ Asm เพื่อแปลงอาร์เรย์ 136 ไบต์ที่ RDI ชี้ไปเป็นอาร์เรย์ 17 องค์ประกอบ u64 ใน RSI สิ่งที่ฉันโพสต์จะทำงานในขณะที่ฉันสร้างโปรแกรมแบบง่ายและคัดลอกจากโปรแกรมนั้นโดยตรง ฉันเปลี่ยนจากการส่งผ่านพอยน์เตอร์ แต่ส่งผ่านการอ้างอิงแบบยืมไปยังอาเรย์แทน และอย่างน้อยในกรณีนี้ นั่นทำให้การปิดด้วยข้อผิดพลาดหนึ่งข้อหายไป ดูเหมือนเป็นข้อบกพร่องของ FFI ฉันจะลองข้อเสนอแนะของคุณในรหัสของฉัน   -  person WDS    schedule 16.12.2018


คำตอบ (1)


ฉันรวบรวม asm ของคุณด้วย gcc -c foo.S เนื่องจากฉันคิดว่าจะได้รับข้อผิดพลาดเวลาประกอบจาก byte แทนที่จะเป็น byte ptr และความไม่ตรงกันกับการลงทะเบียน qword

ในรูปแบบ GAS นั้น byte ประเมินค่าคงที่จำนวนเต็ม 1 ดังนั้น mov byte [rsi], rax จึงเท่ากับ mov 1[rsi], rax สิ่งนี้ถูกต้องตามไวยากรณ์ GAS และเทียบเท่ากับ [1+rsi]

เมื่อคุณแยกชิ้นส่วน foo.o ด้วย objdump -dwrC -Mintel คุณจะเห็น

0000000000000000 <u8_to_u64>:
   0:   48 c7 c0 ff 00 00 00    mov    rax,0xff
   7:   48 89 46 01             mov    QWORD PTR [rsi+0x1],rax
   b:   48 c7 c0 ee 00 00 00    mov    rax,0xee
  12:   48 89 47 01             mov    QWORD PTR [rdi+0x1],rax
  16:   48 31 c0                xor    rax,rax
  19:   c3                      ret    

สังเกตโหมดการกำหนดที่อยู่ [rsi+1] และ [rdi+1]

ไวยากรณ์ GAS สำหรับสิ่งที่คุณพยายามทำคือ:

mov   byte ptr [rsi], 0xff
mov   byte ptr [rdi], 0xee
xor   eax,eax
ret

หรือมีคำแนะนำพิเศษโง่ ๆ ให้ทำการ mov ทันทีเพื่อลงทะเบียนก่อน:

mov   eax, 0xff
mov   [rsi], al
mov   eax, 0xee     # mov al, 0xee  is shorter but false dependency on the old RAX
mov   [rdi], al
xor   eax,eax
ret
person Peter Cordes    schedule 16.12.2018
comment
นี่เป็นการแก้ไขปัญหา ฉันได้ลองย้ายไปที่ [rdi] และ [rsi] ทันที และได้รับการร้องเรียนจากคอมไพเลอร์เกี่ยวกับเรื่องนี้ การใช้ mov byte ptr แทน mov byte ทำให้สามารถมอบหมายโดยตรงได้ทันทีและกำจัดการปิดด้วยข้อผิดพลาดเดียวที่ฉันแนะนำเข้าสู่โปรแกรมโดยไม่ได้ตั้งใจ - person WDS; 16.12.2018
comment
@WDS: หากไม่มีตัวระบุขนาดตัวถูกดำเนินการ mov [mem], imm มีความคลุมเครือและให้ข้อผิดพลาดใช่ นั่นเป็นคำใบ้แรกของคุณว่าไวยากรณ์ของคุณไม่ได้ทำสิ่งที่คุณต้องการ ความจริงที่ว่า mov byte [rsi], rax (ด้วยการลงทะเบียน qword) ไม่ได้ ให้ข้อผิดพลาดแม้ว่าคุณจะพยายามระบุขนาดตัวถูกดำเนินการไบต์ก็เป็นเบาะแสอื่น ๆ ของคุณว่ารหัสของคุณไม่ได้หมายถึง (สำหรับ GAS) สิ่งที่คุณ ตั้งใจ - person Peter Cordes; 16.12.2018