Using Scanf into Global or Local Variables (On the Stack), 32-Bit Calling Convention

Using scanf into global or local variables (on the stack), 32-bit calling convention

I'm not really sure what sort of assembler you're using, however I could get your code to compile with gcc so I stuck with your formatting style (not talking about the AT&T syntax).

Anyway, you should check the documentation for scanf and realise that it takes a format string and pointers to locations in memory of where to store the values read in. It also returns the number of successfully read items and not what was read.

Now do the same and check the documention for printf. You'll see that a format string is required to print your number in a readable form. A suitable format string is "%d\n" to print the number and a newline.

Your code could now look something like this (which compiles and works fine for me with gcc):

.section .rodata

input_format: .string "%d"
output_format: .string "%d\n"

.section .bss
input: .long 0 # reserve 4 bytes of space

.section .text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp

pushl $input # push the ADDRESS of input to have the value stored in it
pushl $input_format # give scanf the ADDRESS of the format string
call scanf # call scanf to get number from the user
addl $8, %esp # clean up the stack

# Note the return value of scanf is passed through eax (same for printf)

pushl input # pass the number to printf BY VALUE
pushl $output_format # pass the ADDRESSS of the output format string to printf
call printf # print input

#return 0 from main:
movl $0, %eax
movl %ebp,%esp
popl %ebp
ret

Note that I would normally use db/dw/dd for allocating memory in the .(ro)data and .bss sections as opposed to .string and .long, so if that part's done slightly wrong you could just fix it up.

You could also use stack space for storing the number, however you already had input declared and I wanted to leave the code as similar to what you had as possible. Same goes for everything else before and after the scanf and printf stuff, I just left that as your code.

EDIT: Here's an example of using the stack to create a local variable, as opposed to having a variable declared in the .bss or .data segment:

.section .rodata

input_format: .string "%d"
output_format: .string "%d\n"

.section .text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp

subl $4, %esp # allocate 4 bytes on the stack for a local variable

# The local variable will be at -4(%ebp)

leal -4(%ebp), %eax # get the ADDRESS of our local variable
pushl %eax # push the ADDRESS of the variable on the stack
pushl $input_format # give scanf the ADDRESS of the format string
call scanf # call scanf to get number from the user
addl $8, %esp # clean up the stack

# Note the return value of scanf is passed through eax (same for printf)

pushl -4(%ebp) # pass the number to printf BY VALUE
pushl $output_format # pass the ADDRESSS of the output format string to printf
call printf # print the input

#return from printf:
movl $0, %eax
movl %ebp,%esp
popl %ebp
ret

How to use scanf in NASM?

I found this 'Programming in NASM.PDF'

; add1.asm
SECTION .data
message1: db "Enter the first number: ", 0
message2: db "Enter the second number: ", 0
formatin: db "%d", 0
formatout: db "%d", 10, 0 ; newline, nul terminator
integer1: times 4 db 0 ; 32-bits integer = 4 bytes
integer2: times 4 db 0 ;
SECTION .text
global _main
extern _scanf
extern _printf

_main:

push ebx ; save registers
push ecx
push message1
call printf

add esp, 4 ; remove parameters
push integer1 ; address of integer1 (second parameter)
push formatin ; arguments are right to left (first parameter)
call scanf

add esp, 8 ; remove parameters
push message2
call printf

add esp, 4 ; remove parameters
push integer2 ; address of integer2
push formatin ; arguments are right to left
call scanf

add esp, 8 ; remove parameters

mov ebx, dword [integer1]
mov ecx, dword [integer2]
add ebx, ecx ; add the values ; the addition
push ebx
push formatout
call printf ; call printf to display the sum
add esp, 8 ; remove parameters
pop ecx
pop ebx ; restore registers in reverse order
mov eax, 0 ; no error
ret

Which is the asm version of this C function:

#include <stdio.h>
int main(int argc, char *argv[])
{
int integer1, integer2;
printf("Enter the first number: ");
scanf("%d", &integer1);
printf("Enter the second number: ");
scanf("%d", &integer2);
printf("%d\n", integer1+integer2);
return 0;
}

Scanf and printf functions in assembly, example on char* and double

32-bit code can't push a 64-bit double from memory with one instruction. 64-bit code doesn't pass args on the stack, so I assume this is intended to be 32-bit code. (Also from using int 0x80.)

push double in 32-bit code is pushl that pushes the low 4 bytes. printf was taking the high 4 bytes (containing the exponent and most-significant bits of the mantissa) from whatever was above it on the stack. In that case, assuming scanf didn't clobber its stack args, the high 4 bytes of your double bit-pattern came from the address $format_indouble.

You probably want pushl double+4 ; pushl double to push the 8-byte double in 2 halves.

(The operand-size suffix is optional here, but I'd recommend it to avoid the ambiguity that got you.)

Or you could use SSE2 movsd to copy 8 bytes to the stack.

    sub    $8, %esp
movsd double, %xmm0
movsd %xmm0, (%esp)

(push is special and can copy from memory to memory. Instructions with explicit source and destination can't do that.)

Of course you don't need static storage space for your inputs; you could pass pointers to stack memory to scanf. Then your double would be on the stack already, and you could just store a pointer to a format string below it.


BTW, you don't have the stack aligned by 16 when you call scanf and printf. The ABI does require this, so you got lucky that your build of glibc happens not to use movaps on stack memory in these functions, which would segfault.

Before main is called, the stack is aligned by 16. That call pushes a return address. So 3 more pushes re-aligns that stack. One dummy push on function entry will set you up for functions with 8 bytes of args.

Also normally you'd add $8, %esp after a call returns to clear the args from the stack.


Variable names: double is a type name (and keyword) in C. That makes it a weird name for an assembly program that calls C functions. Something like dbl might look better.

Using scanf with x86-64 GAS assembly

As you feared, movq %rcx, %rsi is not correct. You need to pass a pointer to memory. Registers are not part of the memory address space and thus you can't have pointers to them. You need to allocate storage either globally or locally. Incidentally, you should not put your data (especially writable) into the default .text section, as that is intended for code and is typically read-only. Also, calling convention usually mandates 16 byte stack pointer alignment, so you should take care of that too.

.globl main

main:
push %rbp # keep stack aligned
mov $0, %eax # clear AL (zero FP args in XMM registers)
leaq f(%rip), %rdi # load format string
leaq x(%rip), %rsi # set storage to address of x
call scanf
pop %rbp
ret

.data

f: .string "%d" # could be in .rodata instead
x: .long 0

(If your environment expects a leading underscore on symbols, then use _main, and probably _scanf.)


There are actually 3 choices for putting addresses of symbols / labels into registers. RIP-relative LEA is the standard way on x86-64. How to load address of function or label into register in GNU Assembler

As an optimization if your variables are in the lower 4GiB of the address space, e.g. in a Linux non-PIE (position-dependent) executable, you can use 32-bit absolute immediates:

    mov  $f, %edi       # load format string
mov $x, %esi # set storage to address of x

movq $f, %rdi would use a 32-bit sign-extended immediate (instead of implicit zero-extension into RDI from writing EDI), but has the same code-size as a RIP-relative LEA.

You can also load the full 64 bit absolute address using the mnemonic movabsq. But don't do that because a 10-byte instruction is bad for code-size, and still needs a runtime fixup because it's not position-independent.

    movabsq $f, %rdi # load format string
movabsq $x, %rsi # set storage to address of x

Upon request: using a local variable for the output could look like:

    subq  $8, %rsp       # allocate 8 bytes from stack
xor %eax, %eax # clear AL (and RAX)
leaq f(%rip), %rdi # load format string
movq %rsp, %rsi # set storage to local variable
call scanf
addq $8, %rsp # restore stack
ret

Scanf ARM Assembly

This is what ended up working for me:

sub     sp, sp, #4

@ Call scanf and store value in r4
ldr r0, addrInp
mov r1, sp
bl scanf
ldr r4, [sp]

add sp, sp, #4

The value the user inputs ends up in r4 in this case. I don't understand why Jesters way didn't work but it just gave a segfault every time.

An observation about variable memory addresses in C

x, y, and z, are integer variables that will be created on the call stack (but see below). The sizeof int is 4 bytes, so that is how much space a compiler will allocate on the stack for those variables. These variables are adjacent to one another, so they are 4 bytes apart.

You can read about how memory is allocated for local variables by looking for information on calling conventions.

In some cases (where you do not use the address-of operator), the compiler may optimize local variables into registers.

NASM x86_64 scanf segmentation fault

The x86-64 calling convention doesn't push the arguments generally. Additionally you have to tell a function with variable count of arguments, how many floating point arguments you provide.

This works:

global main
extern printf, scanf

section .data
msg: db "Enter a number: ",10,0
format:db "%d",0

section .bss
number resb 4

section .text
main:
sub rsp, 8 ; align the stack to a 16B boundary before function calls

mov rdi, msg
mov al, 0
call printf

mov rsi, number
mov rdi, format
mov al, 0
call scanf

add rsp, 8 ; restore the stack
ret

BTW: If you want to work with floating point numbers, you have to align the stack to a 16 byte boundary before calling the function.

How to use data stored in register when calling scanf in nasm assembly

You are using the wrong convention. Obviously you know what you should do, since you had no problem calling printf. You should use the same convention for calling scanf too - the stack argument passing that you used is the 32 bit convention, 64 bit uses registers. Something like this:

lea rdi, [LC5]    ; 1st arg = format
lea rsi, [choice] ; 2nd arg = address of buffer
xor eax, eax ; no xmm registers
call scanf ; stores input in choice

By the way, using an unconstrained %s with 2 bytes of space is a bad idea.

Also, do what Frank said, ie. load a byte (mov bl, [choice]) when you want to process the input.



Related Topics



Leave a reply



Submit