Передача указателя на массив Rust в x86-64 Asm Pointer Off by One

Когда я передаю указатели на массивы из 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-байтовые 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, это не ваш настоящий ассемблерный код.   -  person Peter Cordes    schedule 16.12.2018
comment
@PeterCordes программа, которую я опубликовал, была упрощена по сравнению с тем, что я пытался сделать. Моя реальная программа использует Asm для преобразования 136-байтового массива, на который указывает RDI, в 17-элементный массив u64 в RSI. То, что я опубликовал, действительно работает, поскольку я создал упрощенную программу и скопировал ее непосредственно. Я переключился с передачи указателя на передачу заимствованной ссылки на массив, и, по крайней мере, в этом случае это привело к исчезновению ошибки off by one. Похоже на ошибку FFI. Я попробую ваши предложения в своем коде.   -  person WDS    schedule 16.12.2018


Ответы (1)


Я собрал ваш ассемблер с 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-immediate к регистру:

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