c hello world works

This commit is contained in:
Ciro Santilli
2018-12-04 07:49:38 +00:00
parent 589240ebd1
commit b4e4c124a3
6 changed files with 33 additions and 16 deletions

View File

@@ -239,8 +239,6 @@ Source: link:bios_hello_world.S[]
==== C hello world ==== C hello world
TODO get working.
Same output as <<bios-hello-world>>, but written in C: Same output as <<bios-hello-world>>, but written in C:
.... ....
@@ -250,7 +248,13 @@ cd c_hello_world
Source: link:c_hello_world[] Source: link:c_hello_world[]
Single stage, so still limited to 512 bytes of code + data! But keep in mind the following limitations and difficulties:
* single stage, so still limited to 512 bytes of code + data!
* use use GCC's `-m` which does not produce "real" 16 bit code, but rather 32-bit code with `0x66` and `0x67` prefixes: https://wiki.osdev.org/X86-64_Instruction_Encoding#Legacy_Prefixes
* setting up the initial state and the linker script is much harder and error prone than with assembly
Therefore, for most applications, you will just want to use <<multiboot>> instead, which overcomes all of those problems.
To disassemble the generated C code, try: To disassemble the generated C code, try:
@@ -258,11 +262,20 @@ To disassemble the generated C code, try:
objdump -D -m i8086 main.elf objdump -D -m i8086 main.elf
.... ....
TODO: I see several `eax` references still, why? but note that it still contains references to 32-bit references, e.g.:
....
00007c17 <main>:
7c17: 66 55 push %ebp
7c19: 66 89 e5 mov %esp,%ebp
7c1c: 66 83 ec 10 sub $0x10,%esp
....
This is because those instructions are modified by the prefix `0x66`, which makes them behave like 32-bit.
=== No linker script === No linker script
Print `hello world` without using an explitic linker script: Print `hello world` without using an explicit linker script:
.... ....
make -C no-linker-script run make -C no-linker-script run

2
c_hello_world/clean Executable file
View File

@@ -0,0 +1,2 @@
#!/usr/bin/env bash
rm -f *.o *.elf *.img

View File

@@ -2,5 +2,12 @@
.text .text
.global mystart .global mystart
mystart: mystart:
mov $__stack_top, %sp ljmp $0, $.setcs
.setcs:
xor %ax, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %ss
mov $__stack_top, %esp
cld
call main call main

View File

@@ -8,7 +8,9 @@ SECTIONS
*(.data) *(.data)
*(.rodata) *(.rodata)
__bss_start = .; __bss_start = .;
/* COMMON vs BSS: https://stackoverflow.com/questions/16835716/bss-vs-common-what-goes-where */
*(.bss) *(.bss)
*(COMMON)
__bss_end = .; __bss_end = .;
} }
/* https://stackoverflow.com/questions/53584666/why-does-gnu-ld-include-a-section-that-does-not-appear-in-the-linker-script */ /* https://stackoverflow.com/questions/53584666/why-does-gnu-ld-include-a-section-that-does-not-appear-in-the-linker-script */

View File

@@ -1,18 +1,11 @@
void main(void) { void main(void) {
int i; int i;
char s[] = "hello world"; char s[] = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};
/* Add a bunch of debug prints to see if the control loop is correct. It is. */
__asm__ ("mov $0x0E40, %ax; int $0x10");
for (i = 0; i < sizeof(s); ++i) { for (i = 0; i < sizeof(s); ++i) {
__asm__ ("mov $0x0E41, %ax; int $0x10");
__asm__ ( __asm__ (
"mov %0, %%ax; int $0x10" "int $0x10" : : "a" ((0x0e << 8) | s[i])
:
: "m" (s[i])
: "%ax"
); );
} }
__asm__ ("mov $0x0E40, %ax; int $0x10");
while (1) { while (1) {
__asm__ ("hlt"); __asm__ ("hlt");
}; };

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -eux set -eux
as -ggdb3 --32 -o entry.o entry.S as -ggdb3 --32 -o entry.o entry.S
gcc -c -ggdb3 -m16 -nostartfiles -nostdlib -o main.o -std=c99 main.c gcc -c -ggdb3 -m16 -ffreestanding -fno-PIE -nostartfiles -nostdlib -o main.o -std=c99 main.c
ld -m elf_i386 -o main.elf -T linker.ld entry.o main.o ld -m elf_i386 -o main.elf -T linker.ld entry.o main.o
objcopy -O binary main.elf main.img objcopy -O binary main.elf main.img
qemu-system-x86_64 -drive file=main.img,format=raw qemu-system-x86_64 -drive file=main.img,format=raw