ffcactus
ffcactus

Reputation: 172

How bootloader find the DS section in binary file

I'm study the kernel development. I have a bootloader and can load my binary file to the memory and run it. The binary file is generated from C language, the build process like this:

i686-elf-gcc -c main.c -o main.o -std=gnu99 -ffreestanding -Wall -Wextra
i686-elf-gcc -m32 -Ttext 0x8000 -o startup.elf -ffreestanding -O2 -nostdlib main.o -lgcc
objcopy -O binary startup.elf startup.bin

The i686-elf-gcc is my GCC cross compiler. Since my bootloader is a little big, and I think it has no relationship between the problem, I'm not going to show it here. My bootloader will prepare a the protect mode, enable A20, and setup 4GB code/data descriptor table for the C binary file. And the C binary file works fine. I let it clean the screen, and print "Hello World!", it works. The problem is that if my main.c contains initialized data section, it doesn't work well. For example, if I set the default (X, Y) coordination to (10, 10), the "Hello World" still show at the most top left. The problem is when I convert the elf file to binary, the binary executive can use the information(10, 10).

When I use hexdump to check the binary file, I can find the data value. For example if I have

uint16_t ds = 0x1234;

I can see the 0x1234 in the binary file, it looks like it doesn't locate at the code section but at another section. (For example code section start at 0x0000, but 0x1234 located far at 0x2000) And I also found that the "main" function expect the data section to be saved in the stack, so that it can refer to. So my bootloader need get the data section from the binary file, and push it to the stack. But how can I find location and size of the data section in the binary file? If my solution is wrong, then how to let the bootloader load a executable which contains data section?

Upvotes: 0

Views: 387

Answers (1)

ffcactus
ffcactus

Reputation: 172

My problem is solved. My bootloader only copied 1 sector (2k bytes) from the CD, and my C program is bigger than 2k, so it left the initialized values uncopied.

To make the the question clearer, I'd like to better explain it. Here is an example:

baibin@baibin-Z620:~/workspace/tmp$ cat main.c
#include <stdint.h>

uint16_t ds = 0x1111;
void _start(void) {
        ds++;
}
baibin@baibin-Z620:~/workspace/tmp$ cat Makefile
all:
        i686-elf-gcc -c main.c -o main.o -std=gnu99 -ffreestanding -Wall -Wextra
        i686-elf-gcc -m32 -Ttext 0x8000 -o startup.elf -ffreestanding -nostdlib main.o -lgcc
        objcopy -O binary startup.elf startup.bin
clean:
        @rm main.o startup.*
baibin@baibin-Z620:~/workspace/tmp$ objdump -s -S startup.elf

startup.elf:     file format elf32-i386

Contents of section .text:
 8000 5589e50f b7055090 000083c0 0166a350  U.....P......f.P
 8010 90000090 5dc3                        ....].
Contents of section .eh_frame:
 8018 14000000 00000000 017a5200 017c0801  .........zR..|..
 8028 1b0c0404 88010000 1c000000 1c000000  ................
 8038 c8ffffff 16000000 00410e08 8502420d  .........A....B.
 8048 0552c50c 04040000                    .R......
Contents of section .data:
 9050 1111                                 ..
Contents of section .comment:
 0000 4743433a 2028474e 55292035 2e322e30  GCC: (GNU) 5.2.0
 0010 00                                   .

Disassembly of section .text:

00008000 <_start>:
    8000:       55                      push   %ebp
    8001:       89 e5                   mov    %esp,%ebp
    8003:       0f b7 05 50 90 00 00    movzwl 0x9050,%eax
    800a:       83 c0 01                add    $0x1,%eax
    800d:       66 a3 50 90 00 00       mov    %ax,0x9050
    8013:       90                      nop
    8014:       5d                      pop    %ebp
    8015:       c3                      ret
baibin@baibin-Z620:~/workspace/tmp$ hexdump -C startup.bin
00000000  55 89 e5 0f b7 05 50 90  00 00 83 c0 01 66 a3 50  |U.....P......f.P|
00000010  90 00 00 90 5d c3 00 00  14 00 00 00 00 00 00 00  |....]...........|
00000020  01 7a 52 00 01 7c 08 01  1b 0c 04 04 88 01 00 00  |.zR..|..........|
00000030  1c 00 00 00 1c 00 00 00  c8 ff ff ff 16 00 00 00  |................|
00000040  00 41 0e 08 85 02 42 0d  05 52 c5 0c 04 04 00 00  |.A....B..R......|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00001050  11 11                                             |..|
00001052
baibin@baibin-Z620:~/workspace/tmp$

As you can see, the DS is expected to be located in 0x9050, after the the main be loaded. If my binary only loaded from 0x0000 to 0x0050, the DS won't be initialized properly.


Craig Estey commented and asked the question "How did you discover this? Also, how did you fix this? Before your answer post, I had been considering a data section relocation problem. If you changed your build commands, to help others with similar problems in the future, it would be nice if you could edit your answer and add the changed commands"

This is my reply to Craig Estey's question:

I'm going to verify if my C executable works fine after the bootloader loads it. The easiest way is to show something on the screen. At first, I wrote a very simple print() like this:

void print() {
    uint8_t x = 80 / 2;
    uint8_t y = 25 / 2;
    // print hello world;
}

it works fine. the "hello world" print at the middle of the screen. Then I improve the function. I move the x, y outside as the static value.

uint8_t x = 80 / 2;
uint8_t y = 25 / 2;
void print() {
    // print hello world;
}

My program begins triple faulting. Why can't I define .ds data? I used objdump and hexdump to check what happened to the executable. I found the hexdump can display the x, y value in the binary, and the offset matches the read instruction. So I doubt if my bootloader has bugs?

Yes, my boot loader did have a bug. It only read 1 sector on the CD, so the values were left uncopied. CDs have a sector size of 2k, but the BIOS only read 512 bytes for the first sector. What's more, if I let x = 0, y = 0, and put them as static value outside, do you think what will happen? OMG, triple fault again!

The x, y will turn from .ds to .bss, and won't be included in elf and binary. (elf only has the size, but no initialization value, because the default value is 0).

To generate a workable binary, you need specify the option like this:

objcopy -O binary --set-section-flags .bss=alloc,load,contents startup.elf startup.bin

Upvotes: 1

Related Questions