Merge pull request #2499 from kugelfuhr/kugelfuhr/disable-recursive-calls-to-main

Disallow recursive calls to main() in cc65 mode
This commit is contained in:
Bob Andrews 2024-09-07 14:39:28 +02:00 committed by GitHub
commit 4e2a3bde92
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 63 additions and 22 deletions

View File

@ -824,6 +824,11 @@ and the one defined by the ISO standard:
as it sounds, since the 6502 has so few registers that it isn't as it sounds, since the 6502 has so few registers that it isn't
possible to keep values in registers anyway. possible to keep values in registers anyway.
<p> <p>
<item> In <tt/cc65/ mode, <tt/main()/ cannot be called recursively. If this
is necessary, the program must be compiled in <tt/c89/ or <tt/c99/ mode
using the <tt><ref id="option--standard" name="--standard"></tt>
command line option.
<p>
</itemize> </itemize>
There may be some more minor differences I'm currently not aware of. The There may be some more minor differences I'm currently not aware of. The

View File

@ -507,13 +507,13 @@ void g_enter (unsigned flags, unsigned argsize)
void g_leave (int IsMainFunc) void g_leave (int DoCleanup)
/* Function epilogue */ /* Function epilogue */
{ {
/* In the main function nothing has to be dropped because the program /* In the main function in cc65 mode nothing has to be dropped because
** is terminated anyway. ** the program is terminated anyway.
*/ */
if (!IsMainFunc) { if (DoCleanup) {
/* How many bytes of locals do we have to drop? */ /* How many bytes of locals do we have to drop? */
unsigned ToDrop = (unsigned) -StackPtr; unsigned ToDrop = (unsigned) -StackPtr;

View File

@ -247,7 +247,7 @@ void g_scale (unsigned flags, long val);
void g_enter (unsigned flags, unsigned argsize); void g_enter (unsigned flags, unsigned argsize);
/* Function prologue */ /* Function prologue */
void g_leave (int IsMainFunc); void g_leave (int DoCleanup);
/* Function epilogue */ /* Function epilogue */

View File

@ -1219,9 +1219,6 @@ static void Primary (ExprDesc* E)
/* Is the symbol known? */ /* Is the symbol known? */
if (Sym) { if (Sym) {
/* We found the symbol - skip the name token */
NextToken ();
/* Check for illegal symbol types */ /* Check for illegal symbol types */
CHECK ((Sym->Flags & SC_TYPEMASK) != SC_LABEL); CHECK ((Sym->Flags & SC_TYPEMASK) != SC_LABEL);
if ((Sym->Flags & SC_TYPEMASK) == SC_TYPEDEF) { if ((Sym->Flags & SC_TYPEMASK) == SC_TYPEDEF) {
@ -1230,9 +1227,14 @@ static void Primary (ExprDesc* E)
/* Assume an int type to make E valid */ /* Assume an int type to make E valid */
E->Flags = E_LOC_STACK | E_RTYPE_LVAL; E->Flags = E_LOC_STACK | E_RTYPE_LVAL;
E->Type = type_int; E->Type = type_int;
/* Skip the erroneous token */
NextToken ();
break; break;
} }
/* Skip the name token */
NextToken ();
/* Mark the symbol as referenced */ /* Mark the symbol as referenced */
Sym->Flags |= SC_REF; Sym->Flags |= SC_REF;
@ -1286,7 +1288,23 @@ static void Primary (ExprDesc* E)
** rvalue, too, because we cannot store anything in a function. ** rvalue, too, because we cannot store anything in a function.
** So fix the flags depending on the type. ** So fix the flags depending on the type.
*/ */
if (IsTypeArray (E->Type) || IsTypeFunc (E->Type)) { if (IsTypeArray (E->Type)) {
ED_AddrExpr (E);
} else if (IsTypeFunc (E->Type)) {
/* In cc65 mode we cannot call or take the address of
** main().
*/
if (IS_Get (&Standard) == STD_CC65 &&
strcmp (Sym->Name, "main") == 0) {
/* Adjust the error message depending on a call or an
** address operation.
*/
if (CurTok.Tok == TOK_LPAREN) {
Error ("'main' must not be called recursively");
} else {
Error ("The address of 'main' cannot be taken");
}
}
ED_AddrExpr (E); ED_AddrExpr (E);
} }

View File

@ -646,13 +646,17 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
/* Output the function exit code label */ /* Output the function exit code label */
g_defcodelabel (F_GetRetLab (CurrentFunc)); g_defcodelabel (F_GetRetLab (CurrentFunc));
/* Restore the register variables (not necessary for main function) */ /* Restore the register variables (not necessary for the main function in
if (!F_IsMainFunc (CurrentFunc)) { ** cc65 mode)
*/
int CleanupOnExit = (IS_Get (&Standard) != STD_CC65) ||
!F_IsMainFunc (CurrentFunc);
if (CleanupOnExit) {
F_RestoreRegVars (CurrentFunc); F_RestoreRegVars (CurrentFunc);
} }
/* Generate the exit code */ /* Generate the exit code */
g_leave (F_IsMainFunc (CurrentFunc)); g_leave (CleanupOnExit);
/* Emit references to imports/exports */ /* Emit references to imports/exports */
EmitExternals (); EmitExternals ();

View File

@ -111,6 +111,13 @@ static void ParseRegisterDecl (Declarator* Decl, int Reg)
/* Get the size of the variable */ /* Get the size of the variable */
unsigned Size = SizeOf (Decl->Type); unsigned Size = SizeOf (Decl->Type);
/* Check if this is the main function and we are in cc65 mode. If so, we
** won't save the old contents of the register variables since in cc65
** mode main() may not be called recursively.
*/
int SaveRegVars = (IS_Get (&Standard) != STD_CC65) ||
!F_IsMainFunc (CurrentFunc);
/* Check for an optional initialization */ /* Check for an optional initialization */
if (CurTok.Tok == TOK_ASSIGN) { if (CurTok.Tok == TOK_ASSIGN) {
@ -126,13 +133,13 @@ static void ParseRegisterDecl (Declarator* Decl, int Reg)
/* Save the current contents of the register variable on stack. This is /* Save the current contents of the register variable on stack. This is
** not necessary for the main function. ** not necessary for the main function.
*/ */
if (!F_IsMainFunc (CurrentFunc)) { if (SaveRegVars) {
g_save_regvars (Reg, Size); g_save_regvars (Reg, Size);
} }
/* Add the symbol to the symbol table. We do that now, because for /* Add the symbol to the symbol table. We do that now, because for
** register variables the current stack pointer is implicitly used ** register variables the current stack pointer is implicitly used
** as location for the save area (unused in case of main()). ** as location for the save area (maybe unused in case of main()).
*/ */
Sym = AddLocalSym (Decl->Ident, Decl->Type, Decl->StorageClass, Reg); Sym = AddLocalSym (Decl->Ident, Decl->Type, Decl->StorageClass, Reg);
@ -187,14 +194,14 @@ static void ParseRegisterDecl (Declarator* Decl, int Reg)
/* Save the current contents of the register variable on stack. This is /* Save the current contents of the register variable on stack. This is
** not necessary for the main function. ** not necessary for the main function.
*/ */
if (!F_IsMainFunc (CurrentFunc)) { if (SaveRegVars) {
F_AllocLocalSpace (CurrentFunc); F_AllocLocalSpace (CurrentFunc);
g_save_regvars (Reg, Size); g_save_regvars (Reg, Size);
} }
/* Add the symbol to the symbol table. We do that now, because for /* Add the symbol to the symbol table. We do that now, because for
** register variables the current stack pointer is implicitly used ** register variables the current stack pointer is implicitly used
** as location for the save area (unused in case of main()). ** as location for the save area (maybe unused in case of main()).
*/ */
Sym = AddLocalSym (Decl->Ident, Decl->Type, Decl->StorageClass, Reg); Sym = AddLocalSym (Decl->Ident, Decl->Type, Decl->StorageClass, Reg);
} }

View File

@ -21,6 +21,8 @@ unsigned int *MEMTOP = (unsigned int *)741;
unsigned int *MEMLO = (unsigned int *)743; unsigned int *MEMLO = (unsigned int *)743;
void *allocmem; void *allocmem;
void code(void) { }
int main(void) int main(void)
{ {
allocmem = malloc(257); allocmem = malloc(257);
@ -35,7 +37,7 @@ int main(void)
printf(" MEMLO = $%04X (%u)\n", *MEMLO, *MEMLO); printf(" MEMLO = $%04X (%u)\n", *MEMLO, *MEMLO);
printf(" ----------------------\n"); printf(" ----------------------\n");
printf(" main: $%04X (code)\n", &main); printf(" code: $%04X (code)\n", &code);
printf(" data: $%04X (data)\n", &data); printf(" data: $%04X (data)\n", &data);
printf(" _dos_type: $%04X (bss)\n", &_dos_type); printf(" _dos_type: $%04X (bss)\n", &_dos_type);
printf(" allocmem: $%04X (dyn. data)\n", allocmem); printf(" allocmem: $%04X (dyn. data)\n", allocmem);

View File

@ -11,6 +11,8 @@ static char hex[16] = { "0123456789abcdef" };
static char charbuf[0x20]; static char charbuf[0x20];
static char colbuf[0x20]; static char colbuf[0x20];
void func(void) { }
void main(void) void main(void)
{ {
int stackvar = 42; int stackvar = 42;
@ -65,7 +67,7 @@ void main(void)
p[8],p[9],p[10],p[11],p[12],p[13],p[14],p[15] p[8],p[9],p[10],p[11],p[12],p[13],p[14],p[15]
); );
} }
memcpy(p, main, i = 0); /* test that a zero length doesn't copy 64K */ memcpy(p, func, i = 0); /* test that a zero length doesn't copy 64K */
gotoxy(0,ysize - 1); gotoxy(0,ysize - 1);
for (i = 0; i < xsize; ++i) { for (i = 0; i < xsize; ++i) {

View File

@ -159,12 +159,14 @@ static void Pause(void) {
#endif #endif
} }
static void Nil() { }
int main(void) { int main(void) {
long n0; long n0;
unsigned t; unsigned t;
int c, n1 = 12345, n2, n3; int c, n1 = 12345, n2, n3;
char s1[80], s2[80]; char s1[80], s2[80];
void *p1 = main, *p2 = main, *p3 = main, *p4 = main; void *p1 = Nil, *p2 = Nil, *p3 = Nil, *p4 = Nil;
#ifndef USE_STDIO #ifndef USE_STDIO
clrscr(); clrscr();

View File

@ -22,6 +22,5 @@ return_t main(int argc, char* argv[])
n = 0; /* produce an error */ n = 0; /* produce an error */
/* produce a warning */ /* produce a warning */
} }
int arr[main(0, 0)]; /* produce an error */
int b = 0; int b = 0;
int arr[b]; /* produce an error */

View File

@ -28,6 +28,8 @@ struct S {
} \ } \
} while(0); } while(0);
void func() { }
int main() int main()
{ {
int a; int a;
@ -60,7 +62,7 @@ int main()
TEST_NON_NULL(((struct S*)&a)->a) TEST_NON_NULL(((struct S*)&a)->a)
/* Non-null pointer obtained with cast and -> */ /* Non-null pointer obtained with cast and -> */
TEST_NON_NULL(((struct S*)&main)->a) TEST_NON_NULL(((struct S*)&func)->a)
if (failures != 0) if (failures != 0)
{ {