Related
This question already has answers here:
How to load address of function or label into register
(1 answer)
32-bit absolute addresses no longer allowed in x86-64 Linux?
(1 answer)
Closed 4 months ago.
I'm completing the final assignment for a compilers course and right now the deal is to translate some intermediate representation into x86_64 assembly source code and then build an executable through gcc by running
gcc output.s -o output
This executable should work properly. The issue is that I just can't get my code past GCC when it comes to (at least) one particular instruction. This is it:
mov L0, %rbx
Where L0 is a label.
The whole test file is as follows:
.text
.section .rodata
.text
.globl main
main:
// rbss for now
add $0, %rsp
mov %rsp, %rsi
// register spill area
add $0, %rsp
mov %rsp, %rdi
// store rax => rsp
mov %rax, %rcx
mov %rcx, ( %rsp )
// subI rsp, 4 => rsp
mov %rsp, %rcx
sub $4, %rcx
mov %rcx, %rsp
// lea L0 => rbx
mov L0, %rbx
// store rbp => rsp
mov %rbp, %rcx
mov %rcx, ( %rsp )
// subI rsp, 4 => rsp
mov %rsp, %rcx
sub $4, %rcx
mov %rcx, %rsp
// store rbx => rsp
mov %rbx, %rcx
mov %rcx, ( %rsp )
// subI rsp, 4 => rsp
mov %rsp, %rcx
sub $4, %rcx
mov %rcx, %rsp
// jumpI => Lmain
jmp Lmain
// L0 : halt
L0:
hlt
// Lmain : nop
Lmain:
// addI rsp, 0 => rbp
mov %rsp, %rcx
add $0, %rcx
mov %rcx, %rbp
// subI rsp, 0 => rsp
mov %rsp, %rcx
sub $0, %rcx
mov %rcx, %rsp
// addI rbp, 8 => rsp
mov %rbp, %rcx
add $8, %rcx
mov %rcx, %rsp
// loadAI rbp, 4 => rbp
mov %rdi, %rbx
add %rbp, %rbx
add $4, %rbx
mov ( %rbx ), %rcx
mov %rcx, %rbp
// jump => rbp
mov %rbp, %rbx
jmp *%rbx
Is there anything inherently mistaken about using mov this way? I'm not using call/ret semantics since the translation must be carried out directly from ILOC (a toy/education purpose) intermediate code.
When I try to run the aforementioned command I get some variation of:
/usr/bin/ld: /tmp/cccAoFmz.o: relocation R_X86_64_32S against `.text' can not be used when making a PIE object; recompile with -fPIE
collect2: error: ld returned 1 exit status
Could you guys help me to get a grasp of what's actually going on? I'm quite new to x86 programming and that's my first time with this kind of application. The whole assignment is done, my only issue is getting it to a working state (So no, huahuahua, I'm not getting you guys to do my homework :D).
Is there another way to get what I'm trying to achieve? Is my approach incorrect? I'm out of ideas right now.
Thank you so much :)
Best,
I found example of code on assembly, which finds the maximum number in array named data_items but that example was for x86 and I tried to adapt it for x64 because 32 bit absolute addressing is not supported by 64 bit system.
To be short there are three actions:
lea data_items(%rip), %rdi #(1) Obtaining data_items address
add $4, %rdi #(2) Incrementing the pointer to 4 to read a next item
movl (%rdi), %eax #(3) Reading data at %rdi to %eax
The main questions:
Is it correct way to pointing? Can it produce error after code relocation?
If the %rip register constantly grows, why lea data_items(%rip), %rdi loads correct memory address? May be getting an offset by %rip have special meaning rather than "dataItems + %rip"?
Full adapted code here:
.section __DATA,__data
data_items:
.long 3,67,34,222,45,75,54,34,44,33,22,11,66,0
.section __TEXT,__text
.globl _main
_main:
lea data_items(%rip), %rdi #(1)
movl (%rdi), %eax
movl %eax, %ebx
start_loop:
cmpl $0, %eax
je loop_exit
add $4, %rdi #(2)
movl (%rdi), %eax #(3)
cmpl %ebx, %eax
jle start_loop
movl %eax, %ebx
jmp start_loop
loop_exit:
mov $0x2000001, %rax
mov $0, %rdi
syscall
I'm having a problem implementing quicksort in assembly, when I'm trying to perform the second recursive call to qsort(arr,pi+1,high) I don't know why, but it exits the recursion too early.
I've tried using the stack and writing helper functions, but everything I've tied created new problems. When debbuging, I see that I exit the function when %ebx = 4 and %ecx = 5 and the array is almost sorted array ={10,30,40,50,70,90,80} - I need only to perform the last swap when %ebx = 5 and %ecx = 6, but it exits before, and I don't understand why it won't go back to when %ecx was 6.
I thought perhaps the problem is when I'm making the comparisons between high and low, but If I change those, I'm having different problems (it exists too early the first recursive call). perhaps anyone has an idea what's wrong with my code?
here's my code:
.data
array: .int 10, 80, 30, 90, 40, 50, 70
size: .int 7
.text
.global main
main:
movl $0, %ebx # low index
movl (size), %ecx # high index
dec %ecx # ecx = size -1
qsort:
cmp %ecx, %ebx # high > low
jge end
movsx %ecx, %r13 # push high
movsx %ebx, %r12 # push low
call partition # rax now is the pivot
movq %r12, %rbx # pop low
movl %eax, %ecx # prepare new high = pi
subl $1, %ecx # high = pi-1
call qsort # qsort(arr,low,pi-1)
movl %eax,%ebx # push new low = pi+1
inc %ebx
call qsort # qsort(arr,pi+1,high)
ret
partition:
movq %r12, %rax # get low
movq %r13, %rbx # get high
movl %eax, %ecx # j = low
subl $1, %eax # i = low-1
movl array(,%ebx,4), %edx # pivot = array[high]
swap_loop:
cmp %ebx, %ecx # high > j
jge swap_greater
movl array(,%ecx,4), %esi # esi = array[j]
cmp %edx, %esi # pivot > array[j] ?
jge loops # jmp if array[j] > pivot
inc %eax # i++
# swap
movsx %esi, %rsi
pushq %rsi
movl array(,%eax,4), %edi # edi = array[i]
movl %edi, array(,%ecx,4) # array[j]=array[i]
pop %rsi
movl %esi, array(,%eax,4) # array[i]= previous array[j]
loops:
inc %ecx # j++
jmp swap_loop
swap_greater:
inc %eax
movsx %edx, %rdx
pushq %rdx
movl array(,%eax,4), %edi # edi = array[i+1]
movl %edi, array(,%ebx,4) # array[high]=array[i+1]
pop %rdx
movl %edx, array(,%eax,4) # array[i+1]= previous array[high]
dec %eax
end_p:
inc %eax
ret
end:
inc %ecx
movsx %ecx, %r13 # update high
ret
edit: as Peter Cordes has pointed out - I need to work with the stack in order to retrieve the parameters when I go back from the recursive calls. after re-writing my code using the stack, I have a new problem, I always pop out the same thing on the second recursive call. I don't understand why it doesn't do the "recursive folding" like in C, when I look at the stack there's something weird, since I'm not seeing the values as I excpect ( I excpect to see layers or rax-high-low-rax-high-low...etc) but instead there are those numbers:
native process 42032 In: second_rec L?? PC: 0x4005ea
0x7fffffffdeb0: 0x0000000000000000 0x0000000000000001
0x7fffffffdec0: 0x0000000000000002 0x00000000004005ef
0x7fffffffded0: 0x0000000000000000 0x0000000000000002
0x7fffffffdee0: 0x0000000000000001 0x00000000004005df
0x7fffffffdef0: 0x0000000000000000 0x0000000000000001
0x7fffffffdf00: 0x0000000000000002 0x00000000004005df
0x7fffffffdf10: 0x0000000000000000 0x0000000000000002
0x7fffffffdf20: 0x0000000000000003 0x00000000004005df
0x7fffffffdf30: 0x0000000000000000 0x0000000000000003
0x7fffffffdf40: 0x0000000000000004 0x00007ffff7a05b97
0x7fffffffdf50: 0x0000000000000001 0x00007fffffffe028
0x7fffffffdf60: 0x0000000100008000 0x00000000004005a7
perhaps I'm not pushing\popping in the right timing, but from what I've understood, I'm pushing parameters before call, and popping them when I return from the call... does anyone have an idea what am I doing wrong? the new code:
.data
array: .int 10, 80, 30, 90, 40, 50, 70
size: .int 7
.text
.global main
main:
movl $0, %ebx # low index
movl (size), %ecx # high index
dec %ecx # ecx = size -1
qsort:
cmp %ecx, %ebx # high > low
jge end
movsx %ecx, %rcx # prepare high
movsx %ebx, %rbx # prepare low
pushq %rcx # push high
pushq %rbx # push low
call partition # rax now is the pivot
popq %rbx # pop low
popq %rcx # pop high
movsx %eax, %rax
pushq %rax # push pivot
movl %eax, %ecx # prepare new high = pi
subl $1, %ecx # pi-=1
movsx %ecx, %rcx
pushq %rcx # push high
pushq %rbx # push low
call qsort # qsort(arr,low,pi-1)
second_rec:
popq %rbx # pop low
popq %rcx # pop high
popq %rax # pop pivot
movl %eax, %ecx # high = pi
addl $1, %ecx # high = pi+1
pushq %rax # push pivot
pushq %rcx # push high
pushq %rbx # push low
call qsort # qsort(arr,pi+1,high)
addq $24, %rsp
ret
partition:
movq 8(%rsp), %rax # get low
movq 16(%rsp), %rbx # get high
movl %eax, %ecx # j = low
subl $1, %eax # i = low-1
movl array(,%ebx,4), %edx # pivot = array[high]
swap_loop:
cmp %ebx, %ecx # high > j
jge swap_greater
movl array(,%ecx,4), %esi # esi = array[j]
cmp %edx, %esi # pivot > array[j] ?
jge loops # jmp if array[j] > pivot
inc %eax # i++
# swap
movsx %esi, %rsi
pushq %rsi
movl array(,%eax,4), %edi # edi = array[i]
movl %edi, array(,%ecx,4) # array[j]=array[i]
pop %rsi
movl %esi, array(,%eax,4) # array[i]= previous array[j]
loops:
inc %ecx # j++
jmp swap_loop
swap_greater:
inc %eax
movsx %edx, %rdx
pushq %rdx
movl array(,%eax,4), %edi # edi = array[i+1]
movl %edi, array(,%ebx,4) # array[high]=array[i+1]
pop %rdx
movl %edx, array(,%eax,4) # array[i+1]= previous array[high]
dec %eax
end_p:
inc %eax
ret
end:
ret
Thank you very much everyone for your help!
I am writing a program calling 2 pre-given functions min.c and conv.c, also of course a main.c
min.s goes as follows:
.file "min.c"
.text
.p2align 4,,15
.globl min
.type min, #function
min:
.LFB0:
.cfi_startproc
cmpl %esi, %edi
movl %esi, %eax
cmovle %edi, %eax
ret
.cfi_endproc
.LFE0:
.size min, .-min
.ident "GCC: (Ubuntu 7.4.0-1ubuntu1~16.04~ppa1) 7.4.0"
.section .note.GNU-stack,"",#progbits
conv.c:
char conv(char *, char *, int);
char conv(char *x, char *h, int n) {
char ret = 0;
int i;
for (i = 0; i < n; i++) {
ret += x[i] * h[n-i-1];
}
return ret;
}
min.c:
int min(int, int);
int min(int a, int b) {
if (a < b) return a;
return b;
}
and conv.s goes as follows:
.file "conv.c"
.text
.p2align 4,,15
.globl conv
.type conv, #function
conv:
.LFB0:
.cfi_startproc
testl %edx, %edx
jle .L4
movslq %edx, %rax
leaq -1(%rsi,%rax), %rcx
leaq -2(%rsi,%rax), %rsi
leal -1(%rdx), %eax
xorl %edx, %edx
subq %rax, %rsi
.p2align 4,,10
.p2align 3
.L3:
movzbl (%rdi), %eax
subq $1, %rcx
addq $1, %rdi
mulb 1(%rcx)
addl %eax, %edx
cmpq %rcx, %rsi
jne .L3
movl %edx, %eax
ret
.p2align 4,,10
.p2align 3
.L4:
xorl %edx, %edx
movl %edx, %eax
ret
.cfi_endproc
.LFE0:
.size conv, .-conv
.ident "GCC: (Ubuntu 7.4.0-1ubuntu1~16.04~ppa1) 7.4.0"
.section .note.GNU-stack,"",#progbits
also my small basic piece of code is trying to just go after a pre-given algorithm:
for i from 0 to n+m-2 do
ladj <- min(i+1, m)
radj <- m - min(m+n-(i+1), m)
result[i] <- conv(x + (i+1-ladj), h + radj, ladj-radj)
so i wrote:
.globl conv_arr
conv_arr:
movq $0, %rax # zero out the result
movq $0, %rbx # %rbx is set to 0 to be the counter for the loop, i.e. int i
addq %rsi, %rbp
addq %rcx, %rbp
subq $2, %rbp # 3 lines of code to set a limit for the counter, i.e. n + m - 2
loop: # loop label
cmpq %rbx, %rbp # comparing to keep the loop in scope from i = 0 to n + m - 2
jle return # jump to return label if the counter reaches the limit
pushq %rdi
pushq %rsi # saving all the registers before calling function min( int, int)
movq %rbx, %r10 # copying the value of %rbx into the value of %r10
addq $1, %r10 # adding 1 to the value of %r10
movq %r10, %rdi # changing the value of %rdi ( in this case the first parameter
# to the function min() ) to i + 1
movq %rcx, %rsi # changing the value of %rsi ( in this case the second parameter to
# the function min() ) to m
call min # calling the function min() with the two parameters %rdi, %rsi
popq %rsi
popq %rdi # restoring the values of %rdi and %rsi to their initial values
movq %rax, %r12 # copying the value of the result from function min() to %r12,
# the register which is now set up to be the value of ladj
pushq %rdi
pushq %rsi # saving the values of %rdi and %rsi before calling function min()
movq %rcx, %rdi # copying the value of %rcx ( int m ) into the value of %rdi
addq %rsi, %rdi # adding the value of %rsi ( int n ) into the value of %rdi
subq %rbx, %rdi # subtracting the value of %rbx ( int i ) from the value of %rdi
subq $1, %rdi # subtracting 1 from the value of %rdi
movq %rcx, %rsi # copying the value of %rcx ( int m ) into the value of %rsi
call min # calling the function min() with two parameters %rdi, %rsi
popq %rsi
popq %rdi # restoring the values of %rdi and %rsi to their initial values
movq %rcx, %r13 # copying the value of %rcx ( int m ) into the value of
# register %r13
subq %rax, %r13 # subtracting the value of %rax ( min( m + n - ( i + 1 ), m ) )
# from the value of %rcx ( m )
movq %r13, %r14 # copying the value of %r13 ( m - min( m + n - ( i + 1 ), m ) # ) into the value of %r14, the register which is now set up
# to be the value of radj
pushq %rdi
pushq %rsi
pushq %rdx
pushq %rcx # saving the values of %rdi, %rsi, %rdx and %rcx before calling
# function conv( char*, char*, int )
addq %rbx, %rdi # adding the value of %rbx ( i ) to the value of %rdi ( char* x )
addq $1, %rdi # adding 1 to the value of %rdi
subq %r12, %rdi # subtracting the value of %r12 ( ladj ) from the value of %rdi
movq %rdx, %rsi # copying the value of %rdx ( char* h ) to the value of %rsi
addq %r14, %rsi # adding the value of %r14 ( radj ) to the value of %rsi
movq %r12, %rdx # copying the value of %r12 ( ladj ) to the value of %rdx
subq %r14, %rdx # subtracting the value of %r14 ( radj ) from the value of %rdx
call conv # calling the function conv() with three parameters %rdi, %rsi
# and %rdx
popq %rcx
popq %rdx
popq %rsi
popq %rdi # restoring the values of %rdi, %rsi, %rdx and %rcx to their # initial values
movq %rax, %r8 # copying the value of %rax ( result after calling function conv() # ) into the value of the index that %r8 ( char* result) is # pointing to
incq %rbx # incrementing the value of %rbx ( i ) by 1
incq %r8 # incrementing the value of %r8 ( char* result ) according to the
# value of %rbx ( i ), i.e. result[i]
jmp loop # jump back to the loop label when all the procedures are done
return: # return label
ret
all the comments are given to keep track of what i was attempting to do, i know it needs a lot more research and attention.
When i ran the program it gave a segmentation fault, i ran gdb with it and it says the error was cannot access memory at address xxx:
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400757 in conv (x=0x601050 <signal> "",
h=0xffffffff80601050 <error: Cannot access memory at address 0xffffffff601050> n=15) at conv.c:8.
8 ret += x[i] * h[n-i-1];
I tried breakpoints at lines using conv.c and it went backwards when counting, i.e. when the program reached n=3 it started going back to n=2, n=1 and then all the way back to n=-12 even further.
****IF ANYONE HAS ANY SUGGESTIONS I WOULD REALLY APPRECIATE IT****.
I have been trying for some time now to get a number from a keyboard and comparing it with a value on the stack. If it is correct it will print "Hello World!" and if incorrect, it should print out "Nope!". However, what happens now is no matter the input "jne" is called, nope is printed, and segfault. Perhaps one of you could lend a hand.
.section __DATA,__data
str:
.asciz "Hello world!\n"
sto:
.asciz "Nope!\n"
.section __TEXT,__text
.globl _main
_main:
push %rbp
mov %rsp,%rbp
sub $0x20, %rsp
movl $0x0, -0x4(%rbp)
movl $0x2, -0x8(%rbp)
movl $0x2000003, %eax
mov $0, %edi
subq $0x4, %rsi
movq %rsi, %rcx
syscall
cmp -0x8(%rbp), %edx
je L1
jne L2
xor %rbx, %rbx
xor %rax, %rax
movl $0x2000001, %eax
syscall
L1:
xor %rax, %rax
movl $0x2000004, %eax
movl $1, %edi
movq str#GOTPCREL(%rip), %rsi
movq $14, %rdx
syscall
ret
L2:
xor %eax, %eax
movl $0x2000004, %eax
movl $1, %edi
movq sto#GOTPCREL(%rip), %rsi
movq $6, %rdx
syscall
ret
I would start with this OS/X Syscall tutorial (The 64-bit part in your case). It is written for NASM syntax but the important information is the text and links for the SYSCALL calling convention. The SYSCALL table is found on this Apple webpage. Additional information on the standard calling convention for 64-bit OS/X can be found in the System V 64-bit ABI.
Of importance for SYSCALL convention:
arguments are passed in order via these registers rdi, rsi, rdx, r10, r8 and r9
syscall number in the rax register
the call is done via the syscall instruction
what OS X contributes to the mix is that you have to add 0x20000000 to the syscall number (still have to figure out why)
You have many issues with with your sys_read system call. The SYSCALL table says this:
3 AUE_NULL ALL { user_ssize_t read(int fd, user_addr_t cbuf, user_size_t nbyte); }
So given the calling convention, int fd is in RDI, user_addr_t cbuf (pointer to character buffer to hold return data) is in RSI, and user_size_t nbyte (maximum bytes buffer can contain) is in RDX.
Your program seg faulted on the ret because you didn't have proper function epilogue to match the function prologue at the top:
push %rbp #
mov %rsp,%rbp # Function prologue
You need to do the reverse at the bottom, set the result code in RAX and then do the ret. Something like:
mov %rbp,%rsp # \ Function epilogue
pop %rbp # /
xor %eax, %eax # Return value = 0
ret # Return to C runtime which will exit
# gracefully and return to OS
I did other minor cleanup, but tried to keep the structure of the code similar. You will have to learn more assembly to better understand the code that sets up RSI with the address for sys_read SYSCALL . You should try to find a good tutorial/book on x86-64 assembly language programming in general. Writing a primer on that subject is beyond the scope of this answer.
Code that might be closer to what you were looking for that takes the above into account:
.section __DATA,__data
str:
.asciz "Hello world!\n"
sto:
.asciz "Nope!\n"
.section __TEXT,__text
.globl _main
_main:
push %rbp #
mov %rsp,%rbp # Function prologue
sub $0x20, %rsp # Allocate 32 bytes of space on stack
# for temp local variables
movl $0x2, -4(%rbp) # Number for comparison
# 16-bytes from -20(%rbp) to -5(%rbp)
# for char input buffer
movl $0x2000003, %eax
mov $0, %edi # 0 for STDIN
lea -20(%rbp), %rsi # Address of temporary buffer on stack
mov $16, %edx # Read 16 character maximum
syscall
movb (%rsi), %r10b # RSI = pointer to buffer on stack
# get first byte
subb $48, %r10b # Convert first character to number 0-9
cmpb -4(%rbp), %r10b # Did we find magic number (2)?
jne L2 # If No exit with error message
L1: # If the magic number matched print
# Hello World
xor %rax, %rax
movl $0x2000004, %eax
movl $1, %edi
movq str#GOTPCREL(%rip), %rsi
movq $14, %rdx
syscall
jmp L0 # Jump to exit code
L2: # Print "Nope"
xor %eax, %eax
movl $0x2000004, %eax
movl $1, %edi
movq sto#GOTPCREL(%rip), %rsi
movq $6, %rdx
syscall
L0: # Code to exit main
mov %rbp,%rsp # \ Function epilogue
pop %rbp # /
xor %eax, %eax # Return value = 0
ret # Return to C runtime which will exit
# gracefully and return to OS