This file can be included to 64bit programs written in Euro Assembler.
It contains macros for some basic Linux | Unix interactions:
retrieving of environment and command-line arguments, standard I/O, program termination,
and for access to Linux kernel 64bit ABI
(Application Binary Interface) using SYSCALL
, as described in
[SystemV].
linabi HEAD
sys_
was removed from function names.
%LinSyscall64List %SET \ Declare 64bit syscall_name(number_of_parameters) \ numeric_id read(3),write(3),open(3),close(1),stat(2),fstat(2),lstat(2),poll(3), \ 000..007 lseek(3),mmap(6),mprotect(4),munmap(2),brk(1),rt_sigaction(4), \ 008..013 rt_sigprocmask(4),rt_sigreturn(1),ioctl(3),pread64(4),pwrite64(4),readv(3), \ 014..019 writev(3),access(2),pipe(1),select(5),sched_yield(0),mremap(5),msync(3), \ 020..026 mincore(3),madvise(3),shmget(3),shmat(3),shmctl(3),dup(1),dup2(2),pause(0), \ 027..034 nanosleep(2),getitimer(2),alarm(1),setitimer(3),getpid(0),sendfile(4), \ 035..040 socket(3),connect(3),accept(3),sendto(6),recvfrom(6),sendmsg(3),recvmsg(3), \ 041..047 shutdown(2),bind(3),listen(2),getsockname(3),getpeername(3),socketpair(4), \ 048..053 setsockopt(5),getsockopt(5),clone(4),fork(0),vfork(0),execve(3),exit(1), \ 054..060 wait4(4),kill(2),uname(1),semget(3),semop(3),semctl(4),shmdt(1),msgget(2), \ 061..068 msgsnd(4),msgrcv(5),msgctl(3),fcntl(3),flock(2),fsync(1),fdatasync(1), \ 069..075 truncate(2),ftruncate(2),getdents(3),getcwd(2),chdir(1),fchdir(1),rename(2),\ 076..082 mkdir(2),rmdir(1),creat(2),link(2),unlink(1),symlink(2),readlink(3), \ 083..089 chmod(2),fchmod(2),chown(3),fchown(3),lchown(3),umask(1),gettimeofday(2), \ 090..096 getrlimit(2),getrusage(2),sysinfo(1),times(1),ptrace(4),getuid(0),syslog(3),\ 097..103 getgid(0),setuid(1),setgid(1),geteuid(0),getegid(0),setpgid(2),getppid(0), \ 104..110 getpgrp(0),setsid(0),setreuid(2),setregid(2),getgroups(2),setgroups(2), \ 111..116 setresuid(3),getresuid(3),setresgid(3),getresgid(3),getpgid(1),setfsuid(1), \ 117..122 setfsgid(1),getsid(1),capget(2),capset(2),rt_sigpending(2), \ 123..127 rt_sigtimedwait(4),rt_sigqueueinfo(3),rt_sigsuspend(2),sigaltstack(2), \ 128..131 utime(2),mknod(3),uselib(1),personality(1),ustat(2),statfs(2),fstatfs(2), \ 132..138 sysfs(3),getpriority(2),setpriority(3),sched_setparam(2),sched_getparam(2), \ 139..143 sched_setscheduler(3),sched_getscheduler(1),sched_get_priority_max(1), \ 144..146 sched_get_priority_min(1),sched_rr_get_interval(2),mlock(2),munlock(2), \ 147..150 mlockall(1),munlockall(0),vhangup(0),modify_ldt(3),pivot_root(2),_sysctl(1),\ 151..156 prctl(5),arch_prctl(3),adjtimex(1),setrlimit(2),chroot(1),sync(0),acct(1), \ 157..163 settimeofday(2),mount(5),umount2(2),swapon(2),swapoff(1),reboot(4), \ 164..169 sethostname(2),setdomainname(2),iopl(2),ioperm(3),create_module(?), \ 170..174 init_module(3),delete_module(2),get_kernel_syms(?),query_module(?), \ 175..178 quotactl(4),nfsservctl(?),getpmsg(?),putpmsg(?),afs_syscall(?),tuxcall(?), \ 179..184 security(?),gettid(0),readahead(3),setxattr(5),lsetxattr(5),fsetxattr(5), \ 185..190 getxattr(4),lgetxattr(4),fgetxattr(4),listxattr(3),llistxattr(3), \ 191..195 flistxattr(3),removexattr(2),lremovexattr(2),fremovexattr(2),tkill(2), \ 196..200 time(1),futex(6),sched_setaffinity(3),sched_getaffinity(3), \ 201..204 set_thread_area(?),io_setup(2),io_destroy(1),io_getevents(4),io_submit(3), \ 205..209 io_cancel(3),get_thread_area(?),lookup_dcookie(3),epoll_create(1), \ 210..213 epoll_ctl_old(?),epoll_wait_old(?),remap_file_pages(5),getdents64(3), \ 214..217 set_tid_address(1),restart_syscall(0),semtimedop(4),fadvise64(4), \ 218..221 timer_create(3),timer_settime(4),timer_gettime(2),timer_getoverrun(1), \ 222..225 timer_delete(1),clock_settime(2),clock_gettime(2),clock_getres(2), \ 226..229 clock_nanosleep(4),exit_group(1),epoll_wait(4),epoll_ctl(4),tgkill(3), \ 230..234 utimes(2),vserver(?),mbind(6),set_mempolicy(3),get_mempolicy(5),mq_open(4), \ 235..240 mq_unlink(1),mq_timedsend(5),mq_timedreceive(5),mq_notify(2), \ 241..244 mq_getsetattr(3),kexec_load(4),waitid(5),add_key(4),request_key(4), \ 245..249 keyctl(5),ioprio_set(3),ioprio_get(2),inotify_init(0),inotify_add_watch(3), \ 250..254 inotify_rm_watch(2),migrate_pages(4),openat(4),mkdirat(3),mknodat(4), \ 255..259 fchownat(5),futimesat(3),newfstatat(4),unlinkat(3),renameat(4),linkat(5), \ 260..265 symlinkat(3),readlinkat(4),fchmodat(3),faccessat(3),pselect6(6),ppoll(5), \ 266..271 unshare(1),set_robust_list(2),get_robust_list(3),splice(6),tee(4), \ 272..276 sync_file_range(4),vmsplice(4),move_pages(6),utimensat(4),epoll_pwait(6), \ 277..281 signalfd(3),timerfd_create(2),eventfd(1),fallocate(4),timerfd_settime(4), \ 282..286 timerfd_gettime(2),accept4(4),signalfd4(4),eventfd2(2),epoll_create1(1), \ 287..291 dup3(3),pipe2(2),inotify_init1(1),preadv(5),pwritev(5),rt_tgsigqueueinfo(4),\ 292..297 perf_event_open(5),recvmmsg(5),fanotify_init(2),fanotify_mark(5), \ 298..301 prlimit64(4),name_to_handle_at(5),open_by_handle_at(5),clock_adjtime(2), \ 302..305 syncfs(1),sendmmsg(4),setns(2),getcpu(3),process_vm_readv(6), \ 306..310 process_vm_writev(6),kcmp(5),finit_module(3),sched_setattr(3), \ 311..314 sched_getattr(4),renameat2(5),seccomp(3),getrandom(3),memfd_create(2), \ 315..319 kexec_file_load(5),bpf(3),stub_execveat(5),userfaultfd(1),membarrier(2), \ 320..324 mlock2(3),copy_file_range(6),preadv2(6),pwritev2(6),pkey_mprotext(4), \ 325..329 pkey_alloc(2),pkey_free(1),statx(5),io_pgetevents_time64(6),rseq(4), \ 330..334 ;
This macroinstruction encapsulates invokation of Linux 64bit kernel functions
using machine instruction SYSCALL
.
Function arguments %Arg1..%Arg6 are transferred in registers RDI,RSI,RDX,R10,R8,R9
, respectively.
RAX
contains the function's ordinal number 0..328 from the list
%LinSyscall64List.
read
, write
, open
etc (case insensitive).
Its ordinal number will be provided in register RAX.
When you want to use the fast (nondefault) mode in all LinABI invokations, you don't have to append,Fastmode=Yes
to every invokation ofLinABI
if you set preprocessing %variable in the beginning of your program:%Fastmode %SETB Yes
.
User of robust mode does not have to keep on mind that function's parameter will be loaded
to registers RSI,RDI etc, overwriting their previous contents.
Arguments of macro LinABI Fastmode=off
may be provided in any 64bit GPR, e.g.
LinABI write, stdout, RSI, RCX
.
When arguments are small integers, the robust variant is sometimes shorter, because it loads registers with
PUSH param + POP reg
(2+1 bytes) instead of MOV reg,param
(5 bytes).
The following example demonstrates both modes of LinABI on the function
sys_write
with three arguments:
LinABI write, STDOUT_FILENO, Message, SIZE# Message
|00000000: | ; Fast version.
|00000000: | LinABI write, STDOUT_FILENO, Message, SIZE# Message, Fastmode=Yes
|00000000:BA0E000000 + MOV RDX,14 ; Arg3 SIZE# Message.
|00000005:488D350C000000 + LEA RSI,[Message] ; Arg2 OFFSET# Message, use relative addressing frame.
|0000000C:BF01000000 + MOV RDI,STDOUT_FILENO ; Arg1 StdOutput handle = 1.
|00000011:B801000000 + MOV RAX,1 ; Syscall write = 1.
|00000016:0F05 + SYSCALL ; Perform the kernell call.
|00000018: | ; LinABI in fast mode occupies 24 bytes.
|00000000: | ; Robust version.
|00000000: | LinABI write, STDOUT_FILENO, Message, SIZE# Message, Fastmode=No
|00000000:6A0E + PUSHQ 14 ; Arg3 SIZE# Message.
|00000002:488D050F000000 + LEA RAX,[Message] ; Arg2 OFFSET# Message, use relative addressing frame.
|00000009:50 + PUSH RAX ; Temporary use RAX for transfer.
|0000000A:6A01 + PUSH STDOUT_FILENO ; Arg1 StdOutput handle = 1.
|0000000C:6A01 + PUSH 1 ; Syscall write = 1.
|0000000E:6A03 + PUSH 3 ; Number of Syscall arguments = 3.
|00000010:E814000000 + CALL LinABI@RT ; Invoke the system function, discard the last two pushed parameters.
|00000015:5F5E5A + POP RDI,RSI,RDX ; Epilogue: restore original value of transfer registers.
|00000018: | ; LinABI in robust mode occupies 24 bytes (plus 75 bytes of runtime code, once per program).
LinABI %MACRO Sys_call, Arg1,Arg2,Arg3,Arg4,Arg5,Arg6, Fastmode=%Fastmode %GPR %SET RDI,RSI,RDX,R10,R8,R9 ; Enumerate registers for transfer of arguments. %FnNr %SETA 0 ; Function identification 0..328, transferred in RAX. FnRef %FOR %LinSyscall64List ; Walk through kernel function reference. found %IF "%Sys_call" == "%FnRef[1..%&-3]" ; Omit the last three characters from function reference and compare. %ArgC %SET %FnRef[%&-1] ; Sys_call name was found. Pick up the digit from parenthesis ('0'..'6' or '?'). checkDef %IF "%ArgC" === "?" %ERROR ID=5813,%0 %Sys_call (RAX=%FnNr) is not implemented in the kernel. %EXITMACRO LinABI %ENDIF checkDef checkArg %IF %ArgC <> %# - 1 %ERROR ID=5811,Macro "%0 %Sys_call" requires %ArgC arguments. %EXITMACRO LinABI %ENDIF checkArg mode %IF %Fastmode ; Fast mode. args %WHILE %ArgC >= 1 ; Argument passing. Start with the last one. %Arg %SET %*{%ArgC+1} rel %IF TYPE#(SEGMENT#(%Arg)) = 'A' ; %Arg requires relocation (it's defined in a segment). ref %IF '%Arg[1]' === '[' ; Argument is a relocatable memory variable, e.g. [Symbol+RSI]. LEA %GPR{%ArgC},%Arg ; Use relative addressing frame for relocatable %Arg. MOV %GPR{%ArgC},[%GPR{%ArgC}] ; Dereference the argument value. %ELSE ref ; Argument is a relocatable immediate (pointer), e.g. Symbol. LEA %GPR{%ArgC},[%Arg] ; Use relative addressing frame for relocatable %Arg. %ENDIF ref %ELSE rel ; Arg is scalar, e.g. 1234, RSI, [RBP+40]. %IF '%GPR{%ArgC}' !== '%Arg' ; Skip if the argument already is in transfer register. MOVQ %GPR{%ArgC},%Arg ; otherwise copy the argument to GPR. %ENDIF %ENDIF rel %ArgC %SETA %ArgC-1 ; Arguments are loaded in reversed order. %ENDWHILE args MOV RAX,%FnNr ; Load syscall identification number %FnNr to RAX. SYSCALL ; Perform the kernell call. %EXITMACRO LinABI ; Expansion of macro is completed. %ELSE mode ; Robust mode. ArgNr %FOR %ArgC..1, STEP= -1 ; Push arguments, start with the last one. %Arg %SET %*{%ArgNr+1} rel %IF TYPE#(SEGMENT#(%Arg)) = 'A' ; Argument is relocatable. ref %IF '%Arg[1]' === '[' ; Argument is a relocatable memory variable, e.g. [Symbol+RSI]. LEA RAX,%Arg ; Use relative addressing frame for relocatable %Arg. PUSHQ [RAX] ; Dereference and push the argument value. %ELSE ref ; Argument is a relocatable immediate (pointer), e.g. Symbol. LEA RAX,[%Arg] ; Use relative addressing frame for relocatable %Arg. PUSH RAX ; Push the pointer. %ENDIF ref %ELSE rel ; Arg is scalar, e.g. 1234, RSI, [RBP+40]. PUSHQ %Arg %ENDIF rel %ENDFOR ArgNr PUSHQ %FnNr ; Sys_call identification. PUSHQ %ArgC ; Number of Sys_call arguments (0..6). CALL LinABI@RT ; Runtime performs the SYSCALL and discards the last two pushed parameters. LinABI@RT:: PROC1 ; Robust LinABI runtime with variable number of pushed arguments. PUSH RCX,R11 ; Save registers clobbered by SYSCALL. MOV RCX,[RSP+24] ; %ArgC - number of arguments on stack (0..6). MOV RAX,[RSP+32] ; %FnNr - SYSCALL function identificator. JRCXZ .Syscall: DEC CL XCHG RDI,[RSP+40] ; %Arg1. JRCXZ .Syscall: DEC CL XCHG RSI,[RSP+48] ; %Arg2. JRCXZ .Syscall: DEC CL XCHG RDX,[RSP+56] ; %Arg3. JRCXZ .Syscall: DEC CL XCHG R10,[RSP+64] ; %Arg4. JRCXZ .Syscall: DEC CL XCHG R8,[RSP+72] ; %Arg5. JRCXZ .Syscall: DEC CL XCHG R9,[RSP+80] ; %Arg6. .Syscall: SYSCALL POP R11,RCX ; Restore registers clobbered by SYSCALL. RET 2*8 ; Discard %ArgC and %FnNr. ENDP1 LinABI@RT:: ; Pushed arguments will be discarded by LinABI's epilogue. POP %GPR{1..%ArgC} ; Epilogue: restore original value of transfer registers. %EXITMACRO LinABI ; Expansion of macro is completed. %ENDIF mode %ENDIF found %FnNr %SETA %FnNr+1 ; Try the next function reference in %LinSyscall64List. %ENDFOR FnRef %ERROR ID=5812,Unrecognized function name "%Sys_call". See %%LinSyscall64List in "linabi.htm". %ENDMACRO LinABI
argc
value. It can be GPR or [QWORD_memory_variable].
Frame=RSP
is valid only if nothing has been pushed yet
since the program started. Otherwise use something like
LEA RCX,[RSP+4]
or LEA RCX,[RBP+8]
and then Frame=RCX
../executable --option argumentA argumentB
┌────────┐
│ 0 │ NULL QWORD which terminates array of pointers to env strings.
├────────┤
│ env2 │ Pointer to 2nd ASCIIZ environment string.
├────────┤
│ env1 │ Pointer to 1st ASCIIZ environment string.
├────────┤
│ 0 │ NULL QWORD which terminates array of pointers to arg strings.
├────────┤
│ argv3 │ Pointer to ASCIIZ string argumentB
.
├────────┤
│ argv2 │ Pointer to ASCIIZ string argumentA
.
├────────┤
│ argv1 │ Pointer to ASCIIZ string --option
.
├────────┤
│ execut │ Pointer to this executable file name ./executable
.
├────────┤
│ argc │ Number of arguments including the executable itself 4
.
Frame-> └────────┘
GetArg %MACRO ArgNumber, Frame=RSP PUSHQ %Frame, %ArgNumber CALL GetArgLin64@RT GetArgLin64@RT:: PROC1 ; Stdcalled with Param1=ArgNumber, Param2=%Frame. PUSH RAX,RBX,RDX,RDI MOV RSI,[RSP+6*8] ; Frame. MOV RBX,[RSP+5*8] ; Requested ArgNumber (0..126) or -1. SUB EDX,EDX MOV RCX,[RSI] ; argc (expected 1..128). DEC RCX ; Do not count the executable itself. CMP RBX,-1 ; Test if GetArg was invoked from GetArgCount. JE .90: CMP EBX,ECX JA .80: ; This many arguments are not available. MOV RDI,[RSI+8*RBX+8] TEST RDI STC JZ .80: ; Invalid %Frame. MOV RSI,RDI XOR ECX,ECX XOR EAX,EAX NOT RCX REPNE SCASB ; Find the zero terminator, set CF=0. NOT RCX LEA EDX,[ECX-1] .80: MOV ECX,EDX .90:POP RDI,RDX,RBX,RAX RET 2*8 ENDP1 GetArgLin64@RT:: %ENDMACRO GetArg
argc
value. It can be GPR or [DWORD_memory_variable].
Frame=RSP
is valid only if nothing has been pushed yet
since the program started. Otherwise use something like
LEA RCX,[RSP+4]
or LEA RCX,[RBP+8]
and then Frame=RCX
.GetArgCount %MACRO Frame=RSP GetArg -1, Frame=%Frame %ENDMACRO GetArgCount
Macro PutArg retrieves ArgNumber-th argument from command-line, copies its content to the memory specified by BufPtr, BufSize and appends one NUL character at its end.
SIZE# %BufPtr
will be used instead.
argc
value. It can be GPR or [QWORD_memory_variable].
Frame=RSP
is valid only if nothing has been pushed yet
since the program started. Otherwise use something like
LEA RCX,[RSP+4]
or LEA RCX,[RBP+8]
and then Frame=RCX
.PutArg %MACRO ArgNumber, BufPtr, BufSize, Frame=RSP sized? %IF %# = 2 mem? %IF %^PASS > 1 && TYPE#(SEGMENT# %BufPtr) != 'A' %ERROR ID=5814, 'Please specify the size of output buffer.' %EXITMACRO PutArg %ELSE mem? ; BufPtr is specified as a memory variable with size. %PutArgSize %SETA SIZE# %BufPtr %ENDIF mem? %ELSE sized? ; BufSize is explicitly specified. %PutArgSize %SET %BufSize %ENDIF sized? PUSHQ %Frame, %PutArgSize, %BufPtr, %ArgNumber CALL PutArgLin64@RT:: PutArgLin64@RT::PROC1 PUSH RCX,RSI,RDI MOV RCX,[RSP+7*8] ; Frame. MOV RDI,[RSP+5*8] ; BufPtr. MOV RSI,[RSP+4*8] ; ArgNumber. GetArg RSI,Frame=RCX ; Returns argument in RSI,RCX. JC .50: INC ECX CMP [RSP+6*8],ECX ; BufSize. DEC ECX JNC .50: MOV ECX,[RSP+6*8] ; BufSize. .50: MOV [RSP+2*8],RCX ; Returned RCX. REP MOVSB JC .90: MOV [RDI],CL .90: POP RDI,RSI,RCX RET 4*8 ENDP1 PutArgLin64@RT:: %ENDMACRO PutArg
SIZE# %BufPtr
will be used instead.
argc
value. It can be GPR or [QWORD_memory_variable].
Frame=RSP
is valid only if nothing has been pushed yet
since the program started. Otherwise use something like
LEA RCX,[RSP+4]
or LEA RCX,[RBP+8]
and then Frame=RCX
.executable
with environment strings TEMP=/tmp
and PATH=/bin:/usr/bin
launched from console as
./executable --option argumentA argumentB
PATH=/bin:/usr/bin
.
├────────┤
│ env1 │ Pointer to 1st ASCIIZ environment string TEMP=/tmp
.
├────────┤
│ 0 │ NULL QWORD which terminates array of pointers to arg strings.
├────────┤
│ argv3 │ Pointer to ASCIIZ string argumentB
.
├────────┤
│ argv2 │ Pointer to ASCIIZ string argumentA
.
├────────┤
│ argv1 │ Pointer to ASCIIZ string --option
.
├────────┤
│ execut │ Pointer to this executable file ASCIIZ name ./executable
.
├────────┤
│ argc │ Number of arguments including the executable itself 4
.
Frame-> └────────┘
PutEnv %MACRO EnvName$, BufPtr, BufSize, IgnoreCase=Yes, Frame=RSP sized? %IF %# = 2 mem? %IF %^PASS > 1 && TYPE#(SEGMENT# %BufPtr) != 'A' %ERROR ID=5814, 'Please specify the size of output buffer.' %EXITMACRO PutEnv %ELSE mem? ; BufPtr is specified as a memory variable with size. %PutEnvSize %SETA SIZE# %BufPtr %ENDIF mem? %ELSE sized? ; BufSize is explicitly specified. %PutEnvSize %SET %BufSize %ENDIF sized? %PutEnvCase %SETB %IgnoreCase PUSHQ %Frame, %PutEnvCase, %PutEnvSize, %BufPtr, %EnvName$ CALL PutEnvLin64@RT:: PutEnvLin64@RT:: PROC1 PUSH RAX,RBX,RCX,RDX,RBP,RSI,RDI MOV RBP,RSP XOR ECX,ECX MOV RSI,[RBP+12*8] ; Frame. MOV [RBP+4*8],RCX ; Initialize returned RCX with 0. MOV RDI,[RBP+9*8] ; BufPtr. MOV [RDI],CL ; Initialize empty Buffer. LODSQ ; RAX=argc (1..127). CMP RAX,127 CMC JC .90: ; Abort with CF due to bad Frame. TEST EAX STC JZ .90: ; Abort with CF due to bad Frame. LEA RSI,[RSI+8*RAX] ; Pointers to argv* strings should be followed with NULL pointer. LODSQ TEST RAX ; NUL pointer which terminates argv strings. STC JNZ .90: ; Abort with CF due to bad Frame. MOV RBX,RSI ; RBX points to array of pointers to ASCIIZ environment stringsNAME=value
. MOV RDI,[RBP+8*8] ; EnvName$. MOV RSI,RDI NOT ECX REPNE SCASB NOT RCX MOV EDX,ECX ; RCX=RDX is now the size of EnvName$ string in bytes, including NUL. LEA EDI,[RCX+7] AND EDI,-8 ; Round RDI up to QWORD. SUB RSP,RDI ; Make room for lowercase EnvName$. MOV RDI,RSP .20: LODSB CMP AL,'A' JB .30: CMP AL,'Z' JA .30: OR AL,'x'^'X' ; Convert ASCII character to lowercase. .30: STOSB DEC ECX JNZ .20: .40: MOV RSI,RBX LODSQ ; Pointer to env stringName=Value
. MOV RBX,RSI TEST RAX ; NUL pointer terminates env strings. JZ .90: ; Abort with CF=0, EnvName$ not found. MOV RSI,RAX XOR ECX,ECX MOV AL,'=' NOT RCX MOV RDI,RSI REPNE SCASB JNE .40: NOT RCX CMP ECX,EDX JNE .40: DEC ECX ; Netto size of EnvName$. TESTB [RBP+11*8],1 ; IgnoreCase? JNZ .50: MOV RDI,[RBP+8*8] ; EnvName$ - case sensitive search. REP CMPSB JNE .40: JMP .70: .50: MOV RDI,RSP ; Case insensitive search. .55: LODSB CMP AL,'A' JB .60: CMP AL,'Z' JA .60: OR AL,'x'^'X' ; Convert character to lowercase. .60: CMP AL,[RDI] JNE .40: INC RDI DEC ECX JNZ .55: .70: INC RSI ; Skip '=', point to EnvVal. MOV RDI,RSI XOR EAX,EAX NOT RCX REPNE SCASB SUB RDI,RSI ; RDI= required-by-value brutto size of EnvVal in bytes. MOV RCX,[RBP+10*8] ; BufSize. EnvVal allocated-by-user brutto size in bytes. STC JRCXZ .90: CMP ECX,EDI ; CF=overflow of EnvVal. XCHG ECX,EDI JAE .80: XCHG EDI,ECX .80: LEA EDX,[ECX-1] MOV RDI,[RBP+9*8] ; BufPtr. MOV [RBP+4*8],RDX ; Returned RCX. REP MOVSB .90: MOV RSP,RBP POP RDI,RSI,RBP,RDX,RCX,RBX,RAX RET 5*8 ENDP1 PutEnvLin64@RT:: %ENDMACRO PutEnv
Macro StdOutput writes one or more concatenated strings to the standard output or to other device or file specified with the Handle identifier.
Strings are either zero-terminated, or the keyword Size= must specify their size in bytes. The terminating NUL character is never written.
If keyword Eol=Yes, macro writes LF after all strings.
LF
(0x10) will be written on output after all strings have been written.
StdOutput %MACRO String1,String2,,,Size=-1, Handle=1, Eol=No opN %FOR 1..%#, STEP=1 ; Walk through all ordinal operands. PUSHQ %Handle, %Size rel %IF TYPE#(SEGMENT#(%1)) = 'A' ; String is relocatable. ref %IF '%1[1]' === '[' ; Pointer reference, e.g. [ArrayOfPointersToMsgs+RSI]. PUSH RAX LEA RAX,%1 MOV RAX,[RAX] ; Dereference the pointer. XCHG RAX,[RSP] %ELSE ref ; Absolute reference, e.g. Message. PUSH RAX LEA RAX,[%1] XCHG RAX,[RSP] %ENDIF ref %ELSE rel ; String is not relocatable, e.g. RSI or [RBP+16]. PUSHQ %1 %ENDIF rel CALL StdOutputLin64@RT %SHIFT 1 ; The next string to write. %ENDFOR opN eol %IF %Eol PUSHQ %Handle, 1, 0 ; NUL string pointer represents the request for LF. CALL StdOutputLin64@RT %ENDIF eol StdOutputLin64@RT:: PROC1 ; StdCalled runtime with params String, Size, Handle. PUSH RAX,RBX,RCX,RSI,RDI,R11 PUSHQ 0x0A ; Prepare Eol character on stack. MOV RBX,[RSP+80] ; File descriptor %Handle. MOV RCX,[RSP+72] ; String maximal %Size. MOV RDI,[RSP+64] ; String pointer or 0 for Eol. XOR EAX,EAX CMP RDI,RAX ; NULL pointer in RDI signalizes Eol request. JNE .10: INC RDI ; Size of Eol=1. MOV RSI,RSP ; Pointer to Eol (0x0A). JMP .50: .10: MOV RSI,RDI ; Remember the start of string in RSI. REPNE SCASB ; Find the end of string. JNE .30: DEC RDI ; Omit the NUL terminator. .30: SUB RDI,RSI ; Netto string is now ESI,EDI .50: LinABI write,RBX,RSI,RDI, Fastmode=No SAL RAX ; Copy SF to CF (this signalizes an error). .90: POP RAX,R11,RDI,RSI,RCX,RBX,RAX RET 3*8 ENDP1 StdOutputLin64@RT:: %ENDMACRO StdOutput
Macro StdInput reads from standard input specified by the Handle.
StdInput %MACRO Buffer, Size=, Handle=0 PUSHQ %Handle bs %IF "%Size" === "" PUSHQ SIZE# %Buffer %ELSE bs PUSHQ %Size %ENDIF bs rel %IF TYPE#(SEGMENT#(%Buffer)) = 'A' ; Buffer is relocatable. ref %IF "%Buffer[1]" === "[" ; Pointer reference, e.g. [ArrayOfPointersToBuffers+RSI]. LEA RCX,%Buffer MOV RCX,[RCX] ; Dereference the pointer. PUSH RCX %ELSE ref ; Absolute reference, e.g. InputLine. LEA RCX,[%Buffer] PUSH RCX %ENDIF ref %ELSE rel ; Buffer is not relocatable, e.g. RBX or [RBP+16]. PUSHQ %Buffer %ENDIF CALL StdInputLin64@RT StdInputLin64@RT::PROC1 ; StdCalled with params Handle, BufferSize, BufferPtr. PUSH RAX,RDX,RSI,RDI,R8,R9,R10,R11 MOV RSI,[RSP+72] ; %Buffer. MOV RDX,[RSP+80] ; %Size. MOV RDI,[RSP+88] ; %Handle. LinABI read,RDI,RSI,RDX, Fastmode=Yes MOV RCX,RAX SAL RAX ; Copy SF to CF (this signalizes an error). POP R11,R10,R9,R8,RDI,RSI,RDX,RAX RET 3*8 ENDP1 StdInputLin64@RT:: %ENDMACRO StdInput
Errorlevel=
, this value may also be specified as an ordinal operand.
TerminateProgram %MACRO Errorlevel=0 PUSHQ 60 ; Function sys_exit identification. %IF %# = 1 ; If %Errorlevel is provided as ordinal value. PUSHQ %1 %ELSE ; %Errorlevel is provided as keyword. PUSHQ %Errorlevel %ENDIF POP RDI POP RAX SYSCALL %ENDMACRO TerminateProgram
ENDHEAD linabi