Skip to content

installer: actually build a 64-bit installer - #732

Merged
dscho merged 1 commit into
mainfrom
64-bit-installer
Aug 31, 2026
Merged

installer: actually build a 64-bit installer#732
dscho merged 1 commit into
mainfrom
64-bit-installer

Conversation

@dennisameling

Copy link
Copy Markdown
Member

This fixes git-for-windows/git#6372.

Confirming that the installer is now truly a x64 executable:

image

Inno Setup 7 introduced 64-bit installers, but building 32-bit ones stays the default in both editions; a 64-bit one is only produced when the [Setup] section directive SetupArchitecture is set to x64. Updating the compiler to Inno Setup 7.0.2 therefore did not change the architecture of the installer, even though the v2.55.0(5) release notes already announced it as a 64-bit executable.

Set SetupArchitecture=x64 for the flavors that ship 64-bit Git, i.e. the ones where release.sh sets BITNESS to 64: MINGW64, UCRT64 and CLANGARM64. The i686 installer keeps building as a 32-bit executable. The directive does not exist in Inno Setup 6, hence the version guard.

Flipping that directive alone leaves the installer broken, though: Pascal Script records are always packed, and in a 64-bit installer pointers are eight bytes wide, so the Windows API records declared in exec-with-capture.inc.iss no longer match the layout those functions expect. CreatePipe() reads lpSecurityDescriptor from the wrong offset, fails with ERROR_NOACCESS, and ExecWithCapture() returns False without ever having run the command. Starting the wizard then greets the user with

Unable to get system config (exit code 4294967295):

Widen the pointer-sized fields of SECURITY_ATTRIBUTES and STARTUPINFO to ULONG_PTR and add the padding that the aligned C structs have and a packed record does not, which brings them to the 24 and 104 bytes that the x64 ABI mandates. MSG needs the same treatment for a different reason: PeekMessage() writes the full 48 bytes of that struct, which is more than an unpadded record offers, corrupting the stack and killing the message pump with a fatal exception once a captured command runs long enough for the pump to kick in.

The padding is conditional on the new SETUP_IS_X64 symbol, so 32-bit installers keep the record layout they have always had.

The Win95 and Win2000 code paths in modules.inc.iss declare records with the same problem (PROCESSENTRY32, MODULEENTRY32), but MinVersion is 6.3 and those paths are only taken on Windows versions before Vista, so they are left alone.

Comment thread installer/install.iss
Comment on lines +56 to +61
#if BITNESS=='64' && Ver>=EncodeVer(7, 0, 0)
; Inno Setup 7 builds 32-bit installers unless told otherwise. SETUP_IS_X64
; lets the Pascal Script code declare Windows API records for 64-bit.
#define SETUP_IS_X64
SetupArchitecture=x64
#endif

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a follow-up, we'll probably want to remove all 32-bit / x86-related code from the installer, given that it's no longer supported (except for MinGit which doesn't use this installer).

Comment thread installer/exec-with-capture.inc.iss
Comment thread installer/exec-with-capture.inc.iss Outdated
#ifdef SETUP_IS_X64
timePadding:DWORD;
#endif
pt:TPoint;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, there is a tiny problem here (will fix in a moment): TPoint does not need to be 8-byte aligned, apparently:

#include <stdio.h>
#include <windows.h>

int main(int argc, char **argv)
{
	MSG msg;
#define P(name) printf("offset of " #name ": 0x%02x\n", (int)((char *)&msg. name - (char *)&msg))
        P(hwnd);
        P(message);
        P(wParam);
        P(lParam);
        P(time);
        P(pt);
        printf("offset of lPrivate: 0x%02x\n", (int)((char *)&msg.pt - (char *)&msg) + sizeof(msg.pt));
	return 0;
}

prints

offset of hwnd: 0x00
offset of message: 0x08
offset of wParam: 0x10
offset of lParam: 0x18
offset of time: 0x20
offset of pt: 0x24
offset of lPrivate: 0x2c

And this test InnoSetup program with the adjusted definition agrees:

[Setup]
AppName=MSG Layout Probe
AppVersion=1
DefaultDirName={tmp}\MsgProbe
OutputBaseFilename=msg-probe
SetupArchitecture=x64
Uninstallable=no

[Code]
type
  TByteArray = array of Byte;

  TMsg = record
    hwnd:HWND;
    message:UINT;
    messagePadding:DWORD;
    wParam:ULONG_PTR;
    lParam:ULONG_PTR;
    time:DWORD;
    pt:TPoint;
    lPrivate:DWORD;
  end;

procedure RtlMoveMemory(var Destination: TMsg; Source: TByteArray; Length: SIZE_T);
  external 'RtlMoveMemory@kernel32.dll stdcall';

function InitializeSetup: Boolean;
var
  Bytes: TByteArray;
  Info: TMsg;
  I: Integer;
begin
  SetArrayLength(Bytes, 48);
  for I := 0 to GetArrayLength(Bytes) - 1 do
    Bytes[I] := 0;

  Bytes[$00] := 1;
  Bytes[$08] := 2;
  Bytes[$10] := 3;
  Bytes[$18] := 4;
  Bytes[$20] := 5;
  Bytes[$24] := 6;
  Bytes[$28] := 7;
  Bytes[$2c] := 8;

  RtlMoveMemory(Info, Bytes, SizeOf(Info));

  Log('SizeOf=' + IntToStr(SizeOf(Info)));
  Log('hwnd=' + UIntToStr(Info.hwnd));
  Log('message=' + UIntToStr(Info.message));
  Log('wParam=' + UIntToStr(Info.wParam));
  Log('lParam=' + UIntToStr(Info.lParam));
  Log('time=' + UIntToStr(Info.time));
  Log('pt.x=' + UIntToStr(Info.pt.x));
  Log('pt.y=' + UIntToStr(Info.pt.y));
  Log('lPrivate=' + UIntToStr(Info.lPrivate));

  Result := False;
end;

Output:

2026-08-31 13:04:05.040   SizeOf=48
2026-08-31 13:04:05.040   hwnd=1
2026-08-31 13:04:05.040   message=2
2026-08-31 13:04:05.040   wParam=3
2026-08-31 13:04:05.040   lParam=4
2026-08-31 13:04:05.040   time=5
2026-08-31 13:04:05.040   pt.x=6
2026-08-31 13:04:05.040   pt.y=7
2026-08-31 13:04:05.040   lPrivate=8

Inno Setup 7 introduced 64-bit installers, but building 32-bit ones stays
the default in both editions; a 64-bit one is only produced when the
`[Setup]` section directive `SetupArchitecture` is set to `x64`. Updating
the compiler to Inno Setup 7.0.2 therefore did not change the architecture
of the installer, even though the v2.55.0(5) release notes already
announced it as a 64-bit executable.

Set `SetupArchitecture=x64` for the flavors that ship 64-bit Git, i.e. the
ones where `release.sh` sets `BITNESS` to 64: MINGW64, UCRT64 and
CLANGARM64. The i686 installer keeps building as a 32-bit executable. The
directive does not exist in Inno Setup 6, hence the version guard.

Flipping that directive alone leaves the installer broken, though: Pascal
Script records are always packed, and in a 64-bit installer pointers are
eight bytes wide, so the Windows API records declared in
`exec-with-capture.inc.iss` no longer match the layout those functions
expect. `CreatePipe()` reads `lpSecurityDescriptor` from the wrong offset,
fails with `ERROR_NOACCESS`, and `ExecWithCapture()` returns `False`
without ever having run the command. Starting the wizard then greets the
user with

	Unable to get system config (exit code 4294967295):

Widen the pointer-sized fields of `SECURITY_ATTRIBUTES` and `STARTUPINFO`
to `ULONG_PTR` and add the padding that the aligned C structs have and a
packed record does not, which brings them to the 24 and 104 bytes that the
x64 ABI mandates. `MSG` needs the same treatment for a different reason:
`PeekMessage()` writes the full 48 bytes of that struct, which is more than
an unpadded record offers, corrupting the stack and killing the message
pump with a fatal exception once a captured command runs long enough for
the pump to kick in.

The padding is conditional on the new `SETUP_IS_X64` symbol, so 32-bit
installers keep the record layout they have always had.

The `Win95` and `Win2000` code paths in `modules.inc.iss` declare records
with the same problem (`PROCESSENTRY32`, `MODULEENTRY32`), but `MinVersion`
is 6.3 and those paths are only taken on Windows versions before Vista, so
they are left alone.

This fixes git-for-windows/git#6372.

Signed-off-by: Dennis Ameling <dennis@dennisameling.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
@dscho
dscho force-pushed the 64-bit-installer branch from 418b9e3 to 0dee1bc Compare August 31, 2026 11:08
@dscho

dscho commented Aug 31, 2026

Copy link
Copy Markdown
Member

For the record, I inspected the other record changes (and had GPT look at the remaining DLL calls) and could not find any other required change, so now it's merge time!

@dscho
dscho merged commit bf63160 into main Aug 31, 2026
7 checks passed
@dscho
dscho deleted the 64-bit-installer branch August 31, 2026 11:16
github-actions Bot pushed a commit that referenced this pull request Aug 31, 2026
The installer [is now _actually_ a 64-bit
one](#732), which
fixes the problem that [the external OpenSSH option was broken in Git
for Windows
v2.55.0(5)](git-for-windows/git#6374).

Signed-off-by: gitforwindowshelper[bot] <gitforwindowshelper-bot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Git for Windows installer is still 32-bit as of v2.55.0.5

2 participants