Thread: This compiles!..but segfaults. (Defining main as a functor)

  1. #1
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657

    This compiles!..but segfaults. (Defining main as a functor)

    Though I can't see any application for this...
    Code:
    class Foo
    {
    public:
        int operator()(){return 0;}
    };
    Foo main;
    I was quite surprised to see this compile without a single warning.
    But unfortunately...it gives a segfault as soon as it begins running (and gdb downright refuses to help me track it).
    Any idea of something...say.. calling conventions... that can be modified to make this work ?

    P.S : I have evidence(in the form of putting a print statement in Foo's ctor) that "main" is created before the segfault occurs.

  2. #2
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    There are a couple reasons that won't work. But consider this -- the runtime is a binary component, already compiled. How could it possibly know that your main() is actually a functor? It hasn't seen any of your declarations and could not possibly have a clue about it.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  3. #3
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657
    True... this
    Code:
    int main;
    compiles too.
    Shouldn't this be classified as a compiler bug ?

  4. #4
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Maybe. The compiler can't outright forbid it, because people use compilers to write very non-standard bits of code from time to time. The linker just wants to bind some symbol called "main" -- you provided one, so the linker has nothing to complain about. Unfortunately the symbol is actually a piece of data, not a function, so it pops.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  5. #5
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    This will also compile
    Code:
    int main[] = { 1, 2, 3, };
    but it doesn't mean that it will in any shape or form run (well it might, if you list actual opcodes in the array).

    The linker just doesn't care about anything apart from symbol names. It looks for something called 'main' to call, and as soon as it sees such a symbol, it's happy.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  6. #6
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by manasij7479
    Shouldn't this be classified as a compiler bug ?
    What do you consider to be the correct behaviour? My opinion is that the behaviour is undefined whether you define main as a functor or as a variable in the global namespace, so no matter what happens, there is no compiler bug.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  7. #7
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657
    Quote Originally Posted by Salem View Post
    This will also compile
    (well it might, if you list actual opcodes in the array).
    Seems like a good thing to try out.
    What would be the numbers for putting 1 in eax , 0 in ebx and invoking the kernel for exit be ?
    Last edited by manasij7479; 12-05-2011 at 10:14 PM.

  8. #8
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by manasij7479 View Post
    > (well it might, if you list actual opcodes in the array)
    Seems like a good thing to try out.

    What would be the numbers for putting 1 into eax , 0 in ebx and invoking the kernel for exit be ?
    It wouldn't work. Such an object would be data, and would be placed in the data segment, which is non-executable. It would crash the moment it jumped to it.

    EDIT: But if you do want to try it, you can use:

    Code:
    unsigned char main[] = { 0xBB, 0x00, 0x00, 0x00, 0x00, 0xB8, 0x01, 0x00, 0x00, 0x00, 0xCD, 0x80 };
    Last edited by brewbuck; 12-05-2011 at 10:15 PM.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  9. #9
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657
    Doesn't crash..!!
    ...and the shell shows that it really returns 0.

    Does the array get placed into the code segment or is it just an accident ?

    Gah.. I can't make sense of the assembly ...but it seems to be store as data... and not as code...right?..
    Code:
        .file    "a.c"
        .globl    main
        .data
        .type    main, @object
        .size    main, 12
    main:
        .byte    -69
        .byte    0
        .byte    0
        .byte    0
        .byte    0
        .byte    -72
        .byte    1
        .byte    0
        .byte    0
        .byte    0
        .byte    -51
        .byte    -128
        .ident    "GCC: (GNU) 4.6.2 20111027 (Red Hat 4.6.2-1)"
        .section    .note.GNU-stack,"",@progbits
    Last edited by manasij7479; 12-05-2011 at 10:36 PM.

  10. #10
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Yeah, it's going into the data segment. It's strange if it's really executing. If you change 0xBB, 0x00 to 0xBB, 0x01 does the return code change from 0 to 1?
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  11. #11
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657
    Quote Originally Posted by brewbuck View Post
    Yeah, it's going into the data segment. It's strange if it's really executing. If you change 0xBB, 0x00 to 0xBB, 0x01 does the return code change from 0 to 1?
    Yes ..it does.
    [manasij7479@manasijn code]$ gcc a.c -o a
    [manasij7479@manasijn code]$ ./a
    [manasij7479@manasijn code]$ echo $?
    1
    [manasij7479@manasijn code]$

  12. #12
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Apart from being the name of the entry point for a program (assuming a hosted implementation) the name main is not reserved. It is therefore possible to have member functions, enumerations, and even classes named main. That is why your code compiles.

    However, it is not possible to have an object (or a variable) and a function with the same name declared in a compilation unit, unless they are in different namespaces.

    A static object (even one named main) will always be constructed before the main() function is called. However, the operator() for such an object will not be called unless you write code to call it. If the object is named main, that is not possible from the main() function: you will need to write another function, to be called by the main() function, that - in turn - calls operator() for the object named main.

    There is nothing wrong with the fact your code compiles. Even though the code is meaningless. The linker, when it sees an object named main, probably matches it incorrectly with the main() function called during program startup. Hence, during program startup, it tries to treat the object itself (not the operator()) like a function. Since the object is not a function, trying to call it randomly access some area of memory. Hence your segmentation fault.

    Strictly speaking, your code does not exhibit undefined behaviour. However, it is an edge case that fails basic sanity checks. The standard committee probably never anticipated it or, if they did, assumed no programmer would be stupid or insane enough to attempt such code. Which goes to show: no standard is fool proof.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  13. #13
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by grumpy
    Apart from being the name of the entry point for a program (assuming a hosted implementation) the name main is not reserved. It is therefore possible to have member functions, enumerations, and even classes named main.
    Quote Originally Posted by grumpy
    Strictly speaking, your code does not exhibit undefined behaviour.
    The standard states:
    Quote Originally Posted by C++11 Clause 3.6.1 Paragraph 1a
    A program shall contain a global function called main, which is the designated start of the program.
    I posit that this means that indeed the name main can be used to name enumerations, classes, etc, except in the global namespace (and std, but that's another matter). This is why my opinion is that the behaviour is undefined.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  14. #14
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657
    Quote Originally Posted by grumpy View Post
    ... it tries to treat the object itself (not the operator()) like a function. Since the object is not a function, trying to call it randomly access some area of memory. Hence your segmentation fault.
    I used to know that calling the symbol associated with an object points to the () operator function.. but it is (now) clear that it is not the case.
    Last edited by manasij7479; 12-06-2011 at 01:02 AM.

  15. #15
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657
    Quote Originally Posted by laserlight View Post
    The standard states...
    I think that also means that whenever a compiler finds something called main in global scope...it'll take it as a function...(evident from executing the array above ...giving consistent results with the 2 compilers I tried)

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. help with a functor
    By nullspace in forum C++ Programming
    Replies: 1
    Last Post: 08-25-2011, 11:12 PM
  2. Defining functions below int main() - Best practices?
    By Programmer_P in forum C++ Programming
    Replies: 17
    Last Post: 12-09-2009, 02:08 PM
  3. Defining functions outside main - problem
    By helpmeout in forum C++ Programming
    Replies: 16
    Last Post: 03-22-2006, 02:06 AM
  4. Defining main function!
    By alvifarooq in forum C++ Programming
    Replies: 8
    Last Post: 09-19-2004, 02:00 PM