Walk through the C compilation pipeline: preprocessor, compiler, assembler, and linker. Start with a normal hello world, inspect macros and generated assembly, and end by producing a running binary that has no main() function.
Been there, did that. Wrote shell utilities like that. No linking of Startup.obj, just my code and the linker that turned my .obj into a scatterloadable executable. Run it from the shell, the system provides a default stack and jumps right into the first function.
The whole executable was less than a kilobyte in size. It accessed the system tick timer, used it to seed a RNG, which was actually part of the executable, chose a random quote from a megabyte-sized fortune file, and printed it. Everything clean, all resources closed and returned. It even had error handling.
Been there, did that. Wrote shell utilities like that. No linking of Startup.obj, just my code and the linker that turned my .obj into a scatterloadable executable. Run it from the shell, the system provides a default stack and jumps right into the first function.
The whole executable was less than a kilobyte in size. It accessed the system tick timer, used it to seed a RNG, which was actually part of the executable, chose a random quote from a megabyte-sized fortune file, and printed it. Everything clean, all resources closed and returned. It even had error handling.