Skip to content

Instantly share code, notes, and snippets.

@remzmike
Last active March 28, 2023 19:58
Show Gist options
  • Save remzmike/840e2f7f87411e696f92cfdbf7dc5d42 to your computer and use it in GitHub Desktop.
Save remzmike/840e2f7f87411e696f92cfdbf7dc5d42 to your computer and use it in GitHub Desktop.
Hello C : 01 : Compiling and linking

Part 1: Compiling and linking

Install compiler

We will use the compiler that comes with Microsoft Visual Studio Community 2022.

Google: msvs community edition

https://visualstudio.microsoft.com/vs/community/

During install, it will ask what Workloads you want to install, choose Desktop development with C++.

Compiling

From the start menu, open the developer command prompt installed by Visual Studio.

Developer Command Prompt for VS 2022

It should start you in a directory like this:

C:\Program Files\Microsoft Visual Studio\2022\Community>

Change directory to your desktop:

C:\Program Files\Microsoft Visual Studio\2022\Community>cd %homepath%\desktop

C:\Users\eowilson\Desktop>

Create a folder to work in and change directory into it:

C:\Users\eowilson\Desktop>mkdir hello-c

C:\Users\eowilson\Desktop>cd hello-c

C:\Users\eowilson\Desktop\hello-c>    

Create your first source file:

C:\Users\eowilson\Desktop\hello-c>notepad 1.c

Save this code in it:

#include <stdio.h>

void main()
{
    printf("Hello, World!");
}

Compile and link your code into an executable:

C:\Users\eowilson\Desktop\hello-c>cl 1.c
Microsoft (R) C/C++ Optimizing Compiler Version 19.35.32215 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

1.c
Microsoft (R) Incremental Linker Version 14.35.32215.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:1.exe
1.obj

Two files are created. Each file is created by a different step in the build process.

01/01/2023  09:53 PM           110,080 1.exe
01/01/2023  09:53 PM             1,414 1.obj

When you run cl with no options it will run the compiler and the linker.

You can see that in the output. First, the compiler runs, creating 1.obj.

Microsoft (R) C/C++ Optimizing Compiler Version 19.35.32215 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

1.c

Then the linker runs, automatically, since we didn't tell it not to, creating 1.exe.

Microsoft (R) Incremental Linker Version 14.35.32215.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:1.exe
1.obj

Later, we may want to split these two steps, but for now this is fine.

Run your program to see the output:

C:\Users\eowilson\Desktop\hello-c>1
Hello, World!

Linking explained

The compiler creates obj files from source files.

The linker creates executables by putting together different chunks of compiled code.

Here, the linker created 1.exe from 1.obj and a .lib file containing the standard C library.

A lib file is simply an archive of obj files, usually representing a particular library of functions.

The lib file needed in this case is for the printf call, which is part of the standard C library.

Further, the Microsoft tools automatically link the standard C library for us.

And, by default, the linker did so "statically".

Static Linking

When the linker includes the code from a lib in your exe it's called static linking.

The code for printf is literally in your exe, since the linker put it there.

Dynamic Linking

However, the linker can also create your exe without adding the lib file code to your exe.

This is how programs re-use library code in Windows and other operating systems.

In Windows the shared libraries are .dll files. DLL stands for Dynamic Link Library.

When the linker associates your exe to shared libraries it is called dynamic linking.

The code for printf then comes from a dll which your exe now depends on.

Linking Compared

Let's compile and link both ways to new executables and investigate the differences.

Copy these commands after the > and right click to paste into the command prompt.

> cl /MT 1.c /link /out:1-static.exe

> cl /MD 1.c /link /out:1-dynamic.exe

The dynamic exe is under 9k and the static one is 110k.

01/01/2023  10:39 PM             8,704 1-dynamic.exe
01/01/2023  10:39 PM           110,080 1-static.exe

That's 110k just to print one line, because it includes the standard C runtime code.

However, the 9k exe now depends on an external dll file, which itself depends on more dll's.

You can see the dll files a binary depends on with dumpbin:

C:\Users\eowilson\Desktop\hello-c>dumpbin /IMPORTS 1-dynamic.exe    

But, for better usability, redirect the output to a text file and open it:

C:\Users\eowilson\Desktop\hello-c>dumpbin /IMPORTS 1-dynamic.exe > _.txt & notepad _.txt

This output tells you what code the exe has to import in order to run.

You can see that the 9k exe depends on 7 dll's.

KERNEL32.dll
VCRUNTIME140.dll
api-ms-win-crt-stdio-l1-1-0.dll
api-ms-win-crt-runtime-l1-1-0.dll
api-ms-win-crt-math-l1-1-0.dll
api-ms-win-crt-locale-l1-1-0.dll
api-ms-win-crt-heap-l1-1-0.dll

The first one, KERNEL32, provides Windows api functions. It EXPORTS them.

You can see the functions exported from that dll with:

> dumpbin /EXPORTS C:\Windows\System32\kernel32.dll > _.txt & notepad _.txt

It's a big list of 1600+ Windows api functions.

Even an old function to make your PC beep is in there.

104   67 00037F50 Beep

The second dll, VCRUNTIME140, provides the Microsoft implementation of the standard C runtime.

> dumpbin /EXPORTS C:\Windows\System32\VCRUNTIME140.dll > _.txt & notepad _.txt

It lists standard C functions, like these memory functions:

59   3A 00001170 memchr
60   3B 00001210 memcmp
61   3C 00001310 memcpy
62   3D 00001310 memmove
63   3E 000019C0 memset

A function named printf isn't in there though, because Microsoft defines a wrapper function named printf in stdio.h which calls the external implementation, with a different mangled name depending on build configuration.

But, the underlying implementation is still imported by VCRUNTIME140 from one of the other 6 dll's.

> dumpbin /IMPORTS c:\windows\system32\vcruntime140.dll > _.txt & _.txt

    api-ms-win-crt-stdio-l1-1-0.dll
                180011160 Import Address Table
                180014D80 Import Name Table
                        0 time date stamp
                        0 Index of first forwarder reference

                            D __stdio_common_vsprintf
                            F __stdio_common_vsprintf        

Which gets it from other dll's all the way down.

> dumpbin /EXPORTS c:\windows\system32\downlevel\api-ms-win-crt-stdio-l1-1-0.dll > _.txt & _.txt

    14    D          __stdio_common_vsprintf (forwarded to ucrtbase.__stdio_common_vsprintf)

> dumpbin /EXPORTS c:\windows\system32\downlevel\ucrtbase.dll > _.txt & _.txt

    108   6B 0001AD80 __stdio_common_vfprintf

The static version

With this new understanding of linking, let's look at the statically linked exe.

> dumpbin /IMPORTS 1-static.exe > _.txt & _.txt

It only has one dll requirement: KERNEL32.dll

All of the necessary functionality of VCRUNTIME140 is included in the exe, statically linked.

Now, out of curiosity, let's check the size of VCRUNTIME140.dll.

> dir c:\windows\system32\vcruntime140.dll        

12/13/2022  09:41 AM           109,392 vcruntime140.dll

And the others:

> dir c:\windows\system32\downlevel\api-ms-win-crt-stdio-l1-1-0.dll
12/07/2019  05:09 AM            17,960 api-ms-win-crt-stdio-l1-1-0.dll        

> dir c:\windows\system32\downlevel\api-ms-win-crt-runtime-l1-1-0.dll
12/07/2019  05:09 AM            16,400 api-ms-win-crt-runtime-l1-1-0.dll

> dir c:\windows\system32\downlevel\api-ms-win-crt-math-l1-1-0.dll        
12/07/2019  05:09 AM            21,008 api-ms-win-crt-math-l1-1-0.dll

> dir c:\windows\system32\downlevel\api-ms-win-crt-locale-l1-1-0.dll        
12/07/2019  05:09 AM            12,088 api-ms-win-crt-locale-l1-1-0.dll

> dir c:\windows\system32\downlevel\api-ms-win-crt-heap-l1-1-0.dll        
12/07/2019  05:09 AM            12,600 api-ms-win-crt-heap-l1-1-0.dll

From this you can see how the static version ends up being so much larger.

But, more importantly, you can see the different ways linking works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment