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..334 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=Yesto every invokation ofLinABIif 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.
%IF "%Sys_call"===""
%ERROR ID=5813,%0 function was not specified.
%ENDIF
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
MemAlloc %MACRO Size
LinABI mmap, 0, %Size, PROT_EXEC|PROT_READ|PROT_WRITE, \
MAP_PRIVATE|MAP_32BITS|MAP_ANONYMOUS, -1, 0, Fastmode=no
CMP RAX,-ERR_MAX
CMC
%ENDMACRO MemAlloc
MemFree %MACRO Address, Size=0
LinABI munmap, %Address, %Size, Fastmode=no
CMP RAX,-ERR_MAX
CMC
%ENDMACRO MemFree
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 ECX,EBX
JC .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 argumentBPATH=/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 strings NAME=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 string Name=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, Console=disabled, Unicode=disabled
%IF %Unicode
%ERROR ID=5946, 'Macroinstructions for Linux expect Unicode=off.'
%ENDIF
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