I'm trying to write an x86 version of the 'cat' program as a training of syscall calls in assembly.
I'm struggling a lot with command line arguments.
I use the main symbol as an entry point, so I thought I would find the argc parameter in %rdi and the argv parameter in %rsi.
Actually argc is in %rdi as expected, but I keep segfaulting when trying to pass argv[1] to the open syscall.
Not sure of what I'm doing wrong, here is my assembly code:
main:
cmp $2, %rdi // If argc != 2 return 1
jne .err1
lea 8(%rsi), %rdi // Move argv[1] -> %rdi
xor %rsi, %rsi // 0 to %rsi -> O_RDONLY
xor %rdx, %rdx
mov $2, %rax // Open = syscall 2
syscall
cmp 0, %rax // If open returns <0 -> exit status 2
jl .err2
mov %rax, %rdi // Move fd to %rdi
call cat
ret
.err1:
mov $1, %rax
ret
.err2:
mov $2, %rax
ret
There are two issues with your code.
First, you use lea 8(%rsi), %rdi to retrieve the second argument. Note that rsi points to an array of pointers to command line arguments, so to retrieve the pointer to the second argument, you have to dereference 8(%rsi) using something like mov 8(%rsi), %rdi.
Second, you forgot the dollar sign in front of 0 in cmp $0, %rax. This causes an absolute address mode for address 0 to be selected, effectively dereferencing a null pointer. To fix this, add the missing dollar sign to select an immediate addressing mode.
When I fix both issues, your code as far as you posted it seems to work just fine.
Related
I am trying to write the char *my_strcpy(char *dest, const char *source); in assembly, at&t syntax, that should act exactly like strcpy from C. My c file looks like this:
.globl my_strcpy
my_strcpy:
push %rbp
mov %rsp, %rbp
mov %rdi, %rax
jmp copy_loop
The jump is pointless.
copy_loop:
cmp $0, (%rsi)
You didn't specify whether this should be an 8, 16, 32 or 64-bit compare. When I assemble it, I get a 32-bit compare; e.g. it sees whether the 32-bit word at address %rsi equals zero. You need to change this to cmpb $0, (%rsi).
je end
mov %rsi, %rdi
As user 500 noted, this copies the address in the %rsi register into the %rdi register, overwriting it. This is not what you want. You probably intended something like movb (%rsi), (%rdi), but no such instruction actually exists: x86 does not have such a single instruction to move memory to memory (special exception: see the movsb instruction). So you'll need to first copy the byte at address %rsi into a register, and then copy it onward with another instruction, e.g. mov (%rsi), %cl ; mov %cl, (%rdi). Note the use of the 8-bit %cl register makes it unambiguous that these should be one-byte moves.
movzbl (%rsi), %ecx is a more efficient way to load a byte on modern x86. You still store it by reading CL with mov %cl, (%rdi), but overwriting the whole RCX instead of merging into RCX is better.
addq $1, %rsi
addq $1, %rdi
You might like to learn about the inc instruction, but add is fine.
je copy_loop
I think you mean jmp copy_loop, since the jump here should happen unconditionally. (Or you should rearrange your loop so the conditional branch can be at the bottom. Since you want to copy the terminating 0 byte, you can just copy and then check for 0, like do{}while(c != 0))
end:
leave
ret
As a learning exercise, I've been handwriting assembly. I can't seem to figure out how to load the value of an address into a register.
Semantically, I want to do the following:
_start:
# read(0, buffer, 1)
mov $3, %eax # System call 3 is read
mov $0, %ebx # File handle 0 is stdin
mov $buffer, %ecx # Buffer to write to
mov $1, %edx # Length of buffer
int $0x80 # Invoke system call
lea (%ecx, %ecx), %edi # Pull the value at address into %edi
cmp $97, %edi # Compare to 'a'
je done
I've written a higher-level implementation in C:
char buffer[1];
int main()
{
read(0, buffer, 1);
char a = buffer[0];
return (a == 'a') ? 1 : 0;
}
But compiling with gcc -S produces assembly that doesn't port well into my implementation above.
I think lea is the right instruction I should be using to load the value at the given address stored in %ecx into %edi, but upon inspection in gdb, %edi contains a garbage value after this instruction is executed. Is this approach correct?
Instead of the lea instruction, what you need is:
movzbl (%ecx), %edi
That is, zero extending into the edi register the byte at the memory address contained in ecx.
_start:
# read(0, buffer, 1)
mov $3, %eax # System call 3 is read
mov $0, %ebx # File handle 0 is stdin
mov $buffer, %ecx # Buffer to write to
mov $1, %edx # Length of buffer
int $0x80 # Invoke system call
movzbl (%ecx), %edi # Pull the value at address ecx into edi
cmp $97, %edi # Compare to 'a'
je done
Some advice
You don't really need the movz instruction: you don't need a separate load operation, since you can compare the byte in memory pointed by ecx directly with cmp:
cmpb $97, (%ecx)
You may want to specify the character to be compared against (i.e., 'a') as $'a' instead of $97 in order to improve readability:
cmpb $'a', (%ecx)
Avoiding conditional branches is usually a good idea. Immediately after performing the system call, you could use the following code that uses cmov for determining the return value, which is stored in eax, instead of performing a conditional jump (i.e., the je instruction):
xor %eax, %eax # set eax to zero
cmpb $'a', (%ecx) # compare to 'a'
cmovz %edx, %eax # conditionally move edx(=1) into eax
ret # eax is either 0 or 1 at this point
edx was set to 1 prior to the system call. Therefore, this approach above relies on the fact that edx is preserved across the system call (i.e., the int 0x80 instruction).
Even better, you could use sete on al after the comparison instead of the cmov:
xor %eax, %eax # set eax to zero
cmpb $'a', (%ecx) # compare to 'a'
sete %al # conditionally set al
ret # eax is either 0 or 1 at this point
The register al, which was set to zero by means of xor %eax, %eax, will be set to 1 if the ZF flag was set by the cmp (i.e., if the byte pointed by ecx is 'a'). With this approach you don't need to care about thinking whether the syscall preserves edx or not, since the outcome doesn't depend on edx.
I made a program in x64 assembly, using AT&T syntax, but I don't figure out why mov operator copies address variable to register. Here is my code:
.globl main
.text
main:
mov $a, %rax
mov $format, %rdi # set 1st parameter (format)
mov %rax, %rsi # set 2nd parameter (current_number)
mov $0, %rax # because printf is varargs
sub $8, %rsp # align stack pointer
call printf # printf(format, sum/count)
add $8, %rsp # restore stack pointer
ret
.data
a: .quad 123
format: .asciz "%d\n"
The program outputs 6295616 instead of 123. Please help me understand what I've done wrong.
Because you have indicated with the dollar sign that you want immediate mode. Remove it to get absolute mode.
I am making an addition program using x64 assembly, but it does not display a value when run (compiled with nasm, elf64).
section .text
global _start
_start:
mov rax, 0
add rax, [num1B]
add rax, [num2B]
mov [result], rax
mov rsi, [result]
;mov rdx, 8
mov rax, 4
mov rdi, 1
int 80h
mov rax, 1
mov rdi, 0
int 080h
section .data
num1B: dq 0Ah
num2B: dq 0Ah
result: dq 00h
Does anyone know why this is not displaying anything
1.later use printf instead of interrupts, much better,
2.why put values of numB1 and numB2 instead of their location.
Use: mov rax, numB1.
3.in 64bit nasm assembly you use the:
rdi, rsi, rbx, rcx,... Registers for putting in values for interrupts.
For example:
mov rdi, 01
mov rsi, 00
syscall
DON't USE int0x80!, for more-portability use syscall and besides int 0x80 didn't work on my system.
Hope it helps, Correct me if I'm wrong.
Looks like you want to print 'result' to stdout and you are using the 32-bit
system call values.
In 64-bit linux the system call for write is 1 and you would write to stdout like this... att&t syntax:
first strip values in %rax and push them byte by byte on stack, say %rax
holds the value 0x7ffffff8:
mov $0xa, %rbx # divisor
nibble:
xor %rdx, %rdx # will hold bytes values you need
div %rbx, %rax
push %rdx # save remainder
inc %r8 # count digit, write seems to trash %rcx
cmp $0, %rax # done?
jne nibble # no, get another digit
#set up for write to stdout
mov $1, %rax # sys_call for write
mov $1, %rdi # write to stdout
mov $result, %rsi # addr. of value to print
# now get values from stack, make ascii and write to stdout
decimal:
pop %rdx # get digit off stack
add $0x30, %dl # make ascii printable
movb %dl, result # load addr. with value
mov $1, %rdx # print 1 byte
syscall
dec %r8
jnz decimal # go till %r8 is zero
You just need to set up a 1 byte data holder for digits,either in data section:
.section .data
result:
.byte 0 # reserves 1 byte and inits to 0
or the uninitialized data area:
.section .bss
.lcomm result, 1 # reserves 1 byte
I'm sure there are better ways to do this, should give you some ideas though.
Get a 64-bit system call list, they have changed quite a lot from the 32-bit calls.
I've been having problems getting even the simplest of assembly programs that I write on Linux to run on my FreeBSD machine. Here's the offending code (I'm trying to keep this as simple as possible):
#counts to sixty
.section .data
.section .text
.global _start
_start:
movl $1, %ecx #move $1 into ecx
movl $1, %eax
start_loop:
addl %ecx, %eax #add ecx to eax
cmpl $60, %eax #compare $60 and eax...
je end_loop #if eax = 60 go to end_loop
cmpl $60, %eax #
jle start_loop #jump if eax is < $60...
jmp start_loop #...to start_loop
end_loop:
movl %eax, %ebx #move the value of eax into ebx because ebx holds
#the return value
movb $1, %al #Move $1 into eax (int 1 is the value for the
#exit() syscall
int $0x80
The Linux machine returns the expected resulted which is sixty, whereas the FreeBSD machine consistently returns 164 for the return code. Does anybody know why this is? If so, can you please explain to me what is happening? Also, I should mention that they are both indeed running x86 CPUs. Thanks in advance :)
Refer to the FreeBSD Developer's handbook, and you need to do:
push %eax
mov $1, %eax
push %eax
int $0x80
because:
only the system call vector is passed via register %eax, all arguments are on the stack
the FreeBSD default syscall expects an additional word on the stack, which would be a dummy for inlined uses of int $0x80 but a return address where you do a syscall via a call kernel_entry trampoline (that then can do int $0x80; ret).
If you want to use the Linux convention (some syscall args in regs, called "Alternative Calling convention" in the manual), you have to brand the executable so that the system knows you're using Linux-style syscalls.