Page 1 of 1
Directory organization suggestions
Posted: Fri Mar 27, 2020 11:25 pm
by sunnysideup
Hello! I have been writing a small operating system so far, from a bootloader to a basic kernel. Stuff like interrupt handling, keyboard drivers, etc. are already done. I want to organize my code a little better. Here's what I haven't done yet... No user mode, no libc, etc.
Here's a tree organization of my directory... (you can also find it on
http://www.github.com/kssuraaj28)
Code: Select all
├── boot
│ ├── stage1
│ │ ├── CHS.asm
│ │ ├── debug
│ │ ├── disk_read.asm
│ │ ├── print.asm
│ │ └── stage1.asm
│ └── stage2
│ ├── func16.asm
│ ├── func32.asm
│ ├── GDT.asm
│ ├── pagingsetup.asm
│ └── stage2.asm
├── disk.img
├── kernel
│ ├── asm
│ │ ├── cursor.asm
│ │ ├── cursor.o
│ │ ├── entry.asm
│ │ ├── entry.o
│ │ ├── hal.asm
│ │ ├── hal.o
│ │ ├── interruptstubs.asm
│ │ ├── interruptstubs.o
│ │ └── timer.o
│ ├── c
│ │ ├── io.c
│ │ ├── o.h
│ │ ├── io.o
│ │ ├── hal.c
│ │ ├── hal.h
│ │ ├── hal.o
│ │ ├── inthandling.c
│ │ ├── inthandling.h
│ │ ├── inthandling.o
│ │ ├── kernel.c
│ │ ├── kernel.o
│ │ ├── keyboard.c
│ │ ├── keyboard.h
│ │ ├── keyboard.o
│ │ ├── kshell.c
│ │ ├── kshell.o
│ │ ├── phymem.c
│ │ ├── phymem.h
│ │ ├── phymem.o
│ │ ├── timer.c
│ │ ├── timer.h
│ │ ├── timer.o
│ │ ├── virtmem.c
│ │ ├── virtmem.h
│ │ └── virtmem.o
│ ├── kernel.elf
│ └── linker.ld
├── kernel.bin
├── Makefile
├── readme.md
├── script.sh
├── stage1.bin
└── stage2.bin
Clearly the code organization of the kernel directory is very very poor. What's the tradtional way this is usually done?
Here's the makefile, which describes how I build the OS.
Code: Select all
C_SOURCES = $(wildcard kernel/c/*.c)
ASM_SOURCES = $(wildcard kernel/asm/*.asm)
C_OBJECTS = ${C_SOURCES:.c=.o}
ASM_OBJECTS = ${ASM_SOURCES:.asm=.o}
.PHONY : all assemble run clean
all: assemble
run : assemble
qemu-system-i386 -drive format=raw,file=disk.img -monitor stdio
debug: assemble
qemu-system-i386 -s -hda disk.img &
gdb -ex "target remote localhost:1234" -ex "symbol-file kernel/kernel.elf" -ex "b kmain" -ex "continue"
assemble: disk.img kernel.bin stage1.bin stage2.bin
dd if=stage1.bin of=disk.img bs=1 count=3 seek=0 skip=0 conv=notrunc
dd if=stage1.bin of=disk.img bs=1 count=451 seek=62 skip=62 conv=notrunc
mcopy -i disk.img stage2.bin kernel.bin :: -D o
kernel.bin : kernel/kernel.elf
objcopy -O binary $^ $@
chmod -x $@
#You can use the --print-map option to look at what the linker does
kernel/kernel.elf : $(C_OBJECTS) $(ASM_OBJECTS)
i686-elf-ld $^ -T kernel/linker.ld -e kmain -o $@
chmod -x $@
%.o : %.c
i686-elf-gcc -ffreestanding $< -c -o $@ -Wall -Werror -g
%.o : %.asm
nasm $< -o $@ -f elf32
stage1.bin : boot/stage1/stage1.asm
nasm $^ -f bin -o $@
stage2.bin: boot/stage2/stage2.asm
nasm $^ -f bin -o $@
disk.img:
truncate $@ -s 1M
mkfs.vfat -F12 -S512 -s1 $@
clean :
rm $(C_OBJECTS) $(ASM_OBJECTS) *.bin
This is really bad organization.... and I believe that it would cause scalability problems in the future.. Any suggestions/criticisms?
Re: Directory organization suggestions
Posted: Sat Mar 28, 2020 4:08 am
by bzt
Hi,
sunnysideup wrote:This is really bad organization.... and I believe that it would cause scalability problems in the future.. Any suggestions/criticisms?
I'm not sure what you mean by scalable source tree. But if you meant to be expendable or portable in the future, then I'd recommend organizing your code along those lines. For example, instead of "asm", use "x86" or something. Otherwise your structure is not bad, and if it's x86 only and monolithic, then it's perfectly fine. Maybe I'd create a separate "drivers" directory, but that's all.
No golden rule here, all OSes are different, so a different directory structure fits their need. I'd recommend to think about what parts you want to be extendable and separate those (boot, common executor, drivers etc.). If your OS is going to be multiplatform, then separate platform dependent code from common code too, otherwise don't care.
Cheers,
bzt
Re: Directory organization suggestions
Posted: Sat Mar 28, 2020 4:15 am
by Korona
There is no one-size-fits-all solution for this. If you want your OS to be portable, separate architecture specific code (assembly + C) from architecture-independent code (C files only). You might want to split your directories according to functionality (MM, tasking, etc.) instead of C/assembly.
A few other notes regarding your build system:
- Storing object files and source files in the same directory is bad practice. It prevents you from using multiple build directories which in turn prevents other things like performing clean builds with clang-tidy, scan-build or other static analysis tools. With out-of-source trees, you just delete the build directory to "make clean". You can be 100% sure that a clean rebuild is reproducible, without any effort.
- This might be controversial on this forum but plain makefiles are not an adequate solution in 2020. Use Meson, CMake or any other established build system (but not autotools, please!). Header dependency discovery in plain makefiles is a pain. configure scripts tend to be hand-rolled with plain make which is also a pain to deal with. Many custom makefiles neglect to support --prefix and/or DESTDIR. Many custom makefiles to not support out-of-source builds. Often, wildcards are used that prevent the makefiles from scaling to large directories. The list goes on...
If you don't have experience with build systems, I recommend Meson. It supports cross-compilation (also to bare metal ELF targets) out-of-the-box and does not need customization for non-mainstream OSes.
Re: Directory organization suggestions
Posted: Sat Mar 28, 2020 4:20 am
by bzt
Korona wrote:wildcards are used that prevent the makefiles from scaling to large directories.
What do you mean by that? Would you mind to elaborate? (I'm not saying you're not correct about this, I'm not sure what you mean).
Otherwise I completely agree with you on the other points. Unless you're an experienced programmer and you know exactly what you're doing, hand written configure and make only build is not suitable for you. That requires a lot of experience to do it right, beginners are better off with CMake or something similar.
Cheers,
bzt
Re: Directory organization suggestions
Posted: Sat Mar 28, 2020 5:35 am
by eekee
bzt wrote:instead of "asm", use "x86" or something. Otherwise your structure is not bad, and if it's x86 only and monolithic, then it's perfectly fine.
This can work well, yes. Plan 9 gets a lot of mileage out of it. The kernel build files operate in arch-specific dirs, sometimes building source from `../port`.
Korona wrote:- Storing object files and source files in the same directory is bad practice. It prevents you from using multiple build directories which in turn prevents other things like performing clean builds with clang-tidy, scan-build or other static analysis tools. With out-of-source trees, you just delete the build directory to "make clean". You can be 100% sure that a clean rebuild is reproducible, without any effort.
Yeah. Plan 9 stores object files with source files, and developed bugs where `make clean` didn't clean properly. Also, it means Plan 9 has to use a different extension for each architecture, and uses that extension for a prefix for binaries before they're installed. This creates complexity for Plan 9 with its `mk` and unique compilers; it would be worse with mainstream compilers, make, or other build systems. Better to put object files in a separate directory. (The fact that the extension is different from $objtype also creates problems including complexifying scripts to premature obsolescence. They reassigned '7' from Alpha to Arm64 while my friend was still using Alpha machines. They could instead store object files and pre-install executables under, for example, /386/obj, where executables go in /386/bin and libraries in /386/lib.)
Other than that big caveat, Plan 9 has an interesting build system. Its `mk` is quite programmable. Defaults are provided by standard mkfiles included in most other mkfiles.
bzt wrote:beginners are better off with CMake or something similar.
CMake itself seems to have developed a Red Hat style business model: Make all but beginner tasks difficult enough that people will pay for support contracts. It would probably be best to look into other build systems instead. I got burned badly when Red Hat adopted this business model just as I was getting into Linux.
Re: Directory organization suggestions
Posted: Sat Mar 28, 2020 9:36 am
by sunnysideup
Alright, where would you suggest I put header files? The same directory as their corresponding .c files? This seems like a bad idea... Do I make a include directory? I feel this is more appropriate with a libc.. any suggestions?
Re: Directory organization suggestions
Posted: Sat Mar 28, 2020 10:38 am
by Klakap
I dont use header files, all is on start of c file.
Re: Directory organization suggestions
Posted: Sat Mar 28, 2020 12:50 pm
by nullplan
sunnysideup wrote:Alright, where would you suggest I put header files? The same directory as their corresponding .c files? This seems like a bad idea... Do I make a include directory? I feel this is more appropriate with a libc.. any suggestions?
Depends on taste, really. Include directories seem to be the standard way, though they can get quite complex (you didn't plan on dumping all header files into a single directory, did you?). Also note that there is no need to have one header for every C file, you can group headers together if you'd like. Also, you might need headers for your assembly files as well.
Klakap wrote:I dont use header files, all is on start of c file.
That is an astonishingly bad idea. At work I have to deal with code created like this, and the fallout is horrible to this day. Because this way you end up repeating declarations all over the place, and if any declaration changes, but you overlook one declaration somewhere, the compiler will not notice, nor will the linker, and you end up calling functions with the wrong arguments. Just don't do this.
Re: Directory organization suggestions
Posted: Mon Mar 30, 2020 4:16 am
by sunnysideup
Alright, I've done a bit of remodelling, but my lack of experience seems very relevant
.
Here's my new structure:
Code: Select all
.
├── boot
│ ├── stage1
│ │ ├── CHS.asm
│ │ ├── debug
│ │ ├── disk_read.asm
│ │ ├── print.asm
│ │ └── stage1.asm
│ └── stage2
│ ├── func16.asm
│ ├── func32.asm
│ ├── GDT.asm
│ ├── pagingsetup.asm
│ └── stage2.asm
├── kernel
│ ├── driver
│ │ ├── ATA.c
│ │ ├── ATA.o
│ │ ├── cursor.asm
│ │ ├── cursor.o
│ │ ├── dadio.c
│ │ ├── dadio.o
│ │ ├── keyboard.c
│ │ ├── keyboard.o
│ │ ├── timer.c
│ │ └── timer.o
│ ├── hal
│ │ ├── hal.c
│ │ ├── hal.o
│ │ ├── interruptstubs.asm
│ │ ├── interruptstubs.o
│ │ ├── inthandling.c
│ │ ├── inthandling.o
│ │ ├── x86.asm
│ │ └── x86.o
│ ├── include
│ │ ├── ATA.h
│ │ ├── dadio.h
│ │ ├── FAT12.h
│ │ ├── hal.h
│ │ ├── inthandling.h
│ │ ├── keyboard.h
│ │ ├── phymem.h
│ │ ├── timer.h
│ │ └── virtmem.h
│ ├── kernel
│ │ ├── entry.asm
│ │ ├── entry.o
│ │ ├── kernel.c
│ │ ├── kernel.o
│ │ ├── kshell.c
│ │ └── kshell.o
│ ├── kernel.elf
│ ├── linker.ld
│ └── mem
│ ├── phymem.c
│ ├── phymem.o
│ ├── virtmem.c
│ └── virtmem.o
├── Makefile
├── readme.md
├── script.sh
Code: Select all
C_SOURCES = $(wildcard kernel/*/*.c)
ASM_SOURCES = $(wildcard kernel/*/*.asm)
C_OBJECTS = ${C_SOURCES:.c=.o}
ASM_OBJECTS = ${ASM_SOURCES:.asm=.o}
.PHONY : all assemble run clean
all: run
run : assemble
qemu-system-i386 -drive format=raw,file=disk.img -monitor stdio
debug: assemble
qemu-system-i386 -s -hda disk.img &
gdb -ex "target remote localhost:1234" -ex "symbol-file kernel/kernel.elf" -ex "b kmain" -ex "continue"
assemble: disk.img kernel.bin stage1.bin stage2.bin
dd if=stage1.bin of=disk.img bs=1 count=3 seek=0 skip=0 conv=notrunc
dd if=stage1.bin of=disk.img bs=1 count=451 seek=62 skip=62 conv=notrunc
mcopy -i disk.img stage2.bin kernel.bin :: -D o
kernel.bin : kernel/kernel.elf
objcopy -O binary $^ $@
chmod -x $@
#You can use the --print-map option to look at what the linker does
kernel/kernel.elf : $(C_OBJECTS) $(ASM_OBJECTS)
i686-elf-ld $^ -T kernel/linker.ld -e kmain -o $@
chmod -x $@
%.o : %.c
i686-elf-gcc -ffreestanding $< -c -o $@ -Wall -Werror -g -Ikernel/include ## Using -I seems like a bad idea??
%.o : %.asm
nasm $< -o $@ -f elf32
stage1.bin : boot/stage1/stage1.asm
nasm $^ -f bin -o $@
stage2.bin: boot/stage2/stage2.asm
nasm $^ -f bin -o $@
disk.img:
truncate $@ -s 1M
mkfs.vfat -F12 -S512 -s1 $@
clean :
rm $(C_OBJECTS) $(ASM_OBJECTS) *.bin
The organisation looks better IMHO.. But I'm worried about two things right now:
1. How I deal with header files. This is really worrying me. As you can see, I use -I when I compile, and it seems really really 'patchy'
2. I still want to know how I can efficiently make all my object files get produced a separate directory. What changes can I make in the Makefile to make this happen? (I'm guessing I use some sort of pattern substitution?)
3."C_SOURCES = $(wildcard kernel/*/*.c)" - Really bad idea?
Re: Directory organization suggestions
Posted: Sat Apr 04, 2020 7:03 am
by nexos
In my operating system, all includes are in a directory called include in the root of my source tree. I specify this directory with the -I option in the gcc command line