commit 34b628de0fe4f297e8e5ef969a6355748a8154ef
parent c74e72c72af0f7ce01fe1388f4d542a57c5ed70f
Author: Roberto E. Vargas Caballero <k0ga@shike2.net>
Date: Wed, 4 Feb 2026 12:41:41 +0100
cc1: Disable string emit in initializers
When we are dealing with initializers there are transformations done
to the original strings to fit with the target type, like adding additional
0 or removing characters if it is bigger. That meant that we were emitting
strings that were not actually used, because the initializers were emitting
their own copy of these strings. In case of having an error in initialize()
the disstring variable would remain set, but that is not a problem because
in that state we don't emit anything else.
Also, the function str2ary() had a bug hidden in how the new string
was generated, but due to other bug that string was not used ever.
The new code avoids using str* alike functions because it would break
when the array has embedded NUL characters.
Diffstat:
6 files changed, 98 insertions(+), 22 deletions(-)
diff --git a/src/cmd/scc-cc/cc1/cc1.h b/src/cmd/scc-cc/cc1/cc1.h
@@ -543,7 +543,7 @@ extern struct yystype yylval;
extern char yytext[];
extern int yytoken;
extern unsigned short yylen;
-extern int disexpand, disescape;
+extern int disexpand, disescape, disstring;
extern unsigned cppctx;
extern Input *input;
extern int lexmode, namespace;
diff --git a/src/cmd/scc-cc/cc1/code.c b/src/cmd/scc-cc/cc1/code.c
@@ -341,7 +341,7 @@ emitstring(Symbol *sym, Type *tp)
break;
do {
fprintf(outfp,
- "\t#%c%02X\n",
+ "\t#%c%X\n",
chartype->letter, (*bp++) & 0xFF);
} while (bp < lim && !isprint(*bp));
}
diff --git a/src/cmd/scc-cc/cc1/expr.c b/src/cmd/scc-cc/cc1/expr.c
@@ -709,10 +709,12 @@ primary(void)
sym = yylval.sym;
switch (yytoken) {
case STRING:
- np = constnode(adjstrings(sym));
+ adjstrings(sym);
sym->flags |= SHASINIT;
- emit(ODECL, sym);
- emit(OINIT, np);
+ if (!disstring) {
+ emit(ODECL, sym);
+ emit(OINIT, constnode(sym));
+ }
return varnode(sym);
case BUILTIN:
fun = sym->u.fun;
@@ -927,8 +929,10 @@ unary(void)
case '&': op = OADDR; fun = address; break;
case '*': op = OPTR; fun = content; break;
case SIZEOF:
+ disstring = 1;
next();
tp = sizeexp();
+ disstring = 0;
if (!(tp->prop & TDEFINED))
errorp("sizeof applied to an incomplete type");
return sizeofnode(tp);
diff --git a/src/cmd/scc-cc/cc1/init.c b/src/cmd/scc-cc/cc1/init.c
@@ -1,3 +1,4 @@
+#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -25,6 +26,8 @@ struct init {
struct designator *head;
};
+int disstring;
+
static long long
arydesig(Type *tp, Init *ip)
{
@@ -85,28 +88,35 @@ str2ary(Type *tp)
Node *np;
Type *btp = tp->type;
Symbol *sym;
- size_t len;
+ long long i, olen, len;
char *s;
+ disstring = 1;
np = assign();
+ disstring = 0;
sym = np->left->sym;
if (btp != chartype && btp != uchartype && btp != schartype) {
errorp("array of inappropriate type initialized from string constant");
return constnode(zero);
}
- len = sym->type->n.elem-1;
+ len = tp->n.elem;
+ olen = sym->type->n.elem-1;
if (!(tp->prop & TDEFINED)) {
- tp->n.elem = len+1;
+ len = tp->n.elem = olen+1;
deftype(tp);
- } else if (tp->n.elem < len) {
+ } else if (len < olen) {
warn("initializer-string for array of chars is too long");
}
- len = tp->n.elem;
s = sym->u.s;
sym = newstring(NULL, len);
- strncpy(sym->u.s, s, len);
+ for (i = 0; i < len; i++)
+ sym->u.s[i] = (i < olen) ? *s++ : '\0';
+
+ assert(np->op == OADDR);
+ np->left->sym = sym;
+ np->left->type = sym->type;
np->sym = sym;
np->type = sym->type;
@@ -333,6 +343,28 @@ initlist(Type *tp)
}
static void
+emitstrings(Node *np)
+{
+ unsigned f;
+ Symbol *sym;
+
+ if (!np)
+ return;
+
+ if (np->op == OSYM) {
+ sym = np->sym;
+ f = sym->flags & (SSTRING|SEMITTED);
+ if (f == SSTRING) {
+ emit(ODECL, sym);
+ emit(OINIT, constnode(sym));
+ }
+ }
+
+ emitstrings(np->left);
+ emitstrings(np->right);
+}
+
+static void
autoinit(Symbol *sym, Node *np)
{
Symbol *hidden;
@@ -345,19 +377,24 @@ repeat:
tp = np->type;
goto repeat;
case ARY:
+ if (np->op == OADDR && np->sym->flags & SSTRING)
+ goto hidden_data;
case STRUCT:
- if (np->op == OSYM && np->sym->flags & SINITLST) {
- if (!(np->flags & NCONST))
- abort(); /* TODO */
- hidden = newsym(NS_IDEN, NULL);
- hidden->id = newid();
- hidden->type = sym->type;
- hidden->flags |= SLOCAL | SHASINIT;
- emit(ODECL, hidden);
- emit(OINIT, np);
- np = varnode(hidden);
- }
+ if (np->op != OSYM || (np->sym->flags & SINITLST) == 0)
+ goto expr;
+ hidden_data:
+ if (!(np->flags & NCONST))
+ abort(); /* TODO */
+ hidden = newsym(NS_IDEN, NULL);
+ hidden->id = newid();
+ hidden->type = sym->type;
+ hidden->flags |= SLOCAL | SHASINIT;
+ emit(ODECL, hidden);
+ emit(OINIT, np);
+ np = varnode(hidden);
default:
+ expr:
+ emitstrings(np);
emit(ODECL, sym);
np = node(OASSIGN, tp, varnode(sym), np);
emit(OEXPR, np);
diff --git a/tests/cc/execute/0240-init.c b/tests/cc/execute/0240-init.c
@@ -0,0 +1,34 @@
+struct a {
+ int i;
+ char s[10];
+};
+
+int
+main(void)
+{
+ char *s;
+ struct a b = {0, "hola"};
+
+ s = b.s;
+ if (s[0] != 'h')
+ return 1;
+ if (s[1] != 'o')
+ return 2;
+ if (s[2] != 'l')
+ return 3;
+ if (s[3] != 'a')
+ return 4;
+ if (s[4] != '\0')
+ return 5;
+ if (s[5] != '\0')
+ return 6;
+ if (s[6] != '\0')
+ return 7;
+ if (s[7] != '\0')
+ return 8;
+ if (s[8] != '\0')
+ return 9;
+ if (s[9] != '\0')
+ return 10;
+ return 0;
+}
diff --git a/tests/cc/execute/scc-tests.lst b/tests/cc/execute/scc-tests.lst
@@ -230,3 +230,4 @@
0237-down.c
0238-align.c
0239-fcasts.c
+0240-init.c