mirror of
https://github.com/cc65/cc65.git
synced 2025-01-14 00:32:08 +00:00
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:
commit
4e2a3bde92
@ -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
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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 */
|
||||||
|
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 ();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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) {
|
||||||
|
@ -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();
|
||||||
|
@ -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 */
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user