Go Bare Bones: Difference between revisions

m
Fix lint errors
[unchecked revision][unchecked revision]
No edit summary
m (Fix lint errors)
 
(8 intermediate revisions by 4 users not shown)
Line 1:
{{BeginnersWarning}}
In this tutorial you'll learn how to get started using the Go language to write your own OS. It will be an example of how to create a very minimal system to get text on the screen. It's in no way an example of how you should organize or structure your project.
{{Rating|2}}
 
In this tutorial you'll learn how to get started using the Go language to write your own OS. It will be an example of how to create a very minimal system to get text on the screen. It's in no way an example of how you should organize or structure your project.
<big><b>WAIT! Have you read [[Getting Started]], [[Beginner Mistakes]], and some of the related [[:Category:OS theory|OS theory]]?</b></big>
 
== Preface ==
Line 10 ⟶ 11:
 
== Building a Cross-Compiler ==
:''{{Main article: [[|GCC Cross-Compiler]], [[|Why do I need a Cross Compiler?]]}}
 
The first thing you should do is set up a GCC Cross Compiler that supports Go. To do this read and follow [[GCC Cross-Compiler]] to the letter with one exception. When configuring the build for GCC we need to enable Go to get the i686-elf-gccgo compiler.
 
So instead of using: <sourcesyntaxhighlight lang="bash">../gcc-x.y.z/configure --target=$TARGET --prefix="$PREFIX" --disable-nls --enable-languages=c,c++ --without-headers</sourcesyntaxhighlight> We use: <sourcesyntaxhighlight lang="bash">../gcc-x.y.z/configure --target=$TARGET --prefix="$PREFIX" --disable-nls --enable-languages=c,c++,go --without-headers</sourcesyntaxhighlight>
 
== Overview ==
Line 76 ⟶ 77:
# This is useful when debugging or when you implement call tracing.
.size _start, . - _start
 
# Go doesn't allow you to directly access memory or use pointers. A trick to get
# around this is to have a function that takes a 32bit value and returns it.
# Take a look at 'terminal.go' to see how we use this to access video memory.
.global _itop
.type _itop, @function
_itop:
push %ebp
mov %esp, %ebp
mov 8(%ebp), %eax
mov %ebp, %esp
pop %ebp
ret
.size _itop, . - _itop
 
# The Go runtime is a big problem when wanting to write a kernel in Go.
Line 96 ⟶ 83:
# goes way beyond the scope of this barebone.
#
# Beware that there are allmostalmost no language features you can use at this point.
# The linker will fail with missing symbols when you try to use them.
#
# You have to implement those missing symbols yourself to get things working.
# Sometimes you can get away with declaring them as an empty function.
# But this won't allwaysalways work.
#
# After you get this bare bone working, the first priority should be to write
Line 124 ⟶ 111:
 
You can then assemble boot.s using:
<sourcesyntaxhighlight lang="bash">i686-elf-as boot.s -o boot.o</sourcesyntaxhighlight>
 
== Writing the Terminal package in Go ==
:''Please read [[Printing to Screen]] to understand what this code does.''
 
Now we'll create the file terminal.go. It's the package "terminal" our kernel will depend on for printing text to the screen.
<sourcesyntaxhighlight lang="Go">package terminal
 
import "unsafe"
 
/*
* Map the text mode video memory into a multi-dimensional array that can be safely
* In boot.s we wrote the function _itop that returns the value given to it.
* used from Go.
* With the gccgo compiler you don't have to use cgo to import a function.
*
* The way you import a function is to specify it like: //extern <function>
* Right after that you put the Go function definition you want to use for it.
*
* In this case we make it return a pointer for a multi dimentional array that
* maps the textmode video memory.
*/
 
func get_vidMem(addr uint32) *[25][80][2]byte {
//extern _itop
func buff get_vidMem(addr uint32):= (*[25][80][2]byte)(unsafe.Pointer(uintptr(addr)))
return buff
}
 
/*
Line 226 ⟶ 211:
}
}
</syntaxhighlight>
</source>
 
Compile using: <sourcesyntaxhighlight lang="bash">i686-elf-gccgo -static -Werror -nostdlib -nostartfiles -nodefaultlibs -c terminal.go -o terminal.go.o</sourcesyntaxhighlight>
 
== How imported packages are found ==
Line 239 ⟶ 224:
* .o
 
A .gox file contains just the import data. If you wanted to extract it from our terminal.go.o object file we use: <sourcesyntaxhighlight lang="bash">i686-elf-objcopy -j .go_export terminal.go.o terminal.gox</sourcesyntaxhighlight>
 
== Writing a kernel in Go ==
Line 245 ⟶ 230:
Now we create the file kernel.go that contains the Main() function called from our bootstrap assembly we've already created and compiled above. It will import the terminal package we also created & compiled. Then we use that package to print text to the screen.
 
<sourcesyntaxhighlight lang="go">package kernel
 
import "terminal"
Line 262 ⟶ 247:
// Print our Hello, World!
terminal.Print("Hello, Kernel World!\n")
}</sourcesyntaxhighlight>
 
Compile using: <sourcesyntaxhighlight lang="bash">i686-elf-gccgo -static -Werror -nostdlib -nostartfiles -nodefaultlibs -c kernel.go -o kernel.go.o</sourcesyntaxhighlight>
 
== Linking the Kernel ==
Line 316 ⟶ 301:
 
You can link your kernel using:
<sourcesyntaxhighlight lang="bash">i686-elf-gcc -T linker.ld -o myos.bin -ffreestanding -O2 -nostdlib boot.o terminal.go.o kernel.go.o -lgcc</sourcesyntaxhighlight>
 
The file myos.bin is now your kernel. Note that we are linking against [[libgcc]], which implements various routines that your cross-compiler depends on. Leaving it out will give you problems in the future.
Line 325 ⟶ 310:
 
QEMU supports booting multiboot kernels directly without using a bootable medium:
<sourcesyntaxhighlight lang="bash">qemu-system-i386 -kernel myos.bin</sourcesyntaxhighlight>
 
== External Links ==
Line 331 ⟶ 316:
* [https://www.gnu.org/software/grub/manual/multiboot/multiboot.html Multiboot Specification]
* [https://golang.org/doc/install/gccgo Setting up and using gccgo]
 
[[Category: Bare bones tutorials]]