Debug an AVR assembler project at source level - avr-gcc

I want to debug a code of a plain assembler project for the ATmega2560. I want to use the Microchip debugger for that purpose. The goal is to get source level debugging with all variables and functions, breakpoints etc.
I managed to create a C "stub" file with a main() function that calls the assembler code.
extern int foo(int a);
int main(void)
{
int a = 0;
while (1)
{
a = foo(a);
}
}
But the assembler code also includes the interrupt vector table including the reset vector.
.extern main
.section .vectors
.global RESET_
RESET_: jmp WARM_0
.section code
.global foo
foo:
ret
WARM_0:
call main
ret
.end
Now I want to run the code from the label RESET_. The linker has placed the code in the section .vectors. That's okay so far, but the vector table from the GCC startup files are in that section before the vector table in my code. The GCC startup code must be removed to get my vector to the address 0. Therefore I activate the linker option "Do not use standard start files (-nostartfiles)". That gives the desired result: The reset vector has a jump to RESET_.
But this has the important side effect, that the debugger is not able to debug at source code level anymore. The C file with the main() function is still linked. But the source level debug support is lost.
How can I debug a plan assembler project with Microchip debugger/simulator for AVR8?
Remark: The code in the assembler file is not sufficient for a valid program. It's reduced to get a minimal example that should work in the Microchip environment.

Related

Overriding Weak ISR Handler from Assembly to C++ doesn't compile any code

I am writing code for embedded programming on an ARM 32-bit based SAM D51 microprocessor using the IDE SEGGER Studio. I'm new to embedded programming and am writing my first interrupt.
The vector table with a dummy handler is written in ARM assembly, here's one example, the following code is auto-generated on project creation but marked as weak so it can be overridden in C/C++
ldr r0, =__stack_end__
mov sp, r0
bl SystemInit
b _start
.thumb_func
.weak SystemInit
SystemInit:
bx lr
Anywhere I read online says to simply add a C/C++ function with identical name and it's magically used by the linker because it's not marked as weak. Below is where I'm overriding it.
void SystemInit()
{
printf("Here");
}
However the debugger states that it can't place a breakpoint there because there's no code and in the disassembler it reveals that the entire function has been made into a comment with no code.
I've tried other functions including many of the handler functions but they all do the exact same thing and I have no idea why.
I've even tried forward declaring a weak function and then overriding it or marking the function as volatile. Here is my latest attempt but with the same result:
extern void __attribute__((weak)) SystemInit();
void SystemInit()
{
printf("Here");
}
and another attempt
volatile void SystemInit()
{
printf("Here");
}
They all end in no code being generated for the function and it appearing as a comment in disassembly.
Identifiers in C++ source files have their names mangled. The resulting linker function name is different from SystemInit. To stop C++ compiler from mangling the function name, declare the function with extern "C". That way generated function name will match the function name expected by the linker.

arm-none-eabi-g++ does not correctly handle weak alias with -flto

I am programming an STM32F413 microcontroller with SystemWorkbench 4 stm32. The Interrupt vectors are defined in an assembly startup file as weak aliases like follows:
.weak TIM1_UP_TIM10_IRQHandler
.thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler
And referenced in an object like follows:
g_pfnVectors:
.word _estack
.word Reset_Handler
.word NMI_Handler
.....
.word TIM1_UP_TIM10_IRQHandler
.....
So that the g_pfnVectors is a list of the addresses of the IRQ Handler functions. They are declared as weak aliases, so that if they are not defined by the user, the default handler is used.
I have defined the handler like this:
extern "C" {
void TIM1_UP_TIM10_IRQHandler() {
if (SU_TIM->SR & TIM_SR_UIF) {
SU_TIM->SR &= ~TIM_SR_UIF;
...
}
}
}
This works fine with the normal compiler optimization flags, however I wanted to try if I get smaller and possibly faster code with -flto (mainly for trying it, don't really needed it). But when compiling with -flto, g++ ignores my implementation of the handler and just uses the default handler, my handler isn't in the code at all.
So I tried to force g++ to include the function by adding __attribute__((used)) to the function definition, but it was still not compiled. However if I give it another name, then it was included in the binary. Also if I remove the weak alias and just have a reference to the handler in the startup file, it works too.
So somehow the weak aliases don't work with g++ link time optimization. Maybe someone can tell me what the error is and what I'm doing wrong here.
EDIT:
I have looked at which symbols are created with nm on the resulting .elf File, and the TIM1_UP_TIM10_IRQHandler is exported as a weak symbol with the address of the DefaultHandler. However when viewing just the .o file from the compilation unit containing the TIM1_UP_TIM10_IRQHandler function, it is exported as a symbol in the text section (T). So the linker, for some reason, chooses to keep the weak symbol, even though there is a strong symbol with the same name.
I think you should inform the compiler that it the interrupt __attribute__ ((interrupt ("IRQ"))), which is not needed normally as F4 has the stack by default aligned to 8 by the hardware.
If it does not help the workaround is to have a function pointer assigned with the handler, which will prevent it from discarding (if the pointer itself will not be discarded itself - check with your debugger).
The last resort - change the .s file with the vector table definitions
For those looking for this, still, there is apparently a confirmed bug in GCC 7 related to link-time optimization (-flto):
https://bugs.launchpad.net/gcc-arm-embedded/+bug/1747966
I have just run into this, again, with GCC 8 (gcc-arm-none-eabi-8-2019-q3-update release), the behavior is still the same.
The workaround that also works for me (from https://github.com/ObKo/stm32-cmake/issues/78) is to remove or comment the weak definitions at the end of the startup_XXX.s file, so change, for example
.weak NMI_Handler
.thumb_set NMI_Handler,Default_Handler
to
/*
.weak NMI_Handler
.thumb_set NMI_Handler,Default_Handler
*/
and replace them with your own implementation in a source file:
void NMI_Handler(void)
{
//...
}
All weak handlers need to be removed that are being called, so for example if you have UART1_Handler() defined in the HAL/LL drivers, you need to remove the corresponding .weak entry from the startup_XXX.s file, otherwise the interrupt will lock up the MCU by getting stuck in the default infinite loop, without executing the intended interrupt handler and returning from the interrupt, allowing other code execution to resume.
This bug is still present in gcc-arm-none-eabi-9-2020-q3-update but only for C handlers. Weirdly enough, handlers written in C++ (and declared with extern "C" linkage) are not anymore affected by this bug.
As another workaround, rather than messing with the startup.s file, I found that putting the IRQ handlers in separate .c files and building those (and only those) without LTO does the trick.
For those using CubeIDE and generating IRQ/HAL handlers with CubeMX (aka. "Device Configuration Tool"), all auto-generated handlers are in Core\Src\stm32XXXX_it.c, you just have to edit the properties of this file and remove LTO from the compilation options.
This is sub optimal, but it fits well with auto-generated IRQ/HAL handlers: only the first call (from IRQ handler to HAL handler) is unoptimized, but the HAL code itself is correctly optimized.

LLVM OPT not giving optimised file as output.

The man page for opt says: "It takes LLVM source files as input, runs the specified optimizations or analyses on it, and then outputs the optimized file or the analysis results".
My Goal: To use the inbuilt optimisation pass -dce available in opt. This pass does Dead Code Elimination
My Source file foo.c:
int foo(void)
{
int a = 24;
int b = 25; /* Assignment to dead variable -- dead code */
int c;
c = a * 4;
return c;
}
Here is what I did:
1. clang-7.0 -S -emit-llvm foo.c -o foo.ll
2. opt -dce -S foo.ll -o fooOpt.ll
What I expect : A .ll file in which the dead code (in source code with the comment) part is eliminated.
What I get: fooOpt.ll is the same as non optimised code foo.ll
I have already seen this SO answer, but I didn't get optimised code.
Am I missing something here? Can someone please guide me on the right path.
Thank you.
If you look at the .ll file generated by clang, it will contain a line like this:
attributes #0 = { noinline nounwind optnone sspstrong uwtable ...}
You should remove the optnone attribute here. Whenever a function has the optnone attribute, opt won't touch that function at all.
Now if you try again, you'll notice ... nothing. It still does not work.
This time the problem is that the code is working on memory, not registers. What we need to do is to convert the allocas to registers using -mem2reg. In fact doing this will already optimize away b, so you don't even need the -dce flag.

Is it possible to write a program without using main() function?

I keep getting this question asked in interviews:
Write a program without using main() function?
One of my friends showed me some code using Macros, but i could not understand it.
So the question is:
Is it really possible to write and compile a program without main()?
No you cannot unless you are writing a program in a freestanding environment (embedded environment OS kernel etc.) where the starting point need not be main(). As per the C++ standard main() is the starting point of any program in a hosted environment.
As per the:
C++03 standard 3.6.1 Main function
1 A program shall contain a global function called main, which is the designated start of the program. It is implementation-defined whether a program in a freestanding environment is required to define a main function. [ Note: In a freestanding environment, start-up and
termination is implementation-defined; startup contains the execution of constructors for objects of namespace scope with static storage duration; termination contains the execution of destructors for objects with static storage duration.
What is freestanding Environment & What is Hosted Environment?
There are two kinds of conforming implementations defined in the C++ standard; hosted and freestanding.
A freestanding implementation is one that is designed for programs that are executed without the benefit of an operating system.
For Ex: An OS kernel or Embedded environment would be a freestanding environment.
A program using the facilities of an operating system would normally be in a hosted implementation.
From the C++03 Standard Section 1.4/7:
A freestanding implementation is one in which execution may take place without the benefit of an operating system, and has an implementation-defined set of libraries that includes certain language-support libraries.
Further,
Section: 17.4.1.3.2 Freestanding implementations quotes:
A freestanding implementation has an implementation-defined set of headers. This set shall include at least the following headers, as shown in Table:
18.1 Types <cstddef>
18.2 Implementation properties <limits>
18.3 Start and termination <cstdlib>
18.4 Dynamic memory management <new>
18.5 Type identification <typeinfo>
18.6 Exception handling <exception>
18.7 Other runtime support <cstdarg>
Within standard C++ a main function is required, so the question does not make sense for standard C++.
Outside of standard C++ you can for example write a Windows specific program and use one of Microsoft's custom startup functions (wMain, winMain, wWinmain). In Windows you can also write the program as a DLL and use rundll32 to run it.
Apart from that you can make your own little runtime library. At one time that was a common sport.
Finally, you can get clever and retort that according to the standard's ODR rule main isn't "used", so any program qualifies. Bah! Although unless the interviewers have unusual good sense of humor (and they wouldn't have asked the question if they had) they'll not think that that's a good answer.
Sample program without a visible main function.
/*
7050925.c
$ gcc -o 7050925 7050925.c
*/
#include <stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,t,e)
int begin()
{
printf("How mainless!\n");
}
From: http://learnhacking.in/c-program-without-main-function/
main means an entry point, a point from which your code will start executing. although main is not the first function to run. There are some more code which runs before main and prepares the environment to make your code run. This code then calls main . You can change the name of the main function by recompiling the code of the startup file crt0.c and changing the name of the main function. Or you can do the following:
#include <stdio.h>
extern void _exit (register int code);
_start()
{
int retval;
retval = my_main ();
_exit(retval);
}
int my_main(void)
{
printf("Hello\n");
return 0;
}
Compile the code with:
gcc -o no_main no_main.c -nostartfiles
The -nostartfiles will not include the default startup file. You point to the main entry file with the _start .
main is nothing but a predefined entrypoint for the user code. Therefore you can name it whatever, but at the end of the day you do need an entry point. In C/C++ and other languages the name is selected as main if you make another language or hack the sources of these language compilers then you can change the name of main to pain but it will bring pain, as it will violate the standards.
But manipulating the entry function name is useful for kernel code, the first function to run in the kernel, or code written for embedded systems.
They may refer to a program written for a freestanding implementation. The C++ Standard defines two sorts of implementations. One is a hosted implementation. Programs written for those implementations are required to have a main function. But otherwise, no main function is required if the freestanding implementation doesn't require one. This is useful for operation system kernels or embedded system programs that don't run under an operation system.
Yes
$ cat > hwa.S
write = 0x04
exit = 0xfc
.text
_start:
movl $1, %ebx
lea str, %ecx
movl $len, %edx
movl $write, %eax
int $0x80
xorl %ebx, %ebx
movl $exit, %eax
int $0x80
.data
str: .ascii "Hello, world!\n"
len = . -str
.globl _start
$ as -o hwa.o hwa.S
$ ld hwa.o
$ ./a.out
Hello, world!
The kernel that really runs an executable knows nothing about internal symbols, it just transfers to an entry point specified in binary in the executable image header.
The reason you need a main is because normally your "main program" is really just another module. The entry point is in library-provided startup code written in some combination of C and assembly and that library code just happens to call main so you normally need to provide one. But run the linker directly and you don't.
To include a C module1...
Mac:~/so$ cat > nomain.S
.text
.globl start
start:
call _notmain
Mac:~/so$ as -o nomain.o nomain.S
Mac:~/so$ cat > notmain.c
#include <unistd.h>
void notmain(void) {
write(1, "hi\n", 3);
_exit(0);
}
Mac:~/so$ cc -c notmain.c
Mac:~/so$ ld -w nomain.o notmain.o -lc
Mac:~/so$ ./a.out
hi
1. And I'm also switching to x86-64 here.
Yes it possible to compile with out main but you cannot pass the linking phase though.
g++ -c noMain.cpp -o noMain.o
"Without using main" might also mean that no logic is allowed within main, but the main itself exists. I can imagine the question had this cleared out, but since it's not cleared here, this is another possible answer:
struct MainSub
{
MainSub()
{
// do some stuff
}
};
MainSub mainSub;
int main(int argc, char *argv[]) { return 0; }
What will happen here is that the stuff in MainSub's constructor will execute before the unusable main is executed, and you can place the program's logic there. This of course requires C++, and not C (also not clear from the question).
As long as you are using g++ you could change your entry point with linker option -e, so the following code and compile command may let you create a program without a main() function:
#import <iostream>
class NoMain
{
public:
NoMain()
{
std::cout << "Hello World!" << std::endl;
exit(0);
}
} mainClass;
I gave file name as noname.cpp, and the compile option is:
g++ nomain.cpp -Wl,-e,_mainClass -v
To tell the truth, I didn't fully understand why the code can works fine. I suspect that the address of global variable mainClass is the same to constructor of NoMain class. However, I also have several reasons that I could tell my guess may not correct.
I think the macro reference was to renaming the main function, the following is not my code, and demonstrates this. The compiler still sees a main function though, but technically there's no main from a source point of view. I got it here http://www.exforsys.com/forum/c-and-c/96849-without-main-function-how-post412181.html#post412181
#include<stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,t,e)
int begin()
{
printf(" hello ");
}
Disregarding specific language standards, most linking loader provides some means to declare a function name (entry point) which must be executed when the binary is loaded is loaded into memory.
For old school c language, default was something like 'start' or '_start', defined in so-called crt (c runtime?), which does several householding jobs needed for c standard functions, such as preparing memory heap, initialising static variable areas, parsing command line into argc/argv, etc.
Possibly you could override the entry point function if you take enough care not to use the standard functions which requires those household things (e.g. malloc(), free(), printf(), any class definitions have custom constructor, ...)
Quite restrictive but not impossible if you use functions provided by o/s, not by standard c runtime.
For example, you can make a simple helloworld using write() function on descriptor 1.
When C or C++ code runs, it executes at a known start address, the code here initialises the run-time environment, initialises the stack pointer, performs data initialisation, calls static constructors, then jumps to main().
The code that does this is linked with your code at build time by the linker. In GCC it is usually in crt0.s, with a commercial compiler it is unlikely that this code will be available to you.
In the end, it has to start somewhere and main() is just a symbolic name for that location. It is specified by the language standard so that developers know what to call it, otherwise code would not be portable from one tool chain to another.
If you are writing code for a 'bare-metal' system with no OS or at least no OS in the sense of a process loader (embedded systems often include an RTOS kernel that is started after main()) , then you can in theory call the C code entry point whatever you wish since you usually have complete control over run-time start-up code. But do do so would be foolish and somewhat perverse.
Some RTOS environments such as VxWorks, and most application frameworks in general include main() )or its equivalent) within their own library code so that it runs before the user application code. For example VxWorks applications start from usrAppInit(), and Win32 applications start from WinMain().
Write a class and print your name in the constructor of that class and declare a GLOBAL OBJECT of that class. So the class' constructor gets executed before main. So you can leave the main empty and still print your name.
class MyClass
{
myClass()
{
cout << "printing my name..." <<endl;
}
};
MyClass gObj; // this will trigger the constructor.
int main()
{
// nothing here...
}
I realize this is an old question, but I just found this out and had to share. It will probably not work with all linkers, but it's at least possible to trick ld (I'm running version 2.24.51.20140918) into thinking there is a main-function by doing this:
int main[] {};
or even just
int main;
You can then apply one of the aforementioned tricks to let the program execute some code, e.g. through the use of a constructor:
struct Main
{
Main()
{
cout << "Hello World!\n";
exit(0);
}
} main_;
The exit(0) is to prevent the array from being "called". Good fun :-)
Yes you can do it by changing the entry point of the C language from main() to _start
Here is the code :
#include<stdio.h>
#include<stdlib.h>
int sid()
{
printf("Hallo World\n");
exit(0);
}
Then run the code using gcc complier.Let's assume that you have saved the file with the name of test.c then.
gcc -nostartfiles test.c
./a.out
Maybe it's possible to compile a .data section and fill it with code?
It depends what they mean.
Did they mean:
Write a program with no main() function.
Then generally speaking no.
But there are ways to cheat.
You can use the pre-processor to hide main() in plain sight.
Most compiler allow you to specify the entry point into your code.
By default it is main(int,char*[])
Or did they mean:
Write a program that runs code without using main (to run your code).
This is a relatively simple trick. All objects in the global namespace run their constructors before main() is entered and destruction after main() exits. Thus all you need to do is define a class with a constructor that runs the code you want, then create an object in the global namespace.
Note: The compiler is allowed to optimize these objects for delayed load (but usually does not) but to be safe just put the global in the same file as the main function (that can be empty).
Function main is only default label for address where program will start execution. So technically yes it`s possible, but you have to set name of function that will start execution in your environment.
1) Using a macro that defines main
#include<stdio.h>
#define fun main
int fun(void)
{
printf("stackoverfow");
return 0;
}
Output:
stackoverflow
2) Using Token-Pasting Operator
The above solution has word ‘main’ in it. If we are not allowed to even write main, we ca use token-pasting operator (see this for details)
#include<stdio.h>
#define fun m##a##i##n
int fun()
{
printf("stackoverflow");
return 0;
}
yes it is possible to write a program without main().
But it uses main() indirectly.
following program will help you to understand..
#include<stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,r,e)
int begin()
{
printf(” you are inside main() which is hidden“);
}
The ‘##‘ operator is called the token pasting or token merging operator. That is we can merge two or more characters with it.
In the 2nd line of the program-
define decode(s,t,u,m,p,e,d) m##s##u##t
What is the preprocessor doing here. The macro decode(s,t,u,m,p,e,d) is being expanded as “msut” (The ## operator merges m,s,u & t into msut). The logic is when you pass (s,t,u,m,p,e,d) as argument it merges the 4th,1st,3rd & the 2nd characters(tokens)
Now look at the third line of the program –
define begin decode(a,n,i,m,a,r,e)
Here the preprocessor replaces the macro “begin” with the expansion decode(a,n,i,m,a,r,e). According to the macro definition in the previous line the argument must be expanded so that the 4th,1st,3rd & the 2nd characters must be merged. In the argument(a,n,i,m,a,r,e) 4th,1st,3rd & the 2nd characters are ‘m’,’a’,’i’ & ‘n’.
so it will replace begin by main() by the preprocessor before the program is passed on for the compiler. That’s it…
By using C++ constructors you write a C++ program without the main function with nothing it it. Let's say for example we can print a hello world without writing anything in the main function as follows:
class printMe{
private:
//
public:
printMe(){
cout<<"Hello Wold! "<<endl;
}
protected:
//
}obj;
int main(){}
According to standards, main() is required and the starting point of hosted environments. That's why you've to use tricks to hide the obvious looking main, like the trick posted above.
#include <stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,t,e)
int begin()
{
printf(" hello ");
}
Here, main is written by the macro tricks. It might not be clear at once, but it eventually leads to main. If this is the valid answer for your question, this could be done much easily, like this.
# include <stdio.h>
# define m main
int m()
{
printf("Hell0");
}

Is main() really start of a C++ program?

The section $3.6.1/1 from the C++ Standard reads,
A program shall contain a global
function called main, which is the
designated start of the program.
Now consider this code,
int square(int i) { return i*i; }
int user_main()
{
for ( int i = 0 ; i < 10 ; ++i )
std::cout << square(i) << endl;
return 0;
}
int main_ret= user_main();
int main()
{
return main_ret;
}
This sample code does what I intend it to do, i.e printing the square of integers from 0 to 9, before entering into the main() function which is supposed to be the "start" of the program.
I also compiled it with -pedantic option, GCC 4.5.0. It gives no error, not even warning!
So my question is,
Is this code really Standard conformant?
If it's standard conformant, then does it not invalidate what the Standard says? main() is not start of this program! user_main() executed before the main().
I understand that to initialize the global variable main_ret, the use_main() executes first but that is a different thing altogether; the point is that, it does invalidate the quoted statement $3.6.1/1 from the Standard, as main() is NOT the start of the program; it is in fact the end of this program!
EDIT:
How do you define the word 'start'?
It boils down to the definition of the phrase "start of the program". So how exactly do you define it?
You are reading the sentence incorrectly.
A program shall contain a global function called main, which is the designated start of the program.
The standard is DEFINING the word "start" for the purposes of the remainder of the standard. It doesn't say that no code executes before main is called. It says that the start of the program is considered to be at the function main.
Your program is compliant. Your program hasn't "started" until main is started. The function is called before your program "starts" according to the definition of "start" in the standard, but that hardly matters. A LOT of code is executed before main is ever called in every program, not just this example.
For the purposes of discussion, your function is executed prior to the 'start' of the program, and that is fully compliant with the standard.
No, C++ does a lot of things to "set the environment" prior to the call of main; however, main is the official start of the "user specified" part of the C++ program.
Some of the environment setup is not controllable (like the initial code to set up std::cout; however, some of the environment is controllable like static global blocks (for initializing static global variables). Note that since you don't have full control prior to main, you don't have full control on the order in which the static blocks get initialized.
After main, your code is conceptually "fully in control" of the program, in the sense that you can both specify the instructions to be performed and the order in which to perform them. Multi-threading can rearrange code execution order; but, you're still in control with C++ because you specified to have sections of code execute (possibly) out-of-order.
Your program will not link and thus not run unless there is a main. However main() does not cause the start of the execution of the program because objects at file level have constructors that run beforehand and it would be possible to write an entire program that runs its lifetime before main() is reached and let main itself have an empty body.
In reality to enforce this you would have to have one object that is constructed prior to main and its constructor to invoke all the flow of the program.
Look at this:
class Foo
{
public:
Foo();
// other stuff
};
Foo foo;
int main()
{
}
The flow of your program would effectively stem from Foo::Foo()
You tagged the question as "C" too, then, speaking strictly about C, your initialization should fail as per section 6.7.8 "Initialization" of the ISO C99 standard.
The most relevant in this case seems to be constraint #4 which says:
All the expressions in an initializer for an object that
has static storage duration shall be constant expressions or string literals.
So, the answer to your question is that the code is not compliant to the C standard.
You would probably want to remove the "C" tag if you were only interested to the C++ standard.
Section 3.6 as a whole is very clear about the interaction of main and dynamic initializations. The "designated start of the program" is not used anywhere else and is just descriptive of the general intent of main(). It doesn't make any sense to interpret that one phrase in a normative way that contradicts the more detailed and clear requirements in the Standard.
The compiler often has to add code before main() to be standard compliant. Because the standard specifies that initalization of globals/statics must be done before the program is executed. And as mentioned, the same goes for constructors of objects placed at file scope (globals).
Thus the original question is relevant to C as well, because in a C program you would still have the globals/static initialization to do before the program can be started.
The standards assume that these variables are initialized through "magic", because they don't say how they should be set before program initialization. I think they considered that as something outside the scope of a programming language standard.
Edit: See for example ISO 9899:1999 5.1.2:
All objects with static storage
duration shall be initialized (set to
their initial values) before program
startup. The manner and timing of such
initialization are otherwise
unspecified.
The theory behind how this "magic" was to be done goes way back to C's birth, when it was a programming language intended to be used only for the UNIX OS, on RAM-based computers. In theory, the program would be able to load all pre-initialized data from the executable file into RAM, at the same time as the program itself was uploaded to RAM.
Since then, computers and OS have evolved, and C is used in a far wider area than originally anticipated. A modern PC OS has virtual addresses etc, and all embedded systems execute code from ROM, not RAM. So there are many situations where the RAM can't be set "automagically".
Also, the standard is too abstract to know anything about stacks and process memory etc. These things must be done too, before the program is started.
Therefore, pretty much every C/C++ program has some init/"copy-down" code that is executed before main is called, in order to conform with the initialization rules of the standards.
As an example, embedded systems typically have an option called "non-ISO compliant startup" where the whole initialization phase is skipped for performance reasons, and then the code actually starts directly from main. But such systems don't conform to the standards, as you can't rely on the init values of global/static variables.
Your "program" simply returns a value from a global variable. Everything else is initialization code. Thus, the standard holds - you just have a very trivial program and more complex initialization.
main() is a user function called by the C runtime library.
see also: Avoiding the main (entry point) in a C program
Seems like an English semantics quibble. The OP refers to his block of code first as "code" and later as the "program." The user writes the code, and then the compiler writes the program.
Ubuntu 20.04 glibc 2.31 RTFS + GDB
glibc does some setup before main so that some of its functionalities will work. Let's try to track down the source code for that.
hello.c
#include <stdio.h>
int main() {
puts("hello");
return 0;
}
Compile and debug:
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o hello.out hello.c
gdb hello.out
Now in GDB:
b main
r
bt -past-main
gives:
#0 main () at hello.c:3
#1 0x00007ffff7dc60b3 in __libc_start_main (main=0x555555555149 <main()>, argc=1, argv=0x7fffffffbfb8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffbfa8) at ../csu/libc-start.c:308
#2 0x000055555555508e in _start ()
This already contains the line of the caller of main: https://github.com/cirosantilli/glibc/blob/glibc-2.31/csu/libc-start.c#L308.
The function has a billion ifdefs as can be expected from the level of legacy/generality of glibc, but some key parts which seem to take effect for us should simplify to:
# define LIBC_START_MAIN __libc_start_main
STATIC int
LIBC_START_MAIN (int (*main) (int, char **, char **),
int argc, char **argv,
{
/* Initialize some stuff. */
result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);
exit (result);
}
Before __libc_start_main are are already at _start, which by adding gcc -Wl,--verbose we know is the entry point because the linker script contains:
ENTRY(_start)
and is therefore is the actual very first instruction executed after the dynamic loader finishes.
To confirm that in GDB, we an get rid of the dynamic loader by compiling with -static:
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o hello.out hello.c
gdb hello.out
and then make GDB stop at the very first instruction executed with starti and print the first instructions:
starti
display/12i $pc
which gives:
=> 0x401c10 <_start>: endbr64
0x401c14 <_start+4>: xor %ebp,%ebp
0x401c16 <_start+6>: mov %rdx,%r9
0x401c19 <_start+9>: pop %rsi
0x401c1a <_start+10>: mov %rsp,%rdx
0x401c1d <_start+13>: and $0xfffffffffffffff0,%rsp
0x401c21 <_start+17>: push %rax
0x401c22 <_start+18>: push %rsp
0x401c23 <_start+19>: mov $0x402dd0,%r8
0x401c2a <_start+26>: mov $0x402d30,%rcx
0x401c31 <_start+33>: mov $0x401d35,%rdi
0x401c38 <_start+40>: addr32 callq 0x4020d0 <__libc_start_main>
By grepping the source for _start and focusing on x86_64 hits we see that this seems to correspond to sysdeps/x86_64/start.S:58:
ENTRY (_start)
/* Clearing frame pointer is insufficient, use CFI. */
cfi_undefined (rip)
/* Clear the frame pointer. The ABI suggests this be done, to mark
the outermost frame obviously. */
xorl %ebp, %ebp
/* Extract the arguments as encoded on the stack and set up
the arguments for __libc_start_main (int (*main) (int, char **, char **),
int argc, char *argv,
void (*init) (void), void (*fini) (void),
void (*rtld_fini) (void), void *stack_end).
The arguments are passed via registers and on the stack:
main: %rdi
argc: %rsi
argv: %rdx
init: %rcx
fini: %r8
rtld_fini: %r9
stack_end: stack. */
mov %RDX_LP, %R9_LP /* Address of the shared library termination
function. */
#ifdef __ILP32__
mov (%rsp), %esi /* Simulate popping 4-byte argument count. */
add $4, %esp
#else
popq %rsi /* Pop the argument count. */
#endif
/* argv starts just at the current stack top. */
mov %RSP_LP, %RDX_LP
/* Align the stack to a 16 byte boundary to follow the ABI. */
and $~15, %RSP_LP
/* Push garbage because we push 8 more bytes. */
pushq %rax
/* Provide the highest stack address to the user code (for stacks
which grow downwards). */
pushq %rsp
#ifdef PIC
/* Pass address of our own entry points to .fini and .init. */
mov __libc_csu_fini#GOTPCREL(%rip), %R8_LP
mov __libc_csu_init#GOTPCREL(%rip), %RCX_LP
mov main#GOTPCREL(%rip), %RDI_LP
#else
/* Pass address of our own entry points to .fini and .init. */
mov $__libc_csu_fini, %R8_LP
mov $__libc_csu_init, %RCX_LP
mov $main, %RDI_LP
#endif
/* Call the user's main function, and exit with its value.
But let the libc call main. Since __libc_start_main in
libc.so is called very early, lazy binding isn't relevant
here. Use indirect branch via GOT to avoid extra branch
to PLT slot. In case of static executable, ld in binutils
2.26 or above can convert indirect branch into direct
branch. */
call *__libc_start_main#GOTPCREL(%rip)
which ends up calling __libc_start_main as expected.
Unfortunately -static makes the bt from main not show as much info:
#0 main () at hello.c:3
#1 0x0000000000402560 in __libc_start_main ()
#2 0x0000000000401c3e in _start ()
If we remove -static and start from starti, we get instead:
=> 0x7ffff7fd0100 <_start>: mov %rsp,%rdi
0x7ffff7fd0103 <_start+3>: callq 0x7ffff7fd0df0 <_dl_start>
0x7ffff7fd0108 <_dl_start_user>: mov %rax,%r12
0x7ffff7fd010b <_dl_start_user+3>: mov 0x2c4e7(%rip),%eax # 0x7ffff7ffc5f8 <_dl_skip_args>
0x7ffff7fd0111 <_dl_start_user+9>: pop %rdx
By grepping the source for _dl_start_user this seems to come from sysdeps/x86_64/dl-machine.h:L147
/* Initial entry point code for the dynamic linker.
The C function `_dl_start' is the real entry point;
its return value is the user program's entry point. */
#define RTLD_START asm ("\n\
.text\n\
.align 16\n\
.globl _start\n\
.globl _dl_start_user\n\
_start:\n\
movq %rsp, %rdi\n\
call _dl_start\n\
_dl_start_user:\n\
# Save the user entry point address in %r12.\n\
movq %rax, %r12\n\
# See if we were run as a command with the executable file\n\
# name as an extra leading argument.\n\
movl _dl_skip_args(%rip), %eax\n\
# Pop the original argument count.\n\
popq %rdx\n\
and this is presumably the dynamic loader entry point.
If we break at _start and continue, this seems to end up in the same location as when we used -static, which then calls __libc_start_main.
When I try a C++ program instead:
hello.cpp
#include <iostream>
int main() {
std::cout << "hello" << std::endl;
}
with:
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o hello.out hello.cpp
the results are basically the same, e.g. the backtrace at main is the exact same.
I think the C++ compiler is just calling into hooks to achieve any C++ specific functionality, and things are pretty well factored across C/C++.
TODO:
commented on concrete easy-to-understand examples of what glibc is doing before main. This gives some ideas: What happens before main in C++?
make GDB show the source itself without us having to look at it separately, possibly with us building glibc ourselves: How to compile my own glibc C standard library from source and use it?
understand how the above source code maps to objects such as crti.o that can be seen with gcc --verbose main.c and which end up getting added to the final link
main is called after initializing all the global variables.
What the standard does not specify is the order of initialization of all the global variables of all the modules and statically linked libraries.
Yes, main is the "entry point" of every C++ program, excepting implementation-specific extensions. Even so, some things happen before main, notably global initialization such as for main_ret.