Bulat M. Bulat M. - 2 months ago 16
Linux Question

how to mov 64 bit address to register

I try to open file in assembly, to move value at memory locations(address) to rdi:

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


SOLVED PROBLEM:
I determined length of the file but did not put seek position to beginning of the file like

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"


but it does not move correctly. Also I read that
mov
has 32 bit source operand, but I need to move 64 bits from memory to register. Tried using movq in YASM, but it gave syntax error about invalid combination of opcode and operands.

Answer

Syscall 2 opens a file.

The parameters are:

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

You use the following code:

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

So far so good, however, according to the documentation:

Given a pathname for a file, open() returns a file descriptor, a small, nonnegative integer for use in subsequent system calls.

So you've only done the first part, you hopefully gotten a file descriptor, with that you can read data from the file, you need to add that part to the code.
Finally when you're done you need to clean up after yourself and close the file.

Let me complete the code for you.

//********* 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

About 64 bits vs 32 bits

Also I read that mov has 32 bit source operand [...]

This is complete and utter hogwash.

X64 is a fully fledged 64 bit processor, that does what is says on the tin.
If you use a 64 bit register (any register that starts with an R (and doesn't end with a d or a b)) you use all 64 bits in that register.
Pointers to memory should always go into a 64bits R register.
If a register starts with an E (or ends with a d) it's a 32 bit register (the lower 32 bits).
Any write operation on a 32 bits register zeros out the top 32 bits of the overlapped 64 bit register, e.g. mov eax,1 sets rax to 1; mov eax,-1 sets rax to $0000 0000 FFFF FFFF;

mov eax,2 and mov rax,2 do the exact same thing.
Except that mov eax,2 is two bytes shorter and is thus preferred.

If you read/write from/to memory the operation will follow the size of the register unless you specify otherwise.
If you write an immediate to memory you must specify the size.
You cannot write a 64-bit immediate to memory, you'll have to use 2 instructions to do that: mov R64,imm64 + mov [mem],R64.

Caveat
Any write to a 16-bits or 8-bits register does not zero out the top part of the associated 32/64 bits register!

Conclusion
If you want to write assembly, you need to actually learn assembly from the ground up.
Because you're programming in Linux I recommend getting a copy of Jeff Duntemann's book: Assembly Language Step By Step, for Linux
Jeff is a legend and can explain assembly better than anyone on this planet.

Comments