I am following this half-completed tutorial to develop a simple OS. One step (on page 50) is to compile a simple kernel with $ld -o kernel.bin -Ttext 0x1000 kernel.o --oformat binary
. However I don't really understand what is the option -Ttext
doing.
To make the question specific, why in the following experiment are md5s of kernel_1000.bin
& kernel.bin
equal, kernel_1001.bin
& kernel_1009.bin
equal, and kernel_1007.bin
& kernel_1017.bin
equal, while all other pairs are not equal?
I tried to compile several different kernels with different -Ttext
like the in the following Makefile
:
...
kernel.o: kernel.c
gcc -ffreestanding -c kernel.c
kernel.bin: kernel.o
ld -o $@ kernel.o --oformat binary
kernel_1000.bin: kernel.o
ld -o $@ -Ttext 0x1000 kernel.o --oformat binary
kernel_1001.bin: kernel.o
ld -o $@ -Ttext 0x1001 kernel.o --oformat binary
...
And then I checked their md5:
$ ls *.bin | xargs md5sum
d9248440a2c816e41527686cdb5118e4 kernel_1000.bin
65db5ab465301da1176b523dec387a40 kernel_1001.bin
819a5638827494a4556b7a96ee6e14b2 kernel_1007.bin
d9248440a2c816e41527686cdb5118e4 kernel_1008.bin
65db5ab465301da1176b523dec387a40 kernel_1009.bin
216b24060abce034911642acfa880403 kernel_1015.bin
e92901b1d12d316c564ba7916abca20c kernel_1016.bin
819a5638827494a4556b7a96ee6e14b2 kernel_1017.bin
d9248440a2c816e41527686cdb5118e4 kernel.bin
void main() {
char* video_memory = (char*) 0xb8000;
*video_memory = 'X';
}
$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.9/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.9.2-10' --with-bugurl=file:///usr/share/doc/gcc-4.9/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.9 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.9 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.9-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --with-arch-32=i586 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.9.2 (Debian 4.9.2-10)
$ uname -a
Linux localhost 3.16.0-4-amd64 #1 SMP Debian 3.16.7-ckt20-1+deb8u1 (2015-12-14) x86_64 GNU/Linux
The -Ttext
option puts the .text section of your program by the given address. For example if you are compile this assembly code:
section .text
global _start
_start:
mov al, '!'
jmp l
l: mov ah, 0x0e
mov bh, 0x00
mov bl, 0x07
int 0x10
jmp $
times 510-($-$$) db 0
db 0x55
db 0xaa
with:
nasm -f elf64 -o test.o test.S
ld -o test test.o
And will look on it with the objdump
, you will see that it was linked by default address, something around 0x0000000000400000
for the x86_64
:
~$ objdump -D test
test: file format elf64-x86-64
Disassembly of section .text:
0000000000400080 <_start>:
400080: b0 21 mov $0x21,%al
400082: eb 00 jmp 400084 <l>
0000000000400084 <l>:
400084: b4 0e mov $0xe,%ah
...
...
...
And all addresses in the program (at least in the .text
section) will be relative to this address. If you will add the -Ttext 1000
option, you will see:
~$ objdump -D test
test: file format elf64-x86-64
Disassembly of section .text:
0000000000001000 <_start>:
1000: b0 21 mov $0x21,%al
1002: eb 00 jmp 1004 <l>
0000000000001004 <l>:
1004: b4 0e mov $0xe,%ah
That you program will be linked to start at 0x1000
address and all addresses (including jmp and etc.) will be relative to the 0x1000
to.
This important for two things. In short words, when an operating system kernel loads your program, it loads your executable which is in elf format or in other binary format and reads where the .text
section starts. In our case, you can link your kernel.bin
as you want, because there are no loaders as an operating system kernel and your are master of all memory space.
So if you will link your kernel.bin
to start at 0x1000
, you will know where the code starts to work (of course if it will actually loaded at this place in memory) and if you know the base address of your code, you can get all addresses inside it, something like my_label_inside_program - _start
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With