I wrote a small proof of concept bootloader (for floppy) fro someone else last month.It can be simplified by removing the BPB and CHS if using extended disk reads on media that supports that. It is a proof of concept because it assumes fast A20 support (it should do it properly to make it compatible with most BIOSes) and it reads only one sector at a time (in a loop) which is inefficient on slow floppy media but it does get the hob done. It also relocates the bootloader to 0x600 and starts reading the kernel at 0x800. Both these locations can be easily modified in the linker script. It enters in protected mode and transfers to 32-bit code written in _C_. The bootloader does use a linker script to determine where the BSS section is so that it can be zeroed out, and to determine the location and size of the kernel sectors. The code can be found here:
http://www.capp-sysware.com/misc/osdev/linkedboot/ . You can ignore the code in the two sub-directories. Rather than transferring to a kernel you could transfer to your elf loader. I'm only posting this here because it shows how you can use a linker script to control things.
NASM has a useful incbin directive to include a binary file. You could build an ELF executable first and then in a NASM assembly file use incbin to include the ELF executable directly. You can use a couple of labels to denote the start and end memory locations. A real mode example of code that loads a second stage as a binary file (without a linker script) can be found here (note: the stage2 code in this example is not mine, but someone else's):
http://www.capp-sysware.com/misc/osdev/boot_nasm_reloc/ . os.asm contains the bootloader plus uses incbin to include the second stage binary right after. That is done with code like this:
Code:
; Pad boot sector to 510 bytes and add 2 byte boot signature for 512 total bytes
TIMES 510-($-$$) db 0
dw 0xaa55
section .stage2 vstart=STAGE2_ABS_ADDR align=16
NUM_STAGE2_SECTORS equ (stage2_end-stage2_start+511) / 512
; Number of 512 byte sectors stage2 uses.
stage2_start:
; Insert stage2 binary here. It is done this way since we
; can determine the size(and number of sectors) to load since
; Size = stage2_end-stage2_start
incbin "stage2.bin"
; End of stage2. Make sure this label is LAST in this file!
stage2_end:
I forgot you are using GNU assembler. If you create a separate ELF executable but want to include the ELF executable as raw binary data inside an object that can be linked in with your code you can use objcopy to convert the ELF executable to binary and put it in an object file with something like:
Code:
objcopy --input binary --output elf32-i386 --binary-architecture i386 myfile.elf myfile.o
Doing this may seem convoluted, but it can work. OBJCOPY will even go out of its way to create the object file with some very useful symbols. If you were to use objdump -x myfile.o you'd see that it generated these symbols:
SYMBOL TABLE:
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 g .data 0000000000000000 _binary_myfile_elf_start
000000000000002c g .data 0000000000000000 _binary_myfile_elf_end
000000000000002c g *ABS* 0000000000000000 _binary_myfile_elf_size
How is this useful? Because if you link myfile.o (which contains the ELF executable) to your code you can tell what the address to the start of the ELF file is, where it ends, and its size. You wouldn't need to scan memory for the beginning and the end. You have labels to the start and end of the ELF file. I wrote this
Stackoverflow Answer that takes a text file and puts it into binary form inside an object and then prints out the string with some _C_ code using the generated symbols: