NASM - The Netwide Assembler
NASM Forum => Example Code => Topic started by: TightCoderEx on June 13, 2012, 12:01:17 PM
-
After much experimentation and even though GDB really doesn't like this, I've settled on STDCALL, probably from having done it that way so long, it just seems easier to design algos
This is primarily designed to facilitate non-canonical input in my apps, but additional functionality can be incorporated using mode SET = 1 and modifying whatever flags outside the procedure.
SYS_IOCTL equ 16
TCGETS equ 0x5401
TCSETS equ 0x5402
MASK equ ICANON | ECHO ; ICANON = 2, ECHO = 8
ARG1 equ 16
ARG2 equ 24
global Termios
extern FmtErr
section .text
; =============================================================================================
; bool Termios ( int Function, char *Termios_struct )
; ENTER: ARG1 = Function # 0 = GET, 1 = SET, 2 = TOGGLE
; ARG2 = Pointer to termios structure
; LEAVE: RAX = True (-1), function failed, otherwise False (0).
; ---------------------------------------------------------------------------------------------
Termios: push rbp
mov rbp, rsp ; So ARG1 can be addressed conventionally
push rdx
push rsi ; Preserve
push rdi
; RDX & RDI are common to all modes
xor rdi, rdi ; File descriptor ( can be STDIN or STDOUT )
mov rdx, [rbp + ARG2] ; Pointer termios structure
; Get function mode from caller and because setting is more probably we'll set it
; as default
mov rcx, [rbp + ARG1] ; Get function mode
mov rax, rcx ; Simple way of resetting RAX
mov ax, TCGETS ; Most probable = 5402H
cmp cl, 1
jb .DoFunc
; Toggle bits 1 and 0 of AL. Hopefully kernel never changes this number
.Set xor al, 3 ; Now = 5401H
cmp cl, 1 ; Are we just setting new functionality
jz .DoFunc
; Caller could have done this, but this method save a bit of code overall because
; it might be called multiple times in app.
xor byte [rdx + 12], MASK ; Toggle NON-CANONICAL on/off
; Finish setting up registers.
.DoFunc mov rsi, rax ; Move function into RSI
mov ax, SYS_IOCTL
syscall ; Execute function
or rax, rax
jge .Exit
push 0 ; Have proc find syscall address
push rax ; Unmodifed error code
call FmtErr ; Display error
or rax, -1 ; Set error condition
.Exit pop rdi
pop rsi ; Retrieve
pop rdx
leave ; Kill frame
ret 16 ; Waste 2 arguments
-
Technically, for STDCALL calling convention, shouldn't the method signature be Termios@12
Microsoft ( who designed that convention ) doesn't use it for native x64 apps. I'm just nit-picking ;D
-
I had noticed too by default not even gcc uses STDCALL. I'm not sure the reason, but I do know if a programmer or some kind of change in kernel resets the "D" flag, my apps will be hooped. In MASM, this call would have been decorated _Termios@16. It seems ld requires no such decoration, not even in an archive.
-
Oh yeah, I forgot about that pesky prefix!
Also, are you certain about how MASM decorates your function? I haven't played with MASM since my DOS days.
Given that your method is defined as bool Termios ( int Function, char *Termios_struct )
the signature for Win32 would definitely be _Termios@8 but I would think only the pointer would expand on x64, thus my original _Termios@12. To my knowledge there is no x64 STDCALL so I'm curious as to MASM decorating it as _Termios@16.
-
This is what I use for a test bench. The comment section denotes stack addresses with 0x7ffffffe stripped from them.
0: c8 30 00 00 enter 0x30,0x0 ; 0x250
4: 54 push rsp ; 0x218
5: 6a 00 push 0x0 ; 0x210
7: e8 00 00 00 00 call Termios ; 0x208
NASM complains about pushing a 32 bit register, similarly it doesn't like greater than a 32 bit immediate. I assume the same thing would happen using MASM on W7, which would mean _Termios@16 be the result. Maybe somebody will drop by that uses 64 bit MASM and give us some insight as to what actually happens.
-
I had noticed too by default not even gcc uses STDCALL.
#define STDCALL __attribute__((stdcall))
....
bool STDCALL Termios ( int Function, char *Termios_struct );
-
Thinking a little more about it: x64 stack alignment requirements may be the reason for _Termios@16 in MASM.
At this point I'm bailing out and letting someone else with MASM experience chime in. ;D
-
Compliance with the calling conventions and instruction set is key to understanding the decisions MASM makes.
On x86-64, push/pop is 64-bit. 32-bit immediate values are sign-extended to 64-bit. At the opcode level, push ax, push eax and push rax are all the same (although pushing r8 through r15 involves a prefix) and the current processor mode (16/32/64) dictates the actual data size. It is up to the assembler to figure out if you are doing something "illegal" on a logical level, e.g. pushing a 32-bit register within code meant for 64-bit mode. I am not sure if MASM specifically will indicate such as an illegal operation, or quietly let it pass.
Also, SSE being part of the x86-64 architecture is probably one good reason to standardize around having 16-byte aligned stacks. The x86-64 calling convention found at the first link, below, enforces exactly that -- but don't confuse that (used by almost everything major *but* Windows) with Microsoft's x64 calling convention.
Useful Links:- http://www.x86-64.org/documentation/assembly.html (http://www.x86-64.org/documentation/assembly.html)
- http://www.godevtool.com/GoasmHelp/64bits.htm (http://www.godevtool.com/GoasmHelp/64bits.htm)
- http://en.wikibooks.org/wiki/X86_Disassembly/Calling_Conventions (http://en.wikibooks.org/wiki/X86_Disassembly/Calling_Conventions)
- http://msdn.microsoft.com/en-us/library/ms235286.aspx (http://msdn.microsoft.com/en-us/library/ms235286.aspx)