an object file and its sections
An object file is the half-finished product the assembler hands out for a single translation unit, usually with a .o extension on Unix-like systems (or .obj on Windows). It contains real machine code for the functions in that one source file, but it is not yet runnable on its own: it still has gaps where it refers to things defined in other files, and nobody has yet decided where in memory it will live. Think of it as one chapter of a book, fully typeset but not yet bound together with the others or page-numbered.
Inside, an object file is organised into named sections, each holding one kind of content. The .text section holds the actual machine instructions of your functions. The .data section holds global and static variables that start with a nonzero value. The .bss section reserves space for globals that start at zero (it stores just the size, not a long run of zeros, to keep the file small). The .rodata section holds read-only constants like string literals. Alongside these sit a symbol table (a directory of the names this file defines and the names it still needs from elsewhere) and relocation information (a to-do list of addresses that must be fixed up once final positions are known).
This sectioned layout is what makes the later linking step possible and tidy. The linker can gather all the .text sections from many object files into one block of code, all the .data sections into one block of data, and so on, then assign real addresses and patch the gaps. The same sections, slightly rearranged, end up in the final executable. You can peek inside an object file with tools like nm (to list its symbols) or objdump and readelf (to dump its sections), which is a good way to make the abstract idea concrete.
$ gcc -c utils.c # -c stops after the assembler: makes utils.o, no linking $ nm utils.o 0000000000000000 T add # T = defined in .text (this file provides add) U printf # U = undefined (needs printf from somewhere else)
An object file lists what it provides (T) and what it still needs (U), ready for the linker.
An object file is not runnable by itself: its cross-file references are still unresolved and it has no final memory addresses, both of which only the linker supplies.