Visual Studio: Difference between revisions

no edit summary
[unchecked revision][unchecked revision]
No edit summary
No edit summary
 
(3 intermediate revisions by 2 users not shown)
Line 1:
{{FirstPerson}}
{{You}}
It is possible to use Microsoft Visual C++ to write OS kernel, however keep in mind that all the Visual C++ compiler is able to output is [[OMF]] format object files, and the linker can only produce [[PE Binaries]], so you will have to either use a bootloader which understands these formats or convert them to, for example, [[ELF]] or flat binary.
 
Visual Studio can be used as an integrated development environment for writing assembly, C and C++ with fully featured F5 debugging. Code can be compiled and debugged using either a [[GCC_Cross-Compiler]] toolchain you as you would normally use, or the Microsoft Visual C++ compiler. Due to the fact the standard Visual C++ compiler is only capable of emitting [[OMF]] object files, with the linker only producing [[PE Binaries]], if you pursue this avenue you will have to either use a bootloader which understands these formats or convert them to, for example, [[ELF]] or flat binary.
See also [http://ksrenevasan.blogspot.com/2005/10/writing-multiboot-pe-kernels-using.html Kaushik Srenevasan's blog] to see how you can "[Write] multiboot PE kernels using Visual C++". This is supplemented with [http://ksrenevasan.blogspot.com/2005/10/writing-multiboot-pe-kernels-using_03.html part 2] which explains some of the things not covered in the first part.
 
Due to the fact the only known way of doing native i686-elf operating system development in Visual Studio relies on third-party non-free software, the majority of this article explores how the native capabilities of Visual Studio can be used for doing operating systems development.
 
If you are interested in attempting to use the Visual C++ toolchain for operating systems development, [http://ksrenevasan.blogspot.com/2005/10/writing-multiboot-pe-kernels-using.html Kaushik Srenevasan's blog] serves as a good starting point to see how you can "[Write] multiboot PE kernels using Visual C++". This is supplemented with [http://ksrenevasan.blogspot.com/2005/10/writing-multiboot-pe-kernels-using_03.html part 2] which explains some of the things not covered in the first part.
 
MinGW32's objcopy should do the job (parameters go something like this: -x -g -X -S -Obinary kernel.bin). But you don't have to strip any information from PE file, the other option is to set section alignment in memory to 0x200 which is generally equal to the section alignment on disk and do some math:
<sourcesyntaxhighlight lang="asm">
mov eax, [es:0x3c] ; PE header pointer in MZ header
mov ecx, [es:eax+0x28] ; AddressOfEntryPoint in PE header
</syntaxhighlight>
</source>
 
where es is loaded with base address of your [[PE Binaries|PE]] file in memory, ecx will be loaded with the entry point relative to base address of PE file. Of course, if this is protected mode with a flat 4GB address space, use a register instead of using ES.
 
'''Note:''' The options and procedures described here for Visual C++ operating system development are for VS.NET 2003. Similar procedures should work on earlier versions. Consult the MSDN or post to the forum if you have any questions.
 
== Native i686-elf Development ==
 
====Pre-requisites====
In order to utilize Visual Studio as a development environment for native i686-elf operating system development, you must first install the following pre-requisites
 
* Visual Studio 2015+
* [https://visualgdb.com/ VisualGDB] (see below)
* [https://www.nasm.us/pub/nasm/releasebuilds/2.11.08/win32/nasm-2.11.08-installer.exe NASM 2.11.08]
* A [[GCC_Cross-Compiler|GCC Cross-Compiler toolchain]] that contains GDB. This process has been tested with [https://github.com/lordmilko/i686-elf-tools i686-elf-tools] (GCC 7.1.0 / Binutils 2.28 / GDB 8.0)
* [https://qemu.weilnetz.de/w32/2017/qemu-w32-setup-20170113.exe QEMU 2.8.50.0]
* An assembly syntax highlighter, such as [https://github.com/Trass3r/AsmHighlighter AsmHighlighter] (though you might have to compile it yourself)
 
It is '''strongly recommended''' you attempt to use the recommend versions of these items before attempting to use newer versions. In particular, issues have been observed when using NASM and QEMU versions newer than the ones listed above (see below).
 
Install QEMU to '''C:\Program Files (x86)\qemu''', NASM to '''C:\Program Files (x86)\nasm''' and extract i686-elf-tools to '''C:\Program Files (x86)\i686-elf-tools''' (such that the path to your GCC is '''C:\Program Files (x86)\i686-elf-tools\bin\i686-elf-gcc.exe''')
 
Unfortunately, in order to do operating system development in Visual Studio you will require the third-party add-in '''VisualGDB'''. Unlike all of the other software on this list, VisualGDB is not free. You can download a 30-day trial of VisualGDB from their website to demonstrate the principles outlined in this article and determine how you want to proceed.
 
The first time you launch Visual Studio after installing VisualGDB you may be prompted to configure the '''VISUALGDB_DIR''' environment variable (which you should do) as well as whether you want to utilize their Clang-based IntelliSense engine (you can disable of the options on this prompt). If you are interested in purchasing VisualGDB, it appears you will need the '''Custom''' VisualGDB edition.
 
This process has been tested with VisualGDB 5.1 in Visual Studio 2015. A sample project demonstrating a basic boot sector and kernel that you can compile, debug and step through can be found [https://github.com/lordmilko/VSKernelDev here].
 
====Configuration====
 
The following outlines how you can configure building and debugging your operating system in Visual Studio from scratch. Rather than configuring these one by one, you can also transplant (and rename based on your project name) the *.vgdbsettings files from the [https://github.com/lordmilko/VSKernelDev sample project] into your solution and you should be good to go.
 
* In Visual Studio create a new VisualGDB solution using the '''Custom Project Wizard'''. If prompted to configure the custom project's settings, click '''Finish''' to leave these as their defaults (we will configure them one by one below)
* Right click your project in Solution Explorer and select '''VisualGDB Project Properties'''
 
=====Build Settings=====
 
* On the ''Build Settings'' tab, next to ''Build command:'' click '''Customize''' and fill in the following details
: '''Command''': $(VISUALGDB_DIR)\make.exe
: '''Arguments''': all
: '''Working directory''': $(SourceDir)
 
* Next to ''Clean Command:'' click '''Customize''' and fill in the following details
: '''Command''': $(VISUALGDB_DIR)\make.exe
: '''Arguments''': clean
: '''Working directory''': $(SourceDir)
 
* In the ''Main binary:'' field enter the '''$(BuildDir)\<image>''' where '''<image>''' is the name of the file your '''Makefile''' generates that contains your entire operating system. e.g. '''$(BuildDir)\os-image'''
: The configuration steps outlined in this tutorial assume that all of your output files will be emitted in the most "convenient" place possible (the same place as your source files, the root of your project, etc). If you are interested in having your output files be emitted to a single directory, you should investigate this after you have the basic configuration working.
* Untick the ''Try detecting common Makefile types and updating source lists in them'' option. To begin with we would like to control everything ourselves; you can explore re-enabling options like this once we have the basic configuration working
 
=====Debug Settings=====
 
* On the ''Debug settings'' tab, deselect '''Break-in to GDB using Ctrl-Break events instead of Ctrl-C (required under Cygwin)'''
 
* Next to ''Use a custom GDB executable:'' click '''Customize''' and fill in the following details
: '''GDB debugger executable''': C:\Program Files (x86)\i686-elf-tools\bin\i686-elf-gdb.exe
* Next to ''GDB launch command:'' click '''Customize''' and fill in the following details
: '''Arguments''': --interpreter mi --readnow
: '''Working directory''': $(ProjectDir)
* Tick the '''Use a gdbserver:''' option then click '''Customize''' and fill in the following details
: '''Command''': C:\Program Files (x86)\qemu\qemu-system-i386.exe
: '''Arguments''': -S -gdb tcp::1234,ipv4 -soundhw all -drive file=$(TargetFileName),if=floppy
: '''Working directory''': $(ProjectDir)
* In the ''Target selection command:'' field enter '''-target-select remote :1234'''
 
* Change the '''Debugging start mode:''' to '''Use "continue" command'''
 
=====IntelliSense Settings=====
 
* On the '''IntelliSense Settings''' tab, under ''Clang IntelliSense'' set the ''IntelliSense engine:'' to '''Use native Visual Studio IntelliSense engine'''
 
=====GDB Settings=====
 
* On the '''GDB settings''' tab, untick '''Support 'step into new instance' through breakpoint in: main'''
: We don't need a breakpoint in any kind of main function, but if you want one at a later point you can re-enable this and change the function name to the real entrypoint of your kernel.
 
=====GDB Startup Commands=====
 
* On the '''GDB startup commands''' tab, under ''The following GDB commands will be run AFTER selecting a target:'' enter the following
 
<syntaxhighlight lang="c">
symbol-file kernel.elf
add-symbol-file boot_sect.elf 0x7c00
directory $(RemoteSourceDir)/src
</syntaxhighlight>
: These will load the symbols for your kernel and bootsector respectively, allowing you to debug through the sourcecode in your debugger.
: If you have all of your source under a '''src/''' subdirectory, it appears that NASM may cause all *.asm files but the main one to be resolved using relative, instead of absolute paths, thus resulting in GDB being unable to find them when attempting to set breakpoints. Specifying '''directory $(RemoteSourceDir)/src''' adds the '''src/''' folder as an additional search location for GDB to use when resolving breakpoint locations. If you're not using a '''src/''' folder, then you likely don't need this line
 
If you've successfully completed these steps you should be all ready to start developing your operating system using Visual Studio
 
====Important Considerations====
 
* When you modify an *.asm and recompile, it doesn't seem to automatically detect such files are modified; as such you may need to force rebuild instead, or investigate how to get your make system detect changes to assembly files
* When stepping through assembly code, until you switch to 32-bit protected mode you may have issues with your source files not always lining up with where the debugger is currently at. This seems to be an unavoidable consequence of trying to debug 16-bit code; once you start debugging 32-bit assembly or C/C++ however it all seems to be fine
* When you terminate QEMU, GDB will detect the process was terminated but won't actually end the debug session (as such you'll need to hit the Stop button yourself)
* If you try and create a '''Makefile''' in Solution Explorer with no file extension Visual Studio will probably add a '''.cpp''' to the end of it again and move it under the ''Source files'' filter. You will probably want to remove the extension again and maybe move it under the project root, outside the ''Source files'' filter
* Remember that in C++ projects in Visual Studio, folders in Solution Explorer simply represent "filters" rather than actual folders; as such as you start organizing your files remember to place your new files under the actual folder they belong in, along with organizing them under the correct filter
* QEMU versions newer than the stipualted version may experience errors when the recommended version of GDB 8.0 attempts to attach to them. If you wish to use a newer QEMU version, you will need to investigate whether modifying your QEMU command line arguments or [https://github.com/lordmilko/i686-elf-tools compiling a newer GDB version] resolves this issue
* NASM versions newer than the stipulated version may not generate symbol files properly, resulting in breakpoints in '''%include''''d assembly files always hitting the last line of the file. If you wish to use a newer NASM version, you will need to investigate whether modifying your NASM command line arguments or [https://github.com/lordmilko/i686-elf-tools compiling a newer GDB version] resolves this issue
 
== Visual C++ vs Visual Studio ==
Line 33 ⟶ 137:
== Some basic definitions: ==
 
<sourcesyntaxhighlight lang="c">#define EXTERN extern "C"
#define EXPORT EXTERN __declspec(dllexport) // exported from DLL
#define IMPORT EXTERN __declspec(dllimport) // imported from DLL
Line 53 ⟶ 157:
}
}
</syntaxhighlight>
</source>
 
I use these to create functions that end up with reasonably undecorated names like <code>_SomeFunction@8</code> instead of <code>?@SomeFunction@YAKK000I@@Z</code> (as a __cdecl normal function would be named...) The macros also allow easy import and export from a DLL.
Line 162 ⟶ 266:
To be booted by GRUB, you can make your kernel multiboot. THis involves the embedding of a multiboot header in the first 8K of the image.
This can be done as follows:
<sourcesyntaxhighlight lang="cpp">
//Entry point goes here
 
Line 190 ⟶ 294:
(uint32_t)&entry - BASE_ADDR + LOADBASE
};
</syntaxhighlight>
</source>
 
== The Rebase Utility ==
Line 220 ⟶ 324:
 
This is why if you intend to do 64 bit development in MSVC++ you should have an external assembly layer (seperate versions for 32 bit and 64 bit), or use somewhat more limited intrinsics, and for asm if you want to avoid name decoration you need to declare them in a header file like this:
<sourcesyntaxhighlight lang="cpp">
#ifdef __cplusplus //if this is C++
extern "C" { //declare as C functions
Line 229 ⟶ 333:
} //and if it was C++ we need to close the brackets
#endif
</syntaxhighlight>
</source>
And in your asm layer:
<sourcesyntaxhighlight lang="asm">
BITS 32 ;32 bit version
@disable@0:
Line 242 ⟶ 346:
cli
ret ;No decoration at all
</syntaxhighlight>
</source>
 
== Intrinsics ==
For intrinsics, #include <intrin.h>. This IS suitable for a kernel, but don't ignore standard headers. An example follows:
<sourcesyntaxhighlight lang="cpp">
//main body of intrinsics
#include <intrin.h>
Line 261 ⟶ 365:
__halt(); //HLT
}
</syntaxhighlight>
</source>
 
From more intrinsics, see [http://msdn.microsoft.com/en-us/library/26td21ds.aspx Compiler Intrinsics]