I just wrote this code, it assembles fine. I used GAS and its x86_64.
I linked it like this:
ld -o ./maximum /usr/lib64/crt1.o /usr/lib64/crti.o ./maximum.o -lc
There's no errors. But then I run it and I get this:
bash: ./maximum: No such file or directory
Just to make sure, I ran it and gdb and got the same error.
Here's the code:
Btw, I'm just starting to learn assembly so I'm guessing this code doesn't look to good :P
In fact, I'm pretty sure I used mulq wrong here:
movq %rcx, %rax # add enough memory to the stack frame
movq $8, %rbx
mulq %rbx # to hold all the args
movq %rax, %rbx
addq $8, %rbx # add extra 8 bytes for terminating data item (0)
Thanks,
mikfig
I linked it like this:
ld -o ./maximum /usr/lib64/crt1.o /usr/lib64/crti.o ./maximum.o -lc
There's no errors. But then I run it and I get this:
bash: ./maximum: No such file or directory
Just to make sure, I ran it and gdb and got the same error.
Here's the code:
# PURPOSE: This program finds the maximum number of a
# set of data items.
#
.section .data
hello_msg:
.asciz "Hello from GAS :)\\n"
current_data_item_msg:
.asciz "Current data item #%d: %d\\n"
greatest_data_item:
.asciz "Greatest data item: %d\\n"
exit_msg:
.asciz "See ya!!\\n"
debug_1:
.asciz "Args #: %d\\n"
debug_2:
.asciz "Mem for args: %d + 8 bytes\\n"
.section .text
.globl main
main:
leaq (%rsp), %rdx # need old stack frame to get args
pushq %rbp # Create a new stack frame
movq %rsp, %rbp # following C runtime rules
pushq %rsi
pushq %rdi
pushq %rbx
movq (%rdx), %rcx # Move argc to counter
pushq %rdx # START DEBUG: Print # args
pushq %rcx
pushq %rcx
pushq $debug_1
call printf
addq $16, %rsp
popq %rcx
popq %rdx # END DEBUG
cmpq $2, %rcx # if no arguments, goto no_args
jl no_args
decq %rcx
movq %rdx, %rsi
addq $8, %rsi # skip the program name, store start of args in rsi
movq %rcx, %rax # add enough memory to the stack frame
movq $8, %rbx
mulq %rbx # to hold all the args
movq %rax, %rbx
addq $8, %rbx # add extra 8 bytes for terminating data item (0)
pushq %rdx # START DEBUG: Print mem needed for args on stack
pushq %rbx
pushq %rcx
subq $8, %rbx
pushq %rbx
pushq $debug_2
call printf
addq $16, %rsp
popq %rcx
popq %rbx
popq %rdx # END DEBUG
movq %rsp, %rdi # Make room on stack for args
subq %rbx, %rsp # put starting address of args in %rdi
movq $0, %rax # Going to store args source index in %rax
#
# Go through argv[ i ] and put every argv on the stack
#
# Or if there are no argvs, then just use random numbers
#
# rcx: decrementing counter on number of args left
# rdi: start of stack space for storing args
# rsi: start of pointers to arg strings
# rax: current arg number
arg_loop_start:
cmpq $0, %rcx
je arg_quit
pushq %rcx
movq %rax, %r9
pushq %r9
pushq 0(%rsi,%rax,8)
call atoi
addq $8, %rsp
popq %r9
movq %rax, 0(%rdi,%r9,8) # put current arg in stack
movq %r9, %rax
popq %rcx
incq %rax
decq %rcx
no_args:
movq %rsp, %rdi
subq $40, %rsp # Make room for 5 random data items
pushq $0 # Seed random number gen
call time
addq $8, %rsp
pushq %rax
call srand
addq $8, %rsp
movq $5, %rcx # Use 5 random data items
movq $0, %rax
no_args_loop_start:
cmpq $0, %rcx
je arg_quit
movq %rax, %r9
pushq %r9
call rand
addq $1, %rax # Make sure its not 0
popq %r9
movq %rax, 0(%rdi, %r9, 8)
movq %r9, %rax
incq %rax
decq %rcx
arg_quit:
movq $0, 0(%rdi, %rax, 8) # Add the terminating zero
pushq $hello_msg
call printf
addq $8, %rsp
movq $0, %rcx # Put 0 in the data item index
movq (%rdi), %rbx # Put the first number in ebx as the largest item
start_data_loop:
movq 0(%rdi, %rcx, 4), %rdx
pushq %rcx
pushq %rdx
pushq %rdx
pushq %rcx
pushq $current_data_item_msg
call printf
addq $24, %rsp
popq %rdx
popq %rcx
cmpq $0, %rdx
je data_loop_quit
cmpq %rdx, %rbx
jle not_larger
movq %rdx, %rbx
not_larger:
incq %rcx
data_loop_quit:
pushq %rdx
pushq $greatest_data_item
call printf
addq $16, %rsp
pushq $exit_msg
call printf
sub $8, %rsp
popq %rbx # Destroy the stack frame and exit
popq %rdi
popq %rsi
movq %rbp, %rsp
popq %rbp
ret
Btw, I'm just starting to learn assembly so I'm guessing this code doesn't look to good :P
In fact, I'm pretty sure I used mulq wrong here:
movq %rcx, %rax # add enough memory to the stack frame
movq $8, %rbx
mulq %rbx # to hold all the args
movq %rax, %rbx
addq $8, %rbx # add extra 8 bytes for terminating data item (0)
Thanks,
mikfig
Have you tried "chmod +x ./maximum" before running the executable?
Disclaimer: I am not familiar with 64-bit code. However... I have gotten the exact same error message when linking directly with ld in 32-bit code. The "secret" is this: ld (32-bit) asks, by default, for "/lib/ld-linux.so.1", which does not exist on my system. This is the file that's not found. The workaround is to tell ld "-I /lib/ld-linux.so.2". Problem solved.
I suspect you're encountering a similar problem. I don't know what the "correct" name for the interpreter/dynamic linker is for 64-bit code. (maybe the same?) I would suggest using gcc to do your linking - it knows where to find stuff!
That may add a few bytes to your executable, compared to using ld directly, but "no big deal".
I can't speak to your use of "mulq", but it looks to me as if, after manipulating %rbx, you don't subtract if from %rsp, so I don't think it's doing what you want.
Best,
Frank
I suspect you're encountering a similar problem. I don't know what the "correct" name for the interpreter/dynamic linker is for 64-bit code. (maybe the same?) I would suggest using gcc to do your linking - it knows where to find stuff!
gcc -o maximum maximum.o
That may add a few bytes to your executable, compared to using ld directly, but "no big deal".
I can't speak to your use of "mulq", but it looks to me as if, after manipulating %rbx, you don't subtract if from %rsp, so I don't think it's doing what you want.
Best,
Frank
Disclaimer: I am not familiar with 64-bit code. However... I have gotten the exact same error message when linking directly with ld in 32-bit code. The "secret" is this: ld (32-bit) asks, by default, for "/lib/ld-linux.so.1", which does not exist on my system. This is the file that's not found. The workaround is to tell ld "-I /lib/ld-linux.so.2". Problem solved.
I suspect you're encountering a similar problem. I don't know what the "correct" name for the interpreter/dynamic linker is for 64-bit code. (maybe the same?) I would suggest using gcc to do your linking - it knows where to find stuff!
gcc -o maximum maximum.o
That may add a few bytes to your executable, compared to using ld directly, but "no big deal".
I can't speak to your use of "mulq", but it looks to me as if, after manipulating %rbx, you don't subtract if from %rsp, so I don't think it's doing what you want.
Best,
Frank
Ok, I linked it with gcc. Now I got a segmentation fault, but at least I can debug that. As for mulq, I did subtract %rbx from %rsp, I just didn't show it in that little code snippet.
Thanks,
mikfig
You may want to read "System V AMD64 ABI convention" because you seem to be invoking functions in a c-like fashion.
%rdi, %rsi, %rdx, %rcx, %r8, %r9, stack (+ RAX for vararg functions like printf).
+stack must be aligned on 16 bytes.
%rdi, %rsi, %rdx, %rcx, %r8, %r9, stack (+ RAX for vararg functions like printf).
+stack must be aligned on 16 bytes.
You may want to read "System V AMD64 ABI convention" because you seem to be invoking functions in a c-like fashion.
%rdi, %rsi, %rdx, %rcx, %r8, %r9, stack (+ RAX for vararg functions like printf).
+stack must be aligned on 16 bytes.
Ok thanks, will do.
Short piece on Linux 64-bit with sys_calls here, too...
http://callumscode.com/blog/3
Best,
Frank
http://callumscode.com/blog/3
Best,
Frank
Short piece on Linux 64-bit with sys_calls here, too...
http://callumscode.com/blog/3
Best,
Frank
Alright thanks guys, I've read through some of the AMD64 ABI and figured out most of my problems through that. That's also a cool article fbkotler. Thanks :). So I updated my code, got it running for the most part. It runs just fine if you pass no arguments or if you pass less than 8 arguments. If you pass 8 or more, it will run and get to the final ret instruction; but it will exit with a segmentation fault error.
I'm not sure what the cause is, because I debugged it with gdb and the stack is cleaned up. The registers that are supposed to be reset to the value they had before the function was called isn't being completely done right for some reason. But when it runs with less than 8 arguments that is still the case, just to a lesser extent. I.e. more of the registers supposed to be saved are set to their original value when less than 8 arguments are passed, but not all of them are. Also, when I break at the last instruction, ret, and reset the values to their originals there is still a segmentation fault. So I guess I'm going to have to look more into this.
Anyways, here's the better code:
# PURPOSE: This program finds the maximum number of a
# set of data items. The data items can be given as
# command line arguments or 5 random data items will be generated.
.section .data
hello_msg:
.asciz "Hello from GAS\n"
current_data_item_msg:
.asciz "Current data item #%lld: %lld\n"
greatest_data_item:
.asciz "Greatest data item: %lld\n"
exit_msg:
.asciz "See ya!!\n"
debug_2:
.asciz "Mem for args: %lld + 8 bytes\n"
.section .text
.globl main
main:
pushq %rbp # Create a new stack frame
movq %rsp, %rbp # following C runtime rules
pushq %rsi
pushq %rdi
pushq %rbx
pushq %r12
pushq %r13
pushq %r14
pushq %r15
decl %edi # program name not an "argument"
addq $8, %rsi
#
#
# INPUTS
#
# int argc : ebx
# char** argv : rsi
#
#
cmpl $1, %edi # if no arguments, goto no_args
jl no_args
# previous operation on %edi
# clears higher 32 bits
movq %rdi, %rbx
imulq $8, %rbx # add enough memory to the stack frame
addq $8, %rbx # to hold all the args
# add extra 8 bytes for terminating zero
pushq %rdi # START DEBUG: Print mem needed for args on stack
pushq %rbx
pushq %rsi
subq $8, %rbx
movq %rbx, %rsi
movq $debug_2, %rdi
movq $0, %rax
call printf
popq %rsi
popq %rbx
popq %rdi # END DEBUG
movq %rdi, %rcx
movq %rsp, %rdi # Make room on stack for args
subq %rbx, %rsp # put starting address of args in %rdi
movq $0, %rax # Going to store args source index in %rax
#
# Go through argv[ i ] and put every atoll(*argv) on the stack
#
# Or if there are no argvs, then just use random numbers
#
# rcx: decrementing counter on number of args left
# rdi: start of stack space for storing args
# rsi: start of pointers to arg strings
# rax: current arg number
arg_loop_start:
cmpq $0, %rcx
je arg_quit
pushq %rcx
movq %rax, %r9
pushq %r9
pushq %rdi
pushq %rsi
movq (%rsi,%rax,8), %rdi
call atoll # 64-bit version of atoi
pop %rsi
pop %rdi
pop %r9
movq %rax, (%rdi,%r9,8) # put current arg in stack
movq %r9, %rax
popq %rcx
incq %rax
decq %rcx
jmp arg_loop_start
no_args:
movl $0, %edi # Seed random number gen
call time
movl %eax, %edi
call srand
movq %rsp, %rdi
subq $40, %rsp # Make room for 5 random data items
movq $5, %rcx # Use 5 random data items
movq $0, %rax
# rcx: decrementing counter on number of args left
# rdi: start of stack space for storing args
# rax: current arg number
no_args_loop_start:
cmpq $0, %rcx
je arg_quit
movq %rax, %r9
pushq %rdi
pushq %rcx
pushq %r9
call rand
addl $1, %eax # Make sure its not 0
popq %r9
popq %rcx
popq %rdi
movq %rax, (%rdi, %r9, 8) # rand putting num in %eax should zero out top 32 bits
movq %r9, %rax
incq %rax
decq %rcx
jmp no_args_loop_start
arg_quit:
movq $0, (%rdi, %rax, 8) # Add the terminating zero
pushq %rdi
movq $hello_msg, %rdi
movq $0, %rax
call printf
popq %rdi
movq $0, %rcx # Put 0 in the data item index
movq (%rdi), %rbx # Put the first number in rbx as the largest item
start_data_loop:
movq (%rdi, %rcx, 8), %rdx
pushq %rbx
pushq %rcx
pushq %rdi
pushq %rdx
# 3rd argument already in rdx
movq %rcx, %rsi
movq $current_data_item_msg, %rdi
movq $0, %rax
call printf
popq %rdx
popq %rdi
popq %rcx
popq %rbx
cmpq $0, %rdx
je data_loop_quit
cmpq %rdx, %rbx
jge not_larger
movq %rdx, %rbx
not_larger:
incq %rcx
jmp start_data_loop
data_loop_quit:
incq %rcx # need to pop off terminating zero too
imulq $8, %rcx
addq %rcx, %rsp # clean up used stack space
movq %rbx, %rsi
movq $greatest_data_item, %rdi
movq $0, %rax
call printf
movq $exit_msg, %rdi
movq $0, %rax
call printf
popq %r15 # Destroy the stack frame and exit
popq %r14
popq %r13
popq %r12
popq %rbx # AMD64 ABI, have to preserve %rbp, %rbx, %r12-%r15
popq %rdi # Intel386 ABI, have to preserve %rbp, %rbx, %rdi, %rsi
popq %rsi
movq %rbp, %rsp
popq %rbp
ret
Oh, and I'm thinking that maybe the fact that I neglected to align my stack on 16 bytes may be causing the segfault but I'm not sure.
It is 8 byte aligned at the least. I guess I'll have to think about it. But at least I got it working in most cases.
And thanks a lot for the help guys :)
An easy way to keep stack aligned on 16 bytes is this:
function/label "main" - you can depend on the fact that after entry stack is aligned on 8 bytes ("call" instruction makes the stack unaligned), hence you need ODD number of pushes(or subtract 8*ODD) after function entry
Next, always have EVEN number of pushes(or subtract 8*EVEN) in the code
function/label "main" - you can depend on the fact that after entry stack is aligned on 8 bytes ("call" instruction makes the stack unaligned), hence you need ODD number of pushes(or subtract 8*ODD) after function entry
main:
push rbp
;-- stack aligned
or
main:
sub rsp,3*8
;-- stack aligned
Next, always have EVEN number of pushes(or subtract 8*EVEN) in the code
push rax
push rdx
call somefunc
pop rdx
pop rax
or
sub rsp,4*8
call somefunc
add rsp,4*8
Stack alignment in x64 is critical. As drizz correctly points out the stack must be aligned to 16 bytes. One of the easiest things to do is to push rbp upon function entry which will realign the stack due to the call. Follow with a mov rbp, rsp and you can easily use RBP as the base pointer to the argument offsets ( ie: mov rax, ). There are also optimizations you can make using the x64 ABI but we'll save that topic for another day.
Stack alignment in x64 is critical. As drizz correctly points out the stack must be aligned to 16 bytes. One of the easiest things to do is to push rbp upon function entry which will realign the stack due to the call. Follow with a mov rbp, rsp and you can easily use RBP as the base pointer to the argument offsets ( ie: mov rax, ). There are also optimizations you can make using the x64 ABI but we'll save that topic for another day.
An easy way to keep stack aligned on 16 bytes is this:
function/label "main" - you can depend on the fact that after entry stack is aligned on 8 bytes ("call" instruction makes the stack unaligned), hence you need ODD number of pushes(or subtract 8*ODD) after function entry
main:
push rbp
;-- stack aligned
or
main:
sub rsp,3*8
;-- stack aligned
Next, always have EVEN number of pushes(or subtract 8*EVEN) in the code
push rax
push rdx
call somefunc
pop rdx
pop rax
or
sub rsp,4*8
call somefunc
add rsp,4*8
Ok, I'll go ahead and align my stack. I'm curious though, why is it that the stack needs to be 16 byte aligned? What kind of problems are caused if its not, and why?
Thanks,
mikfig
Ok I cleaned it up, make sure push/pops are even and made sure that the stack space I allocated is divisible by 16.
Now it doesn't get a segmentation fault until you use 9 or more arguments. But ya, the segfault is still there.
Fixed code:
Now it doesn't get a segmentation fault until you use 9 or more arguments. But ya, the segfault is still there.
Fixed code:
# PURPOSE: This program finds the maximum number of a
# set of data items.
#
.section .data
hello_msg:
.asciz "Hello from GAS\n"
current_data_item_msg:
.asciz "Current data item #%lld: %lld\n"
greatest_data_item:
.asciz "Greatest data item: %lld\n"
exit_msg:
.asciz "See ya!!\n"
debug_2:
.asciz "Mem for args: %lld bytes\n"
.section .text
.globl main
main:
pushq %rbp # Create a new stack frame
movq %rsp, %rbp # following C runtime rules
pushq %rsi
pushq %rdi
pushq %rbx
pushq %r9
pushq %r12
pushq %r13
pushq %r14
pushq %r15
addq $8, %rsi # program name not an "argument"
#
#
# INPUTS
#
# int argc : ebx
# char** argv : rsi
#
#
# VARIABLES
#
# stack space for arguments gone through atoll(): rsi
#
cmpl $2, %edi # if no arguments, goto no_args
jl no_args
# previous operation on %edi
# clears higher 32 bits
movq %rdi, %rbx # add enough memory to the stack frame
imulq $8, %rbx # to hold all the args and the terminating zero, i.e. argc
movq %rbx, %rax # make sure stack reservation is 16-byte aligned
cqto
movq $16, %rcx
idivq %rcx # %rbx = argc * 8
addq %rdx, %rbx # %rbx = %rbx + (%rbx % 16)
decq %rdi
pushq %rdi # START DEBUG: Print mem needed for args on stack
pushq %rbx
pushq %rsi
pushq %rax
movq %rbx, %rsi
movq $debug_2, %rdi
movq $0, %rax
call printf
popq %rax
popq %rsi
popq %rbx
popq %rdi # END DEBUG
movq %rdi, %rcx
movq %rsp, %rdi # Make room on stack for args
subq %rbx, %rsp # put starting address of args in %rdi
movq $0, %rax # Going to store args source index in %rax
#
# Go through argv[ i ] and put every argv on the stack
#
# Or if there are no argvs, then just use random numbers
#
# rcx: decrementing counter on number of args left
# rdi: start of stack space for storing args
# rsi: start of pointers to arg strings
# rax: current arg number
arg_loop_start:
cmpq $0, %rcx
je arg_quit
movq %rax, %r9
pushq %rcx
pushq %r9
pushq %rdi
pushq %rsi
pushq %rbx
pushq $0 # have to push a dummy value to keep stack 16-byte aligned
movq (%rsi,%rax,8), %rdi
call atoll # 64-bit version of atoi
popq %rbx
popq %rbx
popq %rsi
popq %rdi
popq %r9
popq %rcx
movq %rax, (%rdi,%r9,8) # put current arg in stack
movq %r9, %rax
incq %rax
decq %rcx
jmp arg_loop_start
no_args:
movl $0, %edi # Seed random number gen
call time
movl %eax, %edi
call srand
movq %rsp, %rdi
subq $48, %rsp # Make room for 5 random data items + terminating 0
movq $48, %rbx
movq $5, %rcx # Use 5 random data items
movq $0, %rax
no_args_loop_start:
cmpq $0, %rcx
je arg_quit
movq %rax, %r9
pushq %rdi
pushq %rcx
pushq %r9
pushq %rbx
call rand
addl $1, %eax # Make sure its not 0
popq %rbx
popq %r9
popq %rcx
popq %rdi
movq %rax, (%rdi, %r9, 8) # rand putting num in %eax should zero out top 32 bits
movq %r9, %rax
incq %rax
decq %rcx
jmp no_args_loop_start
arg_quit:
movq $0, (%rdi, %rax, 8) # Add the terminating zero
pushq %rdi
pushq %rbx
movq $hello_msg, %rdi
movq $0, %rax
call printf
popq %rbx
popq %rdi
movq %rbx, %rsi
movq $0, %rcx # Put 0 in the data item index
movq (%rdi), %rbx # Put the first number in rbx as the largest item
start_data_loop:
movq (%rdi, %rcx, 8), %rdx
pushq %rbx
pushq %rcx
pushq %rdi
pushq %rdx
pushq %rsi
pushq $0 # 16-byte alignment, pushing junk
# 3rd argument already in rdx
movq %rcx, %rsi
movq $current_data_item_msg, %rdi
movq $0, %rax
call printf
popq %rsi
popq %rsi
popq %rdx
popq %rdi
popq %rcx
popq %rbx
cmpq $0, %rdx
je data_loop_quit
cmpq %rdx, %rbx
jge not_larger
movq %rdx, %rbx
not_larger:
incq %rcx
jmp start_data_loop
data_loop_quit:
addq %rsi, %rsp # clean up used stack space
movq %rbx, %rsi
movq $greatest_data_item, %rdi
movq $0, %rax
call printf
movq $exit_msg, %rdi
movq $0, %rax
call printf
popq %r15 # Destroy the stack frame and exit
popq %r14
popq %r13
popq %r12
popq %rbx # AMD64 ABI, have to preserve %rbp, %rbx, %r12-%r15
popq %r9
popq %rdi # Intel386 ABI, have to preserve %rbp, %rbx, %rdi, %rsi
popq %rsi
movq %rbp, %rsp
popq %rbp
ret
movq %rsp, %rdi # Make room on stack for args
subq %rbx, %rsp # put starting address of args in %rdi
Ok, I'll go ahead and align my stack. I'm curious though, why is it that the stack needs to be 16 byte aligned? What kind of problems are caused if its not, and why?
It's the standard ;) ( Win64 ABI requires it also ). IMO it is because floating point arguments are passed in xmm registers and the most efficient store/load instruction requires 16-byte memory alignment. And since SSE2 is implied in x64 and there are other sse2 instructions that require 16byte alignment it reduces the overhead of aligning the stack manually.
movq %rsp, %rdi # Make room on stack for args
subq %rbx, %rsp # put starting address of args in %rdi
Haha ya, I was looking over my code again and found the same mistake :P.
It's because later on when I put my data on the stack I was going towards increasing memory addresses.
I was thinking that movq %rax, (%rdi, %r9, 8 ) was subtracting %r9*8 from %rdi for some reason,
wanting to go from the first "allocated address" to the very top of the stack instead of from the top of the stack to the bottom.
So, silly mistake, and that was what was causing this whole segmentation fault.
Overwriting the return address and shooting everything to hell.
Anyways I fixed all my issues and now it works perfectly. I noted the stack before the first instruction in main() is executed
and before the least ret instruction. I did a diff on 800 bytes on the stack and I kept it squeaky clean :).
And all my stack allocations are 16-byte aligned :).
So as far as I can see I guess I obeyed the ABI, but I have to finish reading through that.
So here's the fixed code :):
# PURPOSE: This program finds the maximum number of a
# set of data items.
#
.section .data
hello_msg:
.asciz "Hello from GAS\n"
current_data_item_msg:
.asciz "Current data item #%lld: %lld\n"
greatest_data_item:
.asciz "Greatest data item: %lld\n"
exit_msg:
.asciz "See ya!!\n"
debug_2:
.asciz "Mem for args: %lld bytes\n"
.section .text
.globl main
main:
pushq %rbp # Create a new stack frame
movq %rsp, %rbp # following C runtime rules
pushq %rsi
pushq %rdi
pushq %rbx
pushq %r9
pushq %r12
pushq %r13
pushq %r14
pushq %r15
addq $8, %rsi # program name not an "argument"
#
#
# INPUTS
#
# int argc : edi
# char** argv : rsi
#
#
# VARIABLES
#
# stack space for arguments gone through atoll(): rsi
#
cmpl $2, %edi # if no arguments, goto no_args
jl no_args
# previous operation on %edi
# clears higher 32 bits
movq %rdi, %rbx # add enough memory to the stack frame
imulq $8, %rbx # to hold all the args and the terminating zero, i.e. argc
movq %rbx, %rax # make sure stack reservation is 16-byte aligned
cqto
movq $16, %rcx
idivq %rcx # %rbx = argc * 8
addq %rdx, %rbx # %rbx = %rbx + (%rbx % 16)
decq %rdi
pushq %rdi # START DEBUG: Print mem needed for args on stack
pushq %rbx
pushq %rsi
pushq %rax
movq %rbx, %rsi
movq $debug_2, %rdi
movq $0, %rax
call printf
popq %rax
popq %rsi
popq %rbx
popq %rdi # END DEBUG
movq %rdi, %rcx # Make room on stack for args
subq %rbx, %rsp # put address of first arg in %rdi
movq %rsp, %rdi
movq $0, %rax # Going to store args source index in %rax
#
# Go through argv[ i ] and put every argv on the stack
#
# Or if there are no argvs, then just use random numbers
#
# rcx: decrementing counter on number of args left
# rdi: start of stack space for storing args
# rsi: start of pointers to arg strings
# rax: current arg number
arg_loop_start:
cmpq $0, %rcx
je arg_quit
movq %rax, %r9
pushq %rcx
pushq %r9
pushq %rdi
pushq %rsi
pushq %rbx
pushq $0 # have to push a dummy value to keep stack 16-byte aligned
movq (%rsi,%rax,8), %rdi
call atoll # 64-bit version of atoi
popq %rbx
popq %rbx
popq %rsi
popq %rdi
popq %r9
popq %rcx
movq %rax, (%rdi,%r9,8) # put current arg in stack
movq %r9, %rax
incq %rax
decq %rcx
jmp arg_loop_start
no_args:
movl $0, %edi # Seed random number gen
call time
movl %eax, %edi
call srand
call rand # Generate a random number of data items
cqto # from 1 to 50
movq $50, %rcx
idivq %rcx
addq $1, %rdx
movq %rdx, %r9
addq $1, %rdx # Need room for terminating zero
imulq $8, %rdx # Make sure the stack allocation is 16-byte aligned
movq %rdx, %rbx
movq %rdx, %rax
cqto
movq $16, %rcx
idivq %rcx
addq %rdx, %rbx
subq %rbx, %rsp # Make room for random data items + terminating 0
movq %rsp, %rdi
movq %r9, %rcx # %rcx: counter, %rax: index
movq $0, %rax
no_args_loop_start:
cmpq $0, %rcx
je arg_quit
movq %rax, %r9
pushq %rdi
pushq %rcx
pushq %r9
pushq %rbx
call rand
addl $1, %eax # Make sure its not 0
popq %rbx
popq %r9
popq %rcx
popq %rdi
movq %rax, (%rdi, %r9, 8) # rand putting num in %eax should zero out top 32 bits
movq %r9, %rax
incq %rax
decq %rcx
jmp no_args_loop_start
arg_quit:
movq $0, (%rdi, %rax, 8) # Add the terminating zero
pushq %rdi
pushq %rbx
movq $hello_msg, %rdi
movq $0, %rax
call printf
popq %rbx
popq %rdi
movq %rbx, %rsi
movq $0, %rcx # Put 0 in the data item index
movq (%rdi), %rbx # Put the first number in rbx as the largest item
start_data_loop:
movq (%rdi, %rcx, 8), %rdx
pushq %rbx
pushq %rcx
pushq %rdi
pushq %rdx
pushq %rsi
pushq $0 # 16-byte alignment, pushing junk
# 3rd argument already in rdx
movq %rcx, %rsi
movq $current_data_item_msg, %rdi
movq $0, %rax
call printf
popq %rsi
popq %rsi
popq %rdx
popq %rdi
popq %rcx
popq %rbx
cmpq $0, %rdx
je data_loop_quit
cmpq %rdx, %rbx
jge not_larger
movq %rdx, %rbx
not_larger:
incq %rcx
jmp start_data_loop
data_loop_quit:
addq %rsi, %rsp # clean up used stack space
movq %rbx, %rsi
movq $greatest_data_item, %rdi
movq $0, %rax
call printf
movq $exit_msg, %rdi
movq $0, %rax
call printf
popq %r15 # Destroy the stack frame and exit
popq %r14
popq %r13
popq %r12
popq %r9 # AMD64 ABI, have to preserve %rbp, %rbx, %r12-%r15
popq %rbx
popq %rdi # Intel386 ABI, have to preserve %rbp, %rbx, %rdi, %rsi
popq %rsi
movq %rbp, %rsp
popq %rbp
ret