EuroAssembler Index Manual Download Source Macros


Sitemap Links Forum Tests Projects

linabi.htm
Enumerations
%LinSyscall64List
Macros
GetArg
GetArgCount
LinABI
PutArg
PutEnv
StdInput
StdOutput
TerminateProgram

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
↑ %LinSyscall64List
Assembly-time array %LinSyscall64List declares mnemonic names assigned to Linux kernel 64bit functions, and also the required number of their arguments, as defined in documentation.
Constant prefix sys_ was removed from function names.
Functions declared with unspecified number of arguments (?) were removed or not implemented in the kernel.
Documentation
[LinSyscall64] [LinSyscall64S] [LinManSyscall]
%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
 ;
↑ LinABI   Sys_call, Arg1,Arg2,Arg3,Arg4,Arg5,Arg6, Fastmode=No

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.

Input
Sys_call is the name of invoked kernel function as specified in %LinSyscall64List enumeration, for instance read, write, open etc (case insensitive). Its ordinal number will be provided in register RAX.
Argr* are zero to six parameters (function arguments). They will be copied to registers RDI, RSI, RDX, R10, R8, R9, respectively, before SYSCALL invokation. Do not use RAX in robust (not Fastmode) when other lower parameters are pointers.
Fastmode=No is Boolean argument. When it's false or omitted (default), the LinABI macro preserves all registers except for RAX which returns the result of Function. Arguments in this robust mode may be provided to this macro in any GPR (except for RAX), regardless of the order required by the kernel function specification.
Fastmode=Yes is faster but it destroys registers RCX and R11 clobbered by kernel, and attention must be paid to the order of parameter passing.
Arguments in fast mode are loaded directly to registers, starting with the last, and they may overwrite the previous contents of R9, R8, R10, RDX, RSI, RDI. Programmer should avoid using those registers for parameter passing in fast mode.
When you want to use the fast (nondefault) mode in all LinABI invokations, you don't have to append ,Fastmode=Yes to every invokation of LinABI if you set preprocessing %variable in the beginning of your program: %Fastmode %SETB Yes.
Output if Fastmode=No
RAX, RFlags as returned from kernel function, i.e. the function result, or error code when it's negative.
All other registers are preserved.
Output if Fastmode=Yes
RAX, RFlags as returned from kernel function, i.e. the function result, or error code when it's negative.
RCX,R11 are undefined.
RDI is Par1 or unchanged.
RSI is Par2 or unchanged.
RDX is Par3 or unchanged.
R10 is Par4 or unchanged.
R8 is Par5 or unchanged.
R9 is Par6 or unchanged.
Example
LinABI write, 1, =B"Hello, world!", 13 LinABI exit, 0
Documented
[LinManSyscall]
Remark

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).

Fast mode is optimised for performance, robust mode is optimised for programmer's convenience.
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
↑ GetArg   ArgNumber, Frame=RSP
Macro GetArg retrieves ArgNumber-th parameter provided on command line.
Parameters on the command line are separated with unquoted white space(s).
Single or double quotes are not returned.
Input
ArgNumber is ordinal number of the required parameter (0..127). The 0-th parameter specifies the executable file itself. ArgNr can be an immediate number, 64bit GPR or memory variable.
Frame=RSP specifies the pointer to process stack, i.e. to the QWORD with process' argc value. It can be GPR or [QWORD_memory_variable].
The default 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.
Output
CF=0,
RSI is pointer to the first byte of the requested argument,
RCX is argument size in bytes.
Error
CF=1 Frame is wrong or the requested argument was not provided.
RCX=0
RSI=undefined.
See also
GetArgCount
Process' stack layout
of a program launched as ./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
↑ GetArgCount Frame=RSP
returns number of arguments provided on the command line of the executed program. Arguments are separated with unquoted space(s).
Input
is taken from the command line which launched the program.
Frame=RSP specifies the pointer to process stack, i.e. to the QWORD with process' argc value. It can be GPR or [DWORD_memory_variable].
The default 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.
Output
CF=0
RCX=number of arguments on the command line which launched the program (0..127).
Error
CF=1 if the Frame= was invalid. RCX is undefined.
Depends on
GetArg
GetArgCount %MACRO Frame=RSP
     GetArg -1, Frame=%Frame
   %ENDMACRO GetArgCount
↑ PutArg   ArgNumber, BufPtr, BufSize, Frame=RSP

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.

Input
ArgNumber is ordinal number of the required parameter. The 0-th parameter specifies the executable file itself. ArgNumber can be an immediate number below 128, 64bit GPR or memory variable.
BufPtr is pointer to the Buffer, i.e. to caller-reserved memory for the argument value.
BufSize is size of the reserved memory in bytes. This 3rd parameter may be omitted, SIZE# %BufPtr will be used instead.
Frame=RSP specifies the pointer to process stack, i.e. to the QWORD with process' argc value. It can be GPR or [QWORD_memory_variable].
The default 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.
Output
CF=0 if the requested value was successfully copied to Buffer and zero-terminated.
RCX=number of bytes written to Buffer, without the terminating NUL character.
RCX=0 when the ArgNumber-th argument does not exist and only NUL was written to Buffer.
Error
CF=1 bad Frame or the argument size overflowed BufSize and is therefore incomplete and not zero-terminated.
RCX=number of bytes written to the Buffer (never exceeds BufSize).
Tested by
tlinabi.htm
Depends on
GetArg
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
↑ PutEnv   EnvName$, BufPtr, BufSize, IgnoreCase=Yes, Frame=RSP
Macro PutEnv retrieves environment-variable with zero-terminated name EnvName$ at run-time, and copies its content to the memory specified by BufPtr, BufSize, including the NUL character at its end.
Input
EnvName$ is pointer to zero-terminated string with environment-variable name.
BufPtr is pointer to the caller-reserved memory for the variable value.
BufSize is size of the reserved memory in bytes. This 3rd parameter may be omitted, SIZE# %BufPtr will be used instead.
IgnoreCase=Yes is extended Boolean value specifying if PutEnv should search for EnvName$ case-insensitively.
Frame=RSP specifies the pointer to process stack, i.e. to the QWORD with process' argc value. It can be GPR or [QWORD_memory_variable].
The default 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.
Output
CF=0 if the requested ASCIIZ value was successfully copied to Buffer.
RCX= number of bytes written to BufPtr, without the terminating NUL character.
RCX=0 when the environment variable is empty or does not exist.
Error
CF=1 bad Frame or the value size overflowed BufSize and is therefore incomplete.
RCX= number of bytes written to BufPtr (never above BufSize).
Tested by
tlinabi.htm
Example
PutEnv =B"HOSTNAME", HostName JC .HostNameTooLong: JRCXZ .HostNameNotSet: [.bss] HostName DB 64*BYTE
Process' stack layout of a program executable with environment strings TEMP=/tmp and PATH=/bin:/usr/bin launched from console as ./executable --option argumentA argumentB
┌────────┐ │ 0 │ NULL QWORD which terminates array of pointers to env strings. ├────────┤ │ env2 │ Pointer to 2nd ASCIIZ environment string 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 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
↑ StdOutput String1, String2,,, Size=-1, Handle=1, Eol=No

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.

Input
StringX is pointer to the buffer with characters-to-be-written.
Size=-1 is the maximal possible string size in bytes. If it is left to -1 (default), strings must be zero-terminated. This parameter applies to all ordinal operands.
Handle=1 is the Linux file descriptor. Possible values are defined in linsfile.htm: Eol=No. When this Boolean keyword is switched on, an additional character LF (0x10) will be written on output after all strings have been written.
Output
CF=0. All registers are preserved.
Error
CF=1 when SYSCALL returned an error.
Depends on
LinABI
Examples
StdOutput TextPart1,TextPart2,TextPart3 StdOutput Eol=Yes ; Write new line (LF) only. StdOutput ="Error writing to file ",FileName, Handle=STDERR_FILENO
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
↑ StdInput Buffer, Size=, Handle=0

Macro StdInput reads from standard input specified by the Handle.

Input
Buffer is offset of memory where the input line will be stored.
Size= is the Buffer size. If omitted (default), macro will use SIZE# attribute of the Buffer.
Handle=0 is the Linux file descriptor. Possible values are defined in linsfile.htm:
Output
CF=0, RCX=number of bytes read.
Error
CF=1, RCX=error code.
Example
StdInput RSI, Size=80
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
↑ TerminateProgram Errorlevel=0
This macro provides exit from the running process and return to the operating system.
It also specifies the Errorlevel (plain number) which can be used to inform the batch script which launched the program whether the program terminated normally or due to some error condition.
Input
Errorlevel= is the return code of the terminating program.
Beside the keyword Errorlevel=, this value may also be specified as an ordinal operand.
When this argument is omitted, it defaults to 0.
Output
is not applicable.
Examples
TerminateProgram 8 ; Ordinal value of errorlevel. TerminateProgram Errorlevel=[WorstErrLevel] ; Keyword value (from memory).
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

▲Back to the top▲