mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Experiment with supporting Windows Arm64 natively
So far I haven't found any way to run native Arm64 code on Windows Arm64 without using MSVC. When I build a PE binary from scratch that should be a valid Windows Arm64 program, the OS refuses to run it. Possibly due to requiring additional content like XML manifests or relocation or control flow integrity data that isn't normally required on x64. I've also tried using VirtualAlloc2() to JIT an Arm64 native function, but VirtualAlloc2 always fails with invalid parameter. I tried using MSVC to create an ARM DLL that my x64 emulated program can link at runtime, to pass a function pointer with ARM code, but LoadLibrary() rejects ARM DLLs as invalid exe The only option left, is likely to write a new program like ape/ape-m1.c which can be compiled by MSVC to load and run an AARCH64 ELF executable. The emulated x64 binary would detect emulation using IsWow64Process2 and then drop the loader executable in a temporary folder, and re-launch the original executable, using the Arm64 segments of the cosmocc fat binary.
This commit is contained in:
parent
1671283f1a
commit
5bd22aef12
12 changed files with 88 additions and 16 deletions
|
@ -53,6 +53,7 @@ EXAMPLES_DIRECTDEPS = \
|
||||||
LIBC_NEXGEN32E \
|
LIBC_NEXGEN32E \
|
||||||
LIBC_NT_ADVAPI32 \
|
LIBC_NT_ADVAPI32 \
|
||||||
LIBC_NT_IPHLPAPI \
|
LIBC_NT_IPHLPAPI \
|
||||||
|
LIBC_NT_MEMORY \
|
||||||
LIBC_NT_KERNEL32 \
|
LIBC_NT_KERNEL32 \
|
||||||
LIBC_NT_NTDLL \
|
LIBC_NT_NTDLL \
|
||||||
LIBC_NT_USER32 \
|
LIBC_NT_USER32 \
|
||||||
|
|
|
@ -23,4 +23,7 @@
|
||||||
#define kNtSecLargePages 0x80000000
|
#define kNtSecLargePages 0x80000000
|
||||||
#define kNtSecWritecombine 0x40000000
|
#define kNtSecWritecombine 0x40000000
|
||||||
|
|
||||||
|
#define kNtPageTargetsInvalid 0x40000000
|
||||||
|
#define kNtPageTargetsNoUpdate 0x40000000
|
||||||
|
|
||||||
#endif /* COSMOPOLITAN_LIBC_NT_ENUM_PAGEFLAGS_H_ */
|
#endif /* COSMOPOLITAN_LIBC_NT_ENUM_PAGEFLAGS_H_ */
|
||||||
|
|
18
libc/nt/kernel32/IsWow64Process2.S
Normal file
18
libc/nt/kernel32/IsWow64Process2.S
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#include "libc/nt/codegen.h"
|
||||||
|
.imp kernel32,__imp_IsWow64Process2,IsWow64Process2
|
||||||
|
|
||||||
|
.text.windows
|
||||||
|
.ftrace1
|
||||||
|
IsWow64Process2:
|
||||||
|
.ftrace2
|
||||||
|
#ifdef __x86_64__
|
||||||
|
push %rbp
|
||||||
|
mov %rsp,%rbp
|
||||||
|
mov __imp_IsWow64Process2(%rip),%rax
|
||||||
|
jmp __sysv2nt
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
mov x0,#0
|
||||||
|
ret
|
||||||
|
#endif
|
||||||
|
.endfn IsWow64Process2,globl
|
||||||
|
.previous
|
|
@ -113,6 +113,7 @@ imp 'GetCurrentProcessId' GetCurrentProcessId kernel32 0
|
||||||
imp 'GetCurrentProcessorNumberEx' GetCurrentProcessorNumberEx kernel32 1
|
imp 'GetCurrentProcessorNumberEx' GetCurrentProcessorNumberEx kernel32 1
|
||||||
imp 'GetCurrentThread' GetCurrentThread kernel32 0
|
imp 'GetCurrentThread' GetCurrentThread kernel32 0
|
||||||
imp 'GetCurrentThreadId' GetCurrentThreadId kernel32 0
|
imp 'GetCurrentThreadId' GetCurrentThreadId kernel32 0
|
||||||
|
imp 'GetDynamicTimeZoneInformation' GetDynamicTimeZoneInformation kernel32 1
|
||||||
imp 'GetEnvironmentStrings' GetEnvironmentStringsW kernel32 1
|
imp 'GetEnvironmentStrings' GetEnvironmentStringsW kernel32 1
|
||||||
imp 'GetEnvironmentVariable' GetEnvironmentVariableW kernel32 3
|
imp 'GetEnvironmentVariable' GetEnvironmentVariableW kernel32 3
|
||||||
imp 'GetExitCodeThread' GetExitCodeThread kernel32 2
|
imp 'GetExitCodeThread' GetExitCodeThread kernel32 2
|
||||||
|
@ -168,8 +169,6 @@ imp 'GetSystemTimePreciseAsFileTime' GetSystemTimePreciseAsFileTime kernel3
|
||||||
imp 'GetSystemTimes' GetSystemTimes kernel32 3
|
imp 'GetSystemTimes' GetSystemTimes kernel32 3
|
||||||
imp 'GetTempPath' GetTempPathW kernel32 2
|
imp 'GetTempPath' GetTempPathW kernel32 2
|
||||||
imp 'GetTempPathA' GetTempPathA kernel32 2
|
imp 'GetTempPathA' GetTempPathA kernel32 2
|
||||||
imp 'GetDynamicTimeZoneInformation' GetDynamicTimeZoneInformation kernel32 1
|
|
||||||
imp 'GetTimeZoneInformation' GetTimeZoneInformation kernel32 1
|
|
||||||
imp 'GetThreadContext' GetThreadContext kernel32 2
|
imp 'GetThreadContext' GetThreadContext kernel32 2
|
||||||
imp 'GetThreadDescription' GetThreadDescription kernel32 2
|
imp 'GetThreadDescription' GetThreadDescription kernel32 2
|
||||||
imp 'GetThreadIOPendingFlag' GetThreadIOPendingFlag kernel32 2
|
imp 'GetThreadIOPendingFlag' GetThreadIOPendingFlag kernel32 2
|
||||||
|
@ -178,6 +177,7 @@ imp 'GetThreadPriority' GetThreadPriority kernel32 1
|
||||||
imp 'GetThreadPriorityBoost' GetThreadPriorityBoost kernel32 2
|
imp 'GetThreadPriorityBoost' GetThreadPriorityBoost kernel32 2
|
||||||
imp 'GetThreadTimes' GetThreadTimes kernel32 5
|
imp 'GetThreadTimes' GetThreadTimes kernel32 5
|
||||||
imp 'GetTickCount64' GetTickCount64 kernel32 0
|
imp 'GetTickCount64' GetTickCount64 kernel32 0
|
||||||
|
imp 'GetTimeZoneInformation' GetTimeZoneInformation kernel32 1
|
||||||
imp 'GetVersionEx' GetVersionExW kernel32 1
|
imp 'GetVersionEx' GetVersionExW kernel32 1
|
||||||
imp 'GetVolumeInformationByHandle' GetVolumeInformationByHandleW kernel32 8
|
imp 'GetVolumeInformationByHandle' GetVolumeInformationByHandleW kernel32 8
|
||||||
imp 'GetVolumePathName' GetVolumePathNameW kernel32 3
|
imp 'GetVolumePathName' GetVolumePathNameW kernel32 3
|
||||||
|
@ -197,6 +197,7 @@ imp 'InitializeCriticalSection' InitializeCriticalSection kernel32 1
|
||||||
imp 'InitializeCriticalSectionAndSpinCount' InitializeCriticalSectionAndSpinCount kernel32 2
|
imp 'InitializeCriticalSectionAndSpinCount' InitializeCriticalSectionAndSpinCount kernel32 2
|
||||||
imp 'InitializeProcThreadAttributeList' InitializeProcThreadAttributeList kernel32 4
|
imp 'InitializeProcThreadAttributeList' InitializeProcThreadAttributeList kernel32 4
|
||||||
imp 'InitializeSRWLock' InitializeSRWLock kernel32 1
|
imp 'InitializeSRWLock' InitializeSRWLock kernel32 1
|
||||||
|
imp 'IsWow64Process2' IsWow64Process2 kernel32 3
|
||||||
imp 'LeaveCriticalSection' LeaveCriticalSection kernel32 1
|
imp 'LeaveCriticalSection' LeaveCriticalSection kernel32 1
|
||||||
imp 'LoadLibrary' LoadLibraryW kernel32 1
|
imp 'LoadLibrary' LoadLibraryW kernel32 1
|
||||||
imp 'LoadLibraryA' LoadLibraryA kernel32 1
|
imp 'LoadLibraryA' LoadLibraryA kernel32 1
|
||||||
|
|
|
@ -43,6 +43,8 @@ bool32 SetDefaultDllDirectories(unsigned dirflags);
|
||||||
bool32 ProcessPrng(void *RandomBuffer, uint32_t RandomBufferLength);
|
bool32 ProcessPrng(void *RandomBuffer, uint32_t RandomBufferLength);
|
||||||
uint32_t GetModuleFileName(int64_t hModule, char16_t *lpFilename,
|
uint32_t GetModuleFileName(int64_t hModule, char16_t *lpFilename,
|
||||||
uint32_t nSize);
|
uint32_t nSize);
|
||||||
|
bool32 IsWow64Process2(intptr_t hProcess, uint16_t *out_pProcessMachine,
|
||||||
|
uint16_t *out_opt_pNativeMachine);
|
||||||
|
|
||||||
#if ShouldUseMsabiAttribute()
|
#if ShouldUseMsabiAttribute()
|
||||||
#include "libc/nt/thunk/runtime.inc"
|
#include "libc/nt/thunk/runtime.inc"
|
||||||
|
|
20
libc/nt/struct/arm64.h
Normal file
20
libc/nt/struct/arm64.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef COSMOPOLITAN_LIBC_NT_STRUCT_ARM64_H_
|
||||||
|
#define COSMOPOLITAN_LIBC_NT_STRUCT_ARM64_H_
|
||||||
|
|
||||||
|
struct NtArm64RuntimeFunction {
|
||||||
|
uint32_t BeginAddress;
|
||||||
|
union {
|
||||||
|
uint32_t UnwindData;
|
||||||
|
struct {
|
||||||
|
uint32_t Flag : 2;
|
||||||
|
uint32_t FunctionLength : 11;
|
||||||
|
uint32_t RegF : 3;
|
||||||
|
uint32_t RegI : 4;
|
||||||
|
uint32_t H : 1;
|
||||||
|
uint32_t CR : 2;
|
||||||
|
uint32_t FrameSize : 9;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* COSMOPOLITAN_LIBC_NT_STRUCT_ARM64_H_ */
|
|
@ -9,26 +9,28 @@
|
||||||
#define kNtMemExtendedParameterPartitionHandle 3
|
#define kNtMemExtendedParameterPartitionHandle 3
|
||||||
#define kNtMemExtendedParameterUserPhysicalHandle 4
|
#define kNtMemExtendedParameterUserPhysicalHandle 4
|
||||||
#define kNtMemExtendedParameterAttributeFlags 5
|
#define kNtMemExtendedParameterAttributeFlags 5
|
||||||
#define kNtMemExtendedParameterMax 6
|
#define kNtMemExtendedParameterImageMachine 6
|
||||||
|
#define kNtMemExtendedParameterMax 7
|
||||||
|
|
||||||
#define kNtMemExtendedParameterGraphics 0x00000001
|
#define kNtMemExtendedParameterGraphics 0x00000001
|
||||||
#define kNtMemExtendedParameterNonpaged 0x00000002
|
#define kNtMemExtendedParameterNonpaged 0x00000002
|
||||||
#define kNtMemExtendedParameterZeroPagesOptional 0x00000004
|
#define kNtMemExtendedParameterZeroPagesOptional 0x00000004
|
||||||
#define kNtMemExtendedParameterNonpagedLarge 0x00000008
|
#define kNtMemExtendedParameterNonpagedLarge 0x00000008
|
||||||
#define kNtMemExtendedParameterNonpagedHuge 0x00000010
|
#define kNtMemExtendedParameterNonpagedHuge 0x00000010
|
||||||
|
#define kNtMemExtendedParameterSoftFaultPages 0x00000020
|
||||||
|
#define kNtMemExtendedParameterEcCode 0x00000040
|
||||||
|
#define kNtMemExtendedParameterImageNoHpat 0x00000080
|
||||||
|
|
||||||
struct NtMemExtendedParameter {
|
struct NtMemExtendedParameter {
|
||||||
struct {
|
uint8_t Type;
|
||||||
uint64_t Type : kNtMemExtendedParameterTypeBits;
|
uint8_t Reserved[7];
|
||||||
uint64_t Reserved : 64 - kNtMemExtendedParameterTypeBits;
|
|
||||||
} DUMMYSTRUCTNAME;
|
|
||||||
union {
|
union {
|
||||||
uint64_t ULong64;
|
uint64_t ULong64;
|
||||||
void *Pointer;
|
void *Pointer;
|
||||||
size_t Size;
|
size_t Size;
|
||||||
intptr_t Handle;
|
intptr_t Handle;
|
||||||
unsigned ULong;
|
unsigned ULong;
|
||||||
} DUMMYUNIONNAME;
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* COSMOPOLITAN_LIBC_NT_STRUCT_MEMEXTENDEDPARAMETER_H_ */
|
#endif /* COSMOPOLITAN_LIBC_NT_STRUCT_MEMEXTENDEDPARAMETER_H_ */
|
||||||
|
|
|
@ -229,6 +229,17 @@ static struct Segment *NewSegment(void) {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ConvertElfMachineToPe(struct Elf *elf) {
|
||||||
|
switch (elf->ehdr->e_machine) {
|
||||||
|
case EM_NEXGEN32E:
|
||||||
|
return kNtImageFileMachineNexgen32e;
|
||||||
|
case EM_AARCH64:
|
||||||
|
return kNtImageFileMachineArm64;
|
||||||
|
default:
|
||||||
|
Die(elf->path, "unsupported e_machine");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Elf64_Addr RelocateVaddrWithinSegment(struct Elf *elf,
|
static Elf64_Addr RelocateVaddrWithinSegment(struct Elf *elf,
|
||||||
Elf64_Addr vaddr_old,
|
Elf64_Addr vaddr_old,
|
||||||
struct Segment *segment) {
|
struct Segment *segment) {
|
||||||
|
@ -811,7 +822,17 @@ static uint32_t GetPeSectionCharacteristics(struct Segment *s) {
|
||||||
// originally in the elf image that ld linked. in order for this to work
|
// originally in the elf image that ld linked. in order for this to work
|
||||||
// the executable needs to be linked in `ld -q` mode, since it'll retain
|
// the executable needs to be linked in `ld -q` mode, since it'll retain
|
||||||
// the .rela sections we'll need later to fixup the binary.
|
// the .rela sections we'll need later to fixup the binary.
|
||||||
static struct ImagePointer GeneratePe(struct Elf *elf, char *fp, int64_t vp) {
|
static struct ImagePointer GeneratePe(struct Elf *elf, char *fp) {
|
||||||
|
|
||||||
|
int64_t vp = 0;
|
||||||
|
Elf64_Phdr *phdr;
|
||||||
|
for (int i = 0; i < elf->ehdr->e_phnum; ++i) {
|
||||||
|
if ((phdr = GetElfProgramHeaderAddress(elf->ehdr, elf->size, i)) &&
|
||||||
|
phdr->p_type == PT_LOAD) {
|
||||||
|
vp = phdr->p_vaddr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Elf64_Sym *entry;
|
Elf64_Sym *entry;
|
||||||
if (!(entry = FindGlobal(elf, "__win32_start")) &&
|
if (!(entry = FindGlobal(elf, "__win32_start")) &&
|
||||||
|
@ -855,7 +876,7 @@ static struct ImagePointer GeneratePe(struct Elf *elf, char *fp, int64_t vp) {
|
||||||
struct NtImageFileHeader *filehdr;
|
struct NtImageFileHeader *filehdr;
|
||||||
filehdr = (struct NtImageFileHeader *)fp;
|
filehdr = (struct NtImageFileHeader *)fp;
|
||||||
fp += sizeof(struct NtImageFileHeader);
|
fp += sizeof(struct NtImageFileHeader);
|
||||||
filehdr->Machine = kNtImageFileMachineNexgen32e;
|
filehdr->Machine = ConvertElfMachineToPe(elf);
|
||||||
filehdr->TimeDateStamp = 1690072024;
|
filehdr->TimeDateStamp = 1690072024;
|
||||||
filehdr->Characteristics =
|
filehdr->Characteristics =
|
||||||
kNtPeFileExecutableImage | kNtImageFileLargeAddressAware |
|
kNtPeFileExecutableImage | kNtImageFileLargeAddressAware |
|
||||||
|
@ -873,7 +894,9 @@ static struct ImagePointer GeneratePe(struct Elf *elf, char *fp, int64_t vp) {
|
||||||
opthdr->FileAlignment = 512;
|
opthdr->FileAlignment = 512;
|
||||||
opthdr->SectionAlignment = MAX(4096, elf->align);
|
opthdr->SectionAlignment = MAX(4096, elf->align);
|
||||||
opthdr->MajorOperatingSystemVersion = 6;
|
opthdr->MajorOperatingSystemVersion = 6;
|
||||||
|
opthdr->MinorOperatingSystemVersion = 2;
|
||||||
opthdr->MajorSubsystemVersion = 6;
|
opthdr->MajorSubsystemVersion = 6;
|
||||||
|
opthdr->MinorSubsystemVersion = 2;
|
||||||
opthdr->Subsystem = kNtImageSubsystemWindowsCui;
|
opthdr->Subsystem = kNtImageSubsystemWindowsCui;
|
||||||
opthdr->DllCharacteristics = kNtImageDllcharacteristicsNxCompat |
|
opthdr->DllCharacteristics = kNtImageDllcharacteristicsNxCompat |
|
||||||
kNtImageDllcharacteristicsHighEntropyVa;
|
kNtImageDllcharacteristicsHighEntropyVa;
|
||||||
|
@ -1116,7 +1139,7 @@ int main(int argc, char *argv[]) {
|
||||||
// translate executable
|
// translate executable
|
||||||
struct Elf *elf = OpenElf(argv[optind]);
|
struct Elf *elf = OpenElf(argv[optind]);
|
||||||
char *buf = Memalign(MAX_ALIGN, 134217728);
|
char *buf = Memalign(MAX_ALIGN, 134217728);
|
||||||
struct ImagePointer ip = GeneratePe(elf, buf, 0x00400000);
|
struct ImagePointer ip = GeneratePe(elf, buf);
|
||||||
if (creat(outpath, 0755) == -1)
|
if (creat(outpath, 0755) == -1)
|
||||||
DieSys(elf->path);
|
DieSys(elf->path);
|
||||||
Pwrite(3, buf, ip.fp - buf, 0);
|
Pwrite(3, buf, ip.fp - buf, 0);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#ifndef COSMOPOLITAN_TOOL_BUILD_ELF2PE_H_
|
#ifndef COSMOPOLITAN_TOOL_BUILD_ELF2PE_H_
|
||||||
#define COSMOPOLITAN_TOOL_BUILD_ELF2PE_H_
|
#define COSMOPOLITAN_TOOL_BUILD_ELF2PE_H_
|
||||||
|
|
||||||
#define __dll_import(DLL, RET, FUNC, ARGS) \
|
#define __dll_import(DLL, RET, FUNC, ARGS) \
|
||||||
extern RET(*const __attribute__((__ms_abi__, __weak__)) FUNC) \
|
extern RET(*const __msabi __attribute__((__weak__)) FUNC) \
|
||||||
ARGS __asm__("\"dll$" DLL "$" #FUNC "\"")
|
ARGS __asm__("\"dll$" DLL "$" #FUNC "\"")
|
||||||
|
|
||||||
#endif /* COSMOPOLITAN_TOOL_BUILD_ELF2PE_H_ */
|
#endif /* COSMOPOLITAN_TOOL_BUILD_ELF2PE_H_ */
|
||||||
|
|
|
@ -79,7 +79,7 @@ o/$(MODE)/tool/hello/hello-pe.ape: \
|
||||||
# elf2pe can generate binaries that don't have dll imports
|
# elf2pe can generate binaries that don't have dll imports
|
||||||
o/$(MODE)/tool/hello/life-pe.dbg: \
|
o/$(MODE)/tool/hello/life-pe.dbg: \
|
||||||
o/$(MODE)/tool/hello/life-pe.o
|
o/$(MODE)/tool/hello/life-pe.o
|
||||||
@$(COMPILE) -ALINK.elf $(LINK) $(LINKARGS) $(OUTPUT_OPTION) -q -e WinMain
|
@$(COMPILE) -ALINK.elf $(LINK) $(LINKARGS) $(OUTPUT_OPTION) -q -e WinMain #-Ttext-segment=0x140000000
|
||||||
o/$(MODE)/tool/hello/life-pe.ape: \
|
o/$(MODE)/tool/hello/life-pe.ape: \
|
||||||
o/$(MODE)/tool/hello/life-pe.dbg \
|
o/$(MODE)/tool/hello/life-pe.dbg \
|
||||||
o/$(MODE)/tool/build/elf2pe
|
o/$(MODE)/tool/build/elf2pe
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||||
╚─────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────*/
|
||||||
#endif
|
#endif
|
||||||
|
#include "libc/nt/thunk/msabi.h"
|
||||||
#include "tool/build/elf2pe.h"
|
#include "tool/build/elf2pe.h"
|
||||||
|
|
||||||
#define STD_OUTPUT_HANDLE -11u
|
#define STD_OUTPUT_HANDLE -11u
|
||||||
|
@ -15,7 +16,7 @@ __dll_import("kernel32.dll", long, GetStdHandle, (unsigned));
|
||||||
__dll_import("kernel32.dll", int, WriteFile,
|
__dll_import("kernel32.dll", int, WriteFile,
|
||||||
(long, const void *, unsigned, unsigned *, void *));
|
(long, const void *, unsigned, unsigned *, void *));
|
||||||
|
|
||||||
__attribute__((__ms_abi__)) long WinMain(void) {
|
__msabi long WinMain(void) {
|
||||||
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "hello world\n", 12, 0, 0);
|
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "hello world\n", 12, 0, 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||||
╚─────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────*/
|
||||||
#endif
|
#endif
|
||||||
|
#include "libc/nt/thunk/msabi.h"
|
||||||
|
|
||||||
__attribute__((__ms_abi__)) long WinMain(void) {
|
__msabi long WinMain(void) {
|
||||||
return 42 << 8;
|
return 42 << 8;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue