Add bound checking to arm, arm64 and riscv64

Checked on:
- i386/x86_64 (linux/windows)
- arm/arm64 (rapberry pi)
- riscv64 (simulator)
Not tested for arm softfloat because raspberry pi does not support it.

Modifications:

Makefile:
  add arm-asm.c to arm64_FILES
  add riscv64-asm.c (new file) to riscv64_FILES

lib/Makefile:
  add fetch_and_add_arm.o(new file) to ARM_O
  add fetch_and_add_arm64.o(new file) to ARM64_O
  add fetch_and_add_riscv64.o(new file) to RISCV64_O
  add $(BCHECK_O) to OBJ-arm/OBJ-arm64/OBJ-riscv64

tcc.h:
  Enable CONFIG_TCC_BCHECK for arm32/arm64/riscv64
  Add arm-asm.c, riscv64-asm.c

tcctok.h:
  for arm use memmove4 instead of memcpy4
  for arm use memmove8 instead of memcpy8

tccgen.c:
  put_extern_sym2: for arm check memcpy/memmove/memset/memmove4/memmove8
                   only use alloca for i386/x86_64
  for arm use memmove4 instead of memcpy4
  for arm use memmove8 instead of memcpy8
  fix builtin_frame_address/builtin_return_address for arm/riscv64

tccrun.c:
  Add riscv64 support
  fix rt_getcontext/rt_get_caller_pc for arm

tccelf.c:
  tcc_load_dll: Print filename for bad architecture

libtcc.c:
  add arm-asm.c/riscv64-asm.c

tcc-doc.texi:
  Add arm, arm64, riscv64 support for bound checking

lib/bcheck.c:
  add __bound___aeabi_memcpy/__bound___aeabi_memmove
      __bound___aeabi_memmove4/__bound___aeabi_memmove8
      __bound___aeabi_memset for arm
  call fetch_and_add_arm/fetch_and_add_arm64/fetch_and_add_riscv64
  __bound_init: Fix type for start/end/ad
  __bound_malloc/__bound_memalign/__bound_realloc/__bound_calloc: Use size + 1

arm-gen.c:
  add bound checking code like i386/x86_64
  assign_regs: only malloc if nb_args != 0
  gen_opi/gen_opf: Fix reload problems

arm-link.c:
  relocate_plt: Fix address calculating

arm64-gen.c:
  add bound checking code like i386/x86_64
  load/store: remove VT_BOUNDED from sv->r
  arm64_hfa_aux/arm64_hfa_aux: Fix array code
  gfunc_prolog: only malloc if n != 0

arm64-link.c:
  code_reloc/gotplt_entry_type/relocate: add R_AARCH64_LDST64_ABS_LO12_NC
  relocate: Use addXXle instead of writeXXle

riscv64-gen.c:
  add bound checking code like i386/x86_64
  add NB_ASM_REGS/CONFIG_TCC_ASM

riscv64-link.c:
  relocate: Use addXXle instead of writeXXle

i386-gen.c/x86_64-gen.c
  gen_bounds_epilog: Fix code (unrelated)

tests/Makefile:
  add $(BTESTS) for arm/arm64/riscv64

tests/tests2/Makefile:
  Use 85 only on i386/x86_64 because of asm code
  Use 113 only on i386/x86_64 because of DLL code
  Add 112/114/115/116 for arm/arm64/riscv64
  Fix FILTER (failed on riscv64)

tests/boundtest.c:
  Only use alloca for i386/x86_64
This commit is contained in:
herman ten brugge 2020-06-16 07:39:48 +02:00
parent 9eef33993a
commit 0b8ee7364a
25 changed files with 865 additions and 77 deletions

View File

@ -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)))

218
arm-gen.c
View File

@ -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);

View File

@ -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;
}
}

View File

@ -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,10 +600,146 @@ 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)
{
@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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)))

View File

@ -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;

16
lib/fetch_and_add_arm.S Normal file
View File

@ -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 <fetch_and_add_arm+0x4>
.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

14
lib/fetch_and_add_arm64.S Normal file
View File

@ -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 <fetch_and_add_arm64>
.int 0xd5033bbf # dmb ish
.int 0x2a0103e0 # mov w0, w1
.int 0xd65f03c0 # ret
.size fetch_and_add_arm64, .-fetch_and_add_arm64

View File

@ -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

View File

@ -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

94
riscv64-asm.c Normal file
View File

@ -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 */

View File

@ -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;

View File

@ -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);

View File

@ -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}.

6
tcc.h
View File

@ -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

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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
@ -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)

View File

@ -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")

View File

@ -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

View File

@ -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) );

View File

@ -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

View File

@ -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);