using printf before and inside a loop x86-64 assem

2020-04-29 16:18发布

I'm having trouble figuring out how to use printf correctly in this function. So the function is called multInts and is supposed to multiply the first element of the first array with the first element of the second array and continue through the whole array. However, the lab instructions specify that I can't call printf in the main function. So, I need to print out the word "Products:\n" and then in each new line after that, print out the product. I don't know how to use printf within the loop, however. The instructor said that we should "call printf in the loop after calculating product" and also to "save and restore caller-save registers before calling printf," but I'm not sure what that means. Here's what my code looks like so far:

.file   "lab4.s"
.section .rodata

.LC0:
    .string "Products: \n"
.LC1: 
    .string "%i \n"
.data
sizeIntArrays:
    .long 5
sizeShortArrays:
    .word 4
intArray1:
    .long 10
    .long 25
    .long 33
    .long 48
    .long 52
intArray2:
    .long 20
    .long -37
    .long 42
    .long -61
    .long -10

##### MAIN FUNCTION
.text
.globl main
.type main,@function

main:
pushq %rbp
movq %rsp, %rbp

#pass parameters and call other functions
movl sizeIntArrays, %edi    #move size to registers for 1st parameter
leaq intArray1, %rsi        #load effective address of intArray1 to register rsi
leaq intArray2, %rdx        #load effective address of intArray2 to register rdx
call multInts           #call multInts function

movq $0, %rax           #return 0 to caller

movq %rbp, %rsp
popq %rbp
ret
.size main,.-main


##### MULTINTS
.globl multInts
.type multInts,@function

multInts:
pushq %rbp
movq %rsp, %rbp

#add code here for what the functions should do
movq $0, %r8            #initialize index for array access in caller save reg
movq $0, %rcx           #initialize 8 byte caller save result reg

loop0:
cmpl %r8d, %edi         #compare index to size
je exit0            #exit if equal
movslq (%rsi,%r8,4),%rax    # Load a long into RAX
movslq (%rdx,%r8,4),%r11    # Load a long into R11
imulq %r11, %rax            # RAX *= R11
addq %rax, %rcx             # RCX += RAX
incq %r8            #increment index
jmp loop0

exit0:
movq $.LC0, %rdi
movq %rcx, %rsi
movq $0, %rax

call printf

movq %rbp, %rsp
popq %rbp
ret
.size multInts,.-multInts

What I've tried to do is just move the printf instruction to before the loop, but it gives me a segmentation fault when trying to run the loop because %rdi and %rsi contain the addresses of the arrays that need to be used in the loop. How do I get around that and which registers should I use? Also, how do I call printf within the loop?

The output should look something like this:

Products:
200
-925
1386
-2928
-520

2条回答
Viruses.
2楼-- · 2020-04-29 16:44

The easiest way to protect a register from being accessed by a subroutine is to push it. According to the ABI V calling convention printf may change any register except RBX, RBP, R12–R15. The registers you need to preserve are RAX, RDX, RSI, RDI, R8 and R11 (RCX is no longer needed), so push before the call to printf and pop them afterwards:

pushq %rax
pushq %rdx
pushq %rsi
pushq %rdi
pushq %r8
pushq %r11
movq $.LC1, %rdi
movq %rax, %rsi
movq $0, %rax
call printf
popq %r11
popq %r8
popq %rdi
popq %rsi
popq %rdx
popq %rax

Now, you can copy the block into the loop. For each printf, you have to think about what needs to be secured:

...
multInts:
pushq %rbp
movq %rsp, %rbp

#add code here for what the functions should do

pushq %rdx                  # Preserve registers
pushq %rdi
pushq %rsi
movq $.LC0, %rdi            # Format string (no further values)
movq $0, %rax               # No vector registers used
call printf                 # Call C function
popq %rsi                   # Restore registers
popq %rdi
popq %rdx

movq $0, %r8                #initialize index for array access in caller save reg

loop0:
cmpl %r8d, %edi             #compare index to size
je exit0                    #exit if equal

movslq (%rsi,%r8,4),%rax    # Load a long into RAX
movslq (%rdx,%r8,4),%r11    # Load a long into R11
imulq %r11, %rax            # RAX *= R11

pushq %rax                  # Preserve registers
pushq %rdx
pushq %rsi
pushq %rdi
pushq %r8
pushq %r11
movq $.LC1, %rdi            # Format string
movq %rax, %rsi             # Value
movq $0, %rax               # No vector registers used
call printf                 # Call C function
popq %r11                   # Restore registers
popq %r8
popq %rdi
popq %rsi
popq %rdx
popq %rax

incq %r8                    #increment index
jmp loop0

exit0:

movq %rbp, %rsp
popq %rbp
ret
...

BTW: .string "%i \n" will force printf only to process the lower 32-bit of RDI. Use .string %lli \n instead.

查看更多
Bombasti
3楼-- · 2020-04-29 16:50

Assume that printf clobbers all the call-clobbered registers (What registers are preserved through a linux x86-64 function call), and use different ones for anything that needs to survive from one iteration of the loop to the next.

Look at compiler output for an example: write a version of your loop in C and compile it with -Og.

Obviously you need to move the instructions that set up the args in registers (like the format string) along with the call printf.

查看更多
登录 后发表回答