extern NR_write extern strlen extern strcat extern itoa section .rodata NL db 0xA bufferLength equ 4096 ERR_buffLen db " ERROR: Failed to complete printf(), reached buffer length!",0xA,0x0 lERR_buffLen equ $-ERR_buffLen-1 ; Errors (perror) perrorMsg db "%s: %s",10,0 perrorInvalid db "%s: Unknown error (errno %d)",10,0 errorMsgs dq em0,em1,em2,em3,em4,em5,em6,em7,em8,em9,em10,em11,em12,em13,em14,em15,em16,em17,em18,em19,em20,em21,em22,em23,em24,em25,em26,em27,em28,em29,em30,em31,em32,em33,em34 em0 db "No error",0 em1 db "Operation not permitted",0 em2 db "No such file or directory",0 em3 db "No such process",0 em4 db "Interrupted system call",0 em5 db "I/O error",0 em6 db "No such device or address",0 em7 db "Argument list too long",0 em8 db "Exec format error",0 em9 db "Bad file number",0 em10 db "No child processes",0 em11 db "Try again",0 em12 db "Out of memory",0 em13 db "Permission denied",0 em14 db "Bad address",0 em15 db "Block device required",0 em16 db "Device or resource busy",0 em17 db "File exists",0 em18 db "Cross-device link",0 em19 db "No such device",0 em20 db "Not a directory",0 em21 db "Is a directory",0 em22 db "Invalid argument",0 em23 db "File table overflow",0 em24 db "Too many open files",0 em25 db "Not a typewriter",0 em26 db "Text file busy",0 em27 db "File too large",0 em28 db "No space left on device",0 em29 db "Illegal seek",0 em30 db "Read-only file system",0 em31 db "Too many links",0 em32 db "Broken pipe",0 em33 db "Math argument out of domain of func",0 em34 db "Math result not representable",0 section .bss printfBuff resb bufferLength printfNBuff resb 32 section .text global print global puts global printf global perror ;----- print (char* string) -----; ; return value: N/A print: call strlen mov rdx, rax mov rax, NR_write mov rsi, rdi mov rdi, 1 syscall ret ;----- puts (char* string) -----; ; return value: N/A puts: mov r10, rdi call print mov rdi, r10 mov rax, NR_write mov rdi, 1 mov rsi, NL mov rdx, 1 syscall ret ;----- printf(const char* string, ...) -----; ; Currently only supports specifiers: %d, %c, %s, %% ; Return value: Amount of printed characters printf: push rbp mov rbp, rsp push r12 push r14 xor r10, r10 xor r14, r14 lea r11, [rel printfBuff] .makeStr: cmp byte [rdi], 0x0 je .finish cmp r14, bufferLength-1 je .finish cmp byte [rdi], '%' je .replaceArg mov r12b, byte [rdi] mov byte [r11], r12b inc r14 jmp .continue .replaceArg: cmp byte [rdi+1], 0x0 je .continue push rdi cmp byte [rdi+1], 'd' je .rep_d cmp byte [rdi+1], '%' je .rep_pct cmp byte [rdi+1], 'c' je .rep_c cmp byte [rdi+1], 's' je .rep_s ;--- invalid specifier ---; mov byte [r11], '%' inc r14 inc r10 jmp .continue ;--- %d ---; .rep_d: cmp r10, 0 cmove rdi, rsi je .convertInt cmp r10, 1 cmove rdi, rdx je .convertInt cmp r10, 2 cmove rdi, rcx je .convertInt cmp r10, 3 cmove rdi, r8 je .convertInt cmp r10, 4 cmove rdi, r9 je .convertInt mov rdi, qword [rbp + 16 + (r10-5)*8] .convertInt: lea rsi, [rel printfNBuff] push rcx push rdx push r8 push r10 call itoa pop r10 pop r8 pop rdx pop rcx mov rsi, rax jmp .insertLoop ;--- %% ---; .rep_pct: mov rdi, '%' dec r10 jmp .charToStr ;--- %c ---; .rep_c: cmp r10, 0 cmove rdi, rsi je .charToStr cmp r10, 1 cmove rdi, rdx je .charToStr cmp r10, 2 cmove rdi, rcx je .charToStr cmp r10, 3 cmove rdi, r8 je .charToStr cmp r10, 4 cmove rdi, r9 je .charToStr mov rdi, qword [rbp + 16 + (r10-5)*8] .charToStr: mov [printfNBuff], dil mov dil, 0 mov [printfNBuff+1], dil lea rsi, [rel printfNBuff] jmp .insertLoop ;--- %s ---; .rep_s: cmp r10, 0 je .insertLoop cmp r10, 1 cmove rsi, rdx je .insertLoop cmp r10, 2 cmove rsi, rcx je .insertLoop cmp r10, 3 cmove rsi, r8 je .insertLoop cmp r10, 4 cmove rsi, r9 je .insertLoop mov rsi, qword [rbp + 16 + (r10-5)*8] ;--- Move fetched data to buffer ---; .insertLoop: cmp r14, bufferLength-1 je .finish cmp byte [rsi], 0x0 je .s0f mov r12b, byte [rsi] mov byte [r11], r12b inc r14 inc rsi inc r11 jmp .insertLoop .s0f: inc r10 pop rdi add rdi, 2 jmp .makeStr .continue: inc rdi inc r11 jmp .makeStr .finish: mov byte [r11], 0x0 mov rax, NR_write mov rdi, 1 lea rsi, [rel printfBuff] mov rdx, r14 syscall mov rax, r14 cmp r14, bufferLength-1 jl .final mov rax, NR_write mov rdi, 2 lea rsi, [rel ERR_buffLen] mov rdx, lERR_buffLen syscall .final: pop r14 pop r12 leave ret ;----- perror(const char *str) -----; ; retrieves last returned error. Uses rax to fetch errno. ; errno must be negative ; Returns nothing (rax restored to original value) perror: push rax cmp rax, 0 jge .quit neg rax cmp rax, 34 jg .unknown mov rsi, rdi lea rdi, [rel perrorMsg] lea rdx, [errorMsgs + rax*8] mov rdx, [rdx] call printf jmp .quit .unknown: mov rsi, rdi lea rdi, [rel perrorInvalid] mov rdx, rax call printf .quit: pop rax ret