arm64-win32 support : linker & -run

This commit is contained in:
Benjamin Oldenburg 2026-04-04 16:29:28 +02:00 committed by grischka
parent f459aff5f6
commit 41bcb4a78f
4 changed files with 256 additions and 17 deletions

View File

@ -50,6 +50,8 @@ ST_FUNC int code_reloc (int reloc_type)
case R_AARCH64_JUMP26:
case R_AARCH64_CALL26:
case R_AARCH64_JUMP_SLOT:
case R_AARCH64_CONDBR19:
case R_AARCH64_TSTBR14:
return 1;
}
return -1;
@ -76,6 +78,8 @@ ST_FUNC int gotplt_entry_type (int reloc_type)
case R_AARCH64_GLOB_DAT:
case R_AARCH64_JUMP_SLOT:
case R_AARCH64_COPY:
case R_AARCH64_CONDBR19:
case R_AARCH64_TSTBR14:
return NO_GOTPLT_ENTRY;
case R_AARCH64_ABS32:
@ -238,8 +242,23 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr,
return;
case R_AARCH64_ADR_PREL_PG_HI21: {
uint64_t off = (val >> 12) - (addr >> 12);
#ifdef TCC_TARGET_PE
/* Weak undefined symbols resolve to address 0 on PE. ADRP cannot
encode that from the default 64-bit image base, so materialize
zero directly and let the paired ADD handle any low addend. */
if ((off + ((uint64_t)1 << 20)) >> 21) {
ElfW(Sym) *sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
if (sym->st_shndx == SHN_UNDEF
&& ELFW(ST_BIND)(sym->st_info) == STB_WEAK) {
write32le(ptr, 0xd2800000 | (read32le(ptr) & 0x1f));
return;
}
tcc_error_noabort("R_AARCH64_ADR_PREL_PG_HI21 relocation failed");
}
#else
if ((off + ((uint64_t)1 << 20)) >> 21)
tcc_error_noabort("R_AARCH64_ADR_PREL_PG_HI21 relocation failed");
#endif
write32le(ptr, ((read32le(ptr) & 0x9f00001f) |
(off & 0x1ffffc) << 3 | (off & 3) << 29));
return;
@ -265,19 +284,58 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr,
write32le(ptr, ((read32le(ptr) & 0xffc003ff) |
(val & 0xff0) << 6));
return;
case R_AARCH64_CONDBR19:
/* Conditional branch: 19-bit signed offset, bits 23:5 */
#ifdef DEBUG_RELOC
printf ("reloc %d @ 0x%lx: val=0x%lx name=%s\n", type, addr, val,
(char *) symtab_section->link->data + sym->st_name);
#endif
if (((val - addr) + ((uint64_t)1 << 20)) & ~(uint64_t)0x1ffffc)
tcc_error_noabort("R_AARCH64_CONDBR19 relocation failed"
" (val=%lx, addr=%lx)", (long)val, (long)addr);
write32le(ptr, ((read32le(ptr) & 0xff00001f) |
(((val - addr) >> 2 & 0x7ffff) << 5)));
return;
case R_AARCH64_TSTBR14:
/* Test and branch: 14-bit signed offset, bits 20:5 */
#ifdef DEBUG_RELOC
printf ("reloc %d @ 0x%lx: val=0x%lx name=%s\n", type, addr, val,
(char *) symtab_section->link->data + sym->st_name);
#endif
if (((val - addr) + ((uint64_t)1 << 15)) & ~(uint64_t)0xfffc)
tcc_error_noabort("R_AARCH64_TSTBR14 relocation failed"
" (val=%lx, addr=%lx)", (long)val, (long)addr);
write32le(ptr, ((read32le(ptr) & 0xfff8001f) |
(((val - addr) >> 2 & 0x3fff) << 5)));
return;
case R_AARCH64_JUMP26:
case R_AARCH64_CALL26:
{
const char *name;
#ifdef DEBUG_RELOC
printf ("reloc %d @ 0x%lx: val=0x%lx name=%s\n", type, addr, val,
(char *) symtab_section->link->data + sym->st_name);
#endif
if (((val - addr) + ((uint64_t)1 << 27)) & ~(uint64_t)0xffffffc)
if (((val - addr) + ((uint64_t)1 << 27)) & ~(uint64_t)0xffffffc) {
#ifdef TCC_TARGET_PE
ElfW(Sym) *sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
if (sym->st_shndx == SHN_UNDEF
&& ELFW(ST_BIND)(sym->st_info) == STB_WEAK) {
write32le(ptr, ARM64_NOP); /* nop */
return;
}
#endif
name = (char *)symtab_section->link->data +
((ElfW(Sym) *)symtab_section->data)[sym_index].st_name;
tcc_error_noabort("R_AARCH64_(JUMP|CALL)26 relocation failed"
" (val=%lx, addr=%lx)", (long)val, (long)addr);
" for '%s' (val=%lx, addr=%lx)",
name, (long)val, (long)addr);
}
write32le(ptr, (0x14000000 |
(uint32_t)(type == R_AARCH64_CALL26) << 31 |
((val - addr) >> 2 & 0x3ffffff)));
return;
}
case R_AARCH64_ADR_GOT_PAGE: {
uint64_t off =
(((s1->got->sh_addr +

5
tcc.h
View File

@ -949,9 +949,10 @@ struct TCCState {
unsigned pe_file_align;
unsigned pe_stack_size;
addr_t pe_imagebase;
# ifdef TCC_TARGET_X86_64
# if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
Section *uw_pdata;
int uw_sym;
int uw_xsym;
unsigned uw_offs;
# endif
#endif
@ -1762,7 +1763,7 @@ ST_FUNC int pe_putimport(TCCState *s1, int dllindex, const char *name, addr_t va
ST_FUNC int pe_setsubsy(TCCState *s1, const char *arg);
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
#endif
#ifdef TCC_TARGET_X86_64
#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack);
#endif
PUB_FUNC int tcc_get_dllexports(const char *filename, char **pp);

196
tccpe.c
View File

@ -50,6 +50,16 @@
# define IMAGE_FILE_MACHINE 0x01C0
# define RSRC_RELTYPE 7 /* ??? (not tested) */
#elif defined TCC_TARGET_ARM64
# define ADDR3264 ULONGLONG
# define PE_IMAGE_REL IMAGE_REL_BASED_DIR64
# define REL_TYPE_DIRECT R_AARCH64_ABS64
# define R_XXX_THUNKFIX R_AARCH64_ABS64
# define R_XXX_RELATIVE R_AARCH64_RELATIVE
# define R_XXX_FUNCCALL R_AARCH64_CALL26
# define IMAGE_FILE_MACHINE 0xAA64
# define RSRC_RELTYPE 3
#elif defined TCC_TARGET_I386
# define ADDR3264 DWORD
# define PE_IMAGE_REL IMAGE_REL_BASED_HIGHLOW
@ -126,7 +136,7 @@ typedef struct _IMAGE_OPTIONAL_HEADER {
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
#ifndef TCC_TARGET_X86_64
#if !defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_ARM64)
DWORD BaseOfData;
#endif
/* NT additional fields. */
@ -225,6 +235,19 @@ typedef struct _IMAGE_BASE_RELOCATION {
#define IMAGE_SIZEOF_BASE_RELOCATION 8
#ifndef IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA
#define IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA 0x0020
#endif
#ifndef IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040
#endif
#ifndef IMAGE_DLLCHARACTERISTICS_NX_COMPAT
#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100
#endif
#ifndef IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000
#endif
#define IMAGE_REL_BASED_ABSOLUTE 0
#define IMAGE_REL_BASED_HIGH 1
#define IMAGE_REL_BASED_LOW 2
@ -250,6 +273,9 @@ typedef struct _IMAGE_BASE_RELOCATION {
#endif /* ndef IMAGE_NT_SIGNATURE */
/* ----------------------------------------------------------- */
#ifndef IMAGE_FILE_MACHINE_ARM64
#define IMAGE_FILE_MACHINE_ARM64 0xAA64
#endif
#ifndef IMAGE_REL_BASED_DIR64
# define IMAGE_REL_BASED_DIR64 10
#endif
@ -261,7 +287,7 @@ struct pe_header
BYTE dosstub[0x40];
DWORD nt_sig;
IMAGE_FILE_HEADER filehdr;
#ifdef TCC_TARGET_X86_64
#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
IMAGE_OPTIONAL_HEADER64 opthdr;
#else
#ifdef _WIN64
@ -605,11 +631,15 @@ static int pe_write(struct pe_info *pe)
0x00E0, /*WORD SizeOfOptionalHeader; */
0x010F, /*WORD Characteristics; */
#define CHARACTERISTICS_DLL 0x230F
#elif defined(TCC_TARGET_ARM64)
0x00F0, /*WORD SizeOfOptionalHeader; */
0x0022 /*WORD Characteristics; */
#define CHARACTERISTICS_DLL 0x2022
#endif
},{
/* IMAGE_OPTIONAL_HEADER opthdr */
/* Standard fields. */
#ifdef TCC_TARGET_X86_64
#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
0x020B, /*WORD Magic; */
#else
0x010B, /*WORD Magic; */
@ -621,29 +651,48 @@ static int pe_write(struct pe_info *pe)
0x00000000, /*DWORD SizeOfUninitializedData; */
0x00000000, /*DWORD AddressOfEntryPoint; */
0x00000000, /*DWORD BaseOfCode; */
#ifndef TCC_TARGET_X86_64
#if !defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_ARM64)
0x00000000, /*DWORD BaseOfData; */
#endif
/* NT additional fields. */
#if defined(TCC_TARGET_ARM)
0x00100000, /*DWORD ImageBase; */
#elif defined(TCC_TARGET_ARM64)
0x140000000ULL, /*ULONGLONG ImageBase; */
#else
0x00400000, /*DWORD ImageBase; */
#endif
0x00001000, /*DWORD SectionAlignment; */
0x00000200, /*DWORD FileAlignment; */
#if defined(TCC_TARGET_ARM64)
0x0006, /*WORD MajorOperatingSystemVersion; */
0x0002, /*WORD MinorOperatingSystemVersion; */
#else
0x0004, /*WORD MajorOperatingSystemVersion; */
0x0000, /*WORD MinorOperatingSystemVersion; */
#endif
0x0000, /*WORD MajorImageVersion; */
0x0000, /*WORD MinorImageVersion; */
#if defined(TCC_TARGET_ARM64)
0x0006, /*WORD MajorSubsystemVersion; */
0x0002, /*WORD MinorSubsystemVersion; */
#else
0x0004, /*WORD MajorSubsystemVersion; */
0x0000, /*WORD MinorSubsystemVersion; */
#endif
0x00000000, /*DWORD Win32VersionValue; */
0x00000000, /*DWORD SizeOfImage; */
0x00000200, /*DWORD SizeOfHeaders; */
0x00000000, /*DWORD CheckSum; */
0x0002, /*WORD Subsystem; */
#if defined(TCC_TARGET_ARM64)
IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA |
IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE |
IMAGE_DLLCHARACTERISTICS_NX_COMPAT |
IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE,
#else
0x0000, /*WORD DllCharacteristics; */
#endif
0x00100000, /*DWORD SizeOfStackReserve; */
0x00001000, /*DWORD SizeOfStackCommit; */
0x00100000, /*DWORD SizeOfHeapReserve; */
@ -702,7 +751,7 @@ static int pe_write(struct pe_info *pe)
break;
case sec_data:
#ifndef TCC_TARGET_X86_64
#if !defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_ARM64)
if (!pe_header.opthdr.BaseOfData)
pe_header.opthdr.BaseOfData = addr;
#endif
@ -1191,7 +1240,11 @@ static int pe_assign_addresses (struct pe_info *pe)
Section *s;
TCCState *s1 = pe->s1;
if (PE_DLL == pe->type)
if (PE_DLL == pe->type
#ifdef TCC_TARGET_ARM64
|| PE_EXE == pe->type || PE_GUI == pe->type
#endif
)
pe->reloc = new_section(s1, ".reloc", SHT_PROGBITS, 0);
//pe->thunk = new_section(s1, ".iedat", SHT_PROGBITS, SHF_ALLOC);
@ -1380,6 +1433,18 @@ static int pe_check_symbols(struct pe_info *pe)
write32le(p + 4, 0xE59CF000); // arm code ldr pc, [ip]
put_elf_reloc(symtab_section, text_section,
offset + 8, R_XXX_THUNKFIX, is->iat_index); // offset to IAT position
#elif defined(TCC_TARGET_ARM64)
p = section_ptr_add(text_section, 24);
/* ldr x16, [pc, #16] */
write32le(p + 0, 0x58000090);
/* ldr x16, [x16] */
write32le(p + 4, 0xf9400210);
/* br x16 */
write32le(p + 8, 0xd61f0200);
/* nop for 8-byte literal alignment */
write32le(p + 12, 0xd503201f);
put_elf_reloc(symtab_section, text_section,
offset + 16, R_XXX_THUNKFIX, is->iat_index);
#else
p = section_ptr_add(text_section, 8);
write16le(p, 0x25FF);
@ -1611,7 +1676,7 @@ static int get_dllexports(int fd, char **pp)
if (IMAGE_DIRECTORY_ENTRY_EXPORT >= oh.NumberOfRvaAndSizes)
goto the_end_0;
addr = oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
} else if (ih.Machine == 0x8664) {
} else if (ih.Machine == 0x8664 || ih.Machine == IMAGE_FILE_MACHINE_ARM64) {
IMAGE_OPTIONAL_HEADER64 oh;
sec_hdroffset = opt_hdroffset + sizeof oh;
if (!read_mem(fd, opt_hdroffset, &oh, sizeof oh))
@ -1834,7 +1899,7 @@ PUB_FUNC int tcc_get_dllexports(const char *filename, char **pp)
/* ------------------------------------------------------------- */
#ifdef TCC_TARGET_X86_64
static unsigned pe_add_uwwind_info(TCCState *s1)
static unsigned pe_add_unwind_info(TCCState *s1)
{
if (NULL == s1->uw_pdata) {
s1->uw_pdata = find_section(s1, ".pdata");
@ -1879,7 +1944,7 @@ ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack)
DWORD UnwindData;
} *p;
d = pe_add_uwwind_info(s1);
d = pe_add_unwind_info(s1);
pd = s1->uw_pdata;
o = pd->data_offset;
p = section_ptr_add(pd, sizeof *p);
@ -1893,9 +1958,110 @@ ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack)
for (n = o + sizeof *p; o < n; o += sizeof p->BeginAddress)
put_elf_reloc(symtab_section, pd, o, R_XXX_RELATIVE, s1->uw_sym);
}
#elif defined(TCC_TARGET_ARM64)
/* ARM64 unwind codes:
save_fplr_x: 10iiiiii - stp x29,lr,[sp,#-(i+1)*8]!
set_fp: 11100001 - mov x29,sp
alloc_s: 000iiiii - sub sp,sp,#i*16 (up to 496 bytes)
alloc_m: 11000iii xxxxxxxx - sub sp,sp,#X*16 (up to 32KB)
end: 11100100 - end of unwind codes
*/
static Section *pe_add_unwind_info(TCCState *s1)
{
Section *s;
if (NULL == s1->uw_pdata) {
s1->uw_pdata = find_section(s1, ".pdata");
s1->uw_pdata->sh_addralign = 4;
}
s = find_section(s1, ".xdata");
if (NULL == s) {
s = new_section(s1, ".xdata", SHT_PROGBITS, SHF_ALLOC);
s->sh_addralign = 4;
}
if (0 == s1->uw_sym)
s1->uw_sym = put_elf_sym(symtab_section, 0, 0, 0, 0,
text_section->sh_num, ".uw_text_base");
if (0 == s1->uw_xsym)
s1->uw_xsym = put_elf_sym(symtab_section, 0, 0, 0, 0,
s->sh_num, ".uw_base");
return s;
}
ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack)
{
TCCState *s1 = tcc_state;
Section *pd, *xd;
unsigned o, n, d, code_bytes, func_len, stack_slots;
unsigned char *q;
uint32_t header;
struct {
DWORD BeginAddress;
DWORD EndAddress;
DWORD UnwindData;
} *p;
xd = pe_add_unwind_info(s1);
pd = s1->uw_pdata;
stack = (stack + 15) & ~15;
stack_slots = stack >> 4;
func_len = (end - start) >> 2;
code_bytes = 0;
if (stack_slots) {
if (stack_slots <= 31) {
code_bytes += 1;
} else if (stack_slots <= 0x7ff) {
code_bytes += 2;
} else {
code_bytes += 4;
}
}
code_bytes += 3; /* set_fp, save_fplr_x, end */
code_bytes = (code_bytes + 3) & ~3;
section_ptr_add(xd, -xd->data_offset & 3);
d = xd->data_offset;
q = section_ptr_add(xd, 4 + code_bytes);
/* Full ARM64 xdata header: E=1 with one epilog and no exception handler. */
header = (func_len & 0x3ffff) | (1u << 21) | ((code_bytes >> 2) << 27);
write32le(q, header);
q += 4;
if (stack_slots) {
if (stack_slots <= 31) {
*q++ = stack_slots; /* alloc_s */
} else if (stack_slots <= 0x7ff) {
*q++ = 0xC0 | (stack_slots >> 8); /* alloc_m */
*q++ = stack_slots & 0xff;
} else {
*q++ = 0xE0; /* alloc_l */
*q++ = (stack_slots >> 16) & 0xff;
*q++ = (stack_slots >> 8) & 0xff;
*q++ = stack_slots & 0xff;
}
}
*q++ = 0xE1; /* set_fp */
*q++ = 0x9B; /* save_fplr_x: stp x29,lr,[sp,#-224]! */
*q++ = 0xE4; /* end */
while ((unsigned)(q - (xd->data + d + 4)) < code_bytes)
*q++ = 0xE3; /* nop padding */
o = pd->data_offset;
p = section_ptr_add(pd, sizeof *p);
p->BeginAddress = start;
p->EndAddress = end;
p->UnwindData = d;
for (n = o + 2 * sizeof p->BeginAddress; o < n; o += sizeof p->BeginAddress)
put_elf_reloc(symtab_section, pd, o, R_XXX_RELATIVE, s1->uw_sym);
put_elf_reloc(symtab_section, pd, n, R_XXX_RELATIVE, s1->uw_xsym);
}
#endif
/* ------------------------------------------------------------- */
#ifdef TCC_TARGET_X86_64
#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
#define PE_STDSYM(n,s) n
#else
#define PE_STDSYM(n,s) "_" n s
@ -1991,7 +2157,7 @@ static void pe_add_runtime(TCCState *s1, struct pe_info *pe)
ST_FUNC int pe_setsubsy(TCCState *s1, const char *arg)
{
static const struct subsy { const char* p; int v; } x[] = {
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
{ "native", 1 },
{ "gui", 2 },
{ "windows", 2 },
@ -2020,10 +2186,16 @@ static void pe_set_options(TCCState * s1, struct pe_info *pe)
{
if (PE_DLL == pe->type) {
/* XXX: check if is correct for arm-pe target */
#if defined(TCC_TARGET_ARM64)
pe->imagebase = 0x180000000ULL;
#else
pe->imagebase = 0x10000000;
#endif
} else {
#if defined(TCC_TARGET_ARM)
pe->imagebase = 0x00010000;
#elif defined(TCC_TARGET_ARM64)
pe->imagebase = 0x140000000ULL;
#else
pe->imagebase = 0x00400000;
#endif
@ -2098,7 +2270,7 @@ ST_FUNC int pe_output_file(TCCState *s1, const char *filename)
pe.thunk = data_section;
pe_build_imports(&pe);
s1->run_main = pe.start_symbol;
#ifdef TCC_TARGET_X86_64
#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
s1->uw_pdata = find_section(s1, ".pdata");
#endif
#endif

View File

@ -1199,7 +1199,11 @@ static int rt_error(rt_frame *f, const char *fmt, ...)
/* translate from ucontext_t* to internal rt_context * */
static void rt_getcontext(ucontext_t *uc, rt_frame *rc)
{
#if defined _WIN64
#if defined _WIN64 && defined __aarch64__
rc->ip = uc->Pc; /* Program Counter */
rc->fp = uc->Fp; /* Frame Pointer (X29) */
rc->sp = uc->Sp; /* Stack Pointer (X30 is LR, but SP is separate) */
#elif defined _WIN64
rc->ip = uc->Rip;
rc->fp = uc->Rbp;
rc->sp = uc->Rsp;
@ -1398,7 +1402,11 @@ static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
/* Generate a stack backtrace when a CPU exception occurs. */
static void set_exception_handler(void)
{
#ifdef _WIN64
AddVectoredExceptionHandler(1, cpu_exception_handler);
#else
SetUnhandledExceptionFilter(cpu_exception_handler);
#endif
}
#endif