Skip to content

Commit 59331cb

Browse files
Merge pull request #1 from oven-sh/claude/winarm64
Windows ARM64 support
2 parents 12882ee + cdc7b03 commit 59331cb

File tree

11 files changed

+185
-15
lines changed

11 files changed

+185
-15
lines changed

.github/workflows/build.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,23 @@ jobs:
8585
.\tcc -I.. libtcc.dll -v ../tests/libtcc_test.c -o libtest.exe && .\libtest.exe
8686
.\tcc -I.. libtcc.dll -run ../tests/libtcc_test.c
8787
88+
test-aarch64-win32:
89+
runs-on: windows-11-arm64
90+
timeout-minutes: 6
91+
steps:
92+
- uses: actions/checkout@v4
93+
- uses: ilammy/msvc-dev-cmd@v1
94+
with:
95+
arch: arm64
96+
- name: build & test tcc (aarch64-win32)
97+
shell: cmd
98+
run: |
99+
echo ::group:: build with clang
100+
cd win32
101+
call build-tcc.bat -t arm64 -c clang
102+
echo ::endgroup::
103+
.\tcc -v
104+
88105
test-armv7-linux:
89106
runs-on: ubuntu-22.04
90107
timeout-minutes: 8

Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ DEF-arm64-osx = $(DEF-arm64) -DTCC_TARGET_MACHO
113113
DEF-arm64-FreeBSD = $(DEF-arm64) -DTARGETOS_FreeBSD
114114
DEF-arm64-NetBSD = $(DEF-arm64) -DTARGETOS_NetBSD
115115
DEF-arm64-OpenBSD = $(DEF-arm64) -DTARGETOS_OpenBSD
116+
DEF-arm64-win32 = $(DEF-arm64) -DTCC_TARGET_PE
116117
DEF-riscv64 = -DTCC_TARGET_RISCV64
117118
DEF-c67 = -DTCC_TARGET_C67 -w # disable warnigs
118119
DEF-x86_64-FreeBSD = $(DEF-x86_64) -DTARGETOS_FreeBSD
@@ -130,7 +131,7 @@ TCCDOCS = tcc.1 tcc-doc.html tcc-doc.info
130131
all: $(PROGS) $(TCCLIBS) $(TCCDOCS)
131132

132133
# cross compiler targets to build
133-
TCC_X = i386 x86_64 i386-win32 x86_64-win32 x86_64-osx arm arm64 arm-wince c67
134+
TCC_X = i386 x86_64 i386-win32 x86_64-win32 x86_64-osx arm arm64 arm64-win32 arm-wince c67
134135
TCC_X += riscv64 arm64-osx
135136
# TCC_X += arm-fpa arm-fpa-ld arm-vfp arm-eabi
136137

@@ -214,6 +215,7 @@ arm-eabi_FILES = $(arm_FILES)
214215
arm-eabihf_FILES = $(arm_FILES)
215216
arm64_FILES = $(CORE_FILES) arm64-gen.c arm64-link.c arm64-asm.c
216217
arm64-osx_FILES = $(arm64_FILES) tccmacho.c
218+
arm64-win32_FILES = $(arm64_FILES) tccpe.c
217219
c67_FILES = $(CORE_FILES) c67-gen.c c67-link.c tcccoff.c
218220
riscv64_FILES = $(CORE_FILES) riscv64-gen.c riscv64-link.c riscv64-asm.c
219221

arm64-gen.c

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,29 @@
1212
#ifdef TARGET_DEFS_ONLY
1313

1414
// Number of registers available to allocator:
15+
#ifdef TCC_TARGET_PE
16+
#define NB_REGS 27 // x0-x17, x30, v0-v7 (x18 reserved on Windows)
17+
#define TREG_R(x) (x) // x = 0..17
18+
#define TREG_R30 18
19+
#define TREG_F(x) (x + 19) // x = 0..7
20+
#define RC_INT (1 << 0)
21+
#define RC_FLOAT (1 << 1)
22+
#define RC_R(x) (1 << (2 + (x))) // x = 0..17
23+
#define RC_R30 (1 << 20)
24+
#define RC_F(x) (1 << (21 + (x))) // x = 0..7
25+
#else
1526
#define NB_REGS 28 // x0-x18, x30, v0-v7
16-
1727
#define TREG_R(x) (x) // x = 0..18
1828
#define TREG_R30 19
1929
#define TREG_F(x) (x + 20) // x = 0..7
20-
21-
// Register classes sorted from more general to more precise:
2230
#define RC_INT (1 << 0)
2331
#define RC_FLOAT (1 << 1)
2432
#define RC_R(x) (1 << (2 + (x))) // x = 0..18
2533
#define RC_R30 (1 << 21)
2634
#define RC_F(x) (1 << (22 + (x))) // x = 0..7
35+
#endif
36+
37+
// Register classes sorted from more general to more precise:
2738

2839
#define RC_IRET (RC_R(0)) // int return register class
2940
#define RC_FRET (RC_F(0)) // float return register class
@@ -79,7 +90,9 @@ ST_DATA const int reg_classes[NB_REGS] = {
7990
RC_INT | RC_R(15),
8091
RC_INT | RC_R(16),
8192
RC_INT | RC_R(17),
93+
#ifndef TCC_TARGET_PE
8294
RC_INT | RC_R(18),
95+
#endif
8396
RC_R30, // not in RC_INT as we make special use of x30
8497
RC_FLOAT | RC_F(0),
8598
RC_FLOAT | RC_F(1),
@@ -1508,6 +1521,13 @@ ST_FUNC void gfunc_epilog(void)
15081521
o(0xa8ce7bfd); // ldp x29,x30,[sp],#224
15091522

15101523
o(0xd65f03c0); // ret
1524+
1525+
#ifdef TCC_TARGET_PE
1526+
{
1527+
unsigned start = arm64_func_sub_sp_offset - 8; /* account for prolog size */
1528+
pe_add_unwind_data(start, ind, -loc);
1529+
}
1530+
#endif
15111531
}
15121532

15131533
ST_FUNC void gen_fill_nops(int bytes)

lib/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ OBJ-arm-vfp = $(OBJ-arm)
7272
OBJ-arm-eabi = $(OBJ-arm)
7373
OBJ-arm-eabihf = $(OBJ-arm)
7474
OBJ-arm-wince = $(ARM_O) $(WIN_O)
75+
OBJ-arm64-win32 = $(ARM64_O) chkstk.o $(WIN_O)
7576
OBJ-riscv64 = $(RISCV64_O) $(LIN_O)
7677

7778
OBJ-extra = $(filter $(EXTRA_O),$(OBJ-$T))

tcc.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -949,7 +949,7 @@ struct TCCState {
949949
unsigned pe_file_align;
950950
unsigned pe_stack_size;
951951
addr_t pe_imagebase;
952-
# ifdef TCC_TARGET_X86_64
952+
# if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
953953
Section *uw_pdata;
954954
int uw_sym;
955955
unsigned uw_offs;
@@ -1772,7 +1772,7 @@ ST_FUNC int pe_putimport(TCCState *s1, int dllindex, const char *name, addr_t va
17721772
ST_FUNC int pe_setsubsy(TCCState *s1, const char *arg);
17731773
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
17741774
#endif
1775-
#ifdef TCC_TARGET_X86_64
1775+
#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
17761776
ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack);
17771777
#endif
17781778
PUB_FUNC int tcc_get_dllexports(const char *filename, char **pp);

tccpe.c

Lines changed: 96 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@
5050
# define IMAGE_FILE_MACHINE 0x01C0
5151
# define RSRC_RELTYPE 7 /* ??? (not tested) */
5252

53+
#elif defined TCC_TARGET_ARM64
54+
# define ADDR3264 ULONGLONG
55+
# define PE_IMAGE_REL IMAGE_REL_BASED_DIR64
56+
# define REL_TYPE_DIRECT R_AARCH64_ABS64
57+
# define R_XXX_THUNKFIX R_AARCH64_ABS64
58+
# define R_XXX_RELATIVE R_AARCH64_RELATIVE
59+
# define R_XXX_FUNCCALL R_AARCH64_CALL26
60+
# define IMAGE_FILE_MACHINE 0xAA64
61+
# define RSRC_RELTYPE 3
62+
5363
#elif defined TCC_TARGET_I386
5464
# define ADDR3264 DWORD
5565
# define PE_IMAGE_REL IMAGE_REL_BASED_HIGHLOW
@@ -261,7 +271,7 @@ struct pe_header
261271
BYTE dosstub[0x40];
262272
DWORD nt_sig;
263273
IMAGE_FILE_HEADER filehdr;
264-
#ifdef TCC_TARGET_X86_64
274+
#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
265275
IMAGE_OPTIONAL_HEADER64 opthdr;
266276
#else
267277
#ifdef _WIN64
@@ -605,11 +615,15 @@ static int pe_write(struct pe_info *pe)
605615
0x00E0, /*WORD SizeOfOptionalHeader; */
606616
0x010F, /*WORD Characteristics; */
607617
#define CHARACTERISTICS_DLL 0x230F
618+
#elif defined(TCC_TARGET_ARM64)
619+
0x00F0, /*WORD SizeOfOptionalHeader; */
620+
0x022F /*WORD Characteristics; */
621+
#define CHARACTERISTICS_DLL 0x222E
608622
#endif
609623
},{
610624
/* IMAGE_OPTIONAL_HEADER opthdr */
611625
/* Standard fields. */
612-
#ifdef TCC_TARGET_X86_64
626+
#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
613627
0x020B, /*WORD Magic; */
614628
#else
615629
0x010B, /*WORD Magic; */
@@ -621,7 +635,7 @@ static int pe_write(struct pe_info *pe)
621635
0x00000000, /*DWORD SizeOfUninitializedData; */
622636
0x00000000, /*DWORD AddressOfEntryPoint; */
623637
0x00000000, /*DWORD BaseOfCode; */
624-
#ifndef TCC_TARGET_X86_64
638+
#if !defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_ARM64)
625639
0x00000000, /*DWORD BaseOfData; */
626640
#endif
627641
/* NT additional fields. */
@@ -702,7 +716,7 @@ static int pe_write(struct pe_info *pe)
702716
break;
703717

704718
case sec_data:
705-
#ifndef TCC_TARGET_X86_64
719+
#if !defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_ARM64)
706720
if (!pe_header.opthdr.BaseOfData)
707721
pe_header.opthdr.BaseOfData = addr;
708722
#endif
@@ -1380,13 +1394,21 @@ static int pe_check_symbols(struct pe_info *pe)
13801394
write32le(p + 4, 0xE59CF000); // arm code ldr pc, [ip]
13811395
put_elf_reloc(symtab_section, text_section,
13821396
offset + 8, R_XXX_THUNKFIX, is->iat_index); // offset to IAT position
1397+
#elif defined TCC_TARGET_ARM64
1398+
p = section_ptr_add(text_section, 16);
1399+
/* ldr x16, [pc, #8] */
1400+
write32le(p + 0, 0x58000050);
1401+
/* br x16 */
1402+
write32le(p + 4, 0xd61f0200);
1403+
put_elf_reloc(symtab_section, text_section,
1404+
offset + 8, R_XXX_THUNKFIX, is->iat_index);
13831405
#else
13841406
p = section_ptr_add(text_section, 8);
13851407
write16le(p, 0x25FF);
13861408
#ifdef TCC_TARGET_X86_64
13871409
write32le(p + 2, (DWORD)-4);
13881410
#endif
1389-
put_elf_reloc(symtab_section, text_section,
1411+
put_elf_reloc(symtab_section, text_section,
13901412
offset + 2, R_XXX_THUNKFIX, is->iat_index);
13911413
#endif
13921414
}
@@ -1893,9 +1915,75 @@ ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack)
18931915
for (n = o + sizeof *p; o < n; o += sizeof p->BeginAddress)
18941916
put_elf_reloc(symtab_section, pd, o, R_XXX_RELATIVE, s1->uw_sym);
18951917
}
1918+
#elif defined TCC_TARGET_ARM64
1919+
/* ARM64 unwind codes:
1920+
save_fplr_x: 10iiiiii - stp x29,lr,[sp,#-(i+1)*8]!
1921+
set_fp: 11100001 - mov x29,sp
1922+
alloc_s: 000iiiii - sub sp,sp,#i*16 (up to 496 bytes)
1923+
alloc_m: 11000iii xxxxxxxx - sub sp,sp,#X*16 (up to 32KB)
1924+
end: 11100100 - end of unwind codes
1925+
*/
1926+
static unsigned pe_add_uwwind_info(TCCState *s1)
1927+
{
1928+
if (NULL == s1->uw_pdata) {
1929+
s1->uw_pdata = find_section(s1, ".pdata");
1930+
s1->uw_pdata->sh_addralign = 4;
1931+
}
1932+
if (0 == s1->uw_sym)
1933+
s1->uw_sym = put_elf_sym(symtab_section, 0, 0, 0, 0,
1934+
text_section->sh_num, ".uw_base");
1935+
if (0 == s1->uw_offs) {
1936+
/* TCC ARM64 prolog: stp x29,lr,[sp,#-224]!; mov x29,sp; sub sp,sp,#N
1937+
Unwind codes (reverse order): alloc_s, set_fp, save_fplr_x, end */
1938+
static const unsigned char uw_info[] = {
1939+
/* .xdata header word 0:
1940+
FunctionLength[17:0]=0 (patched), Vers[1:0]=0, X=0, E=1,
1941+
EpilogCount[4:0]=0, CodeWords[4:0]=1 */
1942+
0x00, 0x00, 0x00, 0x01,
1943+
/* Unwind codes (4 bytes, padded): */
1944+
0xE1, /* set_fp: mov x29,sp */
1945+
0x9B, /* save_fplr_x: stp x29,lr,[sp,#-224]! (224/8-1=27=0x1B) */
1946+
0xE4, /* end */
1947+
0xE3, /* nop (padding) */
1948+
};
1949+
1950+
Section *s = text_section;
1951+
unsigned char *p;
1952+
1953+
section_ptr_add(s, -s->data_offset & 3); /* align */
1954+
s1->uw_offs = s->data_offset;
1955+
p = section_ptr_add(s, sizeof uw_info);
1956+
memcpy(p, uw_info, sizeof uw_info);
1957+
}
1958+
return s1->uw_offs;
1959+
}
1960+
1961+
ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack)
1962+
{
1963+
TCCState *s1 = tcc_state;
1964+
Section *pd;
1965+
unsigned o, n, d;
1966+
struct {
1967+
DWORD BeginAddress;
1968+
DWORD EndAddress;
1969+
DWORD UnwindData;
1970+
} *p;
1971+
1972+
d = pe_add_uwwind_info(s1);
1973+
pd = s1->uw_pdata;
1974+
o = pd->data_offset;
1975+
p = section_ptr_add(pd, sizeof *p);
1976+
1977+
p->BeginAddress = start;
1978+
p->EndAddress = end;
1979+
p->UnwindData = d;
1980+
1981+
for (n = o + sizeof *p; o < n; o += sizeof p->BeginAddress)
1982+
put_elf_reloc(symtab_section, pd, o, R_XXX_RELATIVE, s1->uw_sym);
1983+
}
18961984
#endif
18971985
/* ------------------------------------------------------------- */
1898-
#ifdef TCC_TARGET_X86_64
1986+
#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
18991987
#define PE_STDSYM(n,s) n
19001988
#else
19011989
#define PE_STDSYM(n,s) "_" n s
@@ -1991,7 +2079,7 @@ static void pe_add_runtime(TCCState *s1, struct pe_info *pe)
19912079
ST_FUNC int pe_setsubsy(TCCState *s1, const char *arg)
19922080
{
19932081
static const struct subsy { const char* p; int v; } x[] = {
1994-
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
2082+
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
19952083
{ "native", 1 },
19962084
{ "gui", 2 },
19972085
{ "windows", 2 },
@@ -2098,7 +2186,7 @@ ST_FUNC int pe_output_file(TCCState *s1, const char *filename)
20982186
pe.thunk = data_section;
20992187
pe_build_imports(&pe);
21002188
s1->run_main = pe.start_symbol;
2101-
#ifdef TCC_TARGET_X86_64
2189+
#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
21022190
s1->uw_pdata = find_section(s1, ".pdata");
21032191
#endif
21042192
#endif

tccrun.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1201,7 +1201,11 @@ static int rt_error(rt_frame *f, const char *fmt, ...)
12011201
/* translate from ucontext_t* to internal rt_context * */
12021202
static void rt_getcontext(ucontext_t *uc, rt_frame *rc)
12031203
{
1204-
#if defined _WIN64
1204+
#if defined _WIN64 && defined __aarch64__
1205+
rc->ip = uc->Pc;
1206+
rc->fp = uc->Fp;
1207+
rc->sp = uc->Sp;
1208+
#elif defined _WIN64
12051209
rc->ip = uc->Rip;
12061210
rc->fp = uc->Rbp;
12071211
rc->sp = uc->Rsp;

tests/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ CROSS-TGTS = \
310310
arm-wince \
311311
arm64 \
312312
arm64-osx \
313+
arm64-win32 \
313314
arm64-FreeBSD \
314315
arm64-NetBSD \
315316
arm64-OpenBSD \

tests/tests2/Makefile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,19 @@ ifeq (-$(CONFIG_WIN32)-,-yes-)
4848
SKIP += 117_builtins.test # win32 port doesn't define __builtins
4949
SKIP += 124_atomic_counter.test # No pthread support
5050
endif
51+
# Skip x86-specific inline asm tests for non-x86 architectures
52+
ifneq (,$(filter arm% riscv%,$(ARCH)))
53+
SKIP += 85_asm-outside-function.test
54+
SKIP += 98_al_ax_extend.test
55+
SKIP += 99_fastcall.test
56+
SKIP += 127_asm_goto.test
57+
endif
58+
# Skip pthread tests for Windows cross-compilation targets
59+
ifneq (,$(findstring win32,$(CROSS_TARGET)))
60+
SKIP += 106_versym.test
61+
SKIP += 114_bound_signal.test
62+
SKIP += 124_atomic_counter.test
63+
endif
5164
ifneq (,$(filter OpenBSD FreeBSD NetBSD,$(TARGETOS)))
5265
SKIP += 106_versym.test # no pthread_condattr_setpshared
5366
SKIP += 114_bound_signal.test # libc problem signal/fork

win32/build-tcc.bat

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,12 @@ if (%BINDIR%)==() set BINDIR=%TCCDIR%
9494

9595
set D32=-DTCC_TARGET_PE -DTCC_TARGET_I386
9696
set D64=-DTCC_TARGET_PE -DTCC_TARGET_X86_64
97+
set DARM64=-DTCC_TARGET_PE -DTCC_TARGET_ARM64
9798
set P32=i386-win32
9899
set P64=x86_64-win32
100+
set PARM64=arm64-win32
99101

102+
if %T%==arm64 goto :tarm64
100103
if %T%==64 goto :t64
101104
set D=%D32%
102105
set P=%P32%
@@ -113,6 +116,11 @@ set PX=%P32%
113116
set TX=32
114117
goto :p3
115118

119+
:tarm64
120+
set D=%DARM64%
121+
set P=%PARM64%
122+
goto :p3
123+
116124
:p3
117125
git.exe --version 2>nul
118126
if not %ERRORLEVEL%==0 goto :git_done

0 commit comments

Comments
 (0)