I've been writing C for 2-3 years and recently picked up assembly but as I use Windows I've never needed to use make files before as I've just used Visual Studio. I'm trying to use Cygwin and an i686 cross compiler to compile c and assembly files, and then link them together into a binary file representing my operating system. I am new to make files so I don't know how to do this properly. This is what I've got so far for the Makefile
:
CC = i686-elf-gcc
CC_FLAGS = -c -std=gnu99 -ffreestanding -O2 -Wall -Wextra -I./include
AS = i686-elf-as
LD = i686-elf-gcc -T linker.ld -o myos.bin
LD_FLAGS = -ffreestanding -O2 -nostdlib -lgcc
O_FILES = $(wildcard src/*.o)
all: $(O_FILES)
$(LD) $(LD_FLAGS) $(O_FILES)
src/%.o: src/%.c
$(CC) $(CC_FLAGS) -o $@ $<
src/%.o: src/%.asm
$(AS) -o $@ $<
I'm getting an error stating that the linker can't find the entry symbol _start
so obviously nothing is compiling. How would I fix this?
My src/linker.ld
file that defines _start
as the entry point is:
ENTRY(_start)
SECTIONS
{
. = 1M;
.text BLOCK(4K) : ALIGN(4K)
{
*(.multiboot)
*(.text)
}
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
}
.bss BLOCK(4K) : ALIGN(4K)
{
*(COMMON)
*(.bss)
}
}
The src/boot.asm
file I'm using that defines my _start
label is:
# Declare constants for the multiboot header.
.set ALIGN, 1<<0 # align loaded modules on page boundaries
.set MEMINFO, 1<<1 # provide memory map
.set FLAGS, ALIGN | MEMINFO # this is the Multiboot 'flag' field
.set MAGIC, 0x1BADB002 # 'magic number' lets bootloader find the header
.set CHECKSUM, -(MAGIC + FLAGS) # checksum of above, to prove we are multiboot
.section .multiboot
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM
.section .bss
.align 16
stack_bottom:
.skip 16384 # 16 KiB
stack_top:
.section .text
.global _start
.type _start, @function
_start:
mov $stack_top, %esp
call kernel_main
cli
1: hlt
jmp 1b
.size _start, . - _start
You will need to modify your Makefile
to something like this:
CC = i686-elf-gcc
AS = i686-elf-as
LD = i686-elf-gcc
AS_FLAGS =
LD_FLAGS = -ffreestanding -nostdlib -lgcc -Tlinker.ld
CC_FLAGS = -c -std=gnu99 -ffreestanding -O2 -Wall -Wextra -I./include
C_FILES := $(wildcard src/*.c)
ASM_FILES := $(wildcard src/*.asm)
O_FILES := $(C_FILES:.c=.o) $(ASM_FILES:.asm=.o)
KERNEL_BIN := myos.bin
all: $(KERNEL_BIN)
clean:
rm -f $(KERNEL_BIN) $(O_FILES)
$(KERNEL_BIN): $(O_FILES)
$(LD) $(LD_FLAGS) -o $@ $^
%.o: %.c
$(CC) $(CC_FLAGS) -o $@ $<
%.o: %.asm
$(AS) $(AS_FLAGS) -o $@ $<
This Makefile
is different in that we construct a list of ASM files and C files. I also cleaned up the LD_FLAGS
a bit and added an extra rule to create myos.bin
and to clean
the object and bin files.
In your current code you'd have the Makefile variables with this in them after expansion:
C_FILES = src/string.c src/tty.c src/kernel.c
ASM_FILES = src/boot.asm
O_FILES = src/string.o src/tty.o src/kernel.o src/boot.o
O_FILES
was derived from concatenating both file lists together and replacing the .c
and .asm
extensions with .o
. This is the list of all the objects that need to be generated from their source files.
With GNU Assembler it is customary to use files with .s
extensions (or .S
if you want to use the C preprocessor) rather than .asm
The reason the _start
label wasn't found is because the assembly files were not being processed. This means that boot.asm
was not becoming boot.o
and thus wasn't being linked at all.