2

In C, main() function takes only zero or two arguments.If we provide two arguments,then first argument must be int type.

int main(int argc, char *argv[])

But, I saw following code, when browsing OpenBSD.

int main(void *framep){} 

Is it valid in C?

GCC compiler gives following warnings:

prog.c:3:5: warning: first argument of 'main' should be 'int' [-Wmain]
 int main(void *p) {
     ^~~~
prog.c:3:5: warning: 'main' takes only zero or two arguments [-Wmain]

What is the purpose of it?

msc
  • 33,420
  • 29
  • 119
  • 214
  • It is unused anyway. – CinCout Nov 24 '17 at 06:52
  • 1
    See: [C11 Standard §5.1.2.2.1 Program startup (draft n1570)](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf). See also: [See What should main() return in C and C++?](http://stackoverflow.com/questions/204476/) – David C. Rankin Nov 24 '17 at 07:00

4 Answers4

3

On Linux, during linkage, the library function _start is to be linked to a function main() that is expected to be present in your code.

Then traditionally your main is called by _start with int argc, char *argv[], the number of arguments (including program name) and the actual arguments (plus a trailing NULL).

However on some other implementations, there might be no need to call main this way, or for performance reasons call it with a reduced number of arguments, following a different format.

main() is the starting function of our programs and is passed argc, argv, but after all, it's only a C function and may be passed something else, as long as the convention, on that implementation, is known and accepted.

Déjà vu
  • 28,223
  • 6
  • 72
  • 100
  • Yeah for example VS [accepts 3 arguments](https://msdn.microsoft.com/en-us/library/k104fy6h.aspx). Good answer! ;) – gsamaras Nov 24 '17 at 07:06
2

Oups, this is not a normal program but a kernel, so the normal rules for main do not really apply. When the program starts, no environment exists to pass argument values, and the return value of main will not be used either because when the kernel exits, nothing else exists. One comment says that the definition was modified to only cope with gcc requirements:

return int, so gcc -Werror won't complain

That is explicit in N1256 draft for C11 at 5.1.2.1 Freestanding environment :

In a freestanding environment (in which C program execution may take place without any benefit of an operating system), the name and type of the function called at program startup are implementation-defined. Any library facilities available to a freestanding program, other than the minimal set required by clause 4, are implementation-defined.

The effect of program termination in a freestanding environment is implementationdefined.

As at kernel startup no OS still exists, so it actually runs in a freestanding environment. That probably means that is also needs to be compiled with special flags...

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
1

In the link you provide, framep is not used inside the main function.

And no, it's not standard.

GCC issues warnings as you saw already, but it's worth noting that clang throws an error:

error: first parameter of 'main' (argument count) must be of type 'int'
int main(void *framep){} 
    ^
1 error generated.

From the Standard:

5.1.2.2.1 Program startup 1

The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters: int main(void) { /* ... */ }

or

with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):

int main(int argc, char *argv[]) { /* ...*/ }

or equivalent) or in some other implementation-defined manner.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • Beware, 5.1.2.2.1 is a part of 5.1.2.2 Hosted environment, when a kernel runs in a freestanding environment because no operating system exists before the kernel itself. – Serge Ballesta Nov 24 '17 at 07:14
  • You start off with a non-sequitur. It's implementation-defined what the parameters of `main` should be in freestanding; it might be a mistake to put `(void)` instead – M.M Nov 24 '17 at 07:21
  • Right @M.M thank you, corrected! Serge Ballesta, I know see your answer, nice! – gsamaras Nov 24 '17 at 07:26
  • What is the meaning of "or in some other implementation-defined manner" at the end of 5.1.2.2.1? – lockcmpxchg8b Nov 24 '17 at 07:43
0

You compiled it with g++ to get those errors, if you compile it with gcc you don't get them.

$ gcc test.c

$ g++ test.c
test.c:3:5: warning: first argument of 'int main(void*)' should be 'int' [-Wmain]
 int main(void *framep)
     ^~~~
test.c:3:5: warning: 'int main(void*)' takes only zero or two arguments [-Wmain]

This is important because C doesn't consider the argument types (or number!) to be part of the type of the function (whereas C++ does). There are various reasons, among them being that in C, the caller cleans up the arguments, so if he specifies too many, he also cleans them up. In C++, the callee cleans up arguments, so if he cleans up the wrong number, you end up with a corrupted stack.

On to why you might choose to use int main(void *framep): In the calling convention for C, the arguments are pushed onto the stack, and then the call is made, which places the return address next. The callee will then typically push the old value of EBP, then move the stack pointer into EBP as the "base pointer" for the new stack frame. Then the stack pointer is moved to allocate space of any automatic (local) variables in the callee. I.e., the stack looks like this:

Arg n
Arg n-1
...
Arg 1
Return Addr
Old EBP
Callee locals

Now assume that we'd like to inspect the return address for our function, or read the prior frame pointer (Old EBP) that was pushed. If we were writing in assembly, we'd just dereference relative to the current frame pointer (EBP). But we're writing in C. One way to get a reference would be to take the address of the first argument. That is, &framep, which is the place where Arg1 lives on the stack. Thus (&framep)[-2] should be a void * pointing to the stored prior frame pointer (Old EBP).

(Note: I am assuming Intel architecture, where all pushes to the stack are extended to pointer size by the hardware.)

lockcmpxchg8b
  • 2,205
  • 10
  • 16