.NET applications (EXE files) are an instance of an assembly. But a single application can include multiple assemblies; in fact, it almost always does. I wrote a little program that uses reflection to list all assemblies actively being used by the program itself. When I ran the program against itself, it generated the following list.
mscorlib Microsoft.VisualStudio.HostingProcess.Utilities System.Windows.Forms System System.Drawing Microsoft.VisualStudio.HostingProcess.Utilities.Sync vshost System.Deployment Microsoft.VisualBasic WindowsApplication1 System.Runtime.Remoting
Wow! Twelve assemblies, including WindowsApplication1, the main program. Most of the assemblies are Framework-supplied DLLs. For Microsoft.Visual Basic, it's the Microsoft.VisualBasic.dll assembly; for System, it's the System.dll assembly. All of the assemblies (except the main program assembly) are shared libraries from the GAC. The application can also support private assemblies loaded from local DLL files.
The .NET Framework automatically loaded these assemblies for me when WindowsApplication1 started up; it figured out which ones needed to be loaded by looking in the manifest for WindowsApplication1. When the Framework loaded each assembly, it checked to see if those assemblies in turn needed additional assemblies loaded, and so on. Pretty soon, your once simple application becomes a dumping ground for assemblies all over the GAC. But that's OK, because the purpose of .NET is to manage it all.