diff --git a/Makefile b/Makefile index 5acc450..864b144 100644 --- a/Makefile +++ b/Makefile @@ -176,9 +176,9 @@ arm-fpa-ld_FILES = $(arm_FILES) arm-vfp_FILES = $(arm_FILES) arm-eabi_FILES = $(arm_FILES) arm-eabihf_FILES = $(arm_FILES) -arm64_FILES = $(CORE_FILES) arm64-gen.c arm64-link.c +arm64_FILES = $(CORE_FILES) arm64-gen.c arm64-link.c arm-asm.c c67_FILES = $(CORE_FILES) c67-gen.c c67-link.c tcccoff.c -riscv64_FILES = $(CORE_FILES) riscv64-gen.c riscv64-link.c +riscv64_FILES = $(CORE_FILES) riscv64-gen.c riscv64-link.c riscv64-asm.c # libtcc sources LIBTCC_SRC = $(filter-out tcc.c tcctools.c,$(filter %.c,$($T_FILES))) diff --git a/arm-gen.c b/arm-gen.c index 6423dc3..a2974d5 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -158,6 +158,12 @@ ST_DATA const int reg_classes[NB_REGS] = { static int func_sub_sp_offset, last_itod_magic; static int leaffunc; +#if defined(CONFIG_TCC_BCHECK) +static addr_t func_bound_offset; +static unsigned long func_bound_ind; +static int func_bound_add_epilog; +#endif + #if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) static CType float_type, double_type, func_float_type, func_double_type; ST_FUNC void arm_init(struct TCCState *s) @@ -193,11 +199,17 @@ ST_FUNC void arm_init(struct TCCState *s) } #endif +#define CHECK_R(r) ((r) >= TREG_R0 && (r) <= TREG_LR) + static int two2mask(int a,int b) { + if (!CHECK_R(a) || !CHECK_R(b)) + tcc_error("compiler error! registers %i,%i is not valid",a,b); return (reg_classes[a]|reg_classes[b])&~(RC_INT|RC_FLOAT); } static int regmask(int r) { + if (!CHECK_R(r)) + tcc_error("compiler error! register %i is not valid",r); return reg_classes[r]&~(RC_INT|RC_FLOAT); } @@ -751,6 +763,14 @@ static void gcall_or_jmp(int is_jmp) greloc(cur_text_section, vtop->sym, ind, R_ARM_ABS32); o(vtop->c.i); } +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check && + (vtop->sym->v == TOK_setjmp || + vtop->sym->v == TOK__setjmp || + vtop->sym->v == TOK_sigsetjmp || + vtop->sym->v == TOK___sigsetjmp)) + func_bound_add_epilog = 1; +#endif }else{ if(!is_jmp) o(0xE28FE004); // add lr,pc,#4 @@ -759,6 +779,9 @@ static void gcall_or_jmp(int is_jmp) } } else { /* otherwise, indirect call */ +#ifdef CONFIG_TCC_BCHECK + vtop->r &= ~VT_MUSTBOUND; +#endif r = gv(RC_INT); if(!is_jmp) o(0xE1A0E00F); // mov lr,pc @@ -766,6 +789,121 @@ static void gcall_or_jmp(int is_jmp) } } +#if defined(CONFIG_TCC_BCHECK) + +static void gen_bounds_call(int v) +{ + Sym *sym = external_global_sym(v, &func_old_type); + + greloc(cur_text_section, sym, ind, R_ARM_PC24); + o(0xebfffffe); +} + +/* generate a bounded pointer addition */ +ST_FUNC void gen_bounded_ptr_add(void) +{ + vpush_global_sym(&func_old_type, TOK___bound_ptr_add); + vrott(3); + gfunc_call(2); + vpushi(0); + /* returned pointer is in REG_IRET */ + vtop->r = REG_IRET | VT_BOUNDED; + if (nocode_wanted) + return; + /* relocation offset of the bounding function call point */ + vtop->c.i = (cur_text_section->reloc->data_offset - sizeof(Elf32_Rel)); +} + +/* patch pointer addition in vtop so that pointer dereferencing is + also tested */ +ST_FUNC void gen_bounded_ptr_deref(void) +{ + addr_t func; + int size, align; + Elf32_Rel *rel; + Sym *sym; + + if (nocode_wanted) + return; + + size = type_size(&vtop->type, &align); + switch(size) { + case 1: func = TOK___bound_ptr_indir1; break; + case 2: func = TOK___bound_ptr_indir2; break; + case 4: func = TOK___bound_ptr_indir4; break; + case 8: func = TOK___bound_ptr_indir8; break; + case 12: func = TOK___bound_ptr_indir12; break; + case 16: func = TOK___bound_ptr_indir16; break; + default: + /* may happen with struct member access */ + return; + //tcc_error("unhandled size when dereferencing bounded pointer"); + //func = 0; + //break; + } + sym = external_global_sym(func, &func_old_type); + if (!sym->c) + put_extern_sym(sym, NULL, 0, 0); + /* patch relocation */ + /* XXX: find a better solution ? */ + rel = (Elf32_Rel *)(cur_text_section->reloc->data + vtop->c.i); + rel->r_info = ELF32_R_INFO(sym->c, ELF32_R_TYPE(rel->r_info)); +} + +static void gen_bounds_prolog(void) +{ + /* leave some room for bound checking code */ + func_bound_offset = lbounds_section->data_offset; + func_bound_ind = ind; + func_bound_add_epilog = 0; + o(0xe1a00000); /* ld r0,lbounds_section->data_offset */ + o(0xe1a00000); + o(0xe1a00000); + o(0xe1a00000); /* call __bound_local_new */ +} + +static void gen_bounds_epilog(void) +{ + addr_t saved_ind; + addr_t *bounds_ptr; + Sym *sym_data; + int offset_modified = func_bound_offset != lbounds_section->data_offset; + + if (!offset_modified && !func_bound_add_epilog) + return; + + /* add end of table info */ + bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t)); + *bounds_ptr = 0; + + sym_data = get_sym_ref(&char_pointer_type, lbounds_section, + func_bound_offset, lbounds_section->data_offset); + + /* generate bound local allocation */ + if (offset_modified) { + saved_ind = ind; + ind = func_bound_ind; + o(0xe59f0000); /* ldr r0, [pc] */ + o(0xea000000); /* b $+4 */ + greloc(cur_text_section, sym_data, ind, R_ARM_ABS32); + o(0x00000000); /* lbounds_section->data_offset */ + gen_bounds_call(TOK___bound_local_new); + ind = saved_ind; + } + + /* generate bound check local freeing */ + o(0xe92d0003); /* push {r0,r1} */ + o(0xed2d0b02); /* vpush {d0} */ + o(0xe59f0000); /* ldr r0, [pc] */ + o(0xea000000); /* b $+4 */ + greloc(cur_text_section, sym_data, ind, R_ARM_ABS32); + o(0x00000000); /* lbounds_section->data_offset */ + gen_bounds_call(TOK___bound_local_delete); + o(0xecbd0b02); /* vpop {d0} */ + o(0xe8bd0003); /* pop {r0,r1} */ +} +#endif + static int unalias_ldbl(int btype) { #if LDOUBLE_SIZE == 8 @@ -956,7 +1094,7 @@ static int assign_regs(int nb_args, int float_abi, struct plan *plan, int *todo) ncrn = nsaa = 0; *todo = 0; - plan->pplans = tcc_malloc(nb_args * sizeof(*plan->pplans)); + plan->pplans = nb_args ? tcc_malloc(nb_args * sizeof(*plan->pplans)) : NULL; memset(plan->clsplans, 0, sizeof(plan->clsplans)); for(i = nb_args; i-- ;) { int j, start_vfpreg = 0; @@ -1215,10 +1353,16 @@ void gfunc_call(int nb_args) int def_float_abi = float_abi; int todo; struct plan plan; - #ifdef TCC_ARM_EABI int variadic; +#endif +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gbound_args(nb_args); +#endif + +#ifdef TCC_ARM_EABI if (float_abi == ARM_HARD_FLOAT) { variadic = (vtop[-nb_args].type.ref->f.func_type == FUNC_ELLIPSIS); if (variadic || floats_in_core_regs(&vtop[-nb_args])) @@ -1367,6 +1511,10 @@ from_stack: last_itod_magic=0; leaffunc = 1; loc = 0; +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gen_bounds_prolog(); +#endif } /* generate function epilog */ @@ -1374,6 +1522,11 @@ void gfunc_epilog(void) { uint32_t x; int diff; + +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gen_bounds_epilog(); +#endif /* Copy float return value to core register if base standard is used and float computation is made with VFP */ #if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) @@ -1582,10 +1735,10 @@ void gen_opi(int op) vswap(); c=intr(gv(RC_INT)); vswap(); - opc=0xE0000000|(opc<<20)|(c<<16); + opc=0xE0000000|(opc<<20); if((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { uint32_t x; - x=stuff_const(opc|0x2000000,vtop->c.i); + x=stuff_const(opc|0x2000000|(c<<16),vtop->c.i); if(x) { r=intr(vtop[-1].r=get_reg_ex(RC_INT,regmask(vtop[-1].r))); o(x|(r<<12)); @@ -1593,8 +1746,13 @@ void gen_opi(int op) } } fr=intr(gv(RC_INT)); + if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) { + vswap(); + c=intr(gv(RC_INT)); + vswap(); + } r=intr(vtop[-1].r=get_reg_ex(RC_INT,two2mask(vtop->r,vtop[-1].r))); - o(opc|(r<<12)|fr); + o(opc|(c<<16)|(r<<12)|fr); done: vtop--; if (op >= TOK_ULT && op <= TOK_GT) @@ -1608,15 +1766,19 @@ done: vswap(); r=intr(gv(RC_INT)); vswap(); - opc|=r; if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { fr=intr(vtop[-1].r=get_reg_ex(RC_INT,regmask(vtop[-1].r))); c = vtop->c.i & 0x1f; - o(opc|(c<<7)|(fr<<12)); + o(opc|r|(c<<7)|(fr<<12)); } else { fr=intr(gv(RC_INT)); + if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) { + vswap(); + r=intr(gv(RC_INT)); + vswap(); + } c=intr(vtop[-1].r=get_reg_ex(RC_INT,two2mask(vtop->r,vtop[-1].r))); - o(opc|(c<<12)|(fr<<8)|0x10); + o(opc|r|(c<<12)|(fr<<8)|0x10); } vtop--; break; @@ -1701,9 +1863,9 @@ void gen_opf(int op) vtop--; o(x|0x10000|(vfpr(gv(RC_FLOAT))<<12)); /* fcmp(e)X -> fcmp(e)zX */ } else { - x|=vfpr(gv(RC_FLOAT)); - vswap(); - o(x|(vfpr(gv(RC_FLOAT))<<12)); + gv2(RC_FLOAT,RC_FLOAT); + x|=vfpr(vtop[0].r); + o(x|(vfpr(vtop[-1].r) << 12)); vtop--; } o(0xEEF1FA10); /* fmstat */ @@ -1726,6 +1888,12 @@ void gen_opf(int op) r2=gv(RC_FLOAT); x|=vfpr(r2)<<16; r|=regmask(r2); + if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) { + vswap(); + r=gv(RC_FLOAT); + vswap(); + x=(x&~0xf)|vfpr(r); + } } vtop->r=get_reg_ex(RC_FLOAT,r); if(!fneg) @@ -1808,6 +1976,11 @@ void gen_opf(int op) r2=c2&0xf; } else { r2=fpr(gv(RC_FLOAT)); + if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) { + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + } } break; case '-': @@ -1829,6 +2002,11 @@ void gen_opf(int op) r=fpr(gv(RC_FLOAT)); vswap(); r2=fpr(gv(RC_FLOAT)); + if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) { + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + } } break; case '*': @@ -1841,8 +2019,14 @@ void gen_opf(int op) vswap(); if(c2 && c2<=0xf) r2=c2; - else + else { r2=fpr(gv(RC_FLOAT)); + if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) { + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + } + } x|=0x100000; // muf break; case '/': @@ -1863,6 +2047,11 @@ void gen_opf(int op) r=fpr(gv(RC_FLOAT)); vswap(); r2=fpr(gv(RC_FLOAT)); + if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) { + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + } } break; default: @@ -1915,6 +2104,11 @@ void gen_opf(int op) r2=c2&0xf; } else { r2=fpr(gv(RC_FLOAT)); + if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) { + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + } } --vtop; vset_VT_CMP(op); diff --git a/arm-link.c b/arm-link.c index f58b8a5..0d0fa3c 100644 --- a/arm-link.c +++ b/arm-link.c @@ -155,7 +155,7 @@ ST_FUNC void relocate_plt(TCCState *s1) while (p < p_end) { if (read32le(p) == 0x46c04778) /* PLT Thumb stub present */ p += 4; - add32le(p + 12, x + s1->plt->data - p); + add32le(p + 12, x + (s1->plt->data - p)); p += 16; } } diff --git a/arm64-gen.c b/arm64-gen.c index 021a079..8edb9e8 100644 --- a/arm64-gen.c +++ b/arm64-gen.c @@ -81,6 +81,12 @@ ST_DATA const int reg_classes[NB_REGS] = { RC_FLOAT | RC_F(7) }; +#if defined(CONFIG_TCC_BCHECK) +static addr_t func_bound_offset; +static unsigned long func_bound_ind; +static int func_bound_add_epilog; +#endif + #define IS_FREG(x) ((x) >= TREG_F(0)) static uint32_t intr(int r) @@ -454,7 +460,7 @@ static void arm64_load_cmp(int r, SValue *sv); ST_FUNC void load(int r, SValue *sv) { int svtt = sv->type.t; - int svr = sv->r; + int svr = sv->r & ~VT_BOUNDED; int svrv = svr & VT_VALMASK; uint64_t svcul = (uint32_t)sv->c.i; svcul = svcul >> 31 & 1 ? svcul - ((uint64_t)1 << 32) : svcul; @@ -554,7 +560,7 @@ ST_FUNC void load(int r, SValue *sv) ST_FUNC void store(int r, SValue *sv) { int svtt = sv->type.t; - int svr = sv->r; + int svr = sv->r & ~VT_BOUNDED; int svrv = svr & VT_VALMASK; uint64_t svcul = (uint32_t)sv->c.i; svcul = svcul >> 31 & 1 ? svcul - ((uint64_t)1 << 32) : svcul; @@ -594,11 +600,147 @@ static void arm64_gen_bl_or_b(int b) assert(!b); greloca(cur_text_section, vtop->sym, ind, R_AARCH64_CALL26, 0); o(0x94000000); // bl . +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check && + (vtop->sym->v == TOK_setjmp || + vtop->sym->v == TOK__setjmp || + vtop->sym->v == TOK_sigsetjmp || + vtop->sym->v == TOK___sigsetjmp)) + func_bound_add_epilog = 1; +#endif } - else + else { +#ifdef CONFIG_TCC_BCHECK + vtop->r &= ~VT_MUSTBOUND; +#endif o(0xd61f0000 | (uint32_t)!b << 21 | intr(gv(RC_R30)) << 5); // br/blr + } } +#if defined(CONFIG_TCC_BCHECK) + +static void gen_bounds_call(int v) +{ + Sym *sym = external_global_sym(v, &func_old_type); + + greloca(cur_text_section, sym, ind, R_AARCH64_CALL26, 0); + o(0x94000000); // bl +} + +/* generate a bounded pointer addition */ +ST_FUNC void gen_bounded_ptr_add(void) +{ + vpush_global_sym(&func_old_type, TOK___bound_ptr_add); + vrott(3); + gfunc_call(2); + vpushi(0); + /* returned pointer is in REG_IRET */ + vtop->r = REG_IRET | VT_BOUNDED; + if (nocode_wanted) + return; + /* relocation offset of the bounding function call point */ + vtop->c.i = (cur_text_section->reloc->data_offset - sizeof(ElfW(Rela))); +} + +/* patch pointer addition in vtop so that pointer dereferencing is + also tested */ +ST_FUNC void gen_bounded_ptr_deref(void) +{ + addr_t func; + int size, align; + ElfW(Rela) *rel; + Sym *sym; + + if (nocode_wanted) + return; + + size = type_size(&vtop->type, &align); + switch(size) { + case 1: func = TOK___bound_ptr_indir1; break; + case 2: func = TOK___bound_ptr_indir2; break; + case 4: func = TOK___bound_ptr_indir4; break; + case 8: func = TOK___bound_ptr_indir8; break; + case 12: func = TOK___bound_ptr_indir12; break; + case 16: func = TOK___bound_ptr_indir16; break; + default: + /* may happen with struct member access */ + return; + //tcc_error("unhandled size when dereferencing bounded pointer"); + //func = 0; + //break; + } + sym = external_global_sym(func, &func_old_type); + if (!sym->c) + put_extern_sym(sym, NULL, 0, 0); + /* patch relocation */ + /* XXX: find a better solution ? */ + rel = (ElfW(Rela) *)(cur_text_section->reloc->data + vtop->c.i); + rel->r_info = ELF64_R_INFO(sym->c, ELF64_R_TYPE(rel->r_info)); +} + +static void gen_bounds_prolog(void) +{ + /* leave some room for bound checking code */ + func_bound_offset = lbounds_section->data_offset; + func_bound_ind = ind; + func_bound_add_epilog = 0; + o(0xd503201f); /* nop -> mov x0,#0,lsl #0, lbound section pointer */ + o(0xd503201f); + o(0xd503201f); + o(0xd503201f); + o(0xd503201f); /* nop -> call __bound_local_new */ +} + +static void gen_bounds_epilog(void) +{ + addr_t saved_ind; + addr_t *bounds_ptr; + Sym *sym_data; + int offset_modified = func_bound_offset != lbounds_section->data_offset; + + if (!offset_modified && !func_bound_add_epilog) + return; + + /* add end of table info */ + bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t)); + *bounds_ptr = 0; + + sym_data = get_sym_ref(&char_pointer_type, lbounds_section, + func_bound_offset, lbounds_section->data_offset); + + /* generate bound local allocation */ + if (offset_modified) { + saved_ind = ind; + ind = func_bound_ind; + greloca(cur_text_section, sym_data, ind, R_AARCH64_MOVW_UABS_G0_NC, 0); + o(0xd2800000); /* mov x0,#0,lsl #0, lbound section pointer */ + greloca(cur_text_section, sym_data, ind, R_AARCH64_MOVW_UABS_G1_NC, 0); + o(0xf2a00000); /* movk x0,#0,lsl #16 */ + greloca(cur_text_section, sym_data, ind, R_AARCH64_MOVW_UABS_G2_NC, 0); + o(0xf2c00000); /* movk x0,#0,lsl #32 */ + greloca(cur_text_section, sym_data, ind, R_AARCH64_MOVW_UABS_G3, 0); + o(0xf2e00000); /* movk x0,#0,lsl #48 */ + gen_bounds_call(TOK___bound_local_new); + ind = saved_ind; + } + + /* generate bound check local freeing */ + o(0xf81f0fe0); /* str x0, [sp, #-16]! */ + o(0x3c9f0fe0); /* str q0, [sp, #-16]! */ + greloca(cur_text_section, sym_data, ind, R_AARCH64_MOVW_UABS_G0_NC, 0); + o(0xd2800000); // mov x0,#0,lsl #0 + greloca(cur_text_section, sym_data, ind, R_AARCH64_MOVW_UABS_G1_NC, 0); + o(0xf2a00000); // movk x0,#0,lsl #16 + greloca(cur_text_section, sym_data, ind, R_AARCH64_MOVW_UABS_G2_NC, 0); + o(0xf2c00000); // movk x0,#0,lsl #32 + greloca(cur_text_section, sym_data, ind, R_AARCH64_MOVW_UABS_G3, 0); + o(0xf2e00000); // movk x0,#0,lsl #48 + gen_bounds_call(TOK___bound_local_delete); + o(0x3cc107e0); /* ldr q0, [sp], #16 */ + o(0xf84107e0); /* ldr x0, [sp], #16 */ +} +#endif + static int arm64_hfa_aux(CType *type, int *fsize, int num) { if (is_float(type->t)) { @@ -642,7 +784,7 @@ static int arm64_hfa_aux(CType *type, int *fsize, int num) return num; } } - else if (type->t & VT_ARRAY) { + else if ((type->t & VT_ARRAY) && ((type->t & VT_BTYPE) != VT_PTR)) { int num1; if (!type->ref->c) return num; @@ -659,7 +801,8 @@ static int arm64_hfa_aux(CType *type, int *fsize, int num) static int arm64_hfa(CType *type, int *fsize) { - if ((type->t & VT_BTYPE) == VT_STRUCT || (type->t & VT_ARRAY)) { + if ((type->t & VT_BTYPE) == VT_STRUCT || + ((type->t & VT_ARRAY) && ((type->t & VT_BTYPE) != VT_PTR))) { int sz = 0; int n = arm64_hfa_aux(type, &sz, 0); if (0 < n && n <= 4) { @@ -839,6 +982,11 @@ ST_FUNC void gfunc_call(int nb_args) unsigned long stack; int i; +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gbound_args(nb_args); +#endif + return_type = &vtop[-nb_args].type.ref->type; if ((return_type->t & VT_BTYPE) == VT_STRUCT) --nb_args; @@ -1014,8 +1162,8 @@ ST_FUNC void gfunc_prolog(Sym *func_sym) for (sym = func_type->ref; sym; sym = sym->next) ++n; - t = tcc_malloc(n * sizeof(*t)); - a = tcc_malloc(n * sizeof(*a)); + t = n ? tcc_malloc(n * sizeof(*t)) : NULL; + a = n ? tcc_malloc(n * sizeof(*a)) : NULL; for (sym = func_type->ref; sym; sym = sym->next) t[i++] = &sym->type; @@ -1076,6 +1224,10 @@ ST_FUNC void gfunc_prolog(Sym *func_sym) o(0xd503201f); // nop o(0xd503201f); // nop loc = 0; +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gen_bounds_prolog(); +#endif } ST_FUNC void gen_va_start(void) @@ -1246,6 +1398,11 @@ ST_FUNC void gfunc_return(CType *func_type) ST_FUNC void gfunc_epilog(void) { +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gen_bounds_epilog(); +#endif + if (loc) { // Insert instructions to subtract size of stack frame from SP. unsigned char *ptr = cur_text_section->data + arm64_func_sub_sp_offset; diff --git a/arm64-link.c b/arm64-link.c index 3c891ff..b8c9c0c 100644 --- a/arm64-link.c +++ b/arm64-link.c @@ -37,6 +37,7 @@ int code_reloc (int reloc_type) case R_AARCH64_ADD_ABS_LO12_NC: case R_AARCH64_ADR_GOT_PAGE: case R_AARCH64_LD64_GOT_LO12_NC: + case R_AARCH64_LDST64_ABS_LO12_NC: case R_AARCH64_GLOB_DAT: case R_AARCH64_COPY: return 0; @@ -62,6 +63,7 @@ int gotplt_entry_type (int reloc_type) case R_AARCH64_MOVW_UABS_G3: case R_AARCH64_ADR_PREL_PG_HI21: case R_AARCH64_ADD_ABS_LO12_NC: + case R_AARCH64_LDST64_ABS_LO12_NC: case R_AARCH64_GLOB_DAT: case R_AARCH64_JUMP_SLOT: case R_AARCH64_COPY: @@ -157,10 +159,10 @@ void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t switch(type) { case R_AARCH64_ABS64: - write64le(ptr, val); + add64le(ptr, val); return; case R_AARCH64_ABS32: - write32le(ptr, val); + add32le(ptr, val); return; case R_AARCH64_PREL32: write32le(ptr, val - addr); @@ -193,6 +195,10 @@ void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t write32le(ptr, ((read32le(ptr) & 0xffc003ff) | (val & 0xfff) << 10)); return; + case R_AARCH64_LDST64_ABS_LO12_NC: + write32le(ptr, ((read32le(ptr) & 0xffc003ff) | + (val & 0xff8) << 7)); + return; case R_AARCH64_JUMP26: case R_AARCH64_CALL26: #ifdef DEBUG_RELOC diff --git a/i386-gen.c b/i386-gen.c index 4fb556f..21866bb 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -1089,8 +1089,9 @@ static void gen_bounds_epilog(void) addr_t saved_ind; addr_t *bounds_ptr; Sym *sym_data; + int offset_modified = func_bound_offset != lbounds_section->data_offset; - if (func_bound_offset == lbounds_section->data_offset && !func_bound_add_epilog) + if (!offset_modified && !func_bound_add_epilog) return; /* add end of table info */ @@ -1101,7 +1102,7 @@ static void gen_bounds_epilog(void) func_bound_offset, lbounds_section->data_offset); /* generate bound local allocation */ - if (func_bound_offset != lbounds_section->data_offset) { + if (offset_modified) { saved_ind = ind; ind = func_bound_ind; greloc(cur_text_section, sym_data, ind + 1, R_386_32); diff --git a/lib/Makefile b/lib/Makefile index 9210b38..5f1e3c6 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -43,9 +43,9 @@ endif I386_O = libtcc1.o alloca86.o alloca86-bt.o $(BT_O) X86_64_O = libtcc1.o alloca86_64.o alloca86_64-bt.o $(BT_O) -ARM_O = libtcc1.o armeabi.o alloca-arm.o armflush.o $(BT_O) -ARM64_O = lib-arm64.o $(BT_O) -RISCV64_O = lib-arm64.o $(BT_O) +ARM_O = libtcc1.o armeabi.o alloca-arm.o armflush.o fetch_and_add_arm.o $(BT_O) +ARM64_O = lib-arm64.o fetch_and_add_arm64.o $(BT_O) +RISCV64_O = lib-arm64.o fetch_and_add_riscv64.o $(BT_O) WIN_O = crt1.o crt1w.o wincrt1.o wincrt1w.o dllcrt1.o dllmain.o OBJ-i386 = $(I386_O) $(BCHECK_O) $(DSO_O) @@ -53,15 +53,15 @@ OBJ-x86_64 = $(X86_64_O) va_list.o $(BCHECK_O) $(DSO_O) OBJ-x86_64-osx = $(X86_64_O) va_list.o OBJ-i386-win32 = $(I386_O) chkstk.o $(B_O) $(WIN_O) OBJ-x86_64-win32 = $(X86_64_O) chkstk.o $(B_O) $(WIN_O) -OBJ-arm64 = $(ARM64_O) $(DSO_O) -OBJ-arm = $(ARM_O) $(DSO_O) +OBJ-arm64 = $(ARM64_O) $(BCHECK_O) $(DSO_O) +OBJ-arm = $(ARM_O) $(BCHECK_O) $(DSO_O) OBJ-arm-fpa = $(ARM_O) $(DSO_O) OBJ-arm-fpa-ld = $(ARM_O) $(DSO_O) OBJ-arm-vfp = $(ARM_O) $(DSO_O) OBJ-arm-eabi = $(ARM_O) $(DSO_O) OBJ-arm-eabihf = $(ARM_O) $(DSO_O) OBJ-arm-wince = $(ARM_O) $(WIN_O) -OBJ-riscv64 = $(RISCV64_O) $(DSO_O) +OBJ-riscv64 = $(RISCV64_O) $(BCHECK_O) $(DSO_O) OBJ-extra = $(filter $(B_O),$(OBJ-$T)) OBJ-libtcc1 = $(addprefix $(X),$(filter-out $(OBJ-extra),$(OBJ-$T))) diff --git a/lib/bcheck.c b/lib/bcheck.c index e8f20d0..9335f60 100644 --- a/lib/bcheck.c +++ b/lib/bcheck.c @@ -163,7 +163,7 @@ typedef struct alloca_list_struct { #if defined(_WIN32) #define BOUND_TID_TYPE DWORD #define BOUND_GET_TID GetCurrentThreadId() -#elif defined(__i386__) || defined(__x86_64__) +#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || defined(__riscv) #define BOUND_TID_TYPE pid_t #define BOUND_GET_TID syscall (SYS_gettid) #else @@ -223,6 +223,19 @@ DLL_EXPORT char *__bound_strcat(char *dest, const char *src); DLL_EXPORT char *__bound_strchr(const char *string, int ch); DLL_EXPORT char *__bound_strdup(const char *s); +#if defined(__arm__) +DLL_EXPORT void *__bound___aeabi_memcpy(void *dst, const void *src, size_t size); +DLL_EXPORT void *__bound___aeabi_memmove(void *dst, const void *src, size_t size); +DLL_EXPORT void *__bound___aeabi_memmove4(void *dst, const void *src, size_t size); +DLL_EXPORT void *__bound___aeabi_memmove8(void *dst, const void *src, size_t size); +DLL_EXPORT void *__bound___aeabi_memset(void *dst, int c, size_t size); +DLL_EXPORT void *__aeabi_memcpy(void *dst, const void *src, size_t size); +DLL_EXPORT void *__aeabi_memmove(void *dst, const void *src, size_t size); +DLL_EXPORT void *__aeabi_memmove4(void *dst, const void *src, size_t size); +DLL_EXPORT void *__aeabi_memmove8(void *dst, const void *src, size_t size); +DLL_EXPORT void *__aeabi_memset(void *dst, int c, size_t size); +#endif + #if MALLOC_REDIR #define BOUND_MALLOC(a) malloc_redir(a) #define BOUND_MEMALIGN(a,b) memalign_redir(a,b) @@ -320,6 +333,15 @@ static void fetch_and_add(signed char* variable, signed char value) : // No input-only : "memory" ); +#elif defined __arm__ + extern fetch_and_add_arm(signed char* variable, signed char value); + fetch_and_add_arm(variable, value); +#elif defined __aarch64__ + extern fetch_and_add_arm64(signed char* variable, signed char value); + fetch_and_add_arm64(variable, value); +#elif defined __riscv + extern fetch_and_add_riscv64(signed char* variable, signed char value); + fetch_and_add_riscv64(variable, value); #else *variable += value; #endif @@ -814,10 +836,10 @@ void __bound_init(size_t *p) { FILE *fp; unsigned char found; - unsigned long long start; - unsigned long long end; - unsigned long long ad = - (unsigned long long) __builtin_return_address(0); + unsigned long start; + unsigned long end; + unsigned long ad = + (unsigned long) __builtin_return_address(0); char line[1000]; /* Display exec name. Usefull when a lot of code is compiled with tcc */ @@ -835,7 +857,7 @@ void __bound_init(size_t *p) fp = fopen ("/proc/self/maps", "r"); if (fp) { while (fgets (line, sizeof(line), fp)) { - if (sscanf (line, "%Lx-%Lx", &start, &end) == 2 && + if (sscanf (line, "%lx-%lx", &start, &end) == 2 && ad >= start && ad < end) { found = 1; break; @@ -1099,7 +1121,7 @@ void *__bound_malloc(size_t size, const void *caller) INCR_COUNT(bound_malloc_count); if (ptr) { - tree = splay_insert ((size_t) ptr, size, tree); + tree = splay_insert ((size_t) ptr, size ? size : size + 1, tree); if (tree && tree->start == (size_t) ptr) tree->type = TCC_TYPE_MALLOC; } @@ -1138,7 +1160,7 @@ void *__bound_memalign(size_t size, size_t align, const void *caller) INCR_COUNT(bound_memalign_count); if (ptr) { - tree = splay_insert((size_t) ptr, size, tree); + tree = splay_insert((size_t) ptr, size ? size : size + 1, tree); if (tree && tree->start == (size_t) ptr) tree->type = TCC_TYPE_MEMALIGN; } @@ -1205,7 +1227,7 @@ void *__bound_realloc(void *ptr, size_t size, const void *caller) return NULL; } - new_ptr = BOUND_REALLOC (ptr, size); + new_ptr = BOUND_REALLOC (ptr, size + 1); dprintf(stderr, "%s, %s(): %p, 0x%lx\n", __FILE__, __FUNCTION__, new_ptr, (unsigned long)size); @@ -1216,7 +1238,7 @@ void *__bound_realloc(void *ptr, size_t size, const void *caller) if (ptr) tree = splay_delete ((size_t) ptr, tree); if (new_ptr) { - tree = splay_insert ((size_t) new_ptr, size, tree); + tree = splay_insert ((size_t) new_ptr, size ? size : size + 1, tree); if (tree && tree->start == (size_t) new_ptr) tree->type = TCC_TYPE_REALLOC; } @@ -1259,7 +1281,7 @@ void *__bound_calloc(size_t nmemb, size_t size) if (no_checking == 0) { WAIT_SEM (); INCR_COUNT(bound_calloc_count); - tree = splay_insert ((size_t) ptr, size, tree); + tree = splay_insert ((size_t) ptr, size ? size : size + 1, tree); if (tree && tree->start == (size_t) ptr) tree->type = TCC_TYPE_CALLOC; POST_SEM (); @@ -1387,6 +1409,59 @@ void *__bound_memset(void *s, int c, size_t n) return memset(s, c, n); } +#if defined(__arm__) +void *__bound___aeabi_memcpy(void *dest, const void *src, size_t n) +{ + dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n", + __FILE__, __FUNCTION__, dest, src, (unsigned long)n); + INCR_COUNT(bound_mempcy_count); + __bound_check(dest, n, "memcpy dest"); + __bound_check(src, n, "memcpy src"); + if (check_overlap(dest, n, src, n, "memcpy")) + return dest; + return __aeabi_memcpy(dest, src, n); +} + +void *__bound___aeabi_memmove(void *dest, const void *src, size_t n) +{ + dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n", + __FILE__, __FUNCTION__, dest, src, (unsigned long)n); + INCR_COUNT(bound_memmove_count); + __bound_check(dest, n, "memmove dest"); + __bound_check(src, n, "memmove src"); + return __aeabi_memmove(dest, src, n); +} + +void *__bound___aeabi_memmove4(void *dest, const void *src, size_t n) +{ + dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n", + __FILE__, __FUNCTION__, dest, src, (unsigned long)n); + INCR_COUNT(bound_memmove_count); + __bound_check(dest, n, "memmove dest"); + __bound_check(src, n, "memmove src"); + return __aeabi_memmove4(dest, src, n); +} + +void *__bound___aeabi_memmove8(void *dest, const void *src, size_t n) +{ + dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n", + __FILE__, __FUNCTION__, dest, src, (unsigned long)n); + INCR_COUNT(bound_memmove_count); + __bound_check(dest, n, "memmove dest"); + __bound_check(src, n, "memmove src"); + return __aeabi_memmove8(dest, src, n); +} + +void *__bound___aeabi_memset(void *s, int c, size_t n) +{ + dprintf(stderr, "%s, %s(): %p, %d, 0x%lx\n", + __FILE__, __FUNCTION__, s, c, (unsigned long)n); + INCR_COUNT(bound_memset_count); + __bound_check(s, n, "memset"); + return __aeabi_memset(s, c, n); +} +#endif + int __bound_strlen(const char *s) { const char *p = s; diff --git a/lib/fetch_and_add_arm.S b/lib/fetch_and_add_arm.S new file mode 100644 index 0000000..9d46db4 --- /dev/null +++ b/lib/fetch_and_add_arm.S @@ -0,0 +1,16 @@ + .text + .align 2 + .global fetch_and_add_arm + .type fetch_and_add_arm, %function +fetch_and_add_arm: + .int 0xee070fba # mcr 15, 0, r0, cr7, cr10, {5} + .int 0xe1903f9f # ldrex r3, [r0] + .int 0xe2833001 # add r3, r3, #1 + .int 0xe1802f93 # strex r2, r3, [r0] + .int 0xe3520000 # cmp r2, #0 + .int 0x1afffffa # bne 4 + .int 0xee070fba # mcr 15, 0, r0, cr7, cr10, {5} + .int 0xe1a00003 # mov r0, r3 + .int 0xe12fff1e # bx lr + + .size fetch_and_add_arm, .-fetch_and_add_arm diff --git a/lib/fetch_and_add_arm64.S b/lib/fetch_and_add_arm64.S new file mode 100644 index 0000000..fa27c19 --- /dev/null +++ b/lib/fetch_and_add_arm64.S @@ -0,0 +1,14 @@ + .text + .align 2 + .global fetch_and_add_arm64 + .type fetch_and_add_arm64, %function +fetch_and_add_arm64: + .int 0x885f7c01 # ldxr w1, [x0] + .int 0x11000421 # add w1, w1, #0x1 + .int 0x8802fc01 # stlxr w2, w1, [x0] + .int 0x35ffffa2 # cbnz w2, 0 + .int 0xd5033bbf # dmb ish + .int 0x2a0103e0 # mov w0, w1 + .int 0xd65f03c0 # ret + + .size fetch_and_add_arm64, .-fetch_and_add_arm64 diff --git a/lib/fetch_and_add_riscv64.S b/lib/fetch_and_add_riscv64.S new file mode 100644 index 0000000..3e8c7be --- /dev/null +++ b/lib/fetch_and_add_riscv64.S @@ -0,0 +1,12 @@ + .text + .align 2 + .global fetch_and_add_riscv64 + .type fetch_and_add_riscv64, %function +fetch_and_add_riscv64: + .short 0x4705 # li a4,1 + .int 0x0f50000f # fence iorw,ow + .int 0x04e527af # amoadd.w.aq a5,a4,(a0) + .int 0x0017851b # addiw a0,a5,1 + .short 0x8082 # ret + + .size fetch_and_add_riscv64, .-fetch_and_add_riscv64 diff --git a/libtcc.c b/libtcc.c index 8c4c488..914d592 100644 --- a/libtcc.c +++ b/libtcc.c @@ -34,6 +34,7 @@ #elif defined(TCC_TARGET_ARM64) #include "arm64-gen.c" #include "arm64-link.c" +#include "arm-asm.c" #elif defined(TCC_TARGET_C67) #include "c67-gen.c" #include "c67-link.c" @@ -45,6 +46,7 @@ #elif defined(TCC_TARGET_RISCV64) #include "riscv64-gen.c" #include "riscv64-link.c" +#include "riscv64-asm.c" #else #error unknown target #endif diff --git a/riscv64-asm.c b/riscv64-asm.c new file mode 100644 index 0000000..6b82e77 --- /dev/null +++ b/riscv64-asm.c @@ -0,0 +1,94 @@ +/*************************************************************/ +/* + * RISCV64 dummy assembler for TCC + * + */ + +#ifdef TARGET_DEFS_ONLY + +#define CONFIG_TCC_ASM +#define NB_ASM_REGS 32 + +ST_FUNC void g(int c); +ST_FUNC void gen_le16(int c); +ST_FUNC void gen_le32(int c); + +/*************************************************************/ +#else +/*************************************************************/ +#define USING_GLOBALS +#include "tcc.h" + +static void asm_error(void) +{ + tcc_error("RISCV64 asm not implemented."); +} + +/* XXX: make it faster ? */ +ST_FUNC void g(int c) +{ + int ind1; + if (nocode_wanted) + return; + ind1 = ind + 1; + if (ind1 > cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + cur_text_section->data[ind] = c; + ind = ind1; +} + +ST_FUNC void gen_le16 (int i) +{ + g(i); + g(i>>8); +} + +ST_FUNC void gen_le32 (int i) +{ + gen_le16(i); + gen_le16(i>>16); +} + +ST_FUNC void gen_expr32(ExprValue *pe) +{ + gen_le32(pe->v); +} + +ST_FUNC void asm_opcode(TCCState *s1, int opcode) +{ + asm_error(); +} + +ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier) +{ + asm_error(); +} + +/* generate prolog and epilog code for asm statement */ +ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, + int nb_outputs, int is_output, + uint8_t *clobber_regs, + int out_reg) +{ +} + +ST_FUNC void asm_compute_constraints(ASMOperand *operands, + int nb_operands, int nb_outputs, + const uint8_t *clobber_regs, + int *pout_reg) +{ +} + +ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str) +{ + asm_error(); +} + +ST_FUNC int asm_parse_regvar (int t) +{ + asm_error(); + return -1; +} + +/*************************************************************/ +#endif /* ndef TARGET_DEFS_ONLY */ diff --git a/riscv64-gen.c b/riscv64-gen.c index 5a731e6..4d7f366 100644 --- a/riscv64-gen.c +++ b/riscv64-gen.c @@ -2,6 +2,8 @@ // Number of registers available to allocator: #define NB_REGS 19 // x10-x17 aka a0-a7, f10-f17 aka fa0-fa7, xxx, ra, sp +#define NB_ASM_REGS 32 +#define CONFIG_TCC_ASM #define TREG_R(x) (x) // x = 0..7 #define TREG_F(x) (x + 8) // x = 0..7 @@ -61,6 +63,12 @@ ST_DATA const int reg_classes[NB_REGS] = { 1 << TREG_SP }; +#if defined(CONFIG_TCC_BCHECK) +static addr_t func_bound_offset; +static unsigned long func_bound_ind; +static int func_bound_add_epilog; +#endif + static int ireg(int r) { if (r == TREG_RA) @@ -377,6 +385,14 @@ static void gcall_or_jmp(int docall) R_RISCV_CALL_PLT, (int)vtop->c.i); o(0x17 | (tr << 7)); // auipc TR, 0 %call(func) EI(0x67, 0, tr, tr, 0);// jalr TR, r(TR) +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check && + (vtop->sym->v == TOK_setjmp || + vtop->sym->v == TOK__setjmp || + vtop->sym->v == TOK_sigsetjmp || + vtop->sym->v == TOK___sigsetjmp)) + func_bound_add_epilog = 1; +#endif } else if (vtop->r < VT_CONST) { int r = ireg(vtop->r); EI(0x67, 0, tr, r, 0); // jalr TR, 0(R) @@ -388,6 +404,130 @@ static void gcall_or_jmp(int docall) } } +#if defined(CONFIG_TCC_BCHECK) + +static void gen_bounds_call(int v) +{ + Sym *sym = external_global_sym(v, &func_old_type); + + greloca(cur_text_section, sym, ind, R_RISCV_CALL_PLT, 0); + o(0x17 | (1 << 7)); // auipc TR, 0 %call(func) + EI(0x67, 0, 1, 1, 0); // jalr TR, r(TR) +} + +/* generate a bounded pointer addition */ +ST_FUNC void gen_bounded_ptr_add(void) +{ + vpush_global_sym(&func_old_type, TOK___bound_ptr_add); + vrott(3); + gfunc_call(2); + vpushi(0); + /* returned pointer is in REG_IRET */ + vtop->r = REG_IRET | VT_BOUNDED; + if (nocode_wanted) + return; + /* relocation offset of the bounding function call point */ + vtop->c.i = (cur_text_section->reloc->data_offset - sizeof(ElfW(Rela))); +} + +/* patch pointer addition in vtop so that pointer dereferencing is + also tested */ +ST_FUNC void gen_bounded_ptr_deref(void) +{ + addr_t func; + int size, align; + ElfW(Rela) *rel; + Sym *sym; + + if (nocode_wanted) + return; + + size = type_size(&vtop->type, &align); + switch(size) { + case 1: func = TOK___bound_ptr_indir1; break; + case 2: func = TOK___bound_ptr_indir2; break; + case 4: func = TOK___bound_ptr_indir4; break; + case 8: func = TOK___bound_ptr_indir8; break; + case 12: func = TOK___bound_ptr_indir12; break; + case 16: func = TOK___bound_ptr_indir16; break; + default: + /* may happen with struct member access */ + return; + //tcc_error("unhandled size when dereferencing bounded pointer"); + //func = 0; + //break; + } + sym = external_global_sym(func, &func_old_type); + if (!sym->c) + put_extern_sym(sym, NULL, 0, 0); + /* patch relocation */ + /* XXX: find a better solution ? */ + rel = (ElfW(Rela) *)(cur_text_section->reloc->data + vtop->c.i); + rel->r_info = ELF64_R_INFO(sym->c, ELF64_R_TYPE(rel->r_info)); +} + +static void gen_bounds_prolog(void) +{ + /* leave some room for bound checking code */ + func_bound_offset = lbounds_section->data_offset; + func_bound_ind = ind; + func_bound_add_epilog = 0; + o(0x00000013); /* ld a0,#lbound section pointer */ + o(0x00000013); + o(0x00000013); /* nop -> call __bound_local_new */ + o(0x00000013); +} + +static void gen_bounds_epilog(void) +{ + static Sym label; + addr_t saved_ind; + addr_t *bounds_ptr; + Sym *sym_data; + int offset_modified = func_bound_offset != lbounds_section->data_offset; + + if (!offset_modified && !func_bound_add_epilog) + return; + + /* add end of table info */ + bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t)); + *bounds_ptr = 0; + + sym_data = get_sym_ref(&char_pointer_type, lbounds_section, + func_bound_offset, lbounds_section->data_offset); + + if (!label.v) { + label.v = tok_alloc(".LB0 ", 4)->tok; + label.type.t = VT_VOID | VT_STATIC; + } + /* generate bound local allocation */ + if (offset_modified) { + saved_ind = ind; + ind = func_bound_ind; + label.c = 0; /* force new local ELF symbol */ + put_extern_sym(&label, cur_text_section, ind, 0); + greloca(cur_text_section, sym_data, ind, R_RISCV_GOT_HI20, 0); + o(0x17 | (10 << 7)); // auipc a0, 0 %pcrel_hi(sym)+addend + greloca(cur_text_section, &label, ind, R_RISCV_PCREL_LO12_I, 0); + EI(0x03, 3, 10, 10, 0); // ld a0, 0(a0) + gen_bounds_call(TOK___bound_local_new); + ind = saved_ind; + } + + /* generate bound check local freeing */ + o(0xe02a1101); /* addi sp,sp,-32 sd a0,0(sp) */ + o(0xa82ae42e); /* sd a1,8(sp) fsd fa0,16(sp) */ + label.c = 0; /* force new local ELF symbol */ + put_extern_sym(&label, cur_text_section, ind, 0); + greloca(cur_text_section, sym_data, ind, R_RISCV_GOT_HI20, 0); + o(0x17 | (10 << 7)); // auipc a0, 0 %pcrel_hi(sym)+addend + greloca(cur_text_section, &label, ind, R_RISCV_PCREL_LO12_I, 0); + EI(0x03, 3, 10, 10, 0); // ld a0, 0(a0) + gen_bounds_call(TOK___bound_local_delete); + o(0x65a26502); /* ld a0,0(sp) ld a1,8(sp) */ + o(0x61052542); /* fld fa0,16(sp) addi sp,sp,32 */ +} +#endif static void reg_pass_rec(CType *type, int *rc, int *fieldofs, int ofs) { if ((type->t & VT_BTYPE) == VT_STRUCT) { @@ -440,6 +580,12 @@ ST_FUNC void gfunc_call(int nb_args) int stack_adj = 0, tempspace = 0, ofs, splitofs = 0; SValue *sv; Sym *sa; + +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gbound_args(nb_args); +#endif + areg[0] = 0; /* int arg regs */ areg[1] = 8; /* float arg regs */ sa = vtop[-nb_args].type.ref->next; @@ -691,6 +837,10 @@ ST_FUNC void gfunc_prolog(Sym *func_sym) ES(0x23, 3, 8, 10 + areg[0], -8 + num_va_regs * 8); // sd aX, loc(s0) } } +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gen_bounds_prolog(); +#endif } ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, @@ -732,6 +882,11 @@ ST_FUNC void gfunc_epilog(void) { int v, saved_ind, d, large_ofs_ind; +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + gen_bounds_epilog(); +#endif + loc = (loc - num_va_regs * 8); d = v = (-loc + 15) & -16; diff --git a/riscv64-link.c b/riscv64-link.c index f4db69b..35710a1 100644 --- a/riscv64-link.c +++ b/riscv64-link.c @@ -284,11 +284,11 @@ void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, return; case R_RISCV_32: - write32le(ptr, val); + add32le(ptr, val); return; case R_RISCV_JUMP_SLOT: case R_RISCV_64: - write64le(ptr, val); + add64le(ptr, val); return; case R_RISCV_ADD64: write64le(ptr, read64le(ptr) + val); diff --git a/tcc-doc.texi b/tcc-doc.texi index 976c575..4cc2a7b 100644 --- a/tcc-doc.texi +++ b/tcc-doc.texi @@ -370,7 +370,7 @@ Print statistic information at exit of program. Try to continue in case of a bound checking error. @end table -Note: @option{-b} is only available on i386 (linux and windows) and x86_64 (linux and windows) for the moment. +Note: @option{-b} is only available on i386 (linux and windows), x86_64 (linux and windows), arm, arm64 and riscv64 for the moment. @item -bt[N] Display N callers in stack traces. This is useful with @option{-g} or @option{-b}. diff --git a/tcc.h b/tcc.h index 33dc1fd..4d38494 100644 --- a/tcc.h +++ b/tcc.h @@ -184,7 +184,9 @@ extern long double strtold (const char *__nptr, char **__endptr); #if defined TCC_IS_NATIVE && !defined CONFIG_TCCBOOT # define CONFIG_TCC_BACKTRACE -# if (defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64) \ +# if (defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 || \ + defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64) || \ + defined TCC_TARGET_RISCV64 \ && !defined TCC_UCLIBC && !defined TCC_MUSL # define CONFIG_TCC_BCHECK /* enable bound checking code */ # endif @@ -354,6 +356,7 @@ extern long double strtold (const char *__nptr, char **__endptr); #elif defined TCC_TARGET_ARM64 # include "arm64-gen.c" # include "arm64-link.c" +# include "arm-asm.c" #elif defined TCC_TARGET_C67 # define TCC_TARGET_COFF # include "coff.h" @@ -362,6 +365,7 @@ extern long double strtold (const char *__nptr, char **__endptr); #elif defined(TCC_TARGET_RISCV64) # include "riscv64-gen.c" # include "riscv64-link.c" +# include "riscv64-asm.c" #else #error unknown target #endif diff --git a/tccelf.c b/tccelf.c index d83cc30..059c677 100644 --- a/tccelf.c +++ b/tccelf.c @@ -3123,7 +3123,7 @@ ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level) /* test CPU specific stuff */ if (ehdr.e_ident[5] != ELFDATA2LSB || ehdr.e_machine != EM_TCC_TARGET) { - tcc_error_noabort("bad architecture"); + tcc_error_noabort("bad architecture: %s", filename); return -1; } diff --git a/tccgen.c b/tccgen.c index 7059944..595c591 100644 --- a/tccgen.c +++ b/tccgen.c @@ -886,6 +886,12 @@ ST_FUNC void put_extern_sym2(Sym *sym, int sh_num, /* XXX: avoid doing that for statics ? */ /* if bound checking is activated, we change some function names by adding the "__bound" prefix */ +#if defined(TCC_TARGET_ARM) && defined(TCC_ARM_EABI) + if (strcmp (name, "memcpy") == 0 || + strcmp (name, "memmove") == 0 || + strcmp (name, "memset") == 0) + goto add_bound; +#endif switch(sym->v) { #ifdef TCC_TARGET_PE /* XXX: we rely only on malloc hooks */ @@ -897,6 +903,10 @@ ST_FUNC void put_extern_sym2(Sym *sym, int sh_num, #endif case TOK_memcpy: case TOK_memmove: +#if defined(TCC_TARGET_ARM) && defined(TCC_ARM_EABI) + case TOK_memmove4: + case TOK_memmove8: +#endif case TOK_memset: case TOK_memcmp: case TOK_strlen: @@ -907,12 +917,17 @@ ST_FUNC void put_extern_sym2(Sym *sym, int sh_num, case TOK_strcat: case TOK_strchr: case TOK_strdup: +#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 case TOK_alloca: +#endif case TOK_mmap: case TOK_munmap: case TOK_longjmp: #ifndef TCC_TARGET_PE case TOK_siglongjmp: +#endif +#if defined(TCC_TARGET_ARM) && defined(TCC_ARM_EABI) +add_bound: #endif strcpy(buf, "__bound_"); strcat(buf, name); @@ -3820,9 +3835,9 @@ ST_FUNC void vstore(void) /* address of memcpy() */ #ifdef TCC_ARM_EABI if(!(align & 7)) - vpush_global_sym(&func_old_type, TOK_memcpy8); + vpush_global_sym(&func_old_type, TOK_memmove8); else if(!(align & 3)) - vpush_global_sym(&func_old_type, TOK_memcpy4); + vpush_global_sym(&func_old_type, TOK_memmove4); else #endif /* Use memmove, rather than memcpy, as dest and src may be same: */ @@ -5636,13 +5651,25 @@ ST_FUNC void unary(void) mk_pointer(&type); vset(&type, VT_LOCAL, 0); /* local frame */ while (level--) { +#ifdef TCC_TARGET_RISCV64 + vpushi(2*PTR_SIZE); + gen_op('-'); +#endif mk_pointer(&vtop->type); indir(); /* -> parent frame */ } if (tok1 == TOK_builtin_return_address) { // assume return address is just above frame pointer on stack +#ifdef TCC_TARGET_ARM + vpushi(2*PTR_SIZE); + gen_op('+'); +#elif defined TCC_TARGET_RISCV64 + vpushi(PTR_SIZE); + gen_op('-'); +#else vpushi(PTR_SIZE); gen_op('+'); +#endif mk_pointer(&vtop->type); indir(); } diff --git a/tccrun.c b/tccrun.c index 02f207c..183df10 100644 --- a/tccrun.c +++ b/tccrun.c @@ -648,10 +648,12 @@ static void rt_getcontext(ucontext_t *uc, rt_context *rc) #elif defined(__arm__) rc->ip = uc->uc_mcontext.arm_pc; rc->fp = uc->uc_mcontext.arm_fp; - rc->sp = uc->uc_mcontext.arm_sp; #elif defined(__aarch64__) rc->ip = uc->uc_mcontext.pc; rc->fp = uc->uc_mcontext.regs[29]; +#elif defined(__riscv) + rc->ip = uc->uc_mcontext.__gregs[REG_PC]; + rc->fp = uc->uc_mcontext.__gregs[REG_S0]; #endif } @@ -805,22 +807,9 @@ static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level) *paddr = rc->ip; } else { addr_t fp = rc->fp; - addr_t sp = rc->sp; - if (sp < 0x1000) - sp = 0x1000; - /* XXX: specific to tinycc stack frames */ - if (fp < sp + 12 || fp & 3) - return -1; - while (--level) { - sp = ((addr_t *)fp)[-2]; - if (sp < fp || sp - fp > 16 || sp & 3) - return -1; - fp = ((addr_t *)fp)[-3]; - if (fp <= sp || fp - sp < 12 || fp & 3) - return -1; - } - /* XXX: check address validity with program info */ - *paddr = ((addr_t *)fp)[-1]; + while (--level) + fp = ((addr_t *)fp)[0]; + *paddr = ((addr_t *)fp)[2]; } return 0; #endif @@ -829,7 +818,7 @@ static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level) #elif defined(__aarch64__) static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level) { - if (level == 0) { + if (level == 0) { *paddr = rc->ip; } else { addr_t *fp = (addr_t*)rc->fp; @@ -840,6 +829,20 @@ static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level) return 0; } +#elif defined(__riscv) +static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level) +{ + if (level == 0) { + *paddr = rc->ip; + } else { + addr_t *fp = (addr_t*)rc->fp; + while (--level) + fp = (addr_t *)fp[-2]; + *paddr = fp[-1]; + } + return 0; +} + #else #warning add arch specific rt_get_caller_pc() static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level) diff --git a/tcctok.h b/tcctok.h index 2dfadd6..61e1748 100644 --- a/tcctok.h +++ b/tcctok.h @@ -205,9 +205,9 @@ #if defined TCC_TARGET_ARM # ifdef TCC_ARM_EABI DEF(TOK_memcpy, "__aeabi_memcpy") - DEF(TOK_memcpy4, "__aeabi_memcpy4") - DEF(TOK_memcpy8, "__aeabi_memcpy8") DEF(TOK_memmove, "__aeabi_memmove") + DEF(TOK_memmove4, "__aeabi_memmove4") + DEF(TOK_memmove8, "__aeabi_memmove8") DEF(TOK_memset, "__aeabi_memset") DEF(TOK___aeabi_ldivmod, "__aeabi_ldivmod") DEF(TOK___aeabi_uldivmod, "__aeabi_uldivmod") diff --git a/tests/Makefile b/tests/Makefile index 0a8829f..afdcdcf 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -35,6 +35,15 @@ endif ifeq ($(ARCH),x86_64) TESTS += $(BTESTS) endif +ifeq ($(ARCH),arm) + TESTS += $(BTESTS) +endif +ifeq ($(ARCH),arm64) + TESTS += $(BTESTS) +endif +ifeq ($(ARCH),riscv64) + TESTS += $(BTESTS) +endif endif ifdef CONFIG_OSX # -run only TESTS := hello-run libtest tests2-dir pp-dir diff --git a/tests/boundtest.c b/tests/boundtest.c index 9c13510..50f3d30 100644 --- a/tests/boundtest.c +++ b/tests/boundtest.c @@ -169,21 +169,34 @@ int test13(void) return strlen(tab); } +#if defined __i386__ || defined __x86_64__ +#define allocf(x) +#else +#define alloca(x) malloc(x) +#define allocf(x) free(x) +#endif + int test14(void) { char *p = alloca(TAB_SIZE); + size_t ret; memset(p, 'a', TAB_SIZE); p[TAB_SIZE-1] = 0; - return strlen(p); + ret = strlen(p); + allocf(p); + return ret; } /* error */ int test15(void) { char *p = alloca(TAB_SIZE-1); + size_t ret; memset(p, 'a', TAB_SIZE); p[TAB_SIZE-1] = 0; - return strlen(p); + ret = strlen(p); + allocf(p); + return ret; } /* ok */ @@ -194,6 +207,7 @@ int test16() p = alloca(16); strcpy(p,"12345678901234"); + allocf(p); /* Test alloca embedded in a larger expression */ printf("alloca : %s : %s\n", p, strcpy(alloca(strlen(demo)+1),demo) ); @@ -209,6 +223,7 @@ int test17() p = alloca(16); strcpy(p,"12345678901234"); + allocf(p); /* Test alloca embedded in a larger expression */ printf("alloca : %s : %s\n", p, strcpy(alloca(strlen(demo)),demo) ); diff --git a/tests/tests2/Makefile b/tests/tests2/Makefile index 0a86efa..13b8c60 100644 --- a/tests/tests2/Makefile +++ b/tests/tests2/Makefile @@ -23,8 +23,11 @@ ifeq (,$(filter i386,$(ARCH))) SKIP += 98_al_ax_extend.test 99_fastcall.test endif ifeq (,$(filter i386 x86_64,$(ARCH))) - SKIP += 85_asm-outside-function.test - SKIP += 112_backtrace.test 113_btdll.test + SKIP += 85_asm-outside-function.test # x86 asm + SKIP += 113_btdll.test # dll support needed +endif +ifeq (,$(filter i386 x86_64 arm arm64 riscv64,$(ARCH))) + SKIP += 112_backtrace.test SKIP += 114_bound_signal.test SKIP += 115_bound_setjmp.test SKIP += 116_bound_setjmp2.test @@ -82,7 +85,7 @@ GEN-ALWAYS = 112_backtrace.test: FLAGS += -dt -b 112_backtrace.test 113_btdll.test: FILTER += \ - -e 's;[0-9A-Fa-fx]\{8,\};........;g' \ + -e 's;[0-9A-Fa-fx]\{5,\};........;g' \ -e 's;0x[0-9A-Fa-f]\+;0x?;g' # this test creates two DLLs and an EXE diff --git a/x86_64-gen.c b/x86_64-gen.c index 0a5d339..36176a5 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -744,8 +744,9 @@ static void gen_bounds_epilog(void) addr_t saved_ind; addr_t *bounds_ptr; Sym *sym_data; + int offset_modified = func_bound_offset != lbounds_section->data_offset; - if (func_bound_offset == lbounds_section->data_offset && !func_bound_add_epilog) + if (!offset_modified && !func_bound_add_epilog) return; /* add end of table info */ @@ -756,7 +757,7 @@ static void gen_bounds_epilog(void) func_bound_offset, lbounds_section->data_offset); /* generate bound local allocation */ - if (func_bound_offset != lbounds_section->data_offset) { + if (offset_modified) { saved_ind = ind; ind = func_bound_ind; greloca(cur_text_section, sym_data, ind + 2, R_X86_64_64, 0);