Skip to content

Commit 5bd22ae

Browse files
committed
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.
1 parent 1671283 commit 5bd22ae

File tree

12 files changed

+88
-16
lines changed

12 files changed

+88
-16
lines changed

examples/BUILD.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ EXAMPLES_DIRECTDEPS = \
5353
LIBC_NEXGEN32E \
5454
LIBC_NT_ADVAPI32 \
5555
LIBC_NT_IPHLPAPI \
56+
LIBC_NT_MEMORY \
5657
LIBC_NT_KERNEL32 \
5758
LIBC_NT_NTDLL \
5859
LIBC_NT_USER32 \

libc/nt/enum/pageflags.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,7 @@
2323
#define kNtSecLargePages 0x80000000
2424
#define kNtSecWritecombine 0x40000000
2525

26+
#define kNtPageTargetsInvalid 0x40000000
27+
#define kNtPageTargetsNoUpdate 0x40000000
28+
2629
#endif /* COSMOPOLITAN_LIBC_NT_ENUM_PAGEFLAGS_H_ */

libc/nt/kernel32/IsWow64Process2.S

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include "libc/nt/codegen.h"
2+
.imp kernel32,__imp_IsWow64Process2,IsWow64Process2
3+
4+
.text.windows
5+
.ftrace1
6+
IsWow64Process2:
7+
.ftrace2
8+
#ifdef __x86_64__
9+
push %rbp
10+
mov %rsp,%rbp
11+
mov __imp_IsWow64Process2(%rip),%rax
12+
jmp __sysv2nt
13+
#elif defined(__aarch64__)
14+
mov x0,#0
15+
ret
16+
#endif
17+
.endfn IsWow64Process2,globl
18+
.previous

libc/nt/master.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ imp 'GetCurrentProcessId' GetCurrentProcessId kernel32 0
113113
imp 'GetCurrentProcessorNumberEx' GetCurrentProcessorNumberEx kernel32 1
114114
imp 'GetCurrentThread' GetCurrentThread kernel32 0
115115
imp 'GetCurrentThreadId' GetCurrentThreadId kernel32 0
116+
imp 'GetDynamicTimeZoneInformation' GetDynamicTimeZoneInformation kernel32 1
116117
imp 'GetEnvironmentStrings' GetEnvironmentStringsW kernel32 1
117118
imp 'GetEnvironmentVariable' GetEnvironmentVariableW kernel32 3
118119
imp 'GetExitCodeThread' GetExitCodeThread kernel32 2
@@ -168,8 +169,6 @@ imp 'GetSystemTimePreciseAsFileTime' GetSystemTimePreciseAsFileTime kernel3
168169
imp 'GetSystemTimes' GetSystemTimes kernel32 3
169170
imp 'GetTempPath' GetTempPathW kernel32 2
170171
imp 'GetTempPathA' GetTempPathA kernel32 2
171-
imp 'GetDynamicTimeZoneInformation' GetDynamicTimeZoneInformation kernel32 1
172-
imp 'GetTimeZoneInformation' GetTimeZoneInformation kernel32 1
173172
imp 'GetThreadContext' GetThreadContext kernel32 2
174173
imp 'GetThreadDescription' GetThreadDescription kernel32 2
175174
imp 'GetThreadIOPendingFlag' GetThreadIOPendingFlag kernel32 2
@@ -178,6 +177,7 @@ imp 'GetThreadPriority' GetThreadPriority kernel32 1
178177
imp 'GetThreadPriorityBoost' GetThreadPriorityBoost kernel32 2
179178
imp 'GetThreadTimes' GetThreadTimes kernel32 5
180179
imp 'GetTickCount64' GetTickCount64 kernel32 0
180+
imp 'GetTimeZoneInformation' GetTimeZoneInformation kernel32 1
181181
imp 'GetVersionEx' GetVersionExW kernel32 1
182182
imp 'GetVolumeInformationByHandle' GetVolumeInformationByHandleW kernel32 8
183183
imp 'GetVolumePathName' GetVolumePathNameW kernel32 3
@@ -197,6 +197,7 @@ imp 'InitializeCriticalSection' InitializeCriticalSection kernel32 1
197197
imp 'InitializeCriticalSectionAndSpinCount' InitializeCriticalSectionAndSpinCount kernel32 2
198198
imp 'InitializeProcThreadAttributeList' InitializeProcThreadAttributeList kernel32 4
199199
imp 'InitializeSRWLock' InitializeSRWLock kernel32 1
200+
imp 'IsWow64Process2' IsWow64Process2 kernel32 3
200201
imp 'LeaveCriticalSection' LeaveCriticalSection kernel32 1
201202
imp 'LoadLibrary' LoadLibraryW kernel32 1
202203
imp 'LoadLibraryA' LoadLibraryA kernel32 1

libc/nt/runtime.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ bool32 SetDefaultDllDirectories(unsigned dirflags);
4343
bool32 ProcessPrng(void *RandomBuffer, uint32_t RandomBufferLength);
4444
uint32_t GetModuleFileName(int64_t hModule, char16_t *lpFilename,
4545
uint32_t nSize);
46+
bool32 IsWow64Process2(intptr_t hProcess, uint16_t *out_pProcessMachine,
47+
uint16_t *out_opt_pNativeMachine);
4648

4749
#if ShouldUseMsabiAttribute()
4850
#include "libc/nt/thunk/runtime.inc"

libc/nt/struct/arm64.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#ifndef COSMOPOLITAN_LIBC_NT_STRUCT_ARM64_H_
2+
#define COSMOPOLITAN_LIBC_NT_STRUCT_ARM64_H_
3+
4+
struct NtArm64RuntimeFunction {
5+
uint32_t BeginAddress;
6+
union {
7+
uint32_t UnwindData;
8+
struct {
9+
uint32_t Flag : 2;
10+
uint32_t FunctionLength : 11;
11+
uint32_t RegF : 3;
12+
uint32_t RegI : 4;
13+
uint32_t H : 1;
14+
uint32_t CR : 2;
15+
uint32_t FrameSize : 9;
16+
};
17+
};
18+
};
19+
20+
#endif /* COSMOPOLITAN_LIBC_NT_STRUCT_ARM64_H_ */

libc/nt/struct/memextendedparameter.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,28 @@
99
#define kNtMemExtendedParameterPartitionHandle 3
1010
#define kNtMemExtendedParameterUserPhysicalHandle 4
1111
#define kNtMemExtendedParameterAttributeFlags 5
12-
#define kNtMemExtendedParameterMax 6
12+
#define kNtMemExtendedParameterImageMachine 6
13+
#define kNtMemExtendedParameterMax 7
1314

1415
#define kNtMemExtendedParameterGraphics 0x00000001
1516
#define kNtMemExtendedParameterNonpaged 0x00000002
1617
#define kNtMemExtendedParameterZeroPagesOptional 0x00000004
1718
#define kNtMemExtendedParameterNonpagedLarge 0x00000008
1819
#define kNtMemExtendedParameterNonpagedHuge 0x00000010
20+
#define kNtMemExtendedParameterSoftFaultPages 0x00000020
21+
#define kNtMemExtendedParameterEcCode 0x00000040
22+
#define kNtMemExtendedParameterImageNoHpat 0x00000080
1923

2024
struct NtMemExtendedParameter {
21-
struct {
22-
uint64_t Type : kNtMemExtendedParameterTypeBits;
23-
uint64_t Reserved : 64 - kNtMemExtendedParameterTypeBits;
24-
} DUMMYSTRUCTNAME;
25+
uint8_t Type;
26+
uint8_t Reserved[7];
2527
union {
2628
uint64_t ULong64;
2729
void *Pointer;
2830
size_t Size;
2931
intptr_t Handle;
3032
unsigned ULong;
31-
} DUMMYUNIONNAME;
33+
};
3234
};
3335

3436
#endif /* COSMOPOLITAN_LIBC_NT_STRUCT_MEMEXTENDEDPARAMETER_H_ */

tool/build/elf2pe.c

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,17 @@ static struct Segment *NewSegment(void) {
229229
return s;
230230
}
231231

232+
static int ConvertElfMachineToPe(struct Elf *elf) {
233+
switch (elf->ehdr->e_machine) {
234+
case EM_NEXGEN32E:
235+
return kNtImageFileMachineNexgen32e;
236+
case EM_AARCH64:
237+
return kNtImageFileMachineArm64;
238+
default:
239+
Die(elf->path, "unsupported e_machine");
240+
}
241+
}
242+
232243
static Elf64_Addr RelocateVaddrWithinSegment(struct Elf *elf,
233244
Elf64_Addr vaddr_old,
234245
struct Segment *segment) {
@@ -811,7 +822,17 @@ static uint32_t GetPeSectionCharacteristics(struct Segment *s) {
811822
// originally in the elf image that ld linked. in order for this to work
812823
// the executable needs to be linked in `ld -q` mode, since it'll retain
813824
// the .rela sections we'll need later to fixup the binary.
814-
static struct ImagePointer GeneratePe(struct Elf *elf, char *fp, int64_t vp) {
825+
static struct ImagePointer GeneratePe(struct Elf *elf, char *fp) {
826+
827+
int64_t vp = 0;
828+
Elf64_Phdr *phdr;
829+
for (int i = 0; i < elf->ehdr->e_phnum; ++i) {
830+
if ((phdr = GetElfProgramHeaderAddress(elf->ehdr, elf->size, i)) &&
831+
phdr->p_type == PT_LOAD) {
832+
vp = phdr->p_vaddr;
833+
break;
834+
}
835+
}
815836

816837
Elf64_Sym *entry;
817838
if (!(entry = FindGlobal(elf, "__win32_start")) &&
@@ -855,7 +876,7 @@ static struct ImagePointer GeneratePe(struct Elf *elf, char *fp, int64_t vp) {
855876
struct NtImageFileHeader *filehdr;
856877
filehdr = (struct NtImageFileHeader *)fp;
857878
fp += sizeof(struct NtImageFileHeader);
858-
filehdr->Machine = kNtImageFileMachineNexgen32e;
879+
filehdr->Machine = ConvertElfMachineToPe(elf);
859880
filehdr->TimeDateStamp = 1690072024;
860881
filehdr->Characteristics =
861882
kNtPeFileExecutableImage | kNtImageFileLargeAddressAware |
@@ -873,7 +894,9 @@ static struct ImagePointer GeneratePe(struct Elf *elf, char *fp, int64_t vp) {
873894
opthdr->FileAlignment = 512;
874895
opthdr->SectionAlignment = MAX(4096, elf->align);
875896
opthdr->MajorOperatingSystemVersion = 6;
897+
opthdr->MinorOperatingSystemVersion = 2;
876898
opthdr->MajorSubsystemVersion = 6;
899+
opthdr->MinorSubsystemVersion = 2;
877900
opthdr->Subsystem = kNtImageSubsystemWindowsCui;
878901
opthdr->DllCharacteristics = kNtImageDllcharacteristicsNxCompat |
879902
kNtImageDllcharacteristicsHighEntropyVa;
@@ -1116,7 +1139,7 @@ int main(int argc, char *argv[]) {
11161139
// translate executable
11171140
struct Elf *elf = OpenElf(argv[optind]);
11181141
char *buf = Memalign(MAX_ALIGN, 134217728);
1119-
struct ImagePointer ip = GeneratePe(elf, buf, 0x00400000);
1142+
struct ImagePointer ip = GeneratePe(elf, buf);
11201143
if (creat(outpath, 0755) == -1)
11211144
DieSys(elf->path);
11221145
Pwrite(3, buf, ip.fp - buf, 0);

tool/build/elf2pe.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#ifndef COSMOPOLITAN_TOOL_BUILD_ELF2PE_H_
22
#define COSMOPOLITAN_TOOL_BUILD_ELF2PE_H_
33

4-
#define __dll_import(DLL, RET, FUNC, ARGS) \
5-
extern RET(*const __attribute__((__ms_abi__, __weak__)) FUNC) \
4+
#define __dll_import(DLL, RET, FUNC, ARGS) \
5+
extern RET(*const __msabi __attribute__((__weak__)) FUNC) \
66
ARGS __asm__("\"dll$" DLL "$" #FUNC "\"")
77

88
#endif /* COSMOPOLITAN_TOOL_BUILD_ELF2PE_H_ */

tool/hello/BUILD.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ o/$(MODE)/tool/hello/hello-pe.ape: \
7979
# elf2pe can generate binaries that don't have dll imports
8080
o/$(MODE)/tool/hello/life-pe.dbg: \
8181
o/$(MODE)/tool/hello/life-pe.o
82-
@$(COMPILE) -ALINK.elf $(LINK) $(LINKARGS) $(OUTPUT_OPTION) -q -e WinMain
82+
@$(COMPILE) -ALINK.elf $(LINK) $(LINKARGS) $(OUTPUT_OPTION) -q -e WinMain #-Ttext-segment=0x140000000
8383
o/$(MODE)/tool/hello/life-pe.ape: \
8484
o/$(MODE)/tool/hello/life-pe.dbg \
8585
o/$(MODE)/tool/build/elf2pe

0 commit comments

Comments
 (0)