main() never executing. an academic exercise

Frequently I have heard the question, "Can you write a program without main()?" The answer is yes... if you either delve into assembler or do some command line options to the compiler to redefine the start point and such. Well, as an academic exercise, I wrote the following code. It executes any code you like without ever executing main(). You can use the program's arguments because they're global and you can reliably return a value. The following code is tested and works. This therefore is a fully working program. You can run whatever code you like inside it, return a value for the program, and take command line arguments.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <cstdlib>
#include <cstdio>
#include <iostream>

using namespace std;

int before_main(void);

int the_actual_program(void);

 /* we need a wrapper function to set it's output to something. if we don't,
    we can't let the program have a return value. the reason why is we have to 
    call exit() as part of a function to give this a return value AND bypass
    main(). exit needs to be called with the program's return value. we need
    to assign a global value by a function's return value... that function will
    execute before main(). this function executes another functions that IS the
    actual program bypassing it's own return statement. it never returns, but it
    IS called and executes. and that's our goal.
     */
int before_main(void)
{
    exit(the_actual_program());
    return 0; // this never actually runs. it's here so we can assign before_main()'s value to something
}

int foo_bar = before_main(); // here we make a global assignment which must occur before main()

int main(int argc, char *argv[])
{
    puts("We're inside of Main()"); // this never runs.
    getchar(); // this is a pause so tha twhen you run the program, it'll be proven this enver pops up.
    return foo_bar; // we put this here so the compiler doesn't complain we never use it.
}

int the_actual_program(void)
{
    puts("We're NOT inside of Main()"); // this WILL run
    getchar(); // a pause.
    
    return 0; // yes, this is the program's return value due to exit() in the wrapper function
}


Nota Bene: I know I posted this in another part of the forum by me. There is simply more than one place where it is appropriate. It'll simply get more eyes this way, perhaps answer more questions as well.

Also, the only use I can conceive of this is HOPING that the inline assembler sets the code before main() with higher authority during pre main() assignments and this would allow the programmer to do things s/he otherwise couldn't or that would result in quick segfaults.
This is pretty much the same idea as having a global variable of some class - the constructor will get called before main - you do what you want in the constructor and exit the program.
(Technically you still have a function called main though even if it is not called )
HOLY SHIT HAX

It's pretty cool; but alternatively you could write your own linker script:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ENTRY (some_entry_function)

SECTIONS{
    . = 0x00100000;

    .text :{
        *(.text)
    }

    .rodata ALIGN (0x1000) : {
        *(.rodata)
    }

    .data ALIGN (0x1000) : {
        *(.data)
    }

    .bss : {
        sbss = .;
        *(COMMON)
        *(.bss)
        ebss = .;
    }
}


You would need to compile with the -c switch on GCC and link with ld -T linkerscript.ld... If it doesn't work, don't ask me; I copied almost all of it off the internet ages ago for this very purpose. It was literally the ENTRY part that I added; and the rest I copied exactly.
O_O wow
Also, the only use I can conceive of this is HOPING that the inline assembler sets the code before main() with higher authority during pre main() assignments and this would allow the programmer to do things s/he otherwise couldn't or that would result in quick segfaults.

Oooooh...
closed account (S6k9GNh0)
C and C++ requires that int main at least be implemented or else it's considered undefined which errors out. I recently started a bit of assembly (of which my head exploded four times) and learned that often or not, assembly may be used to start a program by then calling a C or C++ function or the assembly be executed by C or C++ routine functions though this requires int main() be at least implemented and something to be executed originally through C or C++.
Nope. With the linker script above plus some assembly you can easily bypass main():

1
2
3
4
5
6
7
8
/* Define a stack of 16K */
.set  STACKSIZE,    0x4000
.comm stack,        STACKSIZE,  32

entry_point:
    mov $(stack + STACKSIZE),   %esp /* Set up the stack */
    
    call    not_main /* self explanatory */

Compile in GAS with "as -o entry.o entry.s"

Compile this:
1
2
3
4
5
#include <stdio.h>

void not_main(void) {
    printf("We're not in main because there is no main :)")
}

with "gcc -o not_main.o notmain.c -c"; -c is compile-only; it makes object files and doesn't link them

Then link all the above with "ld -T ldscript.ld -o no_main_function entry.o not_main.o"
Run no_main_function and see if it works.

It should.
It was a bit of boredom. Like I noted in there, the only possible use I can think of is the fact that the inline assembler file is assembled into the program after linking has segments and permission levels set up. Or rather, certain inline assembler instructions can only have a certain level of authority when the operating system sets it to be so to prevent a program from doing things like screwing up the OS's stack. This also has something to do with segmenting. I've yet to look at the assembler output though. This might cause the code to be in a different segment because it's run outside of main() to set things up. This would allow you to, possibly, if this convoluted idea works, peak at things in memory and such that you couldnt' otherwise due to segmentation. As an example. Finally, yeah, this really IS nonsense. But it's sublime hilarious nonsense that I did because I was bored. FINALLY, I forgot to note that I used wxDev-C++, which I do beleive uses MinGW.
It's still a cool little cheat though.
There is a way to write a program without main()... how do you suggest that operating systems are developed? The Linux kernel has no main().

I posted this in a different part of the forum and most people didn't like me at all for... jeeze, it's a practice in boredom. I mean, seriously, they had to have done a similar piece in their life multiple times. Something totally useless, but it relieved their boredom.
That's not stricly true; the main() function is in init/main.c I think. But I don't know if that's compiled with the rest of the kernel; I'd assume so because it's a monolithic kernel... but idk.
The function in init/main.c is not named "main()". It is called start_kernel, although this isn't even the entrypoint to
the kernel. The real entrypoint to the kernel is architecture specific, and for i386 it is in bld/arch/i386/head.S.
The "function" is startup_32, and I quote it because it is simply an assembler label.

startup_32 has to replace the "standard" crt0 (which is what "mandates" that every C/C++ program have a function
named main, because it jumps to that symbol) because the first thing that must be done is to initialize the processor's
IDT, LDT, and GDT and initialize the MMU.

Oh. I haven't read anything in init; I've glanced at panic, printk and semaphore; and some of the keyboard stuff, but nothing else really.
Topic archived. No new replies allowed.