| Index | Manual | Download | Source | Macros | Embedded word Case ins.
Sitemap | Links | Forum | Tests | Projects

# linabi.htm
Enumerations
%LinSyscall64List
Macros
GetArg
GetArgCount
LinABI
MemAlloc
MemFree
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..334 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.
    %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 Size
  Macro MemAlloc allocates memory from Linux.
Input
  Size is the number of bytes allocated memory. It will be rounded up to the multiply of pagesize (4 KiB).
Output
  CF=0,
  RAX is pointer to the first byte of the allocated memory.
Error
  CF=1
  RAX=Error number
Depends on
  LinABI
See also
  MemFree

    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 Address, Size
  Macro MemFree unallocates memory from Linux.
Input
  Address as returned from MemAlloc which allocated it.
  Size is the size in bytes of the unallocated memory.
Output
  CF=0,
  RAX=0
Error
  CF=1
  RAX=Error number
Depends on
  LinABI
See also
  MemAlloc

    MemFree %MACRO Address, Size=0
         LinABI munmap, %Address, %Size, Fastmode=no
         CMP RAX,-ERR_MAX
         CMC
        %ENDMACRO MemFree

↑ 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. Special value -1 is used to retrieve the number of arguments.
  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).
         CMP RCX,128
         CMC
         JC .80:
         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

↑ 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+8]` 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:
  - STDOUT_FILENO = 1
  - STDERR_FILENO = 2
  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.
  Unicode=disabled, Console=disabled are ignored, they provide compatibility with Windows version.
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, 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

↑ 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:
  - STDIN_FILENO = 0
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▲