extern printf
extern scanf
global main
section .text
main:
sub rsp, 0x10
mov rbx, rsp
add rbx, 0x08
mov rdi, format1
mov rsi, rbx
xor rax, rax
call scanf
mov rdi, format2
mov rsi, [rbx]
xor rax, rax
call printf
add rsp, 0x10
ret
format1:
db "%d", 0
format2:
db "%d", 0xa, 0
value:
dd 0xa
Above source is same with
#include <stdio.h>
int main(void)
{
int tmp;
scanf("%d", &tmp);
printf("%d\n", tmp);
}
It works well. But I have question. If I change my source code to
extern printf
extern scanf
global main
section .text
main:
mov rdi, format1
mov rsi, value
xor rax, rax
call scanf
mov rdi, format2
mov rsi, [value]
xor rax, rax
call printf
ret
format1:
db "%d", 0
format2:
db "%d", 0xa, 0
value:
dd 0xa
it makes segmentation fault. I think there are no difference between above source code and first one. Did I misunderstand?
In the first code, you're allocating space for a variable (the tmp in your C code, unnamed in the asm code) on the stack and passing the address of it to the scanf function, then passing the value that scanf wrote there to printf.
In the second, you're trying to use a global value allocated in the .text section instead, but .text is read-only by default on most systems. So when scanf tries to write to it, you get a segfault.
Stick a section .data just before value: to put it in the data section instead and it should be fine...
Related
I have the following disassembly of a main function in which a user input is stored using scanf function (at address 0x0000089c). Due to the comparison that is made, I suppose that the user input is stored into the rsp register but I cannot figure out why, as rsp doesn't seem to be pushed on the stack (at least, not near the call to the scanf function).
Here is the disassembly:
0x00000850 sub rsp, 0x18
0x00000854 mov rax, qword fs:[0x28]
0x0000085d mov qword [canary], rax
0x00000862 xor eax, eax
0x00000864 call fcn.00000a3c
0x00000869 lea rsi, str.Insert_input:
0x00000870 mov edi, 1
0x00000875 xor eax, eax
0x00000877 mov dword [rsp], 0
0x0000087e mov dword [var_4h], 0
0x00000886 call sym.imp.__printf_chk
0x0000088b lea rdx, [var_4h]
0x00000890 lea rdi, str.u__u ; "%u %u" ;const char *format
0x00000897 xor eax, eax
0x00000899 mov rsi, rsp
0x0000089c call sym.imp.__isoc99_scanf ; int scanf(const char *format)
0x000008a1 mov eax, dword [rsp]
0x000008a4 cmp eax, 0x1336
0x000008a9 jg 0x867
On x86_64, parameters are passed in registers, so your call to scanf has 3 parameters stored in 3 registers:
rdi pointer to the string "%u %u", the format to parse (two unsigned integers)
rsi should be a unsigned *, pointer to where to put the first parsed integer
rdx pointer to where to put the second parsed integer.
If you look just before the call, rsi is set to rsp (the stack pointer) while rdx is set to point at the global variable var_4h (an extern symbol not defined here).
The stack is used to hold local variables, and in this case rsp points at a block 0x18 "free" bytes (allocated in the first instruction in your block), which is enough space for 6 integers. The one at offset 0 from rsp is what rsi points to, and it is the value read by the mov instruction immediately after the call.
the following code tries to read the command line arguments and then scans them with sscanf() and use the result to emit utf8 text.
I'm failing to call sscanf() and getting segfault error at the line where I call this function
I have already debugged this and I know where is the problem but not how to solve it.
global main
extern puts
extern sscanf
extern printf
extern emit_utf_8
section .text
main:
cmp rdi, 2
jl argumentsError
add rsi, 8 ; skip the name of the program
forloop:
mov r12, rsi
push rdi
push rsi
push r12
sub rsp, 8 ; must align stack before call
; start of for bloc
xor rax, rax
mov rdi, qword [r12]
mov rsi, codePointFormat
mov rdx, qword [codePoint]
call sscanf
cmp rax, 1
je ifthen
jmp else
ifthen:
mov rdi, codePoint
call emit_utf_8
jmp endif
else:
mov rdi, incorrectFormat
mov rsi, r12
call printf
endif:
; end of for bloc
add rsp, 8 ; restore %rsp to pre-aligned value
pop r12
pop rsi
pop rdi
add rsi, 8 ; point to next argument
dec rdi ; count down
jnz forloop ; if not done counting keep going
ret
argumentsError: mov rdi, argumentsRequiredMessage
call puts
mov rdi, argumentDescription
call puts
xor rax, rax
inc rax
ret
section .data
argumentsRequiredMessage:
db "This program requires one or more command line arguments,", 0
argumentDescription: db "one for each code point to encode as UTF-8.", 0
incorrectFormat: db "(%s incorrect format)", 0
codePointFormat: db "U+%6X", 0
section .bss
codePoint: resb 8 ; The code point from sscanf should go here.
Is there a way to pass that third argument?
sscanf() signature.
in __cdecl sscanf(const char *const _buffer, const char *const _Format, ...)
I'm using ubuntu 19.04 64 bit
This question already has an answer here:
Why does clang produce inefficient asm with -O0 (for this simple floating point sum)?
(1 answer)
Closed 3 years ago.
I used https://godbolt.org/ with "x86-64 gcc 9.1" to assemble the following C code to understand why passing a pointer to a local variable as a function argument works. Now I have difficulties to understand some steps.
I commented on the lines I have difficulties with.
void printStr(char* cpStr) {
printf("str: %s", cpStr);
}
int main(void) {
char str[] = "abc";
printStr(str);
return 0;
}
.LC0:
.string "str: %s"
printStr:
push rbp
mov rbp, rsp
sub rsp, 16 ; why allocate 16 bytes when using it just for the pointer to str[0] which is 4 bytes long?
mov QWORD PTR [rbp-8], rdi ; why copy rdi to the stack...
mov rax, QWORD PTR [rbp-8] ; ... just to copy it into rax again? Also rax seems to already contain the pointer to str[0] (see *)
mov rsi, rax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
nop
leave
ret
main:
push rbp
mov rbp, rsp
sub rsp, 16 ; why allocate 16 bytes when "abc" is just 4 bytes long?
mov DWORD PTR [rbp-4], 6513249
lea rax, [rbp-4] ; pointer to str[0] copied into rax (*)
mov rdi, rax ; why copy the pointer to str[0] to rdi?
call printStr
mov eax, 0
leave
ret
Thanks to the help of Jester I could solve my confusion. The following code is compiled with the "-O1" flag of GCC (for me the best optimization level to understand what's going on):
.LC0:
.string "str: %s"
printStr:
sub rsp, 8
; now the call to printf gets prepared, rdi = first argument, rsi = second argument
mov rsi, rdi ; move str[0] to rsi
mov edi, OFFSET FLAT:.LC0 ; move address of static string literal "str: %s" to edi
mov eax, 0 ; set eax to the number of vector registers used, because printf is a varargs function
call printf
add rsp, 8
ret
main:
sub rsp, 24
mov DWORD PTR [rsp+12], 6513249 ; create string "abc" on the stack
lea rdi, [rsp+12] ; move address of str[0] (pointer to 'a') to rdi (first argument for printStr)
call printStr
mov eax, 0
add rsp, 24
ret
As Jester said, the 16 bytes were allocated for alignment. There is a good post on Stack Overflow which explains this here.
Edit:
There is a post on Stack Overflow which explains why al is zeroed before a call to a varargs function here.
I'm trying to input values into an array in x86-64 Intel assembly, but I can't quite figure it out.
I'm creating an array in segement .bss. Then I try to pass the address of the array along to another module by using r15. Inside that module I prompt the user for a number that I then insert into the array. But it doesn't work.
I'm trying to do the following
segment .bss
dataArray resq 15 ; Array that will be manipulated
segment .text
mov rdi, dataArray ; Store memory address of array so the next module can use it.
call inputqarray ; Calling inputqarray module
Inside of inputqarary I have:
mov r15, rdi ; Move the memory address of the array into r15 for safe keeping
push qword 0 ; Make space on the stack for the value we are reading
mov rsi, rsp ; Set the second argument to point to the new locaiton on the stack
mov rax, 0 ; No SSE input
mov rdi, oneFloat ; "%f", 0
call scanf ; Call C Standard Library scanf function
call getchar ; Clean the input stream
pop qword [r15]
I then try to output the value entered by the use by doing
push qword 0
mov rax, 1
mov rdi, oneFloat
movsd xmm0, [dataArray]
call printf
pop rax
Unfortunately, all I get for output is 0.00000
Output is 0 because you are using the wrong format specifier. It should be "%lf"
Next, no need to push and pop in your procedure. Since your going to pass the address of the data array to scanf, and that will be in rsi, just pass it in rsi; one less move.
You declared your array as 15 QWORDS, is that correct - 120 bytes? or did you mean resb 15?
This works and should get you on your way:
extern printf, scanf, exit
global main
section .rodata
fmtFloatIn db "%lf", 0
fmtFloatOut db `%lf\n`, 0
section .bss
dataArray resb 15
section .text
main:
sub rsp, 8 ; stack pointer 16 byte aligned
mov rsi, dataArray
call inputqarray
movsd xmm0, [dataArray]
mov rdi, fmtFloatOut
mov rax, 1
call printf
call exit
inputqarray:
sub rsp, 8 ; stack pointer 16 byte aligned
; pointer to buffer is in rsi
mov rdi, fmtFloatIn
mov rax, 0
call scanf
add rsp, 8
ret
Since you are passing params in rdi to the C functions, this is not on Windows.
I have some assembly code that uses scanf and printf and I'm running into some problems. When both of these functions are used in the same code, the values in the registers seem to be lost. The program basically loads a number and prints it out. We run it using
nasm -f elf64 file.asm && gcc -o file file.o && ./file
on linux
Here's our code:
extern printf
extern scanf
section .data
a db "set: ", 0
b db "not set: ", 0
reading db "Please enter a number: ", 0
message db "\n", 0
printsent db "%s", 10, 0
printint db "%d", 10, 0
printchar db "%c", 10, 0
readInt db "%d", 0
input db "%d", 0
section .text
global main
main:
hatta:
push rbp,
mov rbp, rsp,
push rbx,
xor rax, rax,
mov rdi, printsent,
mov rsi, reading
call printf,
pop rbx,
xor rax, rax,
mov rdi, readInt,
call scanf,
mov rbx, rdi
push rbx,
xor rax, rax,
mov rdi, printint,
mov rsi, rbx,
call printf,
pop rbx,
pop rbp,
ret
The odd thing is that if the line mov rdi, printint, is removed, we obtain the correct values. However, if we do the same thing with printsentence, we get a segmentation fault. Could anyone tell us the reason for this?
Thanks!
There are two errors in your scanf usage, possibly based on one false assumption: You seem to mean that scanf returns the loaded number in rdi and that no further argument is needed with format "%d". In truth the number (if scanned successfully) is returned in the memory pointed to by the second argument. Thus, the following changes make the code work.
pop rbx, | ;delete
=
xor rax, rax, = xor rax, rax,
mov rdi, readInt, = mov rdi, readInt,
> mov rsi, rsp
call scanf, = call scanf,
mov rbx, rdi | pop rbx,
Concerning if the line mov rdi, printint, is removed, we obtain the correct values - I doubt that.
I don't understand why you have the C flag here, there is no C code involved, but to your question:
As far as I remember the calling convention in Linux glibc x64 for printf(format, argument) is format in rdi, argument in rsi.
If you remove mov rdi, printsent, then you are calling printf(undefined,"Please enter a number: "). You did not provide a format argument in rdi, but printf
doesn't know that and uses whatever is in rdi at that moment. Most likely an invalid memory address which thus invokes a SIGSEGV.
By default function calls in x86 are supposed to be non-destructive on the arguments, though this is not a requirement. Standard library functions generally are. They achieve this by pushing the arguments on to the stack and reload them when done.
As such when you call scanf(readInt, ...) it will restore the pointer to readInt which has the same contents as printint in rdi when it returns. As such removing the line mov rdi, printint, has no effect because rdi contains a valid pointer to the format you need.