tccgen.c: fix two scope problems
Some checks are pending
build and test / test-x86_64-linux (push) Waiting to run
build and test / test-x86_64-osx (push) Waiting to run
build and test / test-aarch64-osx (push) Waiting to run
build and test / test-x86_64-win32 (push) Waiting to run
build and test / test-i386-win32 (push) Waiting to run
build and test / test-armv7-linux (push) Waiting to run
build and test / test-aarch64-linux (push) Waiting to run
build and test / test-riscv64-linux (push) Waiting to run

... 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: case R_ARM_PLT32:
{ {
int x, is_thumb, is_call, h, blx_avail, is_bl, th_ko; 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 #ifdef DEBUG_RELOC
printf ("reloc %d: x=0x%x val=0x%x ", type, x, val); printf ("reloc %d: x=0x%x val=0x%x ", type, x, val);
#endif #endif
write32le(ptr, read32le(ptr) & 0xff000000); code &= 0xff000000;
if (x & 0x800000) x <<= 2;
x -= 0x1000000; if (x & 0x2000000)
x = (unsigned) x << 2; x -= 0x4000000;
blx_avail = (CONFIG_TCC_CPUVER >= 5); blx_avail = (CONFIG_TCC_CPUVER >= 5);
is_thumb = val & 1; 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)); is_call = (type == R_ARM_CALL || (type == R_ARM_PC24 && is_bl));
x += val - addr; x += val - addr;
#ifdef DEBUG_RELOC #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 */ /* Only reached if blx is avail and it is a call */
if (is_thumb) { if (is_thumb) {
x |= h << 24; x |= h << 24;
write32le(ptr, 0xfa << 24); /* bl -> blx */ code = 0xfa000000; /* bl -> blx */
} }
write32le(ptr, read32le(ptr) | x); write32le(ptr, code | x);
} }
return; return;
/* Since these relocations only concern Thumb-2 and blx instruction was /* 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 *prev;
struct mem_debug_header *next; struct mem_debug_header *next;
int line_num; int line_num;
char file_name[MEM_DEBUG_FILE_LEN + 1]; char file_name[MEM_DEBUG_FILE_LEN];
unsigned magic2; unsigned magic2;
ALIGNED(16) unsigned char magic3[4]; 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; header->size = size;
write32le(MEM_DEBUG_CHECK3(header), MEM_DEBUG_MAGIC3); write32le(MEM_DEBUG_CHECK3(header), MEM_DEBUG_MAGIC3);
header->line_num = line; header->line_num = line;
ofs = strlen(file) - MEM_DEBUG_FILE_LEN; ofs = strlen(file) + 1 - MEM_DEBUG_FILE_LEN;
strncpy(header->file_name, file + (ofs > 0 ? ofs : 0), MEM_DEBUG_FILE_LEN); strcpy(header->file_name, file + (ofs > 0 ? ofs : 0));
header->file_name[MEM_DEBUG_FILE_LEN] = 0;
WAIT_SEM(&mem_sem); WAIT_SEM(&mem_sem);
header->next = mem_debug_chain; header->next = mem_debug_chain;
header->prev = NULL; header->prev = NULL;

5
tcc.c
View File

@ -323,13 +323,12 @@ redo:
tcc_set_output_type(s, TCC_OUTPUT_MEMORY); tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
print_search_dirs(s); print_search_dirs(s);
} }
if (opt < 0) err:
ret = 1, opt = -1;
if (opt) { if (opt) {
if (opt < 0) err:
ret = 1;
tcc_delete(s); tcc_delete(s);
return ret; return ret;
} }
if (s->nb_files == 0) { if (s->nb_files == 0) {
tcc_error_noabort("no input files"); tcc_error_noabort("no input files");
} else if (s->output_type == TCC_OUTPUT_PREPROCESS) { } 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 # define O_BINARY 0
#endif #endif
#ifndef offsetof
#ifdef __clang__ // clang -fsanitize compains about: NULL+value #ifdef __clang__ // clang -fsanitize compains about: NULL+value
#define offsetof(type, field) __builtin_offsetof(type, field) #define offsetof(type, field) __builtin_offsetof(type, field)
#endif #else
#ifndef offsetof
#define offsetof(type, field) ((size_t) &((type *)0)->field) #define offsetof(type, field) ((size_t) &((type *)0)->field)
#endif #endif
#endif
#ifndef countof #ifndef countof
#define countof(tab) (sizeof(tab) / sizeof((tab)[0])) #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_UNION (1 << VT_STRUCT_SHIFT | VT_STRUCT)
#define VT_ENUM (2 << VT_STRUCT_SHIFT) /* integral type is an enum really */ #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(t) ((t & VT_STRUCT_MASK) == VT_ENUM)
#define IS_ENUM_VAL(t) ((t & VT_STRUCT_MASK) == VT_ENUM_VAL) #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) ST_FUNC void tcc_debug_fix_forw(TCCState *s1, CType *t)
{ {
if (!(s1->do_debug & 2)) if (!(s1->do_debug & 2))
return; return;
if (0 == s1->dwarf) {
stabs_struct_complete(s1, t);
return;
}
if ((t->t & VT_BTYPE) == VT_STRUCT && t->ref->c != -1) { if ((t->t & VT_BTYPE) == VT_STRUCT && t->ref->c != -1) {
int i, j, debug_type, g = check_global(t->ref); int i, j, debug_type, g = check_global(t->ref);
int *n_forw_hash; int *n_forw_hash;
struct _debug_forw_hash **forw_hash; struct _debug_forw_hash **forw_hash;
forw_hash = g ? &debug_forw_hash_global : &debug_forw_hash_local; 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; n_forw_hash = g ? &n_debug_forw_hash_global : &n_debug_forw_hash_local;
for (i = 0; i < *n_forw_hash; i++) 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; return offset;
} }
#define STRUCT_NODEBUG(s) \ static int STRUCT_NODEBUG(Sym *s)
(s->a.nodebug || \ {
((s->v & ~SYM_FIELD) >= SYM_FIRST_ANOM && \ return
((s->type.t & VT_BTYPE) == VT_BYTE || \ (s->a.nodebug ||
(s->type.t & VT_BTYPE) == VT_BOOL || \ ((s->v & ~SYM_FIELD) >= SYM_FIRST_ANOM &&
(s->type.t & VT_BTYPE) == VT_SHORT || \ ((s->type.t & VT_BTYPE) == VT_BYTE ||
(s->type.t & VT_BTYPE) == VT_INT || \ (s->type.t & VT_BTYPE) == VT_BOOL ||
(s->type.t & VT_BTYPE) == VT_LLONG))) (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) 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) { if ((type & VT_BTYPE) == VT_STRUCT) {
t = t->type.ref; t = t->type.ref;
debug_type = tcc_debug_find(s1, t, 0); if (stabs_struct_find(s1, t, &debug_type)) {
if (debug_type == -1) {
debug_type = tcc_debug_add(s1, t, 0);
cstr_new (&str); cstr_new (&str);
cstr_printf (&str, "%s:T%d=%c%d", cstr_printf (&str, "%s:T%d=%c%d",
(t->v & ~SYM_STRUCT) >= SYM_FIRST_ANOM (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, debug_type,
IS_UNION (t->type.t) ? 'u' : 's', IS_UNION (t->type.t) ? 'u' : 's',
t->c); t->c);
while (t->next) { while (t->next) {
int pos, size, align; int pos, size, align;
t = t->next; t = t->next;
if (STRUCT_NODEBUG(t)) if (STRUCT_NODEBUG(t))
continue; continue;
tcc_debug_check_forw(s1, t, -1);
cstr_printf (&str, "%s:", 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); tcc_get_debug_info (s1, t, &str);
if (t->type.t & VT_BITFIELD) { if (t->type.t & VT_BITFIELD) {
pos = t->c * 8 + BIT_POS(t->type.t); 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)) { else if (IS_ENUM(type)) {
Sym *e = t = t->type.ref; Sym *e = t = t->type.ref;
if (stabs_struct_find(s1, t, &debug_type)) {
debug_type = tcc_debug_find(s1, t, 0);
if (debug_type == -1) {
debug_type = tcc_debug_add(s1, t, 0);
cstr_new (&str); cstr_new (&str);
cstr_printf (&str, "%s:T%d=e", cstr_printf (&str, "%s:T%d=e",
(t->v & ~SYM_STRUCT) >= SYM_FIRST_ANOM (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) if (debug_type > N_DEFAULT_DEBUG)
return; return;
} }
if (NULL == result) /* from stabs_struct_complete() */
return;
if (n > 0) if (n > 0)
cstr_printf (result, "%d=", ++debug_next_type); cstr_printf (result, "%d=", ++debug_next_type);
t = s; t = s;
@ -1836,12 +1872,19 @@ static void tcc_get_debug_info(TCCState *s1, Sym *s, CString *result)
} }
else else
break; break;
tcc_debug_check_forw(s1, t, -1);
t = t->type.ref; t = t->type.ref;
} }
cstr_printf (result, "%d", debug_type); 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) static int tcc_get_dwarf_info(TCCState *s1, Sym *s)
{ {
int type; 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_data1(dwarf_info_section, DWARF_ABBREV_BASE_TYPE);
dwarf_uleb128(dwarf_info_section, default_debug[i - 1].size); dwarf_uleb128(dwarf_info_section, default_debug[i - 1].size);
dwarf_data1(dwarf_info_section, default_debug[i - 1].encoding); 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; *strchr(name, ':') = 0;
dwarf_strp(dwarf_info_section, name); dwarf_strp(dwarf_info_section, name);
dwarf_info.base_type_used[i - 1] = debug_type; 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' sym_bind == STB_GLOBAL ? 'G' : func_ind != -1 ? 'V' : 'S'
); );
tcc_get_debug_info(s1, sym, &str); tcc_get_debug_info(s1, sym, &str);
tcc_debug_check_forw(s1, sym, -1);
if (sym_bind == STB_GLOBAL) if (sym_bind == STB_GLOBAL)
tcc_debug_stabs(s1, str.data, N_GSYM, 0, NULL, 0, 0); tcc_debug_stabs(s1, str.data, N_GSYM, 0, NULL, 0, 0);
else else
@ -2437,11 +2479,8 @@ ST_FUNC void tcc_debug_typedef(TCCState *s1, Sym *sym)
{ {
CString str; CString str;
cstr_new (&str); cstr_new (&str);
cstr_printf (&str, "%s:t", cstr_printf (&str, "%s:t", get_tok_str(sym->v, NULL));
(sym->v & ~SYM_FIELD) >= SYM_FIRST_ANOM
? "" : get_tok_str(sym->v, NULL));
tcc_get_debug_info(s1, sym, &str); 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); tcc_debug_stabs(s1, str.data, N_LSYM, 0, NULL, 0, 0);
cstr_free (&str); cstr_free (&str);
} }

View File

@ -1294,16 +1294,21 @@ static Sym *sym_copy(Sym *s0, Sym **ps)
Sym *s; Sym *s;
s = sym_malloc(), *s = *s0; s = sym_malloc(), *s = *s0;
s->prev = *ps, *ps = s; 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); sym_link(s, 1);
return s; 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) static void sym_copy_ref(Sym *s, Sym **ps)
{ {
int bt = s->type.t & VT_BTYPE; 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; Sym **sp = &s->type.ref;
for (s = *sp, *sp = NULL; s; s = s->next) { for (s = *sp, *sp = NULL; s; s = s->next) {
Sym *s2 = sym_copy(s, ps); Sym *s2 = sym_copy(s, ps);
@ -5962,13 +5967,10 @@ ST_FUNC void unary(void)
learn = 1; learn = 1;
next(); next();
} else { } else {
AttributeDef ad_tmp; int v;
int itmp; parse_btype(&type, &ad, 0);
CType cur_type; type_decl(&type, &ad, &v, TYPE_ABSTRACT);
if (compare_types(&controlling_type, &type, 0)) {
parse_btype(&cur_type, &ad_tmp, 0);
type_decl(&cur_type, &ad_tmp, &itmp, TYPE_ABSTRACT);
if (compare_types(&controlling_type, &cur_type, 0)) {
if (has_match) { if (has_match) {
tcc_error("type match twice"); 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}; enum{ in = 0};
#define myassert(X) do{ if(!X) printf("%d: assertion failed\n", __LINE__); }while(0)
int main(){ int main_1(){
{ {
myassert(!in); myassert(!in);
if(sizeof(enum{in=1})) myassert(in); if(sizeof(enum{in=1})) myassert(in);
@ -38,6 +42,45 @@ int main(){
for(;sizeof(enum{in=1});){ myassert(in); break; } for(;sizeof(enum{in=1});){ myassert(in); break; }
myassert(!in); //OK 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"