From a7076ff238955ba66ab4c9b63a461031a227293d Mon Sep 17 00:00:00 2001 From: anders_k Date: Mon, 3 Mar 2014 18:10:53 +0000 Subject: [PATCH] Basic AMD64 System::Call support git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@6444 212acab6-be3b-0410-9dea-997c60f758d6 --- Contrib/System/SConscript | 21 +++- Contrib/System/Source/Call-amd64.S | 181 +++++++++++++++++++++++++++++ Contrib/System/Source/System.c | 41 ++++--- Contrib/System/Source/System.h | 36 +++--- Contrib/System/System.html | 4 +- Docs/src/history.but | 6 + SCons/Config/ms | 3 +- SCons/Tools/mstoolkit.py | 14 ++- Source/exehead/Ui.c | 8 +- 9 files changed, 268 insertions(+), 46 deletions(-) create mode 100644 Contrib/System/Source/Call-amd64.S diff --git a/Contrib/System/SConscript b/Contrib/System/SConscript index 6839f889..40512458 100644 --- a/Contrib/System/SConscript +++ b/Contrib/System/SConscript @@ -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' diff --git a/Contrib/System/Source/Call-amd64.S b/Contrib/System/Source/Call-amd64.S new file mode 100644 index 00000000..5e1670da --- /dev/null +++ b/Contrib/System/Source/Call-amd64.S @@ -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 diff --git a/Contrib/System/Source/System.c b/Contrib/System/Source/System.c index 678b3503..9b3f3f0c 100644 --- a/Contrib/System/Source/System.c +++ b/Contrib/System/Source/System.c @@ -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 diff --git a/Contrib/System/Source/System.h b/Contrib/System/Source/System.h index 4c9ac27f..945cbdee 100644 --- a/Contrib/System/Source/System.h +++ b/Contrib/System/Source/System.h @@ -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 diff --git a/Contrib/System/System.html b/Contrib/System/System.html index bd592f82..29d54b51 100644 --- a/Contrib/System/System.html +++ b/Contrib/System/System.html @@ -422,7 +422,7 @@ System::Free $0
@@ -436,7 +436,7 @@ System::Free $0 c -cdecl calling convention (the stack restored by caller). By default stdcall calling convention is used (the stack restored by callee). +cdecl calling convention (the stack restored by caller). By default stdcall calling convention is used on x86 (the stack restored by callee). diff --git a/Docs/src/history.but b/Docs/src/history.but index 509d0a0c..73fa5248 100644 --- a/Docs/src/history.but +++ b/Docs/src/history.but @@ -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> helper macros to Util.nsh diff --git a/SCons/Config/ms b/SCons/Config/ms index 8072fba9..735e6c95 100644 --- a/SCons/Config/ms +++ b/SCons/Config/ms @@ -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 diff --git a/SCons/Tools/mstoolkit.py b/SCons/Tools/mstoolkit.py index e4f8f23a..d200dc3c 100644 --- a/SCons/Tools/mstoolkit.py +++ b/SCons/Tools/mstoolkit.py @@ -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 diff --git a/Source/exehead/Ui.c b/Source/exehead/Ui.c index be314d83..c6ab9558 100644 --- a/Source/exehead/Ui.c +++ b/Source/exehead/Ui.c @@ -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;