2016-09-06 3 views
-2

попытке открыть файл в сборке, чтобы переместить значение в ячейках памяти (адрес) до RDI:как Mov 64-битный адрес, чтобы зарегистрировать

mov r13, [rsi+8*1] ; rsi - where addresses of cmdline parameters are. 
mov [file_1], r13 
; some calls... 
mov rax, 2 
mov rdi, [file_1] ; 
xor esi, esi ; automatically zero-extended to rsi 
syscall 

решаемой задачи: Я определил длину файла но не ставил искать позицию начала файла как

mov rax, 8 
mov rdi, [rsp+.fd_1] 
xor esi, esi 
mov edx, edx 
syscall 

Debugging:

(gdb) p/x &file_1 
$1 = 0x601058 
(gdb) x/xg 0x601058 
0x601058: 0x00007fffffffe16a 
(gdb) x/s 0x7fffffffe16a 
0x7fffffffe16a: "abc" 

, но он не двигается правильно. Также я прочитал, что mov имеет бит исходного операнда, но мне нужно переместить 64 бита из памяти для регистрации. Пробовал использовать movq в YASM, но он дал синтаксическую ошибку о недопустимой комбинации опкода и операндов.

+0

Как это связано с QuickTime? 'ESI' - 32-битная, а не 64-разрядная. Вы должны исправить код сборки, чтобы вместо этого использовать 64-разрядный регистр. Почему вы XOR'ing ESI с собой? На основании кода, который вы предоставили, ничего не содержится в ESI –

+0

изменено, было ошибкой. –

+0

Ваш код сборки все еще неверен. –

ответ

2

Syscall 2 открывает файл.

Параметры:

rax: syscall #2 
rdi: pointer: zero terminated filename 
rsi: int: flags 
rdx: int: mode 

Вы можете использовать следующий код:

... 
mov rax, 2   //syscall_2 = open file 
... 
syscall 

До сих пор так хорошо, однако, в соответствии с документацией:

Учитывая путь для файл, open() возвращает дескриптор файла, небольшое неотрицательное целое число для использования в последующих системных вызовах.

Итак, вы только сделали первую часть, вы, надеюсь, получили дескриптор файла, с этим вы можете считывать данные из файла, вам нужно добавить эту часть кода.
Наконец, когда вы закончите, вам нужно очистить после себя и закрыть файл.

Позвольте мне заполнить для вас код.

//********* get a file_descriptor. 
push r15    //save non-volatile registers 
push r14 
mov eax,2   //syscall_2 = open file 
mov rdi,[file_1]  //filename 
xor esi,esi   //flags = O_RDONLY 
xor edx,edx   //mode = 0, good habits: don't leave parameters undefined. 
syscall    //get me a file descriptor (fd). 
test eax,eax   //is the fd positive? 
mov edi,eax   //param1=fd 
mov eax,1   //set failure code, just in case. 
js failure   //no,it's negative,report failure. 

//*********** read some data 
//Step 2, we have a fd, lets read the data in the file. 

mov r15,rax   //save the fd in a non-volatile register for later. 
xor eax,eax   //syscall_0 = read data from file 
mov rsi,[buffer]  //an array to place the data into 
        //`buffer` is the pointer to the buffer. 
mov edx,[len]  //the max_len of the buffer, must be >= 2. 
dec edx    //keep an extra byte to put a zero terminator into. 
//put 2 zero's if you're reading unicode_16 data. 
syscall    //Read that data. 
xor r14,r14   //assume a length of zero in case of failure. 
        //we can't jump to failure, we still need to clean up! 
test eax,eax   //do we have a fail? 
cmovs rax,r14  //if failure, then set zero length result, else do nothing. 
mov [len],eax  //set len to the length of data read. 
mov byte ptr [buffer+eax],0 //add a zero byte to terminate the data. 

//************ clean up 
//we are done, got the data. Let's close the file. 

mov r14,rax   //if rax=0 then we had an error, store for later use 
mov rdi,r15   //param1 = fd 
mov eax,3   //syscall_3: close file 
syscall    //close that file. 
test eax,eax   //did close go ok? 
mov eax,3   //set failure code just in case. 
js failure   //negative = failure 
//************ report back 
//if we get here, all went ok, report success and return. 
xor eax,eax   //return 0 for success. 
//we still need to check if `read()` failed. 
sub r14,1   //carry = 1 only if r14 = 0 = failure else carry = 0 
//sub eax,(0+carry) //if success then no-op else eax=eax-1 
sbc eax,eax   //eax = -1 if we had a failure when reading. 
failure: 
pop r14    //restore non-volatile registers 
pop r15 
ret     //all done 

Около 64 бит против 32 бита

Также я прочитал, что мы имеют 32 бит операнда-источник [...]

Это полное и абсолютное фигню.

X64 - это полноценный 64-битный процессор, который делает то, что говорит на жестяной основе.
Если используется 64-битный регистр (регистр любой, который начинается с R (и не заканчивается с d или b)) использовать все 64 бита в этом регистре.
Указатели на память должны всегда перейдите в регистр 64 бит R.
Если регистр начинается с E (или заканчивается d), это 32-разрядный регистр (нижние 32 бита).
Любая операция записи в 32-битном регистре содержит нулевые верхние 32 бита перекрытого 64-битного регистра, например. mov eax,1 комплекты rax - 1; mov eax,-1 комплектов rax до $0000 0000 FFFF FFFF;

mov eax,2 и mov rax,2 делать то же самое.
За исключением того, что mov eax,2 на два байта короче и, следовательно, является предпочтительным.

Если вы читаете/записываете из/в память, операция будет соответствовать размеру регистра, если вы не укажете иное.
Если вы пишете немедленно в память, вы должны указать размер.
Вы не можете записать 64-разрядную оперативную память в память, вам нужно будет использовать 2 инструкции для этого: mov R64,imm64 + mov [mem],R64.

Caveat
Любой запись в 16-ти бит или 8 бит регистр делает не ноля из верхней части соответствующих бит 32/64 регистра!

Заключение
Если вы хотите написать сборку, вам нужно на самом деле узнать сборку с нуля.
Поскольку вы программируете в Linux, я рекомендую получить копию книги Джеффа Дунтемана: Assembly Language Step By Step, for Linux
Джефф - легенда и может объяснить сборку лучше всех на этой планете.

+0

'mov eax, 2' на 2 байта короче. 'mov eax, 2' - 5 байт: opcode + imm32. 'mov rax, 2' использует кодировку' REX mov r/m64, imm32', поэтому это 7 байт, включая байты ModR/M (которые не имеют 5-байтной версии). Версия REX для 5-байтовой кодировки - это 10-байтовый 'mov r64, imm64' (REX + opcode + imm64) –

+0

Как вы выяснили, что OP никогда не называл' read() '? Я попытался выяснить этот вопрос, и он все еще не показывает достаточно кода, чтобы иметь смысл. О, возможно, это то, что на самом деле означал их последний комментарий. –

+0

@PeterCordes Я понял, что код, который он показал, - это весь код, который у него есть. Это имеет смысл. Чтобы открыть файл в MS Word, нужно сделать все, что нужно сделать с файлом (полная остановка). Это просто, что syscall не работает так. – Johan

0

Я определил длину файла, но не ставил искать позицию начала файла

ли вы найти длину, прочитав до конца, оставляя позицию файла на EOF? Не делайте этого в первую очередь, используйте fstat, чтобы найти длину файла, который вы открыли, если он есть.

Он будет иметь длину, если он является обычным файловым или блочным устройством, и он не будет, если это труба, сокет или tty (или другое символьное устройство). Но длина может измениться, прежде чем вы закончите читать, если другой процесс изменит ее.

Единственный другой способ для позиции файла указывать на конец - это если вы открыли O_APPEND в флагах.


Посмотрите на то, что система требует strace cat infloop.c использования (я «закомментирована» и описал те, которые не имеют отношения, если вы используете фиксированного размера статического буфера, сделать что-то подобное, если вы хотите)

open("infloop.c", O_RDONLY)    = 3 
fstat(3, {st_mode=S_IFREG|0664, st_size=29, ...}) = 0 # get the size 
    # fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0 
    # mmap(NULL, 139264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff78a7eb000 # allocate a buffer 
read(3, "\nint main()\n{\n while(1);\n}\n", 131072) = 29 
write(1, "\nint main()\n{\n while(1);\n}\n", 29) = 29 
read(3, "", 131072)      = 0   # read returning 0 means EOF 
    # munmap(0x7ff78a7eb000, 139264)   = 0  # free the buffer 
close(3)        = 0 
+0

Я определял длину не путем считывания до конца файла и подсчета символов-байтов в процессе, а с помощью lseek, который сразу же устанавливает позицию после окончания файла и возвращает текущую длину в 'rax'. Предпочтительнее lseek или fstat? –

+1

@BulatM: Вы имеете в виду 'lseek (fd, 0, SEEK_END)'? fstat также предпочтительнее этого, если вы не хотите, чтобы текущая позиция файла изменилась до конца файла. fstat также сообщает вам, есть ли у вас обычный файл или терминал (например, если кто-то запускал вашу программу с помощью '/ dev/tty') или что-то еще. Обычно лучше просто проверить возвращаемое значение 'read()' для обнаружения EOF, а не запрашивать длину раньше времени. Обратите внимание, как вывод 'strace cat' делает 2-й' read' после того, как он уже находится в конце файла? –

+0

Спасибо, я понял, кошка ободряюще, однако использование lseek имеет некоторое «преимущество» в том, что быстрее кодируется как на C, так и на сборке. –