Carlos Montiers | 17 Jul 05:39 2014
Picon

Correcting the prototype of __getmainargs and add validation to crt1.c

Hello.
I found that the current protoype of __getmainargs in crt1.c return void.
But according to the official internal.h from microsoft sdk it return int.

The documentation says: return 0 if successful; a negative value if unsuccessful.
I decompiled it from msvcrt.dll for windows xp.
And the only possible return value for error is -1 and is caused because a malloc cannot get memory.

It do a similar to this:
if ( globb ) {
    result = __setargv;
} else {
    result = _setargv();
}
if ( result >= 0 ) {
*argc = __argc;
*argv = __argv;
*env = _environ;
}
return result;

It only change the values of argc y argv if success the internal function that it call.
This internals functions __setargv and __setargv() ask for memory with malloc and if it is sucess set that pointer in the global variable __argv. if __argv is NULL because the memory cannot allocated on the heap. It return -1. Then __getmainargs check the return, if it is not a negative number it set argc, argv and env to the corresponding global variables: __argc, __argv, __environ.

Also i write a demo that demostrates that __getmainargs left untouched argc and argv if it fails. (attached)

When you run, it ful the heap (warning: it not free it. I tested on a a virtual machine with windows xp with 64 mb).

When you run it print:
Setting argc to -1 and argv to 1.
Content of argc:-1 argv:0x1
Calling __getmainargs.
__getmainargs failed!
Result of __getmainargs: -1
Content of argc:-1 argv:0x1

Beause it, if __getmainargs fails, it not set argv to NULL or argc to 0.
I added a if that check it, and print out a error message. But I not know if this is the recommended way for handle this error. A maybe possibles ways for handle it is:
set argc to 0 and argv to NULL and env to NULL
call to abort()
call to _exit(3)
call to exit(3)
call to ExitProcess(3)

I choose the last, and also I ad a error message, but it is can be optional:
This is the handle that I added:
if (__getmainargs(&argc, &argv, &env, 0, &start_info)) {
        // __getmainargs failed because possible few memory on the heap.
        fprintf(stderr, "Error getting the main args.");
        // terminate with exit code of 3, similar to abort()
        ExitProcess(3);
}

The documentation not says nothing about free argv, maybe because on sucess it points to the global variable __argv and maybe because it is global is free with exit functions (but i not know if this is true).

Here I prefer use ExitProcess instead of exit, because internally exit do some things and in the end it call to ExitProcess.
I think that ExitProcess is more speedy than exit, but maybe it not do the cleans that exit function makes. Exit function maybe deallocate the global variable __argv

Also, I found that avira antivir detect as false positive a executable compiled with tiny c when it not use ExitProcess, then have a crt using ExitProcess in some point avoid a false detection of our executable.

I use this in some crt for bypass a false detection:
/*
  This function is never called.
  Is used for avoid AntiVir false detection: TR/Crypt.XPACK.Gen
*/
void nothing(void)
{
    ExitProcess(0);
}

Because it I choose use ExitProcess for handle the error of __getmainargs function.


Carlos.











Attachment (poc_getmainargs.c): text/x-csrc, 1284 bytes
<div><div dir="ltr">
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div><div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>Hello.<br>
</div>I found that the current protoype of __getmainargs in crt1.c return void.<br>
</div>But according to the official internal.h from microsoft sdk it return int.<br><br>
</div>The documentation says: return 0 if successful; a negative value if unsuccessful.<br>
</div>I decompiled it from msvcrt.dll for windows xp.<br>
</div>And the only possible return value for error is -1 and is caused because a malloc cannot get memory.<br><br>
</div>It do a similar to this:<br>if ( globb ) {<br>&nbsp;&nbsp;&nbsp; result = __setargv;<br>} else {<br>&nbsp;&nbsp;&nbsp; result = _setargv();<br>}<br>if ( result &gt;= 0 ) {<br>*argc = __argc;<br>*argv = __argv;<br>*env = _environ;<br>}<br>return result;<br><br>
</div>It only change the values of argc y argv if success the internal function that it call.<br>
</div>This internals functions __setargv and __setargv() ask for memory with malloc and if it is sucess set that pointer in the global variable __argv. if __argv is NULL because the memory cannot allocated on the heap. It return -1. Then __getmainargs check the return, if it is not a negative number it set argc, argv and env to the corresponding global variables: __argc, __argv, __environ.<br><br>
</div></div>Also i write a demo that demostrates that __getmainargs left untouched argc and argv if it fails. (attached)<br><br>
</div>When you run, it ful the heap (warning: it not free it. I tested on a a virtual machine with windows xp with 64 mb).<br><br>
</div>When you run it print:<br>Setting argc to -1 and argv to 1.<br>Content of argc:-1 argv:0x1<br>Calling __getmainargs.<br>__getmainargs failed!<br>Result of __getmainargs: -1<br>Content of argc:-1 argv:0x1<br><br>
</div>Beause it, if __getmainargs fails, it not set argv to NULL or argc to 0.<br>
</div>I added a if that check it, and print out a error message. But I not know if this is the recommended way for handle this error. A maybe possibles ways for handle it is:<br>
set argc to 0 and argv to NULL and env to NULL<br>
</div>call to abort()<br>
</div>call to _exit(3)<br>call to exit(3)<br>
</div>
<div>call to ExitProcess(3)<br>
</div>
<div><br></div>
<div>I choose the last, and also I ad a error message, but it is can be optional:<br>
</div>
<div>This is the handle that I added:<br>if&nbsp;(__getmainargs(&amp;argc,&nbsp;&amp;argv,&nbsp;&amp;env,&nbsp;0,&nbsp;&amp;start_info))&nbsp;{
<div class="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;__getmainargs&nbsp;failed&nbsp;because&nbsp;possible&nbsp;few&nbsp;memory&nbsp;on&nbsp;the&nbsp;heap.</div>
<div class="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf(stderr,&nbsp;"Error&nbsp;getting&nbsp;the&nbsp;main&nbsp;args.");</div>
<div class="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;terminate&nbsp;with&nbsp;exit&nbsp;code&nbsp;of&nbsp;3,&nbsp;similar&nbsp;to&nbsp;abort()</div>
<div class="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ExitProcess(3);</div>
<div class="">}</div>
<br>
</div>
<div>The documentation not says nothing about free argv, maybe because on sucess it points to the global variable __argv and maybe because it is global is free with exit functions (but i not know if this is true).<br><br>
</div>
<div>Here I prefer use ExitProcess instead of exit, because internally exit 
do some things and in the end it call to ExitProcess.<br>
</div>
<div>I think that ExitProcess is more speedy than exit, but maybe it not do the cleans that exit function makes. Exit function maybe deallocate the global variable __argv<br><br>
</div>
<div>Also, I found that avira antivir detect as false positive a executable compiled with tiny c when it not use ExitProcess, then have a crt using ExitProcess in some point avoid a false detection of our executable.<br><br>
</div>
<div>I use this in some crt for bypass a false detection:<br>/*<br>&nbsp; This function is never called.<br>&nbsp; Is used for avoid AntiVir false detection: TR/Crypt.XPACK.Gen<br>
*/<br>void nothing(void)<br>{<br>&nbsp;&nbsp;&nbsp; ExitProcess(0);<br>}<br><br>
</div>
<div>Because it I choose use ExitProcess for handle the error of __getmainargs function.<br><br><br>
</div>
<div>Carlos.<br>
</div>
<div>
<br><br>
</div>
<div>
<br><br><br><br><br><br>
</div>
<div>
<br><br><br>
</div>
</div></div>
James Russell Moore | 5 Jul 15:23 2014
Picon

Building Unicode applications with TCC in Windows.

Hello, I have a small utility which makes use of wide characters in Windows. It's written using _TCHAR though so it can be built with multibyte characters as well.

The main() function header is written as follows:

int _tmain(int argc, _TCHAR* argv[])

Translates to "main" in a multibyte build and "wmain" in a Unicode build, but TCC seems to expect a main() function instead of a wmain() one so it can't produce Unicode builds (I tried with a mob build as well).

Is is expected or is there something wrong? Would it be possible to make a modification to enable wmain() to be an entry point as well as main()?

Attached to this email is a small test case.

Thanks for your time anyway.
Attachment (test.c): text/x-csrc, 202 bytes
<div><div dir="ltr">
<div>Hello, I have a small utility which makes use of wide characters in Windows. It's written using _TCHAR though so it can be built with multibyte characters as well.</div>
<div><br></div>
<div>The main() function header is written as follows:</div>

<div><br></div>
<div>int _tmain(int argc, _TCHAR* argv[])</div>
<div><br></div>
<div>Translates to "main" in a multibyte build and "wmain" in a Unicode build, but TCC seems to expect a main() function instead of a wmain() one so it can't produce Unicode builds (I tried with a mob build as well).</div>

<div><br></div>
<div>Is is expected or is there something wrong? Would it be possible to make a modification to enable wmain() to be an entry point as well as main()?</div>
<div><br></div>
<div>Attached to this email is a small test case.</div>

<div><br></div>
<div>Thanks for your time anyway.</div>
</div></div>
jiang | 3 Jul 03:30 2014

回复: Request push

It doesn't matter, I'm not very urgent. thank you.but I need time to find their own patch mistake.

Best regards,

Jiang

Thomas Preud'homme <robotux <at> celest.fr>编写:

>Le mercredi 02 juillet 2014, 00:07:02 jiang a écrit :
>> I'm sorry, I was remiss.
>> I did not find a space with gitk.
>> This is my new commit:89000c18dc7d5ccb2687948f94fe49d392990dab
>
>That's great. Thanks. For the ";;;" patch I won't be able to review for 2 
>weeks as I'll be away from my computer. See if you can find someone else to 
>review or else wait for my return.
>
>Best regards,
>
>Thomas
_______________________________________________
Tinycc-devel mailing list
Tinycc-devel <at> nongnu.org
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
Thomas Preud'homme | 1 Jul 16:14 2014
Picon

Re: isxdigit conversion inconsistency with widechar

Le lundi 30 juin 2014, 15:03:35 Carlos Montiers a écrit :
> 
> Yes. It is not a bug. But, I post this for the knowledge. I inspect
> mscvrt.dll and isxdigit internally call to _isctype. Then I think is more
> speedy call directly to _isctype instead of isxdigit. But the current macro
> of tiny c not call to it like the counterpart iswxdigit. Because it I post
> my little fix:
> 
> #undef        isxdigit
> #undef        iswxdigit
> #define        isxdigit(d)      _isctype(d, _HEX)
> #define        iswxdigit(d)    iswctype(d, _HEX)

If you can provide number that shows it's indeed slower then it is indeed a 
bug and someone might fix it someday. Else I doubt somebody will feel motivated 
enough to change this (I might be wrong).

Best regards,

Thomas
Le lundi 30 juin 2014, 15:03:35 Carlos Montiers a écrit :
> 
> Yes. It is not a bug. But, I post this for the knowledge. I inspect
> mscvrt.dll and isxdigit internally call to _isctype. Then I think is more
> speedy call directly to _isctype instead of isxdigit. But the current macro
> of tiny c not call to it like the counterpart iswxdigit. Because it I post
> my little fix:
> 
> #undef        isxdigit
> #undef        iswxdigit
> #define        isxdigit(d)      _isctype(d, _HEX)
> #define        iswxdigit(d)    iswctype(d, _HEX)

If you can provide number that shows it's indeed slower then it is indeed a 
bug and someone might fix it someday. Else I doubt somebody will feel motivated 
enough to change this (I might be wrong).

Best regards,

Thomas
jiang | 30 Jun 16:58 2014

回复: Request push

I'm sorry! Is I am too impatient. Thank you for your remind and Suggestions. I'll take the time to put a bug in
greater detail.

Best regards,

jiang

Thomas Preud'homme <robotux <at> celest.fr>编写:

>Le lundi 30 juin 2014, 16:49:02 jiang a écrit :
>> Struct improved algorithms and add struct warnings and other warnings.
>
>Hi Jiang,
>
>> 
>> The following can be compiled:
>> struct st{int a;;;;;;;};
>> 
>> If you do not have advice, tomorrow pushed into the mob.
>
>No Jiang, that's not how it works. There is several problem with this request:
>
>1) You need to realize that we are all volunteer spending some of our free 
>time to improve tcc. We don't necessarily check emails about tcc everyday and 
>anyway 1 day is too short to review. Another consequence of this is that we 
>don't have to answer within a given delay. We will do our best to answer but 
>you just need to wait. Of course if too much time (like 2 weeks) pass without 
>answer you can ping to ask if we saw your email. And finally I think in your 
>case you should not commit without an approval first. Eventually, when your 
>commit will start to improve we might tell you that you don't need to ask for 
>a review anymore.
>
>2) I gave you some comments on another patch. Before asking for another review 
>you could maybe post a new version that fixes all my comments. It gives the 
>feeling that you don't want to address the remarks and discourages people to 
>review your patches.
>
>3) Don't bundle several changes in one patch. It makes it more difficult to 
>review. So you make one patch that fixes the bug you mentions and others (or 
>maybe just one other, it depends how big it is) with the warnings
>
>4) Your patch contains some formatting issues:
>
>+                        if (v == 0 && (type1.t & VT_BTYPE) != VT_STRUCT){
>+                            tcc_warning("declaration does not
declare 
>anything");
>+                            break;
>+                        }if (type_size(&type1, &align) < 0) {
>+                            if ((a == TOK_STRUCT) && (type1.t & VT_ARRAY))
>+                                flexible = 1;
>+                            else
>+                                tcc_error("field '%s' has
incomplete type", 
>get_tok_str(v, NULL));
>                         }
>
>There should be a space between the closing parenthesis ')' and the opening 
>curly brace '{'. The second "if" should be on a new line or should be preceded 
>by a "else"
>
>5) The commit message is not very informative. For the commit with the bugfix 
>you could say "Allow tcc to parse:" and then the example. See commit 
>82969f045c99b4d1ef833de35117c17b326b46c0 for an example. You can also simply 
>explain what bug you fix "Fix parsing of arbitrary number of semicolons" or 
>something better and that brings me to the next comment.
>
>6) You need to understand how a language is designed.
>
>-                parse_btype(&btype, &ad);
>+                if(!parse_btype(&btype, &ad)){
>+                    if (tok == ';'){
>+                        skip(';');
>+                        continue;
>
>Here you are saying that it's ok to have struct st {int a;;;;;} but not struct 
>st {int a:10;;;;}. A better place to add such a check would be near the end of 
>the function, around the "skip(';'). You need to understand why this is 
>authorized. So as an exercise grab the C99 standard at [1] and tell me why 
>this is authorized. Hint: start to read from section 6.7.2.1. Definitions are 
>recursive, so you need to browse quite a lot to understand things. Try to tell 
>me which lexical elements to follow (sorry, I don't know the proper term, 
>shame on me) to create your testcase. Something like:
>
>struct-or-union-specifier
>  -> struct-or-union
>  -> identifier (no need to detail here)
>  -> struct-declaration-list
>     -> struct-declaration
>
>etc…
>
>[1] http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf
>
>Best regards,
>
>Thomas
_______________________________________________
Tinycc-devel mailing list
Tinycc-devel <at> nongnu.org
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
jiang | 30 Jun 10:49 2014

Re: Request push

Struct improved algorithms and add struct warnings and other warnings.

The following can be compiled:
struct st{int a;;;;;;;};

If you do not have advice, tomorrow pushed into the mob.

jiang
commit 8be2fbd82ef16c8f846fccb46689827ed37997eb
Author: jiang <30155751@...>
Date:   Mon Jun 30 16:40:16 2014 +0800

    Add warning

diff --git a/libtcc.c b/libtcc.c
index 7caa7c1..79a2d2c 100644
--- a/libtcc.c
+++ b/libtcc.c
 <at>  <at>  -1397,8 +1397,9  <at>  <at>  static const FlagDef warning_defs[] = {
     { offsetof(TCCState, warn_unsupported), 0, "unsupported" },
     { offsetof(TCCState, warn_write_strings), 0, "write-strings" },
     { offsetof(TCCState, warn_error), 0, "error" },
-    { offsetof(TCCState, warn_implicit_function_declaration), WD_ALL,
-      "implicit-function-declaration" },
+    { offsetof(TCCState, warn_implicit_function_declaration), WD_ALL,
"implicit-function-declaration" },
+    { offsetof(TCCState, warn_return_type), WD_ALL, "return-type" },
+    { offsetof(TCCState, warn_char_subscripts), WD_ALL, "char-subscripts" },
 };

 ST_FUNC int set_flag(TCCState *s, const FlagDef *flags, int nb_flags,
diff --git a/tcc.h b/tcc.h
index c93cedf..596b22f 100644
--- a/tcc.h
+++ b/tcc.h
 <at>  <at>  -594,6 +594,8  <at>  <at>  struct TCCState {
     int warn_error;
     int warn_none;
     int warn_implicit_function_declaration;
+    int warn_return_type;
+    int warn_char_subscripts;

     /* compile with debug symbol (and use them if error during execution) */
     int do_debug;
diff --git a/tccgen.c b/tccgen.c
index 1a89d4a..227864e 100644
--- a/tccgen.c
+++ b/tccgen.c
 <at>  <at>  -2898,42 +2898,47  <at>  <at>  static void struct_decl(CType *type, int u, int tdef)
             offset = 0;
             flexible = 0;
             while (tok != '}') {
-                parse_btype(&btype, &ad);
+                if(!parse_btype(&btype, &ad)){
+                    if (tok == ';'){
+                        skip(';');
+                        continue;
+                    }else if(tok == TOK_EOF || tok == '{')
+                        expect("specifier-qualifier-list at end of input");
+                }
                 while (1) {
-		    if (flexible)
-		        tcc_error("flexible array member '%s' not at the end of struct",
-                              get_tok_str(v, NULL));
+                    if (flexible)
+                        tcc_error("flexible array member '%s' not at the end of struct", get_tok_str(v, NULL));
                     bit_size = -1;
                     v = 0;
                     type1 = btype;
                     if (tok != ':') {
                         type_decl(&type1, &ad, &v, TYPE_DIRECT | TYPE_ABSTRACT);
-                        if (v == 0 && (type1.t & VT_BTYPE) != VT_STRUCT)
-                            expect("identifier");
-                        if (type_size(&type1, &align) < 0) {
-			    if ((a == TOK_STRUCT) && (type1.t & VT_ARRAY) && c)
-			        flexible = 1;
-			    else
-			        tcc_error("field '%s' has incomplete type",
-                                      get_tok_str(v, NULL));
+                        if (v == 0 && (type1.t & VT_BTYPE) != VT_STRUCT){
+                            tcc_warning("declaration does not declare anything");
+                            break;
+                        }if (type_size(&type1, &align) < 0) {
+                            if ((a == TOK_STRUCT) && (type1.t & VT_ARRAY))
+                                flexible = 1;
+                            else
+                                tcc_error("field '%s' has incomplete type", get_tok_str(v, NULL));
                         }
                         if ((type1.t & VT_BTYPE) == VT_FUNC ||
                             (type1.t & (VT_TYPEDEF | VT_STATIC | VT_EXTERN | VT_INLINE)))
-                            tcc_error("invalid type for '%s'", 
-                                  get_tok_str(v, NULL));
+                            tcc_error("invalid type for '%s'", get_tok_str(v, NULL));
                     }
                     if (tok == ':') {
                         next();
                         bit_size = expr_const();
                         /* XXX: handle v = 0 case for messages */
                         if (bit_size < 0)
-                            tcc_error("negative width in bit-field '%s'", 
-                                  get_tok_str(v, NULL));
+                            tcc_error("negative width in bit-field '%s'", get_tok_str(v, NULL));
                         if (v && bit_size == 0)
-                            tcc_error("zero width for bit-field '%s'", 
-                                  get_tok_str(v, NULL));
+                            tcc_error("zero width for bit-field '%s'", get_tok_str(v, NULL));
                     }
-                    size = type_size(&type1, &align);
+                    if(type1.t & VT_VLA)
+                        size = 0, align = 1;
+                    else
+                        size = type_size(&type1, &align);
                     if (ad.a.aligned) {
                         if (align < ad.a.aligned)
                             align = ad.a.aligned;
 <at>  <at>  -2946,12 +2951,9  <at>  <at>  static void struct_decl(CType *type, int u, int tdef)
                     lbit_pos = 0;
                     if (bit_size >= 0) {
                         bt = type1.t & VT_BTYPE;
-                        if (bt != VT_INT && 
-                            bt != VT_BYTE && 
-                            bt != VT_SHORT &&
-                            bt != VT_BOOL &&
-                            bt != VT_ENUM &&
-                            bt != VT_LLONG)
+                        if (bt != VT_INT && bt != VT_BYTE &&
+                            bt != VT_SHORT && bt != VT_BOOL &&
+                            bt != VT_ENUM && bt != VT_LLONG)
                             tcc_error("bitfields must have scalar type");
                         bsize = size * 8;
                         if (bit_size > bsize) {
 <at>  <at>  -3023,13 +3025,19  <at>  <at>  static void struct_decl(CType *type, int u, int tdef)
                         *ps = ss;
                         ps = &ss->next;
                     }
-                    if (tok == ';' || tok == TOK_EOF)
+                    if (tok == ';' || tok == '}')
                         break;
                     skip(',');
                 }
-                skip(';');
+                if(tok == '}'){
+                    tcc_warning("no ';' at end of struct or union");
+                    break;
+                }else
+                    skip(';');
             }
-            skip('}');
+            next();
+            if (!c && flexible)
+                tcc_error("flexible array member '%s' in otherwise empty struct", get_tok_str(v, NULL));
             /* store size and alignment */
             s->c = (c + maxalign - 1) & -maxalign; 
             s->r = maxalign;
 <at>  <at>  -4037,6 +4045,8  <at>  <at>  ST_FUNC void unary(void)
         } else if (tok == '[') {
             next();
             gexpr();
+            if(tcc_state->warn_char_subscripts && (vtop->type.t & (VT_BTYPE|VT_DEFSIGN|VT_UNSIGNED)) == VT_BYTE)
+                tcc_warning("array subscript has type 'char'");
             gen_op('+');
             indir();
             skip(']');
 <at>  <at>  -5510,6 +5520,8  <at>  <at>  static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
     Sym *flexible_array;

     flexible_array = NULL;
+    if (has_init && (type->t & VT_VLA))
+        tcc_error("Variable length array cannot be initialized");
     if ((type->t & VT_BTYPE) == VT_STRUCT) {
         Sym *field = type->ref->next;
         if (field) {
 <at>  <at>  -5907,12 +5919,13  <at>  <at>  ST_FUNC void gen_inline_functions(void)
 /* 'l' is VT_LOCAL or VT_CONST to define default storage type */
 static int decl0(int l, int is_for_loop_init)
 {
-    int v, has_init, r;
+    int v, has_init, r, imp;
     CType type, btype;
     Sym *sym;
     AttributeDef ad;

     while (1) {
+        imp = 0;
         if (!parse_btype(&btype, &ad)) {
             if (is_for_loop_init)
                 return 0;
 <at>  <at>  -5922,11 +5935,13  <at>  <at>  static int decl0(int l, int is_for_loop_init)
                 next();
                 continue;
             }
-            if (l == VT_CONST &&
-                (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3)) {
+            if (l == VT_CONST){
+                if (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3){
                 /* global asm block */
-                asm_global_instr();
-                continue;
+                    asm_global_instr();
+                    continue;
+                }
+                imp = 1;
             }
             /* special test for old K&R protos without explicit int
                type. Only accepted when defining global data */
 <at>  <at>  -5934,10 +5949,15  <at>  <at>  static int decl0(int l, int is_for_loop_init)
                 break;
             btype.t = VT_INT;
         }
-        if (((btype.t & VT_BTYPE) == VT_ENUM ||
-             (btype.t & VT_BTYPE) == VT_STRUCT) && 
-            tok == ';') {
+        if (tok == ';') {
+            int bt = btype.t & VT_BTYPE;
             /* we accept no variable after */
+            if(btype.t & (VT_CONSTANT|VT_VOLATILE))
+                tcc_warning("useless type qualifier in empty declaration.'%s'before", get_tok_str(tok, NULL));
+            if(bt != VT_STRUCT && bt != VT_ENUM)
+                tcc_warning("useless type name in empty declaration '%s'", get_tok_str(tok, NULL));
+            if((bt == VT_STRUCT) && ((btype.ref->v & ~SYM_STRUCT) >= SYM_FIRST_ANOM))
+                tcc_warning("unnamed struct/union that defines no instances");
             next();
             continue;
         }
 <at>  <at>  -6097,6 +6117,9  <at>  <at>  static int decl0(int l, int is_for_loop_init)
                     sym->a = ad.a;
                     sym->type.t |= VT_TYPEDEF;
                 } else {
+                    if(imp && (type.t & VT_BTYPE) != VT_FUNC)
+                        tcc_warning("data definition has no type or storage class: '%s'",
+                            get_tok_str(v, NULL));
                     r = 0;
                     if ((type.t & VT_BTYPE) == VT_FUNC) {
                         /* external function definition */
 <at>  <at>  -6108,8 +6131,6  <at>  <at>  static int decl0(int l, int is_for_loop_init)
                         r |= lvalue_type(type.t);
                     }
                     has_init = (tok == '=');
-                    if (has_init && (type.t & VT_VLA))
-                        tcc_error("Variable length array cannot be initialized");
                     if ((btype.t & VT_EXTERN) || ((type.t & VT_BTYPE) == VT_FUNC) ||
                         ((type.t & VT_ARRAY) && (type.t & VT_STATIC) &&
                          !has_init && l == VT_CONST && type.ref->c < 0)) {
diff --git a/tests/tests2/46_grep.c b/tests/tests2/46_grep.c
index 3123bc3..d789ee0 100644
--- a/tests/tests2/46_grep.c
+++ b/tests/tests2/46_grep.c
 <at>  <at>  -15,6 +15,7  <at>  <at> 
  * privileges were granted by DECUS.
  */
 #include <stdio.h>
+#include <ctype.h>
 #include <stdlib.h>

 /*
diff --git a/tests/tests2/64_macro_nesting.c b/tests/tests2/64_macro_nesting.c
index 44b582f..2123a75 100644
--- a/tests/tests2/64_macro_nesting.c
+++ b/tests/tests2/64_macro_nesting.c
 <at>  <at>  -1,3 +1,4  <at>  <at> 
+#include <stdio.h>
 #define CAT2(a,b) a##b
 #define CAT(a,b) CAT2(a,b)
 #define AB(x) CAT(x,y)
diff --git a/tests/tests2/70_warn_test.c b/tests/tests2/70_warn_test.c
new file mode 100644
index 0000000..5543426
--- /dev/null
+++ b/tests/tests2/70_warn_test.c
 <at>  <at>  -0,0 +1,18  <at>  <at> 
+#include <stdio.h>
+#define uint8_t unsigned char
+const struct {
+    const uint8_t code
+};
+
+a;
+b,c;
+
+int main(void)
+{
+    char c = 1;
+    int b[2];
+    b[c] = 9;
+    printf("%d\n", b[c]);
+    printf("ok\n");
+    return 0;
+}
diff --git a/tests/tests2/70_warn_test.expect b/tests/tests2/70_warn_test.expect
new file mode 100644
index 0000000..bae7348
--- /dev/null
+++ b/tests/tests2/70_warn_test.expect
 <at>  <at>  -0,0 +1,10  <at>  <at> 
+70_warn_test.c:5: warning: no ';' at end of struct or union
+70_warn_test.c:5: warning: useless type qualifier in empty declaration.';'before
+70_warn_test.c:5: warning: unnamed struct/union that defines no instances
+70_warn_test.c:7: warning: data definition has no type or storage class: 'a'
+70_warn_test.c:8: warning: data definition has no type or storage class: 'b'
+70_warn_test.c:8: warning: data definition has no type or storage class: 'c'
+70_warn_test.c:14: warning: array subscript has type 'char'
+70_warn_test.c:15: warning: array subscript has type 'char'
+9
+ok
diff --git a/tests/tests2/71_err_struct.c b/tests/tests2/71_err_struct.c
new file mode 100644
index 0000000..276c993
--- /dev/null
+++ b/tests/tests2/71_err_struct.c
 <at>  <at>  -0,0 +1,8  <at>  <at> 
+typedef struct {
+    const uint8_t* code;
+} st;
+
+int main(void)
+{
+    return 0;
+}
diff --git a/tests/tests2/71_err_struct.expect b/tests/tests2/71_err_struct.expect
new file mode 100644
index 0000000..239c174
--- /dev/null
+++ b/tests/tests2/71_err_struct.expect
 <at>  <at>  -0,0 +1  <at>  <at> 
+71_err_struct.c:2: error: ',' expected (got "*")
diff --git a/tests/tests2/72_err_struct.c b/tests/tests2/72_err_struct.c
new file mode 100644
index 0000000..e3d7afd
--- /dev/null
+++ b/tests/tests2/72_err_struct.c
 <at>  <at>  -0,0 +1,11  <at>  <at> 
+#include <stdio.h>
+typedef struct {
+    int;
+    int;
+;{};
+
+int main(void)
+{
+    printf("ok\n");
+    return 0;
+}
diff --git a/tests/tests2/72_err_struct.expect b/tests/tests2/72_err_struct.expect
new file mode 100644
index 0000000..7aaf45a
--- /dev/null
+++ b/tests/tests2/72_err_struct.expect
 <at>  <at>  -0,0 +1,3  <at>  <at> 
+72_err_struct.c:3: warning: declaration does not declare anything
+72_err_struct.c:4: warning: declaration does not declare anything
+72_err_struct.c:5: error: specifier-qualifier-list at end of input expected
diff --git a/tests/tests2/73_err_struct.c b/tests/tests2/73_err_struct.c
new file mode 100644
index 0000000..6851b76
--- /dev/null
+++ b/tests/tests2/73_err_struct.c
 <at>  <at>  -0,0 +1,11  <at>  <at> 
+#include <stdio.h>
+typedef struct {
+    int a[];
+    int b;
+};
+
+int main(void)
+{
+    printf("ok\n");
+    return 0;
+}
diff --git a/tests/tests2/73_err_struct.expect b/tests/tests2/73_err_struct.expect
new file mode 100644
index 0000000..d42b710
--- /dev/null
+++ b/tests/tests2/73_err_struct.expect
 <at>  <at>  -0,0 +1  <at>  <at> 
+73_err_struct.c:4: error: flexible array member 'a' not at the end of struct
diff --git a/tests/tests2/74_err_struct.c b/tests/tests2/74_err_struct.c
new file mode 100644
index 0000000..40c5fd4
--- /dev/null
+++ b/tests/tests2/74_err_struct.c
 <at>  <at>  -0,0 +1,10  <at>  <at> 
+#include <stdio.h>
+typedef struct {
+    int a[];
+};
+
+int main(void)
+{
+    printf("ok\n");
+    return 0;
+}
diff --git a/tests/tests2/74_err_struct.expect b/tests/tests2/74_err_struct.expect
new file mode 100644
index 0000000..2338ad0
--- /dev/null
+++ b/tests/tests2/74_err_struct.expect
 <at>  <at>  -0,0 +1  <at>  <at> 
+74_err_struct.c:4: error: flexible array member 'a' in otherwise empty struct
diff --git a/tests/tests2/Makefile b/tests/tests2/Makefile
index 64532a1..104d456 100644
--- a/tests/tests2/Makefile
+++ b/tests/tests2/Makefile
 <at>  <at>  -84,7 +84,12  <at>  <at>  TESTS =	\
  66_macro_concat_end.test \
  67_macro_concat.test \
  68_macro_concat.test \
- 69_macro_concat.test
+ 69_macro_concat.test \
+ 70_warn_test.test \
+ 71_err_struct.test \
+ 72_err_struct.test \
+ 73_err_struct.test \
+ 74_err_struct.test

 # 34_array_assignment.test -- array assignment is not in C standard

 <at>  <at>  -109,10 +114,10  <at>  <at>  all test: $(filter-out $(SKIP),$(TESTS))
 %.test: %.c %.expect
 	 <at> echo Test: $*...

-	 <at> $(TCC) -run $< $(ARGS) >$*.output 2>&1 || true
+	 <at> $(TCC) -Wall -run $< $(ARGS) >$*.output 2>&1 || true
 	 <at> diff -bu $*.expect $*.output && rm -f $*.output

-	 <at> ($(TCC) $< -o $*.exe && ./$*.exe $(ARGS)) >$*.output2 2>&1 || true
+	 <at> ($(TCC) -Wall $< -o $*.exe && ./$*.exe $(ARGS)) >$*.output2 2>&1 || true
 	 <at> diff -bu $*.expect $*.output2 && rm -f $*.output2 $*.exe

 clean:
commit 8be2fbd82ef16c8f846fccb46689827ed37997eb
Author: jiang <30155751@...>
Date:   Mon Jun 30 16:40:16 2014 +0800

    Add warning

diff --git a/libtcc.c b/libtcc.c
index 7caa7c1..79a2d2c 100644
--- a/libtcc.c
+++ b/libtcc.c
 <at>  <at>  -1397,8 +1397,9  <at>  <at>  static const FlagDef warning_defs[] = {
     { offsetof(TCCState, warn_unsupported), 0, "unsupported" },
     { offsetof(TCCState, warn_write_strings), 0, "write-strings" },
     { offsetof(TCCState, warn_error), 0, "error" },
-    { offsetof(TCCState, warn_implicit_function_declaration), WD_ALL,
-      "implicit-function-declaration" },
+    { offsetof(TCCState, warn_implicit_function_declaration), WD_ALL,
"implicit-function-declaration" },
+    { offsetof(TCCState, warn_return_type), WD_ALL, "return-type" },
+    { offsetof(TCCState, warn_char_subscripts), WD_ALL, "char-subscripts" },
 };

 ST_FUNC int set_flag(TCCState *s, const FlagDef *flags, int nb_flags,
diff --git a/tcc.h b/tcc.h
index c93cedf..596b22f 100644
--- a/tcc.h
+++ b/tcc.h
 <at>  <at>  -594,6 +594,8  <at>  <at>  struct TCCState {
     int warn_error;
     int warn_none;
     int warn_implicit_function_declaration;
+    int warn_return_type;
+    int warn_char_subscripts;

     /* compile with debug symbol (and use them if error during execution) */
     int do_debug;
diff --git a/tccgen.c b/tccgen.c
index 1a89d4a..227864e 100644
--- a/tccgen.c
+++ b/tccgen.c
 <at>  <at>  -2898,42 +2898,47  <at>  <at>  static void struct_decl(CType *type, int u, int tdef)
             offset = 0;
             flexible = 0;
             while (tok != '}') {
-                parse_btype(&btype, &ad);
+                if(!parse_btype(&btype, &ad)){
+                    if (tok == ';'){
+                        skip(';');
+                        continue;
+                    }else if(tok == TOK_EOF || tok == '{')
+                        expect("specifier-qualifier-list at end of input");
+                }
                 while (1) {
-		    if (flexible)
-		        tcc_error("flexible array member '%s' not at the end of struct",
-                              get_tok_str(v, NULL));
+                    if (flexible)
+                        tcc_error("flexible array member '%s' not at the end of struct", get_tok_str(v, NULL));
                     bit_size = -1;
                     v = 0;
                     type1 = btype;
                     if (tok != ':') {
                         type_decl(&type1, &ad, &v, TYPE_DIRECT | TYPE_ABSTRACT);
-                        if (v == 0 && (type1.t & VT_BTYPE) != VT_STRUCT)
-                            expect("identifier");
-                        if (type_size(&type1, &align) < 0) {
-			    if ((a == TOK_STRUCT) && (type1.t & VT_ARRAY) && c)
-			        flexible = 1;
-			    else
-			        tcc_error("field '%s' has incomplete type",
-                                      get_tok_str(v, NULL));
+                        if (v == 0 && (type1.t & VT_BTYPE) != VT_STRUCT){
+                            tcc_warning("declaration does not declare anything");
+                            break;
+                        }if (type_size(&type1, &align) < 0) {
+                            if ((a == TOK_STRUCT) && (type1.t & VT_ARRAY))
+                                flexible = 1;
+                            else
+                                tcc_error("field '%s' has incomplete type", get_tok_str(v, NULL));
                         }
                         if ((type1.t & VT_BTYPE) == VT_FUNC ||
                             (type1.t & (VT_TYPEDEF | VT_STATIC | VT_EXTERN | VT_INLINE)))
-                            tcc_error("invalid type for '%s'", 
-                                  get_tok_str(v, NULL));
+                            tcc_error("invalid type for '%s'", get_tok_str(v, NULL));
                     }
                     if (tok == ':') {
                         next();
                         bit_size = expr_const();
                         /* XXX: handle v = 0 case for messages */
                         if (bit_size < 0)
-                            tcc_error("negative width in bit-field '%s'", 
-                                  get_tok_str(v, NULL));
+                            tcc_error("negative width in bit-field '%s'", get_tok_str(v, NULL));
                         if (v && bit_size == 0)
-                            tcc_error("zero width for bit-field '%s'", 
-                                  get_tok_str(v, NULL));
+                            tcc_error("zero width for bit-field '%s'", get_tok_str(v, NULL));
                     }
-                    size = type_size(&type1, &align);
+                    if(type1.t & VT_VLA)
+                        size = 0, align = 1;
+                    else
+                        size = type_size(&type1, &align);
                     if (ad.a.aligned) {
                         if (align < ad.a.aligned)
                             align = ad.a.aligned;
 <at>  <at>  -2946,12 +2951,9  <at>  <at>  static void struct_decl(CType *type, int u, int tdef)
                     lbit_pos = 0;
                     if (bit_size >= 0) {
                         bt = type1.t & VT_BTYPE;
-                        if (bt != VT_INT && 
-                            bt != VT_BYTE && 
-                            bt != VT_SHORT &&
-                            bt != VT_BOOL &&
-                            bt != VT_ENUM &&
-                            bt != VT_LLONG)
+                        if (bt != VT_INT && bt != VT_BYTE &&
+                            bt != VT_SHORT && bt != VT_BOOL &&
+                            bt != VT_ENUM && bt != VT_LLONG)
                             tcc_error("bitfields must have scalar type");
                         bsize = size * 8;
                         if (bit_size > bsize) {
 <at>  <at>  -3023,13 +3025,19  <at>  <at>  static void struct_decl(CType *type, int u, int tdef)
                         *ps = ss;
                         ps = &ss->next;
                     }
-                    if (tok == ';' || tok == TOK_EOF)
+                    if (tok == ';' || tok == '}')
                         break;
                     skip(',');
                 }
-                skip(';');
+                if(tok == '}'){
+                    tcc_warning("no ';' at end of struct or union");
+                    break;
+                }else
+                    skip(';');
             }
-            skip('}');
+            next();
+            if (!c && flexible)
+                tcc_error("flexible array member '%s' in otherwise empty struct", get_tok_str(v, NULL));
             /* store size and alignment */
             s->c = (c + maxalign - 1) & -maxalign; 
             s->r = maxalign;
 <at>  <at>  -4037,6 +4045,8  <at>  <at>  ST_FUNC void unary(void)
         } else if (tok == '[') {
             next();
             gexpr();
+            if(tcc_state->warn_char_subscripts && (vtop->type.t & (VT_BTYPE|VT_DEFSIGN|VT_UNSIGNED)) == VT_BYTE)
+                tcc_warning("array subscript has type 'char'");
             gen_op('+');
             indir();
             skip(']');
 <at>  <at>  -5510,6 +5520,8  <at>  <at>  static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
     Sym *flexible_array;

     flexible_array = NULL;
+    if (has_init && (type->t & VT_VLA))
+        tcc_error("Variable length array cannot be initialized");
     if ((type->t & VT_BTYPE) == VT_STRUCT) {
         Sym *field = type->ref->next;
         if (field) {
 <at>  <at>  -5907,12 +5919,13  <at>  <at>  ST_FUNC void gen_inline_functions(void)
 /* 'l' is VT_LOCAL or VT_CONST to define default storage type */
 static int decl0(int l, int is_for_loop_init)
 {
-    int v, has_init, r;
+    int v, has_init, r, imp;
     CType type, btype;
     Sym *sym;
     AttributeDef ad;

     while (1) {
+        imp = 0;
         if (!parse_btype(&btype, &ad)) {
             if (is_for_loop_init)
                 return 0;
 <at>  <at>  -5922,11 +5935,13  <at>  <at>  static int decl0(int l, int is_for_loop_init)
                 next();
                 continue;
             }
-            if (l == VT_CONST &&
-                (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3)) {
+            if (l == VT_CONST){
+                if (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3){
                 /* global asm block */
-                asm_global_instr();
-                continue;
+                    asm_global_instr();
+                    continue;
+                }
+                imp = 1;
             }
             /* special test for old K&R protos without explicit int
                type. Only accepted when defining global data */
 <at>  <at>  -5934,10 +5949,15  <at>  <at>  static int decl0(int l, int is_for_loop_init)
                 break;
             btype.t = VT_INT;
         }
-        if (((btype.t & VT_BTYPE) == VT_ENUM ||
-             (btype.t & VT_BTYPE) == VT_STRUCT) && 
-            tok == ';') {
+        if (tok == ';') {
+            int bt = btype.t & VT_BTYPE;
             /* we accept no variable after */
+            if(btype.t & (VT_CONSTANT|VT_VOLATILE))
+                tcc_warning("useless type qualifier in empty declaration.'%s'before", get_tok_str(tok, NULL));
+            if(bt != VT_STRUCT && bt != VT_ENUM)
+                tcc_warning("useless type name in empty declaration '%s'", get_tok_str(tok, NULL));
+            if((bt == VT_STRUCT) && ((btype.ref->v & ~SYM_STRUCT) >= SYM_FIRST_ANOM))
+                tcc_warning("unnamed struct/union that defines no instances");
             next();
             continue;
         }
 <at>  <at>  -6097,6 +6117,9  <at>  <at>  static int decl0(int l, int is_for_loop_init)
                     sym->a = ad.a;
                     sym->type.t |= VT_TYPEDEF;
                 } else {
+                    if(imp && (type.t & VT_BTYPE) != VT_FUNC)
+                        tcc_warning("data definition has no type or storage class: '%s'",
+                            get_tok_str(v, NULL));
                     r = 0;
                     if ((type.t & VT_BTYPE) == VT_FUNC) {
                         /* external function definition */
 <at>  <at>  -6108,8 +6131,6  <at>  <at>  static int decl0(int l, int is_for_loop_init)
                         r |= lvalue_type(type.t);
                     }
                     has_init = (tok == '=');
-                    if (has_init && (type.t & VT_VLA))
-                        tcc_error("Variable length array cannot be initialized");
                     if ((btype.t & VT_EXTERN) || ((type.t & VT_BTYPE) == VT_FUNC) ||
                         ((type.t & VT_ARRAY) && (type.t & VT_STATIC) &&
                          !has_init && l == VT_CONST && type.ref->c < 0)) {
diff --git a/tests/tests2/46_grep.c b/tests/tests2/46_grep.c
index 3123bc3..d789ee0 100644
--- a/tests/tests2/46_grep.c
+++ b/tests/tests2/46_grep.c
 <at>  <at>  -15,6 +15,7  <at>  <at> 
  * privileges were granted by DECUS.
  */
 #include <stdio.h>
+#include <ctype.h>
 #include <stdlib.h>

 /*
diff --git a/tests/tests2/64_macro_nesting.c b/tests/tests2/64_macro_nesting.c
index 44b582f..2123a75 100644
--- a/tests/tests2/64_macro_nesting.c
+++ b/tests/tests2/64_macro_nesting.c
 <at>  <at>  -1,3 +1,4  <at>  <at> 
+#include <stdio.h>
 #define CAT2(a,b) a##b
 #define CAT(a,b) CAT2(a,b)
 #define AB(x) CAT(x,y)
diff --git a/tests/tests2/70_warn_test.c b/tests/tests2/70_warn_test.c
new file mode 100644
index 0000000..5543426
--- /dev/null
+++ b/tests/tests2/70_warn_test.c
 <at>  <at>  -0,0 +1,18  <at>  <at> 
+#include <stdio.h>
+#define uint8_t unsigned char
+const struct {
+    const uint8_t code
+};
+
+a;
+b,c;
+
+int main(void)
+{
+    char c = 1;
+    int b[2];
+    b[c] = 9;
+    printf("%d\n", b[c]);
+    printf("ok\n");
+    return 0;
+}
diff --git a/tests/tests2/70_warn_test.expect b/tests/tests2/70_warn_test.expect
new file mode 100644
index 0000000..bae7348
--- /dev/null
+++ b/tests/tests2/70_warn_test.expect
 <at>  <at>  -0,0 +1,10  <at>  <at> 
+70_warn_test.c:5: warning: no ';' at end of struct or union
+70_warn_test.c:5: warning: useless type qualifier in empty declaration.';'before
+70_warn_test.c:5: warning: unnamed struct/union that defines no instances
+70_warn_test.c:7: warning: data definition has no type or storage class: 'a'
+70_warn_test.c:8: warning: data definition has no type or storage class: 'b'
+70_warn_test.c:8: warning: data definition has no type or storage class: 'c'
+70_warn_test.c:14: warning: array subscript has type 'char'
+70_warn_test.c:15: warning: array subscript has type 'char'
+9
+ok
diff --git a/tests/tests2/71_err_struct.c b/tests/tests2/71_err_struct.c
new file mode 100644
index 0000000..276c993
--- /dev/null
+++ b/tests/tests2/71_err_struct.c
 <at>  <at>  -0,0 +1,8  <at>  <at> 
+typedef struct {
+    const uint8_t* code;
+} st;
+
+int main(void)
+{
+    return 0;
+}
diff --git a/tests/tests2/71_err_struct.expect b/tests/tests2/71_err_struct.expect
new file mode 100644
index 0000000..239c174
--- /dev/null
+++ b/tests/tests2/71_err_struct.expect
 <at>  <at>  -0,0 +1  <at>  <at> 
+71_err_struct.c:2: error: ',' expected (got "*")
diff --git a/tests/tests2/72_err_struct.c b/tests/tests2/72_err_struct.c
new file mode 100644
index 0000000..e3d7afd
--- /dev/null
+++ b/tests/tests2/72_err_struct.c
 <at>  <at>  -0,0 +1,11  <at>  <at> 
+#include <stdio.h>
+typedef struct {
+    int;
+    int;
+;{};
+
+int main(void)
+{
+    printf("ok\n");
+    return 0;
+}
diff --git a/tests/tests2/72_err_struct.expect b/tests/tests2/72_err_struct.expect
new file mode 100644
index 0000000..7aaf45a
--- /dev/null
+++ b/tests/tests2/72_err_struct.expect
 <at>  <at>  -0,0 +1,3  <at>  <at> 
+72_err_struct.c:3: warning: declaration does not declare anything
+72_err_struct.c:4: warning: declaration does not declare anything
+72_err_struct.c:5: error: specifier-qualifier-list at end of input expected
diff --git a/tests/tests2/73_err_struct.c b/tests/tests2/73_err_struct.c
new file mode 100644
index 0000000..6851b76
--- /dev/null
+++ b/tests/tests2/73_err_struct.c
 <at>  <at>  -0,0 +1,11  <at>  <at> 
+#include <stdio.h>
+typedef struct {
+    int a[];
+    int b;
+};
+
+int main(void)
+{
+    printf("ok\n");
+    return 0;
+}
diff --git a/tests/tests2/73_err_struct.expect b/tests/tests2/73_err_struct.expect
new file mode 100644
index 0000000..d42b710
--- /dev/null
+++ b/tests/tests2/73_err_struct.expect
 <at>  <at>  -0,0 +1  <at>  <at> 
+73_err_struct.c:4: error: flexible array member 'a' not at the end of struct
diff --git a/tests/tests2/74_err_struct.c b/tests/tests2/74_err_struct.c
new file mode 100644
index 0000000..40c5fd4
--- /dev/null
+++ b/tests/tests2/74_err_struct.c
 <at>  <at>  -0,0 +1,10  <at>  <at> 
+#include <stdio.h>
+typedef struct {
+    int a[];
+};
+
+int main(void)
+{
+    printf("ok\n");
+    return 0;
+}
diff --git a/tests/tests2/74_err_struct.expect b/tests/tests2/74_err_struct.expect
new file mode 100644
index 0000000..2338ad0
--- /dev/null
+++ b/tests/tests2/74_err_struct.expect
 <at>  <at>  -0,0 +1  <at>  <at> 
+74_err_struct.c:4: error: flexible array member 'a' in otherwise empty struct
diff --git a/tests/tests2/Makefile b/tests/tests2/Makefile
index 64532a1..104d456 100644
--- a/tests/tests2/Makefile
+++ b/tests/tests2/Makefile
 <at>  <at>  -84,7 +84,12  <at>  <at>  TESTS =	\
  66_macro_concat_end.test \
  67_macro_concat.test \
  68_macro_concat.test \
- 69_macro_concat.test
+ 69_macro_concat.test \
+ 70_warn_test.test \
+ 71_err_struct.test \
+ 72_err_struct.test \
+ 73_err_struct.test \
+ 74_err_struct.test

 # 34_array_assignment.test -- array assignment is not in C standard

 <at>  <at>  -109,10 +114,10  <at>  <at>  all test: $(filter-out $(SKIP),$(TESTS))
 %.test: %.c %.expect
 	 <at> echo Test: $*...

-	 <at> $(TCC) -run $< $(ARGS) >$*.output 2>&1 || true
+	 <at> $(TCC) -Wall -run $< $(ARGS) >$*.output 2>&1 || true
 	 <at> diff -bu $*.expect $*.output && rm -f $*.output

-	 <at> ($(TCC) $< -o $*.exe && ./$*.exe $(ARGS)) >$*.output2 2>&1 || true
+	 <at> ($(TCC) -Wall $< -o $*.exe && ./$*.exe $(ARGS)) >$*.output2 2>&1 || true
 	 <at> diff -bu $*.expect $*.output2 && rm -f $*.output2 $*.exe

 clean:
Carlos Montiers | 30 Jun 04:10 2014
Picon

isxdigit conversion inconsistency with widechar

Hello.
Currently, in the last version (from git) of tiny c, when you use the function:

isxdigit it produce this call: isxdigit

but when you use the widechar version: iswxdigit :

iswxdigit produce this call: iswctype(d, _HEX)

Then, when you use isxdigit tiny c would be generate the call to:
_isctype(d, _HEX)

I use this little fix:
#undef        isxdigit
#undef        iswxdigit
#define        isxdigit(d)      _isctype(d, _HEX)
#define        iswxdigit(d)    iswctype(d, _HEX)


Carlos.


<div><div dir="ltr">
<div>
<div>
<div>
<div>
<div>Hello.<br>Currently, in the last version (from git) of tiny c, when you use the function:<br><br>isxdigit it produce this call: isxdigit<br><br>
</div>but when you use the widechar version: iswxdigit :<br><br>iswxdigit produce this call: iswctype(d, _HEX)<br><br>
</div>Then, when you use isxdigit tiny c would be generate the call to:<br>_isctype(d, _HEX)<br>
</div>
<br>
</div>I use this little fix:<br>#undef&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; isxdigit<br>
#undef&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; iswxdigit<br>#define&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; isxdigit(d)&nbsp;&nbsp;&nbsp; &nbsp; _isctype(d, _HEX)<br>#define&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; iswxdigit(d)&nbsp;&nbsp;&nbsp; iswctype(d, _HEX)<br><br><br>
</div>
<div>Carlos.<br><br>
</div>
<br>
</div></div>
Carlos Montiers | 30 Jun 03:38 2014
Picon

Problem using alloca

Hello.
I requesting memory from the stack using the function alloca.
I know that the use of it is discourage, but I want use it anyways.

I write a code for windows that use it and I tested ok, but only when I compile with gcc. Compiling it with tiny c fails, the program crash.
Maybe is a bug of tiny c.


This is the code:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <malloc.h>
#include <setjmp.h>

int _resetstkoflw ( void );

static jmp_buf alloca_jmp_buf;

LONG WINAPI AllocaExceptionFilter(EXCEPTION_POINTERS *ExceptionInfo)
{
   
    switch(ExceptionInfo->ExceptionRecord->ExceptionCode) {
        case STATUS_STACK_OVERFLOW :
           
            // reset the stack
            if (0 == _resetstkoflw()) {
                printf("Could not reset the stack!\n");
                _exit(1);
            }
           
            longjmp(alloca_jmp_buf, 1);
           
            break;
    }
   
    return EXCEPTION_EXECUTE_HANDLER;
}

//Alloca survive. Programmed by Carlos Montiers.

int main()
{
    void * m;
    int alloca_jmp_res;
    LPTOP_LEVEL_EXCEPTION_FILTER prev;
   
    //replace the exception filter function saving the previous
    prev = SetUnhandledExceptionFilter(AllocaExceptionFilter);
   
    alloca_jmp_res = setjmp(alloca_jmp_buf);
    if ( (0 == alloca_jmp_res) ) {
        m = alloca( INT_MAX );
       
    } else if ( (1 == alloca_jmp_res) ) {
        m = NULL;
       
    }
   
    //restore exception filter function
    SetUnhandledExceptionFilter(prev);
   
    if (! m) {
        printf("alloca Failed\n");
    }
   
    printf("Bye\n");
    return 1;

}
<div><div dir="ltr">
<div>
<div>Hello.<br>I requesting memory from the stack using the function alloca.<br>
</div>
<div>I know that the use of it is discourage, but I want use it anyways.<br><br>
</div>
<div>I write a code for windows that use it and I tested ok, but only when I compile with gcc. Compiling it with tiny c fails, the program crash.<br>
</div>
<div>Maybe is a bug of tiny c.<br>
</div>
<div>
<br><br>
</div>This is the code:<br><br>#include &lt;windows.h&gt;<br>#include &lt;stdio.h&gt;<br>#include &lt;stdlib.h&gt;<br>#include &lt;limits.h&gt;<br>#include &lt;malloc.h&gt;<br>
#include &lt;setjmp.h&gt;<br><br>int _resetstkoflw ( void );<br><br>static jmp_buf alloca_jmp_buf;<br><br>LONG WINAPI AllocaExceptionFilter(EXCEPTION_POINTERS *ExceptionInfo)<br>{<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; switch(ExceptionInfo-&gt;ExceptionRecord-&gt;ExceptionCode) {<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; case STATUS_STACK_OVERFLOW :<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // reset the stack<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if (0 == _resetstkoflw()) {<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; printf("Could not reset the stack!\n");<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; _exit(1);<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; longjmp(alloca_jmp_buf, 1);<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; return EXCEPTION_EXECUTE_HANDLER;<br>}<br><br>
</div>//Alloca survive. Programmed by Carlos Montiers.<br><br><div>int main()<br>{<br>&nbsp;&nbsp;&nbsp; void * m;<br>&nbsp;&nbsp;&nbsp; int alloca_jmp_res;<br>&nbsp;&nbsp;&nbsp; LPTOP_LEVEL_EXCEPTION_FILTER prev;<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; //replace the exception filter function saving the previous<br>&nbsp;&nbsp;&nbsp; prev = SetUnhandledExceptionFilter(AllocaExceptionFilter);<br>
&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; alloca_jmp_res = setjmp(alloca_jmp_buf);<br>&nbsp;&nbsp;&nbsp; if ( (0 == alloca_jmp_res) ) {<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; m = alloca( INT_MAX );<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; } else if ( (1 == alloca_jmp_res) ) {<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; m = NULL;<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; //restore exception filter function<br>&nbsp;&nbsp;&nbsp; SetUnhandledExceptionFilter(prev);<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; if (! m) {<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; printf("alloca Failed\n");<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; printf("Bye\n");<br>&nbsp;&nbsp;&nbsp; return 1;<br><br>}<br>
</div>
</div></div>
YX Hao | 29 Jun 14:31 2014

Is it a bug or something else?

Hi there,

It is a crash of the complied program, when I played TCC with a small library "http parser"
(https://github.com/joyent/http-parser). And GCC works correctly.

This is out of my knowledge, and I will describe it as clear as possible.

It is about the "test.c".

After some debugging, I find the crash line is calling "test_multiple3()" (L: 3366, L: 3558).

---------------------------------------
  int i, j, k;
  ......
  for (i = 0; i < request_count; i++) {
    if (!requests[i].should_keep_alive) continue;
    for (j = 0; j < request_count; j++) {
      if (!requests[j].should_keep_alive) continue;
      for (k = 0; k < request_count; k++) {
        test_multiple3(&requests[i], &requests[j], &requests[k]);
      }
    }
  }
---------------------------------------

According to a debugger, the program uses a " DWORD PTR SS:[EBP-4]" to store the counters. For example "(i =
0; i < request_count; i++)":

---------------------------------------
CPU Disasm
Address   Hex dump          Command                                  Comments
004055C9  |.  B8 00000000   MOV EAX,0
004055CE  |.  8945 FC       MOV DWORD PTR SS:[EBP-4],EAX             ; i = 0;
004055D1  |>  8B45 FC       MOV EAX,DWORD PTR SS:[EBP-4]
004055D4  |.  8B4D EC       MOV ECX,DWORD PTR SS:[EBP-14]
004055D7  |.  39C8          CMP EAX,ECX
004055D9  |.  0F8D CD000000 JGE 004056AC                             ; if (i > request_count) true;
004055DF  |.  E9 0B000000   JMP 004055EF
004055E4  |>  8B45 FC       MOV EAX,DWORD PTR SS:[EBP-4]
004055E7  |.  89C1          MOV ECX,EAX
004055E9  |.  40            INC EAX                                  ; i++;
004055EA  |.  8945 FC       MOV DWORD PTR SS:[EBP-4],EAX
004055ED  |.^ EB E2         JMP SHORT 004055D1 ---------------------------------------

Entering "test_multiple3()", it stores "EBP" by pushing to stack, and then some operations flush the
stored data, as the routine falls to a label "test". Stange!

---------------------------------------
  if (read != 0) {
    print_error(total, read);
    abort();
  }

test:

  if (message_count != num_messages) {
    fprintf(stderr, "\n\n*** Parser didn't see 3 messages only %d *** \n", num_messages);
    abort();
  }
---------------------------------------

CPU Disasm
Address   Hex dump          Command                                  Comments
0040483A  |> \B8 00000000   MOV EAX,0
0040483F  |.  50            PUSH EAX                                 ; /Arg2 => 0
00404840  |.  B8 00000000   MOV EAX,0                                ; |
00404845  |.  50            PUSH EAX                                 ; |Arg1 => 0
00404846  |.  E8 BED6FFFF   CALL 00401F09                            ; \t.00401F09
0040484B  |.  83C4 08       ADD ESP,8
0040484E  |.  8945 E4       MOV DWORD PTR SS:[LOCAL.7],EAX
00404851  |.  8B45 E4       MOV EAX,DWORD PTR SS:[LOCAL.7]
00404854  |.  83F8 00       CMP EAX,0
00404857  |.  0F84 15000000 JE 00404872
0040485D  |.  8B45 E4       MOV EAX,DWORD PTR SS:[LOCAL.7]
00404860  |.  50            PUSH EAX
00404861  |.  8B45 EC       MOV EAX,DWORD PTR SS:[LOCAL.5]
00404864  |.  50            PUSH EAX
00404865  |.  E8 CAE1FFFF   CALL 00402A34
0040486A  |.  83C4 08       ADD ESP,8
0040486D  |.  E8 96950000   CALL <JMP.&msvcrt.abort>                 ; [MSVCRT.abort
00404872  |>  8965 00       MOV DWORD PTR SS:[LOCAL.0],ESP           ;???
00404875  |>  8B65 00       MOV ESP,DWORD PTR SS:[LOCAL.0]           ;???
00404878  |.  8B45 FC       MOV EAX,DWORD PTR SS:[LOCAL.1]
0040487B  |.  8B0D 44607E00 MOV ECX,DWORD PTR DS:[7E6044]
00404881  |.  39C8          CMP EAX,ECX
00404883  |.  0F84 24000000 JE 004048AD
---------------------------------------

Why are the "???" marked commands there? They flushed the "EBP".

---------------
TCC: latest mob
OS: windows

Attachment is my modified "wassert()" in case of you need it.

Regards,
YX
Attachment (wassert.c): application/octet-stream, 2641 bytes
Hi there,

It is a crash of the complied program, when I played TCC with a small library "http parser"
(https://github.com/joyent/http-parser). And GCC works correctly.

This is out of my knowledge, and I will describe it as clear as possible.

It is about the "test.c".

After some debugging, I find the crash line is calling "test_multiple3()" (L: 3366, L: 3558).

---------------------------------------
  int i, j, k;
  ......
  for (i = 0; i < request_count; i++) {
    if (!requests[i].should_keep_alive) continue;
    for (j = 0; j < request_count; j++) {
      if (!requests[j].should_keep_alive) continue;
      for (k = 0; k < request_count; k++) {
        test_multiple3(&requests[i], &requests[j], &requests[k]);
      }
    }
  }
---------------------------------------

According to a debugger, the program uses a " DWORD PTR SS:[EBP-4]" to store the counters. For example "(i =
0; i < request_count; i++)":

---------------------------------------
CPU Disasm
Address   Hex dump          Command                                  Comments
004055C9  |.  B8 00000000   MOV EAX,0
004055CE  |.  8945 FC       MOV DWORD PTR SS:[EBP-4],EAX             ; i = 0;
004055D1  |>  8B45 FC       MOV EAX,DWORD PTR SS:[EBP-4]
004055D4  |.  8B4D EC       MOV ECX,DWORD PTR SS:[EBP-14]
004055D7  |.  39C8          CMP EAX,ECX
004055D9  |.  0F8D CD000000 JGE 004056AC                             ; if (i > request_count) true;
004055DF  |.  E9 0B000000   JMP 004055EF
004055E4  |>  8B45 FC       MOV EAX,DWORD PTR SS:[EBP-4]
004055E7  |.  89C1          MOV ECX,EAX
004055E9  |.  40            INC EAX                                  ; i++;
004055EA  |.  8945 FC       MOV DWORD PTR SS:[EBP-4],EAX
004055ED  |.^ EB E2         JMP SHORT 004055D1 ---------------------------------------

Entering "test_multiple3()", it stores "EBP" by pushing to stack, and then some operations flush the
stored data, as the routine falls to a label "test". Stange!

---------------------------------------
  if (read != 0) {
    print_error(total, read);
    abort();
  }

test:

  if (message_count != num_messages) {
    fprintf(stderr, "\n\n*** Parser didn't see 3 messages only %d *** \n", num_messages);
    abort();
  }
---------------------------------------

CPU Disasm
Address   Hex dump          Command                                  Comments
0040483A  |> \B8 00000000   MOV EAX,0
0040483F  |.  50            PUSH EAX                                 ; /Arg2 => 0
00404840  |.  B8 00000000   MOV EAX,0                                ; |
00404845  |.  50            PUSH EAX                                 ; |Arg1 => 0
00404846  |.  E8 BED6FFFF   CALL 00401F09                            ; \t.00401F09
0040484B  |.  83C4 08       ADD ESP,8
0040484E  |.  8945 E4       MOV DWORD PTR SS:[LOCAL.7],EAX
00404851  |.  8B45 E4       MOV EAX,DWORD PTR SS:[LOCAL.7]
00404854  |.  83F8 00       CMP EAX,0
00404857  |.  0F84 15000000 JE 00404872
0040485D  |.  8B45 E4       MOV EAX,DWORD PTR SS:[LOCAL.7]
00404860  |.  50            PUSH EAX
00404861  |.  8B45 EC       MOV EAX,DWORD PTR SS:[LOCAL.5]
00404864  |.  50            PUSH EAX
00404865  |.  E8 CAE1FFFF   CALL 00402A34
0040486A  |.  83C4 08       ADD ESP,8
0040486D  |.  E8 96950000   CALL <JMP.&msvcrt.abort>                 ; [MSVCRT.abort
00404872  |>  8965 00       MOV DWORD PTR SS:[LOCAL.0],ESP           ;???
00404875  |>  8B65 00       MOV ESP,DWORD PTR SS:[LOCAL.0]           ;???
00404878  |.  8B45 FC       MOV EAX,DWORD PTR SS:[LOCAL.1]
0040487B  |.  8B0D 44607E00 MOV ECX,DWORD PTR DS:[7E6044]
00404881  |.  39C8          CMP EAX,ECX
00404883  |.  0F84 24000000 JE 004048AD
---------------------------------------

Why are the "???" marked commands there? They flushed the "EBP".

---------------
TCC: latest mob
OS: windows

Attachment is my modified "wassert()" in case of you need it.

Regards,
YX
大熊猫 | 29 Jun 06:32 2014

Request push

Thank you Thomas, I hope you agree with my request.
I'll change it a warning message, and then add some tests, along with a push up!

"Not follow GCC blindly."
I was too blind! I'm not very familiar with C99, but I think there is a lot to learn from GCC place!

jiang

> I'll push a patch.

I'll take this as "I'd like to commit/push this patch, what do you think of
it?"

The patch itself looks good but it would be better if you add a testcase that
would fail without your patch. You already have a program that exhibit the
bug, you just need to transform it so that it returns 0 if tcc behaves
correctly and 1 else.

I also have a minor nitpick: "\'%s\' may not appear in parameter list",
get_tok_str(var, NULL)

That is, use single quote and put the symbol at the beginning of the sentence,
that would avoid the colon.

If you fix those (testcase is very important), you are welcome to commit/push
your patch.

In the future it would be better if you provide a patch as given by git
format-patch. This way we'd have the message that you intend to use in your
commit. And finally please try to find a title more specific than "Request push".
Try to find a title that explain what you want to fix. In this case you could
choose "Fix identifier list parsing". If you can refer to a section in C99
standard in your message and your commit message that's the icing on the cake
but we don't do it systematically ourselves. However I think it is good
practice as GCC can sometimes deviate from the standard and we should try to
adhere to the standard, not follow GCC blindly.

Thanks for your patch.

Best regards,

Thomas
<div><div>
<p dir="ltr"><span class="hps">Thank</span> <span class="hps">you</span> <span class="hps">Thomas,</span> <span class="hps">I</span> <span class="hps">hope</span> <span class="hps">you</span> <span class="hps">agree with</span> <span class="hps">my request.</span> <br><span class="hps">I'll</span> <span class="hps">change it</span> <span class="hps">a warning message</span><span>, and then</span> <span class="hps">add some</span> <span class="hps">tests</span><span>,</span> <span class="hps">along with</span> <span class="hps">a push</span> <span class="hps">up</span><span>!</span> <br><br><span class="hps">"Not follow GCC blindly."</span> <br><span class="hps">I was</span> <span class="hps">too blind</span><span>!</span> <span class="hps">I'm</span> <span class="hps">not very familiar with</span> <span class="hps">C99</span><span>,</span> <span class="hps">but</span> <span class="hps">I think</span> <span class="hps">there is</span> <span class="hps">a lot to</span> <span class="hps">learn from</span> <span class="hps">GCC</span> <span class="hps">place</span><span>!</span></p>
<p dir="ltr"><span>jiang</span></p>
<blockquote dir="ltr">
<div>&gt; I'll push a patch.<br><br>I'll take this as "I'd like to commit/push this patch, what do you think of <br>it?"<br><br>The patch itself looks good but it would be better if you add a testcase that <br>would fail without your patch. You already have a program that exhibit the <br>bug, you just need to transform it so that it returns 0 if tcc behaves <br>correctly and 1 else.<br><br>I also have a minor nitpick: "\'%s\' may not appear in parameter list", <br>get_tok_str(var, NULL)<br><br>That is, use single quote and put the symbol at the beginning of the sentence, <br>that would avoid the colon.<br><br>If you fix those (testcase is very important), you are welcome to commit/push <br>your patch.<br><br>In the future it would be better if you provide a patch as given by git <br>format-patch. This way we'd have the message that you intend to use in your <br>commit. And finally please try to find a title more specific than "Request push". <br>Try to find a title that explain what you want to fix. In this case you could <br>choose "Fix identifier list parsing". If you can refer to a section in C99 <br>standard in your message and your commit message that's the icing on the cake <br>but we don't do it systematically ourselves. However I think it is good <br>practice as GCC can sometimes deviate from the standard and we should try to <br>adhere to the standard, not follow GCC blindly.<br><br>Thanks for your patch.<br><br>Best regards,<br><br>Thomas</div>
</blockquote>
</div></div>
YX Hao | 28 Jun 22:56 2014

To document "-l" option clearer

Hi,

"Usage: tcc [options...] [-o outfile] [-c] infile(s)..."

Is "-l" option must follow the infile(s)? Yes! I tried different positions. Before, "undefined symbol"
error; behind, ok. Because of I had forgotten it. Usually, a library is appended by reviewing.

I searched and found the reason out. A linker only adds the undefined symbols that the (previous) existing
program codes need.

It is a trick that experienced one knows (usage format from gcc). If the usage is organized in a clearer way,
it may be better. How to do it simple and clear?

Explicit Is Better Than Implicit. :p

Regards,
YX


Gmane