COFF object files are the compiler´s output. They contain the processor dependent opcodes that describe the logic that you wrote in C. They also contain global variables, text strings, linker directives, debug information and others.
After you generate them you must use a tool called linker to merge them together into a single file to be deployed as EXE, DLL or SYS. During this post we will explore the details of these COFF object files.
Below you can see an UML class diagram showing the objetcs that a COFF object file contain or could contain.

This diagram shows that every COFF obect file has one symbol table and at least one section. Sections can have relocations, but it´s not mandatory (check the .debug$S section below).
To show you these details I will use the following code. This is a simple C program that allocate some bytes from the heap (exactly what malloc does) and then print a Hello World string with the address alllocated.

To create the COFF object file we must compile it. To do it use the following command line.

As I already said in my last post the /c parameter says to the compiler to just generate the object file and don´t call the linker automatically.
After you have compiled it you can use the dumpbin utility to check these details (this is the tool that I used to get these images). Just type the following command line to get these results:
dumpbin /all filename.obj
Based on the dumpbin output and in the UML diagram above, you can see that COFF object files are composed of sections. One of them is the .drectve section. This section contains command line parameters that the linker reads and uses during the linking phase. These parameters are interpreted as they are defined. As you can see below by default the compilation process inserts two parameters, /DEFAULTLIB:”LIBCMT” and /DEFAULTLIB: “OLDNAMES”. The purpose of these parameters are to provide the C Runtime libraries for use by the linker. LICBMT is the library that contains the static multi-thread version of the CRT. The OLDNAMES is a library that provides function names translations like malloc to _malloc.

Another section is the .debug$S. This section contains debug information that we can use later to debug problems with this code. There are three formats that Microsoft C/C++ compiler can generate, they are COFF, CodeView and Program Database. The following article describes these formats and use very well. I strongly recommend that you read it.
generating debug information with visual c++
http://www.debuginfo.com/articles/gendebuginfo.html

The .data section contains the global variables and strings used. As you can see in the image below the Hello World! %p\n is there. Why? Because the compiler when see strings declared this way will put them in this section. You will also see here global variables (variables declared outside a function).

The .text section is where your code lives. At the RAW DATA #4 section in the image below you can see a lot of hexadecimal numbers (55 8B EC 51 6A …). These numbers are the opcodes that the processor executes. For each assembly mnemonic (MOV, ADD, CALL, etc) we have an opcode defined on the Intel/AMD manuals.

If we disassembly the opcodes inside this section we will get the following assembly code (output from Windbg !uf command):

Basically what we have here is the function epilog (configure the stack for use), then we have the opcodes that define the logic that you wrote in C and to finish we have the function prolog (restore the stack as it was before this function was called). All these opcodes are executed sequentially by the processor. After an opcode is executed the EIP register is incremented to point to the next opcode to be executed. And this will repeat as long the processor has power supply.
To finish this post you can see below one of the most important things inside a COFF object file, the symbol table. This table contains every function or variable exported by this object file that other object files can use. It also defines every function or variable that this object file needs (this dependencies will be resolved later by the linker). These dependencies are contained in other object files or LIB files.

During my studies I faced a question about the linker process, so I sent an e-mail to Tim Roberts (he has a very deep knowledge about the compile and linking process and other things as well) about this question. Below you can see his answer. He explained it so well that I want to share with you all. Thanks Tim!
George Luiz Bittencourt wrote:
> …
> When I create two object files (test1.obj and test2.obj) and each one have a function with the same name (test) and then link them together I get the following error:
> *error LNK2005: _test already defined in test2.obj*
>
> I agree with this error because there are two distinct functions with the same name, but if I create a LIB file with the second object file (test2.obj) inside it and then link the first object file with this LIB I don´t get this error anymore.
>
> IMHO I would have to get this error too, because still there is two functions with the same name. Do you know if there is any special treatment in this situation?
>
Yes, there is a big difference.
When you specify an object file to the linker, that file is FORCED into your executable. It is fully included. When you specify a library file, the objects in that library are simply AVAILABLE for use. They are not loaded unless they are needed.
As the linker brings in each object file, it keeps track of the list of names that it has not been able to find. Each object file probably satisfies some of the names that were already on that list, and adds new names to the list. After all of the object files are forced in, then the linker starts scanning the libraries. If there is a module in the library that includes one of the unsatisfied names, the linker will bring in that module. Otherwise, it leaves it out.
That’s the whole point of a library: to offer a whole bunch of services, while not requiring programs to bulk up on a bunch of stuff they don’t need.
It’s still possible to get a LNK2005 when using a library. Say that my executable has functions called “aaa” and “bbb”, and it needs functions called “ccc” and “ddd”. Now, lets’s say I have a library called mylib.lib that includes four separate objects:
aaa.obj defines aaa
bbb.obj defines bbb
ccc.obj defines ccc
ddd.obj defines ddd
Here, there is no chance for a LNK2005. But, let’s say that I reorganize my sources to combine two functions:
aaa.obj defines aaa
bbb.obj defines bbb and ccc
ddd.obj defines ddd
Now, when the linker goes to find “ccc”, it will bring in “bbb.obj”.
However, that defines “bbb”, which is already defined in my program.
That will trigger a LNK2005 error.
–
Tim Roberts
BOOKS
- Linkers and Loaders
- Compilers: Principles, Techniques, & Tools
Hope it helped you!
-George