tccgen.c: fix two scope problems

... see tests

Also:
- tccgen.c: cleanup _Generic a little
- libtcc.c: cleanup mem-debug a little
- arm-link.c: read/write only once
- tcc.h: another fix for clang offsetof
- tccdbg.c:
  * stabs: handle forward struct/enum decls
  * stabs: fix anonymous struct members (must not have a .L123 name)
  * avoid strncpy()
This commit is contained in:
grischka
2025-08-18 22:55:38 +02:00
parent 4e6c4db340
commit acb2a909dd
8 changed files with 166 additions and 60 deletions

View File

@@ -194,17 +194,18 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr,
case R_ARM_PLT32:
{
int x, is_thumb, is_call, h, blx_avail, is_bl, th_ko;
x = read32le(ptr) & 0xffffff;
unsigned code = read32le(ptr);
x = code & 0x00ffffff;
#ifdef DEBUG_RELOC
printf ("reloc %d: x=0x%x val=0x%x ", type, x, val);
#endif
write32le(ptr, read32le(ptr) & 0xff000000);
if (x & 0x800000)
x -= 0x1000000;
x = (unsigned) x << 2;
code &= 0xff000000;
x <<= 2;
if (x & 0x2000000)
x -= 0x4000000;
blx_avail = (CONFIG_TCC_CPUVER >= 5);
is_thumb = val & 1;
is_bl = read32le(ptr) >> 24 == 0xeb;
is_bl = code == 0xeb000000;
is_call = (type == R_ARM_CALL || (type == R_ARM_PC24 && is_bl));
x += val - addr;
#ifdef DEBUG_RELOC
@@ -220,9 +221,9 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr,
/* Only reached if blx is avail and it is a call */
if (is_thumb) {
x |= h << 24;
write32le(ptr, 0xfa << 24); /* bl -> blx */
code = 0xfa000000; /* bl -> blx */
}
write32le(ptr, read32le(ptr) | x);
write32le(ptr, code | x);
}
return;
/* Since these relocations only concern Thumb-2 and blx instruction was

View File

@@ -326,7 +326,7 @@ struct mem_debug_header {
struct mem_debug_header *prev;
struct mem_debug_header *next;
int line_num;
char file_name[MEM_DEBUG_FILE_LEN + 1];
char file_name[MEM_DEBUG_FILE_LEN];
unsigned magic2;
ALIGNED(16) unsigned char magic3[4];
};
@@ -367,9 +367,8 @@ PUB_FUNC void *tcc_malloc_debug(unsigned long size, const char *file, int line)
header->size = size;
write32le(MEM_DEBUG_CHECK3(header), MEM_DEBUG_MAGIC3);
header->line_num = line;
ofs = strlen(file) - MEM_DEBUG_FILE_LEN;
strncpy(header->file_name, file + (ofs > 0 ? ofs : 0), MEM_DEBUG_FILE_LEN);
header->file_name[MEM_DEBUG_FILE_LEN] = 0;
ofs = strlen(file) + 1 - MEM_DEBUG_FILE_LEN;
strcpy(header->file_name, file + (ofs > 0 ? ofs : 0));
WAIT_SEM(&mem_sem);
header->next = mem_debug_chain;
header->prev = NULL;

5
tcc.c
View File

@@ -323,13 +323,12 @@ redo:
tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
print_search_dirs(s);
}
if (opt < 0) err:
ret = 1, opt = -1;
if (opt) {
if (opt < 0) err:
ret = 1;
tcc_delete(s);
return ret;
}
if (s->nb_files == 0) {
tcc_error_noabort("no input files");
} else if (s->output_type == TCC_OUTPUT_PREPROCESS) {

8
tcc.h
View File

@@ -95,13 +95,13 @@ extern long double strtold (const char *__nptr, char **__endptr);
# define O_BINARY 0
#endif
#ifndef offsetof
#ifdef __clang__ // clang -fsanitize compains about: NULL+value
#define offsetof(type, field) __builtin_offsetof(type, field)
#endif
#ifndef offsetof
#else
#define offsetof(type, field) ((size_t) &((type *)0)->field)
#endif
#endif
#ifndef countof
#define countof(tab) (sizeof(tab) / sizeof((tab)[0]))
@@ -1074,7 +1074,7 @@ struct filespec {
#define VT_UNION (1 << VT_STRUCT_SHIFT | VT_STRUCT)
#define VT_ENUM (2 << VT_STRUCT_SHIFT) /* integral type is an enum really */
#define VT_ENUM_VAL (3 << VT_STRUCT_SHIFT) /* integral type is an enum constant really */
#define VT_ENUM_VAL (4 << VT_STRUCT_SHIFT) /* integral type is an enum constant really */
#define IS_ENUM(t) ((t & VT_STRUCT_MASK) == VT_ENUM)
#define IS_ENUM_VAL(t) ((t & VT_STRUCT_MASK) == VT_ENUM_VAL)

View File

@@ -1675,16 +1675,20 @@ static void tcc_debug_check_forw(TCCState *s1, Sym *t, int debug_type)
}
}
static void stabs_struct_complete(TCCState *s1, CType *t);
ST_FUNC void tcc_debug_fix_forw(TCCState *s1, CType *t)
{
if (!(s1->do_debug & 2))
return;
if (0 == s1->dwarf) {
stabs_struct_complete(s1, t);
return;
}
if ((t->t & VT_BTYPE) == VT_STRUCT && t->ref->c != -1) {
int i, j, debug_type, g = check_global(t->ref);
int *n_forw_hash;
struct _debug_forw_hash **forw_hash;
forw_hash = g ? &debug_forw_hash_global : &debug_forw_hash_local;
n_forw_hash = g ? &n_debug_forw_hash_global : &n_debug_forw_hash_local;
for (i = 0; i < *n_forw_hash; i++)
@@ -1722,14 +1726,46 @@ static int tcc_debug_add(TCCState *s1, Sym *t, int dwarf)
return offset;
}
#define STRUCT_NODEBUG(s) \
(s->a.nodebug || \
((s->v & ~SYM_FIELD) >= SYM_FIRST_ANOM && \
((s->type.t & VT_BTYPE) == VT_BYTE || \
(s->type.t & VT_BTYPE) == VT_BOOL || \
(s->type.t & VT_BTYPE) == VT_SHORT || \
(s->type.t & VT_BTYPE) == VT_INT || \
(s->type.t & VT_BTYPE) == VT_LLONG)))
static int STRUCT_NODEBUG(Sym *s)
{
return
(s->a.nodebug ||
((s->v & ~SYM_FIELD) >= SYM_FIRST_ANOM &&
((s->type.t & VT_BTYPE) == VT_BYTE ||
(s->type.t & VT_BTYPE) == VT_BOOL ||
(s->type.t & VT_BTYPE) == VT_SHORT ||
(s->type.t & VT_BTYPE) == VT_INT ||
(s->type.t & VT_BTYPE) == VT_LLONG)));
}
static int stabs_struct_find(TCCState *s1, Sym *t, int *p_id)
{
/* A struct/enum has a ref to its type but that type has no ref.
So we can (ab)use it for some info. Here:
s->c : stabs type id
s->r : already defined in stabs */
Sym *s = t->type.ref;
/*
if (s && s->v != (SYM_FIELD|0x00DEBBED)) {
tcc_error_noabort("tccdbg: internal error: %s", get_tok_str(t->v, 0));
if (p_id)
*p_id = 0;
return 0;
}
*/
if (NULL == p_id)
return s && !s->r && t->c >= 0;
if (NULL == s) {
/* just use global_stack always */
s = sym_push2(&global_stack, SYM_FIELD|0x00DEBBED, 0, ++debug_next_type);
t->type.ref = s;
}
*p_id = s->c;
if (s->r || t->c < 0) /* already defined or still incomplete */
return 0;
s->r = 1;
return 1;
}
static void tcc_get_debug_info(TCCState *s1, Sym *s, CString *result)
{
@@ -1750,9 +1786,7 @@ static void tcc_get_debug_info(TCCState *s1, Sym *s, CString *result)
}
if ((type & VT_BTYPE) == VT_STRUCT) {
t = t->type.ref;
debug_type = tcc_debug_find(s1, t, 0);
if (debug_type == -1) {
debug_type = tcc_debug_add(s1, t, 0);
if (stabs_struct_find(s1, t, &debug_type)) {
cstr_new (&str);
cstr_printf (&str, "%s:T%d=%c%d",
(t->v & ~SYM_STRUCT) >= SYM_FIRST_ANOM
@@ -1760,15 +1794,16 @@ static void tcc_get_debug_info(TCCState *s1, Sym *s, CString *result)
debug_type,
IS_UNION (t->type.t) ? 'u' : 's',
t->c);
while (t->next) {
int pos, size, align;
t = t->next;
if (STRUCT_NODEBUG(t))
continue;
tcc_debug_check_forw(s1, t, -1);
cstr_printf (&str, "%s:",
get_tok_str(t->v, NULL));
(t->v & ~SYM_FIELD) >= SYM_FIRST_ANOM
? "" : get_tok_str(t->v, NULL)
);
tcc_get_debug_info (s1, t, &str);
if (t->type.t & VT_BITFIELD) {
pos = t->c * 8 + BIT_POS(t->type.t);
@@ -1787,10 +1822,7 @@ static void tcc_get_debug_info(TCCState *s1, Sym *s, CString *result)
}
else if (IS_ENUM(type)) {
Sym *e = t = t->type.ref;
debug_type = tcc_debug_find(s1, t, 0);
if (debug_type == -1) {
debug_type = tcc_debug_add(s1, t, 0);
if (stabs_struct_find(s1, t, &debug_type)) {
cstr_new (&str);
cstr_printf (&str, "%s:T%d=e",
(t->v & ~SYM_STRUCT) >= SYM_FIRST_ANOM
@@ -1817,6 +1849,10 @@ static void tcc_get_debug_info(TCCState *s1, Sym *s, CString *result)
if (debug_type > N_DEFAULT_DEBUG)
return;
}
if (NULL == result) /* from stabs_struct_complete() */
return;
if (n > 0)
cstr_printf (result, "%d=", ++debug_next_type);
t = s;
@@ -1836,12 +1872,19 @@ static void tcc_get_debug_info(TCCState *s1, Sym *s, CString *result)
}
else
break;
tcc_debug_check_forw(s1, t, -1);
t = t->type.ref;
}
cstr_printf (result, "%d", debug_type);
}
static void stabs_struct_complete(TCCState *s1, CType *t)
{
if (stabs_struct_find(s1, t->ref, NULL)) {
Sym s = {0}; s.type = *t;
tcc_get_debug_info(s1, &s, NULL);
}
}
static int tcc_get_dwarf_info(TCCState *s1, Sym *s)
{
int type;
@@ -1993,7 +2036,7 @@ static int tcc_get_dwarf_info(TCCState *s1, Sym *s)
dwarf_data1(dwarf_info_section, DWARF_ABBREV_BASE_TYPE);
dwarf_uleb128(dwarf_info_section, default_debug[i - 1].size);
dwarf_data1(dwarf_info_section, default_debug[i - 1].encoding);
strncpy(name, default_debug[i - 1].name, sizeof(name) -1);
pstrcpy(name, sizeof name, default_debug[i - 1].name);
*strchr(name, ':') = 0;
dwarf_strp(dwarf_info_section, name);
dwarf_info.base_type_used[i - 1] = debug_type;
@@ -2404,7 +2447,6 @@ ST_FUNC void tcc_debug_extern_sym(TCCState *s1, Sym *sym, int sh_num, int sym_bi
sym_bind == STB_GLOBAL ? 'G' : func_ind != -1 ? 'V' : 'S'
);
tcc_get_debug_info(s1, sym, &str);
tcc_debug_check_forw(s1, sym, -1);
if (sym_bind == STB_GLOBAL)
tcc_debug_stabs(s1, str.data, N_GSYM, 0, NULL, 0, 0);
else
@@ -2437,11 +2479,8 @@ ST_FUNC void tcc_debug_typedef(TCCState *s1, Sym *sym)
{
CString str;
cstr_new (&str);
cstr_printf (&str, "%s:t",
(sym->v & ~SYM_FIELD) >= SYM_FIRST_ANOM
? "" : get_tok_str(sym->v, NULL));
cstr_printf (&str, "%s:t", get_tok_str(sym->v, NULL));
tcc_get_debug_info(s1, sym, &str);
tcc_debug_check_forw(s1, sym, -1);
tcc_debug_stabs(s1, str.data, N_LSYM, 0, NULL, 0, 0);
cstr_free (&str);
}

View File

@@ -1294,16 +1294,21 @@ static Sym *sym_copy(Sym *s0, Sym **ps)
Sym *s;
s = sym_malloc(), *s = *s0;
s->prev = *ps, *ps = s;
if ((s->v & ~SYM_STRUCT) < SYM_FIRST_ANOM)
if ((s->v & ~SYM_STRUCT) < SYM_FIRST_ANOM && ps == &local_stack)
sym_link(s, 1);
return s;
}
/* copy s->type.ref to stack 'ps' for VT_FUNC and VT_PTR */
/* Symbol 's' was locally declared 'extern' (or as function), and
is on global_stack. Now must copy its 'ref' to global_stack too */
static void sym_copy_ref(Sym *s, Sym **ps)
{
int bt = s->type.t & VT_BTYPE;
if (bt == VT_FUNC || bt == VT_PTR || (bt == VT_STRUCT && s->sym_scope)) {
if (bt == VT_PTR
|| bt == VT_FUNC
|| ((bt == VT_STRUCT || IS_ENUM(s->type.t))
&& s->type.ref
&& s->type.ref->sym_scope)) {
Sym **sp = &s->type.ref;
for (s = *sp, *sp = NULL; s; s = s->next) {
Sym *s2 = sym_copy(s, ps);
@@ -5962,13 +5967,10 @@ ST_FUNC void unary(void)
learn = 1;
next();
} else {
AttributeDef ad_tmp;
int itmp;
CType cur_type;
parse_btype(&cur_type, &ad_tmp, 0);
type_decl(&cur_type, &ad_tmp, &itmp, TYPE_ABSTRACT);
if (compare_types(&controlling_type, &cur_type, 0)) {
int v;
parse_btype(&type, &ad, 0);
type_decl(&type, &ad, &v, TYPE_ABSTRACT);
if (compare_types(&controlling_type, &type, 0)) {
if (has_match) {
tcc_error("type match twice");
}

View File

@@ -1,7 +1,11 @@
#include <stdio.h>
int printf(const char*, ...);
#define myassert(x) \
printf("%s:%d: %s : \"%s\"\n", __FILE__,__LINE__,(x)?"ok":"error",#x)
enum{ in = 0};
#define myassert(X) do{ if(!X) printf("%d: assertion failed\n", __LINE__); }while(0)
int main(){
int main_1(){
{
myassert(!in);
if(sizeof(enum{in=1})) myassert(in);
@@ -38,6 +42,45 @@ int main(){
for(;sizeof(enum{in=1});){ myassert(in); break; }
myassert(!in); //OK
}
return 0;
}
/* --------------------------------------------- */
int main_2()
{
char c = 'a';
void func1(char c); /* param 'c' must not shadow local 'c' */
func1(c);
return 0;
}
void func1(char c)
{
myassert(c == 'a');
}
struct st { int a; };
/* --------------------------------------------- */
int main_3()
{
struct st func(void);
struct st st = func(); /* not an 'incompatible redefinition' */
myassert(st.a == 10);
return 0;
}
struct st func(void)
{
struct st st = { 10 };
return st;
}
/* --------------------------------------------- */
int main()
{
main_1();
main_2();
main_3();
return 0;
}

View File

@@ -0,0 +1,23 @@
129_scopes.c:10: ok : "!in"
129_scopes.c:11: ok : "in"
129_scopes.c:12: ok : "!in"
129_scopes.c:15: ok : "!in"
129_scopes.c:16: ok : "in"
129_scopes.c:17: ok : "!in"
129_scopes.c:20: ok : "!in"
129_scopes.c:21: ok : "in"
129_scopes.c:22: ok : "!in"
129_scopes.c:25: ok : "!in"
129_scopes.c:26: ok : "!in"
129_scopes.c:27: ok : "!in"
129_scopes.c:31: ok : "!in"
129_scopes.c:32: ok : "in"
129_scopes.c:33: ok : "!in"
129_scopes.c:36: ok : "!in"
129_scopes.c:37: ok : "in"
129_scopes.c:38: ok : "!in"
129_scopes.c:41: ok : "!in"
129_scopes.c:42: ok : "in"
129_scopes.c:43: ok : "!in"
129_scopes.c:59: ok : "c == 'a'"
129_scopes.c:69: ok : "st.a == 10"