Linkers become abundantly clear when you write an OS from scratch.
Hardware has very peculiar rules for how it loads. The old floppy bootloader would only load the first sector (512 bytes), and after that it's that 512-byte code blocks job to finish loading the code and running it (often called the 2nd stage bootloader).
So writing this makes it super obvious what linkers do. At first you hardcode everything to set addresses. But then a function grows and no longer fits.
So now you have functions + their lengths, as well as a few holes for where your global variables go
And then different .c files may want different global (or static) variables. So now you need to somehow add the lengths of all data segments across all your .c files together.
And then suddenly you understand Linkers, and just use LD / Elf files.
--------
It's a bit of a trial by fire. But not really??? There are super simple computers out there called MicroControllers with just 200 page manuals describing everything.
Writing a bootloader for some simple Atmel AVR chip is perfect for this learning experience. ATMega328p is the classic but there are better more modern chips.
But ATMega328p was popular 15 years ago and still is manufactured in large numbers today
I wrote a bootloader for an iPod Mini when I was an undergrad, and honestly, I don’t think that would have helped me understand linking the first time around. With 20 years of hindsight and lots more hacking experience I can see the connection, but it’s not an obvious one.
Writing a bootloader in one file is easy enough and will avoid the need of a linker.
The issue is when you have two, three or four .c files that are compiled as separate units that then need to be combined together.
Today, AVR chips and assembly works perfectly fine with .elf objects. But you will likely need to mess with linker scripts to get your bootloader working across different setups.
Especially if you have an element of dynamic boot loading (ex: bootloader program that later continues to load more Application code off of a MicroSD card or UART or over I2C comms.
I'm really not sure how far you can get with this toy project without running into immediate linker issues (or linker scripts).
Hardware has very peculiar rules for how it loads. The old floppy bootloader would only load the first sector (512 bytes), and after that it's that 512-byte code blocks job to finish loading the code and running it (often called the 2nd stage bootloader).
So writing this makes it super obvious what linkers do. At first you hardcode everything to set addresses. But then a function grows and no longer fits.
So now you have functions + their lengths, as well as a few holes for where your global variables go
And then different .c files may want different global (or static) variables. So now you need to somehow add the lengths of all data segments across all your .c files together.
And then suddenly you understand Linkers, and just use LD / Elf files.
--------
It's a bit of a trial by fire. But not really??? There are super simple computers out there called MicroControllers with just 200 page manuals describing everything.
Writing a bootloader for some simple Atmel AVR chip is perfect for this learning experience. ATMega328p is the classic but there are better more modern chips.
But ATMega328p was popular 15 years ago and still is manufactured in large numbers today