Basic AMD64 System::Call support

git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@6444 212acab6-be3b-0410-9dea-997c60f758d6
This commit is contained in:
anders_k 2014-03-03 18:10:53 +00:00
parent a7261be70c
commit a7076ff238
9 changed files with 268 additions and 46 deletions

View file

@ -26,14 +26,25 @@ docs = Split("""
Import('BuildPlugin env')
if env['TARGET_ARCH'] != 'amd64':
defs = ['SYSTEM_EXPORTS']
msvc = 'msvc' in env['TOOLS'] or 'mstoolkit' in env['TOOLS']
if env['TARGET_ARCH'] != 'amd64' or msvc: # BUGBUG: Call-amd64.S is missing GAS macros
srcsuff = ''
if env['TARGET_ARCH'] != 'x86':
srcsuff = '-' + env['TARGET_ARCH']
defs += ['SYSTEM_NOCALLBACKS'] # BUGBUG: Remove this when CallBack() is implemented
if msvc: # BUGBUG: Remove this when GAS is fixed
defs += ['SYSTEM_PARTIALCALLSUPPORT']
filename = 'Call' + srcsuff
conf = env.Configure()
if conf.TryCompile('END', '.S'):
files += ['Source/Call.S']
files += ['Source/'+filename+'.S']
elif conf.TryCompile('.end', '.sx'):
files += ['Source/Call.sx']
files += ['Source/'+filename+'.sx']
else:
print 'WARNING: System.dll: unable to find assembler for Call.S'
print 'WARNING: System.dll: unable to find assembler for '+filename+'.S'
conf.Finish()
else:
print 'WARNING: System.dll: missing Win64 code, dynamic function calls not supported'
@ -45,7 +56,7 @@ BuildPlugin(
examples,
docs,
nodeflib = False,
defines = ['SYSTEM_EXPORTS']
defines = defs
)
res = 'Resource/Resource.rc'

View file

@ -0,0 +1,181 @@
;#
;# This file is a part of NSIS.
;#
;# Copyright (C) 2014 Anders Kjersem
;#
;# Licensed under the zlib/libpng license (the "License");
;# you may not use this file except in compliance with the License.
;#
;# Licence details can be found in the file COPYING.
;#
;# This software is provided 'as-is', without any express or implied
;# warranty.
;#
;#
;# MASM:
;# ml64.exe /c Call-amd64.S
;#
; .if 0
;# MASM
SECTION_DATA equ .data
SECTION_CODE equ .code
FUNC_DECL MACRO name
name PROC
ENDM
FUNC_END MACRO name
name ENDP
ENDM
;# ~MASM
IF 0
; .else
;# GNU
.intel_syntax noprefix
#define IFDEF .ifdef
#define ELSE .else
#define ENDIF .endif
#define SECTION_DATA .data
#define SECTION_CODE .text
#define END .end
#define EXTERN .extern
;# ~GNU
ENDIF
EXTERN __imp_GetLastError : PROC
IFDEF SYSTEM_LOG_DEBUG
EXTERN __imp_IsDebuggerPresent : PROC
ENDIF
EXTERN LastError : DWORD
EXTERN GetProcOffset : PROC
EXTERN GetParamsOffset : PROC
EXTERN GetSizeOfProcParam : PROC
EXTERN GetValueOffsetParam : PROC
EXTERN SetProcResultOk : PROC
EXTERN GetErrorOption : PROC
SECTION_CODE
FUNC_DECL CallProc2 ;# rcx=SystemProc* edx=ParamCount
mov [rsp+8h], r12
mov [rsp+10h], r13
mov [rsp+18h], r14
;#mov [rsp+20h], r15
;# The stack is unaligned on function entry. We have to calculate the required
;# stack size for our parameters + maybe 8 padding bytes to end up 16 byte aligned.
pSystemProc equ r14
mov pSystemProc, rcx ;# Save SystemProc*
;# Not required since we zero-extend eax: xor rax, rax
mov r13d, edx ;# Save ParamCount
imul eax, edx, 8
and edx, 1
jnz noparamalignpadding
lea eax, [eax+8] ;# sizeof(params) + 8 will make us 16 byte aligned
noparamalignpadding:
cmp eax, 28h ;# The ABI guarantees shadow space for the 4 register parameters
ja computedstacksize
mov eax, 28h ;# Minimum (4*8) + 8 to align
computedstacksize:
mov r12d, eax ;# Save stack size (Zero-extended mov)
sub rsp, r12
IFDEF SYSTEM_LOG_DEBUG
;# BUGBUG: Remove this
call qword ptr [__imp_IsDebuggerPresent]
test eax, eax
jz nodbgbrk
int 3
nodbgbrk:
ENDIF
;# We are going to set all stack parameters including the first 4,
;# it does not hurt to do that and it allows us to copy them to
;# their registers without reading pSystemProc->Params[1..3] again
call GetSizeOfProcParam
mov r9, rax ;# Store sizeof(ProcParameter)
call GetValueOffsetParam
mov r8, rax ;# Store FIELD_OFFSET(ProcParameter,Value)
call GetParamsOffset
lea r10, [pSystemProc+rax] ;# Store pSystemProc+FIELD_OFFSET(SystemProc,Params)
mov ecx, r13d ;# Zero-extended mov
test rcx, rcx
jz callthefunc
setparameter:
mov rax, r9
mul rcx ;# rax = sizeof(ProcParameter) * paramidx (paramidx is 1 based because the return value is stored in Params[0])
add rax, r10 ;# rax += pSystemProc->Params
mov rax, qword ptr [rax+r8] ;# rax = pSystemProc->Params[paramidx].Value
dec rcx
mov [rsp+(8*rcx)], rax
inc rcx
loop setparameter
;# The 4 parameter registers are all volatile so we might as well assign all of them:
;# setparam4:
;# cmp r13d, 4
;# jb setparam3
mov r9, [rsp+(8*3)]
;# setparam3:
;# cmp r13d, 3
;# jb setparam2
mov r8, [rsp+(8*2)]
;# setparam2:
;# cmp r13d, 2
;# jb setparam1
mov rdx, [rsp+(8*1)]
;# setparam1:
;# cmp r13d, 1
;# jb callthefunc
mov rcx, [rsp+(8*0)]
callthefunc:
call GetProcOffset
mov r10, qword ptr [pSystemProc+rax]
xor rax, rax ;# Fix bug #1535007
call r10
mov r13, rax ;# Save return value
mov rcx, pSystemProc
call GetErrorOption
test eax, eax
jz capturegle_done
call qword ptr [__imp_GetLastError]
mov dword ptr [LastError], eax
capturegle_done:
;# proc->Params[0].Value = pSystemProc->Proc's return value
call GetParamsOffset
mov rdx, rax ;# This assumes that the next function is not going to clobber rdx!
call GetValueOffsetParam
add rdx, rax
mov qword ptr [pSystemProc+rdx], r13
mov rcx, pSystemProc
call SetProcResultOk ;# BUGBUG: This is pointless, system.c should just assume we are OK
mov rax, pSystemProc ;# Return SystemProc*
;# Epilog:
;# http://msdn.microsoft.com/en-us/library/tawsa7cb claims that only
;# add/lea rsp and pop is valid in the epilog. Unwind might fail on our version?
add rsp, r12 ;# Restore stack
;# Restore nonvolatile registers:
mov r12, [rsp+8h]
mov r13, [rsp+10h]
mov r14, [rsp+18h]
;#mov r15, [rsp+20h]
ret
FUNC_END CallProc2
END

View file

@ -275,6 +275,7 @@ TODO: CallProc/Back not implemeted.
Fake the behavior of the System plugin for the LoadImage API function so MUI works.
BUGBUG: MUI is leaking DeleteObject and failing GetClientRect
*/
#ifndef SYSTEM_PARTIALCALLSUPPORT
SystemProc* CallProc(SystemProc *proc)
{
INT_PTR ret, *place;
@ -294,12 +295,13 @@ SystemProc* CallProc(SystemProc *proc)
if (place) *place = ret;
return proc;
}
#endif // ~SYSTEM_PARTIALCALLSUPPORT
SystemProc* CallBack(SystemProc *proc)
{
proc->ProcResult = PR_ERROR;
return proc;
}
#endif
#endif // ~_WIN64
PLUGINFUNCTION(Call)
@ -355,7 +357,7 @@ PLUGINFUNCTION(Call)
proc->Params[0] = pp; // Restore old return param
}
else
ParamsOut(proc);
ParamsOut(proc);
}
if (proc->ProcResult != PR_CALLBACK)
@ -768,7 +770,9 @@ SystemProc *PrepareProc(BOOL NeedForCall)
break;
case _T('!'): temp = -temp; break;
case _T('c'):
#ifndef _WIN64
temp2 = POPT_CDECL;
#endif
break;
case _T('r'):
temp2 = POPT_ALWRETURN;
@ -993,16 +997,19 @@ void ParamsIn(SystemProc *proc)
#ifdef SYSTEM_LOG_DEBUG
{
TCHAR buf[666];
wsprintf(buf, _T("\t\t\tParam In %d: type %d value ")SYSFMT_HEXPTR _T(" value2 0x%08X"), i,
par->Type, par->Value, par->_value);
UINT32 hi32 = 0;
#ifndef _WIN64
hi32 = par->_value;
#endif
wsprintf(buf, _T("\t\t\tParam In %d:\tType=%d Value=")SYSFMT_HEXPTR _T(" hi32=0x%08X"), i,
par->Type, par->Value, hi32);
SYSTEM_LOG_ADD(buf);
SYSTEM_LOG_POST;
}
#endif
if (i == 0) break;
if (i == proc->ParamCount) i = 0;
else i++;
if (i == proc->ParamCount) i = 0; else i++;
}
}
@ -1101,7 +1108,7 @@ void ParamsOut(SystemProc *proc)
#ifdef SYSTEM_LOG_DEBUG
{
TCHAR dbgbuf[99];
wsprintf(dbgbuf, _T(") %d:\tType=%d Optn=%d Size=%d Data="),
wsprintf(dbgbuf, _T(")\t%d:\tType=%d Optn=%d Size=%d Data="),
i, proc->Params[i].Type, proc->Params[i].Option, proc->Params[i].Size);
SYSTEM_LOG_ADD(dbgbuf);
SYSTEM_LOG_ADD(realbuf);
@ -1118,7 +1125,7 @@ void ParamsOut(SystemProc *proc)
HANDLE CreateCallback(SystemProc *cbproc)
{
#ifdef SYSTEM_X64
#ifdef SYSTEM_AMD64
return BUGBUG64(HANDLE) NULL;
#else
char *mem;
@ -1285,7 +1292,7 @@ BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID lpReserved)
#ifdef SYSTEM_X86
retexpr[0] = (char) 0xC2;
retexpr[2] = 0x00;
#elif defined(SYSTEM_X64)
#elif defined(SYSTEM_AMD64)
retexpr[0] = BUGBUG64(0);
#else
#error TODO
@ -1329,9 +1336,9 @@ unsigned int GetErrorOption(SystemProc *proc)
/*
Returns offset for element Proc of SystemProc structure
*/
unsigned int GetProcOffset(void)
UINT_PTR GetProcOffset(void)
{
return (unsigned int)(&(((SystemProc *)0)->Proc));
return (UINT_PTR)(&(((SystemProc *)0)->Proc));
}
/*
@ -1369,15 +1376,15 @@ unsigned int GetParamCount(SystemProc *proc)
/*
Returns offset for element Params of SystemProc structure
*/
unsigned int GetParamsOffset(void)
UINT_PTR GetParamsOffset(void)
{
return (unsigned int)(&(((SystemProc *)0)->Params));
return (UINT_PTR)(&(((SystemProc *)0)->Params));
}
/*
Returns size of ProcParameter structure
*/
unsigned int GetSizeOfProcParam(void)
UINT_PTR GetSizeOfProcParam(void)
{
return (sizeof(ProcParameter));
}
@ -1393,11 +1400,12 @@ unsigned int GetSizeOffsetParam(void)
/*
Returns offset for element Value of ProcParameter structure
*/
unsigned int GetValueOffsetParam(void)
UINT_PTR GetValueOffsetParam(void)
{
return (unsigned int)(&(((ProcParameter *)0)->Value));
return (UINT_PTR)(&(((ProcParameter *)0)->Value));
}
#ifndef _WIN64
/*
Returns offset for element _value of ProcParameter structure
*/
@ -1405,6 +1413,7 @@ unsigned int Get_valueOffsetParam(void)
{
return (unsigned int)(&(((ProcParameter *)0)->_value));
}
#endif
/*
Sets "CLONE" option

View file

@ -4,16 +4,17 @@
// This should probably be moved to platform.h at some point
#if defined(_M_X64) || defined(_M_AMD64) || defined(__amd64__)
# define SYSTEM_X64
# define SYSTEM_AMD64
#elif defined(_M_IX86) || defined(__i386__) || defined(_X86_)
# define SYSTEM_X86
# define SYSTEM_X86
#else
# error "Unknown architecture!"
# error "Unknown architecture!"
#endif
#ifdef _WIN64
#define SYSFMT_HEXPTR _T("0x%016IX")
# define SYSFMT_HEXPTR _T("0x%016IX")
#else
#define SYSFMT_HEXPTR _T("0x%08X")
# define SYSFMT_HEXPTR _T("0x%08X")
#endif
@ -25,9 +26,9 @@
// defined with this macro as being exported.
#ifdef SYSTEM_EXPORTS
#define SYSTEM_API __declspec(dllexport)
# define SYSTEM_API __declspec(dllexport)
#else
#define SYSTEM_API __declspec(dllimport)
# define SYSTEM_API __declspec(dllimport) // BUGBUG: This is a plugin, who is going to import the functions directly?
#endif
#define NEW_STACK_SIZE 256*256
@ -123,7 +124,7 @@ struct tag_CallbackThunk
#pragma pack(pop)
*/
char asm_code[10];
#elif defined(SYSTEM_X64)
#elif defined(SYSTEM_AMD64)
char asm_code[BUGBUG64(1)]; // TODO: BUGBUG64
#else
#error "Asm thunk not implemeted for this architecture!"
@ -135,7 +136,7 @@ struct tag_CallbackThunk
// Free() only knows about pNext in CallbackThunk, it does not know anything about the assembly, that is where this helper comes in...
#ifdef SYSTEM_X86
# define GetAssociatedSysProcFromCallbackThunkPtr(pCbT) ( (SystemProc*) *(unsigned int*) (((char*)(pCbT))+1) )
#elif defined(SYSTEM_X64)
#elif defined(SYSTEM_AMD64)
# define GetAssociatedSysProcFromCallbackThunkPtr(pCbT) BUGBUG64(NULL)
#else
# error "GetAssociatedSysProcFromCallbackThunkPtr not defined for the current architecture!"
@ -145,14 +146,23 @@ struct tag_CallbackThunk
extern const int ParamSizeByType[]; // Size of every parameter type (*4 bytes)
extern HANDLE CreateCallback(SystemProc *cbproc);
extern SystemProc *PrepareProc(BOOL NeedForCall);
extern SystemProc* PrepareProc(BOOL NeedForCall);
extern void ParamAllocate(SystemProc *proc);
extern void ParamsDeAllocate(SystemProc *proc);
extern void ParamsIn(SystemProc *proc);
extern void ParamsOut(SystemProc *proc);
extern SystemProc *CallProc(SystemProc *proc);
extern SystemProc *CallBack(SystemProc *proc);
extern SystemProc *RealCallBack();
#ifdef SYSTEM_AMD64
#ifdef SYSTEM_PARTIALCALLSUPPORT
extern SystemProc* CallProc2(SystemProc *proc, UINT_PTR ParamCount);
#define CallProc(p) CallProc2((p), (p)->ParamCount) // ParamCount is passed as a parameter so CallProc2 can determine the required stack size without a function call
#endif
#else // !SYSTEM_AMD64
extern SystemProc* CallProc(SystemProc *proc);
#endif // ~SYSTEM_AMD64
#ifndef SYSTEM_NOCALLBACKS
extern SystemProc* CallBack(SystemProc *proc);
extern SystemProc* RealCallBack();
#endif
extern void CallStruct(SystemProc *proc);
#ifdef _UNICODE

View file

@ -422,7 +422,7 @@ System::Free $0
<blockquote>
<ul>
<li>To find out the index of a member in a COM interface, you need to search for the definition of this COM interface in the header files that come with Visual C/C++ or the Platform SDK. Remember the index is zero based.</li>
<li>If a function can't be found, an `A' will be appended to its name and it will be looked up again. This is done because a lot of Windows API functions have two versions, one for ANSI strings and one for Unicode strings. The ANSI version of the function is marked with `A' and the Unicode version is marked with `W'. For example: lstrcpyA and lstrcpyW.</li>
<li>If a function can't be found or the <code>t</code> parameter type was used, an `A' or `W' will be appended to its name and it will be looked up again. This is done because a lot of Windows API functions have two versions, one for ANSI strings and one for Unicode strings. The ANSI version of the function is marked with `A' and the Unicode version is marked with `W'. For example: lstrcpyA and lstrcpyW.</li>
</ul>
</blockquote>
@ -436,7 +436,7 @@ System::Free $0
</tr>
<tr>
<th>c</th>
<td>cdecl calling convention (the stack restored by caller). By default stdcall calling convention is used (the stack restored by callee).</td>
<td>cdecl calling convention (the stack restored by caller). By default stdcall calling convention is used on x86 (the stack restored by callee).</td>
<td></td>
</tr>
<tr>

View file

@ -6,6 +6,12 @@ Released on ?, 2014
\S1{v3.0b0-rl} Release Notes
\S1{v3.0b0-cl} Changelog
\S2{} Major Changes
\b Basic AMD64 System::Call support
\S2{} Minor Changes
\b Added Int<32|64|Ptr><Op|Cmp[U]> helper macros to Util.nsh

View file

@ -45,7 +45,8 @@ defenv.Append(CPPDEFINES = [('NSISCALL', '$STDCALL')])
### asm
defenv.Append(ASFLAGS = ['/coff'])
if 'x86' in defenv.get('TARGET_ARCH','x86'):
defenv.Append(ASFLAGS = ['/coff']) # ML64 does not support /coff
### debug

View file

@ -287,9 +287,10 @@ def generate(env):
include_path, lib_path, exe_path, sdk_path = "", "", "", ""
targ_arc = env.get('TARGET_ARCH', 'x86')
if os.environ.has_key('MSVC_USE_SCRIPT') and "None" == os.environ['MSVC_USE_SCRIPT']:
for x in ['INCLUDE', 'LIB', 'PATH']: env['ENV'][x] = ""
for x in ['INCLUDE', 'LIB', 'PATH', 'CL', 'LINK', 'ML']: env['ENV'][x] = ""
if not env.WhereIs('cl', os.environ['PATH']):
raise SCons.Errors.InternalError("CL not found in %s" % os.environ['PATH'])
include_path = os.environ['INCLUDE']
@ -309,8 +310,8 @@ def generate(env):
env.PrependENVPath('INCLUDE', include_path)
env.PrependENVPath('LIB', lib_path)
env.PrependENVPath('PATH', exe_path)
env['ENV']['CPU'] = 'i386' # TODO: Check TARGET_ARCH for AMD64
env['ENV']['CPU'] = (targ_arc.upper(), 'i386')['x86' in targ_arc.lower()] # i386 or AMD64
env['ENV']['TARGETOS'] = 'BOTH'
env['ENV']['APPVER'] = '4.0'
env['ENV']['MSSDK'] = sdk_path
@ -319,7 +320,7 @@ def generate(env):
env['ENV']['INETSDK'] = sdk_path
env['ENV']['MSSDK'] = sdk_path
env['ENV']['MSTOOLS'] = sdk_path
env['CFILESUFFIX'] = '.c'
env['CXXFILESUFFIX'] = '.cc'
@ -329,7 +330,10 @@ def generate(env):
env['AR'] = '"' + sdk_path_AR + '"'
env['ARFLAGS'] = SCons.Util.CLVar('/nologo')
env['ARCOM'] = "${TEMPFILE('$AR $ARFLAGS /OUT:$TARGET $SOURCES')}"
if 'AMD64' in targ_arc.upper():
env['AS'] = 'ml64'
env['SHLINK'] = '$LINK'
env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS /dll')
env['_SHLINK_TARGETS'] = win32ShlinkTargets

View file

@ -1056,11 +1056,11 @@ static INT_PTR CALLBACK DirProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM l
{
BOOL (WINAPI *GDFSE)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER) =
#ifdef _WIN64
GetDiskFreeSpaceEx;
GetDiskFreeSpaceEx;
#else
myGetProcAddress(MGA_GetDiskFreeSpaceEx);
#endif
myGetProcAddress(MGA_GetDiskFreeSpaceEx);
if (GDFSE)
#endif
{
ULARGE_INTEGER available64;
ULARGE_INTEGER a, b;
@ -1093,7 +1093,7 @@ static INT_PTR CALLBACK DirProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM l
}
}
if (!available_set) // TODO: Can GetDiskFreeSpace succeed when ..Ex failed on x64?
if (!available_set && sizeof(void*) <= 4)
{
DWORD spc, bps, fc, tc;
TCHAR *root;