Comment lire l'entrée STDIN en x86_64 assemblée?
Je suis en train d'apprendre x86_64 assemblée et j'essayais standard d'entrée-sortie aujourd'hui, et je suis tombé sur ce post L'apprentissage de l'assemblée de l'écho du nom du programme Comment pourrais-je faire de même pour la lecture de l'entrée STDIN (à l'aide de SYSCALL instruction)? Surtout si je sais que l'entrée sera toujours un entier et je veux lire un registre?
EDIT: @Daniel Kozar de la réponse qui m'a aidé à comprendre comment STDIN et STDOUT trucs de travailler avec le SYSCALL instruction sur Linux. J'ai tenté d'écrire un petit programme qui lit un nombre à partir de la console d'entrée et imprime le caractère ascii correspondant à ce numéro. Dire si vous tapez 65 comme entrée, vous devriez obtenir Une sortie. Et aussi un caractère de nouvelle ligne. Si à tous, il permet à toute personne d'autre 🙂
section .text
global _start
_start:
mov rdi, 0x0 ; file descriptor = stdin = 0
lea rsi, [rsp+8] ; buffer = address to store the bytes read
mov rdx, 0x2 ; number of bytes to read
mov rax, 0x0 ; SYSCALL number for reading from STDIN
syscall ; make the syscall
xor rax, rax ; clear off rax
mov rbx, [rsp+8] ; read the first byte read into rsp+8 by STDIN call to rbp
sub rbx, 0x30 ; Since this is read as a character, it is obtained as ASCII value, so subtract by 0x30 to get the number
and rbx, 0xff ; This ensures that everything other than the last byte is set to 0 while the last byte is as is
mov rax, rbx ; move this value to rax since we want to store the final result in rax
shl rbx, 0x1 ; We need to multiply this by 10 so that we can add up all the digits read so multiplying the number by 2 and then by 8 and adding them up, so multiply by 2 here
shl rax, 0x3 ; multiply by 8 here
add rax, rbx ; add 8 times multiplied value with 2 times multiplied value to get 10 times multiplied value
mov rbx, [rsp+9] ; now read the next byte (or digit)
sub rbx, 0x30 ; Again get the digit value from ASCII value of that digit's character
and rbx, 0xff ; clear higher bytes
add rax, rbx ; Add this to rax as unit's place value
mov [rsp+8], rax ; Move the entire byte to rax
mov rdi, 0x1 ; file descriptor = stdout
lea rsi, [rsp+8] ; buffer = address to write to console
mov rdx, 0x1 ; number of bytes to write
mov rax, 0x1 ; SYSCALL number for writing to STDOUT
syscall ; make the syscall
xor rax, rax ; clear off rax
mov rax, 0xa ; move the new line character to rax
mov [rsp+8], rax ; put this on the stack
mov rdi, 0x1 ; file descriptor = stdout
lea rsi, [rsp+8] ; buffer = address to write to console
mov rdx, 0x1 ; number of bytes to write
mov rax, 0x1 ; SYSCALL number for writing to STDOUT
syscall ; make the syscall
mov rdi, 0 ; set exit status = 0
mov rax, 60 ; SYSCALL number for EXIT
syscall ; make the syscall
EDIT 2: Voici ma tentative de lire un entier non signé de 32 bits nombre entier décimal à partir de l'entrée standard, le stockent sous forme de nombre entier pour les calculs, puis d'écrire que de retour à std.
section .text
global _start
_start:
;Read from STDIN
mov rdi, 0x0 ; file descriptor = stdin = 0
lea rsi, [rsp+8] ; buffer = address to store the bytes read
mov rdx, 0xa ; number of bytes to read
mov rax, 0x0 ; SYSCALL number for reading from STDIN
syscall ; make the syscall
; Ascii to decimal conversion
xor rax, rax ; clear off rax
mov rbx, 0x0 ; initialize the counter which stores the number of bytes in the string representation of the integer
lea rsi, [rsp+8] ; Get the address on the stack where the first ASCII byte of the integer is stored.
rnext:
mov rcx, [rsi] ; Read the byte on the stack at the address represented by rsi
cmp rcx, 0xa ; Check if it is a newline character
je return ; If so we are done
cmp rbx, 0xa ; OR check if we have read 10 bytes (the largest 32 bit number contains 10 digits, so we will have to process at most 10 bytes
jg return ; If so we are done
sub rcx, 0x30 ; For the byte read, subtract by 0x30/48 to get the value from the ASCII code. 0 == 0x30 in ASCII, 1 == 0x31 in ASCII and so on.
and rcx, 0xff ; Clear off the higher order bytes to ensure there is no interference
mov rdx, rax ; We need to multiple this by 10 to get the next byte which goes to the unit's place and this byte becomes the ten's value. So make a copy
shl rax, 0x3 ; Multiply the original by 8 (Shift left by 3 is multiply by 8)
shl rdx, 0x1 ; Multiply the copy by 2 (Shift left by 1 is multiply by 2)
add rax, rdx ; Add these a * 8 + a * 2 to get a * 10.
add rax, rcx ; Add the digit to be at the units place to the original number
add rsi, 1 ; Advance the memory address by 1 to read the next byte
inc rbx ; Increment the digit counter
jmp rnext ; Loop until we have read all the digits or max is reached.
return:
push rax ; Push the read number on to the stack
; write New Line
mov rax, 0xa ; move the new line character to rax
mov [rsp+8], rax ; put this on the stack
mov rdi, 0x1 ; file descriptor = stdout
lea rsi, [rsp+8] ; buffer = address to write to console
mov rdx, 0x1 ; number of bytes to write
mov rax, 0x1 ; SYSCALL number for writing to STDOUT
syscall ; make the syscall
; Convert from Decimal to bytes
xor rdx, rdx ; Clear rdx which stores obtains a single digit of the number to convert to ASCII bytes
mov r8, 0x0 ; Initialize the counter containing the number of digits
pop rax ; Pop the read number from the stack
mov rbx, 0xa ; We store the divisor which is 10 for decimals (base-10) in rbx. rbx will be the divisor.
wnext:
div rbx ; Divide the number in rdx:rax by rbx to get the remainder in rdx
add rdx, 0x30 ; Add 0x30 to get the ASCII byte equivalent of the remainder which is the digit in the number to be written to display.
push rdx ; Push this byte to the stack. We do this because, we get the individial digit bytes in reverse order. So to reverse the order we use the stack
xor rdx, rdx ; Clear rdx preparing it for next division
inc r8 ; Increment the digits counter
cmp rax, 0x0 ; Continue until the number becomes 0 when there are no more digits to write to the console.
jne wnext ; Loop until there aren't any more digits.
popnext:
cmp r8, 0x0 ; Check if the counter which contains the number of digits to write is 0
jle endw ; If so there are no more digits to write
mov rdx, 0x1 ; number of bytes to write
mov rsi, rsp ; buffer = address to write to console
mov rdi, 0x1 ; file descriptor = stdout
mov rax, 0x1 ; SYSCALL number for writing to STDOUT
syscall ; make the syscall
dec r8 ; Decrement the counter
pop rbx ; Pop the current digit that was already written to the display preparing the stack pointer for next digit.
jmp popnext ; Loop until the counter which contains the number of digits goes down to 0.
endw:
; write New Line
xor rax, rax ; clear off rax
mov rax, 0xa ; move the new line character to rax
mov [rsp+9], rax ; put this on the stack
mov rdi, 0x1 ; file descriptor = stdout
lea rsi, [rsp+9] ; buffer = address to write to console
mov rdx, 0x1 ; number of bytes to write
mov rax, 0x1 ; SYSCALL number for writing to STDOUT
syscall ; make the syscall
; Exit
mov rdi, 0 ; set exit status = 0
mov rax, 60 ; SYSCALL number for EXIT
syscall ; make the syscall
À l'aide de
syscall
dépend du système d'exploitation.Je suis à l'utilisation de Linux. Exact que le code fonctionne pour moi.
Aussi, il sera bien si l'un de vous peut me dire comment faire pour imprimer un caractère de saut de ligne à la sortie standard (STDOUT) directement, sans avoir à créer une nouvelle variable .les données le concernant. Qui sera vraiment utile.
stackoverflow.com/questions/12495530/...
OriginalL'auteur Madhusudan.C.S | 2012-03-10
Vous devez vous connecter pour publier un commentaire.
Tout d'abord : il n'y a pas variables dans l'assemblée. Il y a juste les étiquettes de certains types de données. Les données sont, de par leur conception, les non - au moins dans la vraie assembleurs, non HLA (par exemple, MASM).
La lecture de l'entrée standard est obtenue en utilisant l'appel système
read
. Je suppose que vous avez déjà lu le post que vous avez mentionné et vous savez comment le système d'appel d'appels en x64 Linux. En supposant que vous êtes à l'aide de MSNA (ou quelque chose qui ressemble à sa syntaxe), et que vous souhaitez stocker l'entrée destdin
à l'adressebuffer
, où vous avez réservéBUFSIZE
octets de la mémoire, de l'exécution de l'appel système devrait ressembler à ceci :À son retour,
rax
contiendra le résultat de la syscall. Si vous voulez en savoir plus sur la façon dont il fonctionne, veuillez consulterman 2 read
.L'analyse d'un nombre entier en langage d'assemblage n'est pas si simple. Depuis
read
ne vous donne qu'une plaine de données binaires qui s'affiche sur l'entrée standard, vous avez besoin de convertir la valeur de l'entier vous-même. Gardez à l'esprit que ce que vous tapez sur le clavier est envoyé à l'application que les codes ASCII (ou tout autre encodage que vous utilisez peut-être - je suis en supposant ASCII ici). Par conséquent, vous avez besoin de convertir les données d'un fichier ASCII est codé décimal en binaire.Une fonction en C pour la conversion d'une telle structure normale unsigned int ressemblerait à quelque chose comme ceci :
De la conversion à l'assemblée (et en l'élargissant à l'appui des nombres signés) est laissé comme exercice pour le lecteur. 🙂
Dernier mais non le moindre - la
write
syscall vous oblige à toujours passer un pointeur vers une mémoire tampon avec les données qui sont censés être écrit à un descripteur de fichier. Par conséquent, si vous souhaitez insérer un saut de ligne, il n'y a pas d'autre moyen que de créer un buffer contenant la séquence de saut de ligne.mov rsi, [rsp+8]
de déplacer le contenu réel de la pile sur le registre. Ce que vous voulez, c'est l'adresse, auquel caslea rsi, [rsp+8]
fonctionnent très bien. Et oui, vous pouvez utiliser la pile pour tous vos lecture/écriture de besoin.chaîne->uint32_t pour x86-64: MSNA de l'Assemblée convertir les données d'entrée en entier?. Aussi, une meilleure façon de mettre des adresses statiques dans des registres est
lea rsi, [rel buffer]
. Plus courte et plus efficace quemov r64, imm64
, et n'a pas besoin d'une réinstallation pour l'exécution de correction à TARTE/PIC.OriginalL'auteur Daniel Kamil Kozar
Si vous êtes en mesure d'utiliser un
scanf
, voici une solution simple à faire:Que c'est. Il est similaire avec des chaînes. J'espère que cela aidera quelqu'un.
scanf
au cours des dernières glibc (compilé par version récente de GCC) sera effectivement erreur de segmentation si vous laissez un mannequinpush
de principal, à l'instar de ce code. glibc scanf Segmentation des défauts lorsqu'il est appelé à partir d'une fonction qui ne s'aligne pas RSP Autre que cela, ouimov rsi, x
fonctionne, mais il est inefficace par rapport àlea rsi, [rel x]
pour obtenir une adresse statique dans un registre.OriginalL'auteur akelec