Assembly Language
Linux elf64-exempel som inte använder glibc
Sök…
Användargränssnitt
Jag skulle våga säga att 80% av den behandling som sker i moderna datorsystem inte kräver användarinteraktion, till exempel kärnkod för Linux, OSX och Windows. För dem som gör det finns det två grunder som är interaktivitet via tangentbord ( pekdon ) och konsol. Detta exempel och andra i min serie är orienterade kring textbaserad konsol (VT100-emulering) och tangentbord.
I och för sig är detta exempel mycket enkelt, men det är en viktig byggsten för mer komplexa algoritmer.
Subrtx.asm
STDIN equ 0
STDOUT equ 1
SYS_READ equ 0
SYS_WRITE equ 1
global gets, strlen, print, atoq
section .text
Eftersom detta enbart är avsett för tangentbord är sannolikheten för fel nästan ingen. Jag kan tänka mig ofta att programmet kommer att kunna överväga buffertstorlek för att kringgå buffertöverskridande, men det är inte garanterat på grund av indirekt.
; =============================================================================
; Accept canonical input from operator for a maximum of EDX bytes and replace
; terminating CR with NULL.
; ENTER: RSI = Pointer to input buffer
; EDX = Maximum number of characters
; LEAVE: EAX = Number of characters entered
; R11 = Modified by syscall, all others preserved.
; FLAGS: ZF = Null entry, NZ otherwise.
; _____________________________________________________________________________
gets: push rcx
push rdi
xor eax, eax ; RAX = SYS_READ
mov edi, eax ; RDI = STDIN
syscall
; TODO: Should probably do some error trapping here, especially for
; buffer overrun, but I'll see if it becomes an issue over time.
dec eax ; Bump back to CR
mov byte [rsi+rax], 0 ; Replace it with NULL
pop rdi
pop rcx
ret
Till att börja med var detta avsett att kringgå behovet av att antingen koda eller manuellt beräkna en stränglängd för skrivning (2). Sedan bestämde jag mig för att införa en avgränsare, nu kan den användas för att söka efter valfri karaktär (0 - FF). Detta öppnar till exempel texten för inslagning av ord, så att etiketten strlen är lite av en felaktig fel eftersom man i allmänhet skulle tro att resultatet kommer att bli antalet synliga tecken.
; =============================================================================
; Determine length, including terminating character EOS. Result may include
; VT100 escape sequences.
; ENTER: RDI = Pointer to ASCII string.
; RCX Bits 31 - 08 = Max chars to scan (1 - 1.67e7)
; 07 - 00 = Terminating character (0 - FF)
; LEAVE: RAX = Pointer to next string (optional).
; FLAGS: ZF = Terminating character found, NZ otherwise (overrun).
; _____________________________________________________________________________
strlen: push rcx ; Preserve registers used by proc so
push rdi ; it's non-destructive except for RAX.
mov al, cl ; Byte to scan for in AL.
shr ecx, 8 ; Shift max count into bits 23 - 00
; NOTE: Probably should check direction flag here, but I always set and
; reset DF in the process that is using it.
repnz scasb ; Scan for AL or until ECX = 0
mov rax, rdi ; Return pointer to EOS + 1
pop rdi ; Original pointer for proglogue
jz $ + 5 ; ZF indicates EOS was found
mov rax, rdi ; RAX = RDI, NULL string
pop rcx
ret
Avsikten med denna procedur är att förenkla slingdesignen i samtalsproceduren.
; =============================================================================
; Display an ASCIIZ string on console that may have embedded VT100 sequences.
; ENTER: RDI = Points to string
; LEAVE: RAX = Number of characters displayed, including EOS
; = Error code if SF
; RDI = Points to byte after EOS.
; R11 = Modified by syscall all others preserved
; FLAGS: ZF = Terminating NULL was not found. NZ otherwise
; SF = RAX is negated syscall error code.
;______________________________________________________________________________
print: push rsi
push rdx
push rcx
mov ecx, -1 << 8 ; Scan for NULL
call strlen
push rax ; Preserve point to next string
sub rax, rdi ; EAX = End pntr - Start pntr
jz .done
; size_t = write (int STDOUT, char *, size_t length)
mov edx, eax ; RDX = length
mov rsi, rdi ; RSI = Pointer
mov eax, SYS_WRITE
mov edi, eax ; RDI = STDOUT
syscall
or rax, rax ; Sets SF if syscall error
; NOTE: This procedure is intended for console, but in the event STDOUT is
; redirected by some means, EAX may return error code from syscall.
.done: pop rdi ; Retrieve pointer to next string.
pop rcx
pop rdx
pop rsi
ret
Slutligen ett exempel på hur dessa funktioner kan användas.
Generic.asm
global _start
extern print, gets, atoq
SYS_EXIT equ 60
ESC equ 27
BSize equ 96
section .rodata
Prompt: db ESC, '[2J' ; VT100 clear screen
db ESC, '[4;11H' ; " Position cursor to line 4 column 11
db 'ASCII -> INT64 (binary, octal, hexidecimal, decimal), '
db 'Packed & Unpacked BCD and floating point conversions'
db 10, 10, 0, 9, 9, 9, '=> ', 0
db 10, 9, 'Bye'
db ESC, '[0m' ; VT100 Reset console
db 10, 10, 0
section .text
_start: pop rdi
mov rsi, rsp
and rsp, byte 0xf0 ; Align stack on 16 byte boundary.
call main
mov rdi, rax ; Copy return code into ARG0
mov eax, SYS_EXIT
syscall
; int main ( int argc, char *args[] )
main: enter BSize, 0 ; Input buffer on stack
mov edi, Prompt
call print
lea rsi, [rbp-BSize] ; Establish pointer to input buffer
mov edx, BSize ; Max size for read(2)
.Next: push rdi ; Save pointer to "=> "
call print
call gets
jz .done
call atoq ; Convert string pointed to by RSI
pop rdi ; Restore pointer to prompt
jmp .Next
.done: call print ; RDI already points to "Bye"
xor eax, eax
leave
ret
Makefile
OBJECTS = Subrtx.o Generic.o
Generic : $(OBJECTS)
ld -oGeneric $(OBJECTS)
readelf -WS Generic
Generic.o : Generic.asm
nasm -g -felf64 Generic.asm
Subrtx.o : Subrtx.asm
nasm -g -felf64 Subrtx.asm
clean:
rm -f $(OBJECTS) Generic