I need help figuring out how to link my program/os

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
SomeGuyWithAKeyboard
Member
Member
Posts: 27
Joined: Thu Aug 25, 2022 3:54 pm

I need help figuring out how to link my program/os

Post by SomeGuyWithAKeyboard »

I have this really simple and basic real mode command line system programmed entirely in assembly. I want to do a few moderately complex operations that I really, really don't want to make in assembly and would like to use C or C++. Ideally, I need to make a standalone C++ function and come up with a way to access that function from the assembly section where all the stuff happens.

Now I wasn't using any linking of any kind before I decided I need to try to get C/C++ working. My original build script (not found in the linked github repository) was as follows:

Code: Select all

#!bin/bash
nasm BIOS.ASM -o bios.o
nasm bootloader.asm -o bootloader.o
cat bios.bin >> bootloader.bin
After running my build script, I can run it in qemu by typing "qemu-system-i386 bootloader.bin -cpu=486-v1". I also run it on real hardware by copying the contents of bootloader.bin into a compact flash card using dd and putting that into a cf to ide adapter.

It must be noted that the target system is 486 architecture so I need to try to keep non-486 opcodes out if at all possible. Other people have had success in the past using open watcom for this type of thing. It supports compiling into 16 bit real mode as well as cpus as old as 8088. I always get stuck when figuring out linking for these types of projects though so I need help. I finally figured out a way to at least make a linker script in a way that doesn't spit out errors but of course it doesn't work and I don't really know what else to try.

First, I run open watcom "wpp" on my test helloworld.cpp file. Wpp is for compiling c++ without linking according to this source. If it matters, I compile using

Code: Select all

/usr/bin/watcom/binl/wpp helloworld.cpp
which gives me a file called helloworld.o.

Next, I run

Code: Select all

nasm -f elf -o bootloader.o bootloader.asm
nasm -f elf -o bios.o BIOS.ASM
This compiles the bootloader and bios into o files for the linker. There's elf, elf32 and elf64. elf32 and elf seem to work the same. Using bin instead just gives me an error saying "bootloader.o: file not recognized: file format not recognized" when I run any variation of the link command that I've tried. I'm currently using "ld -melf_i386 -T link.ld" for the linking step, not sure if that the best thing to use. Anyway, that doesn't actually boot up when I try to do it with qemu and that's about the extent of what I have been able to accomplish, it was hard enough to get all those commands to run without errors in the first place.

Anyway I don't know what to try next but here's a github repository for the project.
https://github.com/Xeraster/RealModeOS
"buildScript.sh" is the build script, "link.ld" is the linker file, if what I have isn't correct I will need help figuring that out too. The bootloader is loaded into memory location 07c00h which loads the rest of the program into memory at location 07e00h and then jumps to 07e00h to start the program.

Another thing that kind of has me baffled is that in the file "COMMANDS.ASM", there are 3 entries called "debug1cmd", "debug2cmd" and "debug3cmd". The declaration looks like "debug3cmd db "debug3",0,$". I have to change that db to a dw or the link command gives me the following error:never mind now it always happens. I didn't really change anything, don't know whats going on there.

Code: Select all

bios.o: in function `debug3cmd':
BIOS.ASM:(.text+0x17e4): relocation truncated to fit: R_386_8 against `.text'
Why it does it for those 3 entries and none of the others that are written in the exact same way? I have no idea and I don't know if it's important or not.

Let me know if I need to post any additional information. I don't like asking simple beginner-like questions if they can be avoided but getting linking to work is always such an ordeal.
Octocontrabass
Member
Member
Posts: 5562
Joined: Mon Mar 25, 2013 7:01 pm

Re: I need help figuring out how to link my program/os

Post by Octocontrabass »

SomeGuyWithAKeyboard wrote:It must be noted that the target system is 486 architecture so I need to try to keep non-486 opcodes out if at all possible. Other people have had success in the past using open watcom for this type of thing.
GCC and Clang are perfectly capable of building code that will run in real mode on a 486, although it'll be limited to a single segment.
SomeGuyWithAKeyboard wrote:The bootloader is loaded into memory location 07c00h which loads the rest of the program into memory at location 07e00h and then jumps to 07e00h to start the program.
It shouldn't jump to 0x7E00 with all that extra padding you've got in there. Also, you're including BIOS.ASM in your bootloader, so if you assemble it separately and try to link it you'll have two copies of most of your code. At the very least, you need to get rid of that include directive if you want to build those two files separately.
SomeGuyWithAKeyboard wrote:never mind now it always happens. I didn't really change anything, don't know whats going on there.
Without quotation marks, $ refers to the current address, which doesn't fit in a byte the way "$" does. You already fixed this problem, though.
SomeGuyWithAKeyboard wrote:Let me know if I need to post any additional information. I don't like asking simple beginner-like questions if they can be avoided but getting linking to work is always such an ordeal.
Have you ever used NASM's "extern" and "global" directives?
rdos
Member
Member
Posts: 3296
Joined: Wed Oct 01, 2008 1:55 pm

Re: I need help figuring out how to link my program/os

Post by rdos »

I think OpenWatcom is the best alternative. Even if GCC might be able to build some special real-mode code, it really cannot handle 16-bit or segmentation.
SomeGuyWithAKeyboard
Member
Member
Posts: 27
Joined: Thu Aug 25, 2022 3:54 pm

Re: I need help figuring out how to link my program/os

Post by SomeGuyWithAKeyboard »

Yes, I'm using watcom because it's better for real mode and older stuff. I actually made a command line system with c++ and g++ but it doesn't work on my 486 for some reason despite working on qemu even with -cpu 486-v1. It also doesn't work on newer stuff because newer bioses are smart enough to see my bootloader, be like "nah, I ain't booting from that non-standardized trash" and just act like the drive isn't bootable. I tried different methods of using cross compilers with no luck, for all I know it's not even opcode related and I kind of need to get this current project working so that I can have the tools I need to begin to troubleshoot what's going on in the first place but that's for another day.

From my understanding, in nasm using global allows you to access an assembly subroutine from c/c++ (if all your linking is set up correctly) and extern is for accessing c/c++ functions from assembly, if your linking stuff is set up correctly.

I did leave the bios asm include in the bootloader and just changed it so that I only compile bootloader.asm. I did eventually get it to compile and link the nasm code using ld in a way that it actually boots up and runs. The trick seems to be to compile to elf32. Just because a file ends in .o doesn't mean the ld linker can understand it. I updated the git repository to show these changes.

When I compile helloworld.cpp with wpp, I get a helloworld.o file. Running "nm helloworld.o" tells me the file format isn't recognized and when I add that to my link command so that the link step is "ld -melf_i386 -T link.ld helloworld.o", it also says the file format isn't recognized.

After some digging and further studying of the watcom documentation, I'm not entirely sure if watcom can compile to elf or not. wpp compiles to an .o file but from my understanding not all .o files are in elf format. There is a watcom linker called wlink and the watcom linking documentation does reference the existence of elf files but I have yet to find anything about actually generating an elf file with watcom. I'm going to keep digging though because if doing it this way is possible, that would be the best. I do know of a guy who has a homebrewed 80286 system and uses assembly and c code with open watcom, maybe I can reach out to him and see how he's linking his stuff.

If it truly is possible to use gcc or g++ to make limited real mode code, that might be a rabbit hole worth going doing instead but if it has to jump into protected mode, it probably won't work on the target hardware.

So I found a source that says "elf" is a valid form parameter for wlink. So I'm thinking I can compile the c++ file with wpp, use wlink to convert it into an elf and then have ld do that final step where it combines the bootloader.o and the helloworld elf into the flat binary. I have my linker.lnk file looking like this:

Code: Select all

format elf
option map
name test
file helloworld.o
and then I run the command "wlink @linker.lnk". This gives me this output:

Code: Select all

Open Watcom Linker Version 2.0 beta May 26 2023 02:07:29 (32-bit)
Copyright (c) 2002-2023 The Open Watcom Contributors. All Rights Reserved.
Portions Copyright (c) 1985-2002 Sybase, Inc. All Rights Reserved.
Source code is available under the Sybase Open Watcom Public License.
See http://www.openwatcom.org/ for details.
loading object files
Warning! W1080: file helloworld.o is a 16-bit object file
searching libraries
Warning! W1080: file clibs.lib is a 16-bit object file
Warning! W1080: file clibs.lib is a 16-bit object file
Warning! W1080: file clibs.lib is a 16-bit object file
Warning! W1080: file plibs.lib is a 16-bit object file
Warning! W1080: file clibs.lib is a 16-bit object file
Warning! W1080: file clibs.lib is a 16-bit object file
Warning! W1080: file clibs.lib is a 16-bit object file
Warning! W1080: file clibs.lib is a 16-bit object file
Warning! W1080: file clibs.lib is a 16-bit object file
Warning! W1080: file clibs.lib is a 16-bit object file
Warning! W1080: file plibs.lib is a 16-bit object file
Warning! W1080: file clibs.lib is a 16-bit object file
Warning! W1080: file clibs.lib is a 16-bit object file
Warning! W1080: file clibs.lib is a 16-bit object file
Warning! W1080: file clibs.lib is a 16-bit object file
creating map file
creating an ELF executable
Error! E2063: file clibs.lib(stk086.asm): invalid relocation for flat memory model at 0804811a
Error! E2063: file clibs.lib(stk086.asm): invalid relocation for flat memory model at 08048127
Error! E2063: file clibs.lib(stk086.asm): invalid relocation for flat memory model at 0804812f
Error! E2063: file clibs.lib(stk086.asm): invalid relocation for flat memory model at 08048137
Error! E2063: file clibs.lib(stk086.asm): invalid relocation for flat memory model at 08049066
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 08048152
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 0804818b
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 08048190
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 0804819b
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080481a0
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080481b1
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080481c0
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080481d5
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080481f0
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080481f8
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 08048217
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 0804821e
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 08048223
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 08048228
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 0804823a
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 0804823f
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080482a5
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080482a9
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080482b0
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080482b4
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080482b8
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080482bb
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080482be
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080482c8
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080482ce
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080482d1
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080482d5
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080482d8
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080482dc
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080482df
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080482e3
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080482e8
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080482eb
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080482ef
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080482fc
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 08048303
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 08048313
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 08048318
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 08048322
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 08048341
Error! E2063: file clibs.lib(cstart): invalid relocation for flat memory model at 080483df
Error! E2063: file plibs.lib(fsroot.cpp): invalid relocation for flat memory model at 080483ea
Error! E2063: file plibs.lib(fsroot.cpp): invalid relocation for flat memory model at 0804906c
Error! E2063: file clibs.lib(cmain086.c): invalid relocation for flat memory model at 0804840b
Error! E2063: file clibs.lib(cmain086.c): invalid relocation for flat memory model at 0804840e
Error! E2063: file clibs.lib(initrtns.c): invalid relocation for flat memory model at 0804845b
Error! E2063: file clibs.lib(initrtns.c): invalid relocation for flat memory model at 08048460
Error! E2063: file clibs.lib(initrtns.c): invalid relocation for flat memory model at 080484b7
Error! E2063: file clibs.lib(initrtns.c): invalid relocation for flat memory model at 080484bc
Error! E2063: file clibs.lib(enterdb.c): invalid relocation for flat memory model at 08048509
Error! E2063: file clibs.lib(exit.c): invalid relocation for flat memory model at 08048528
Error! E2063: file clibs.lib(exit.c): invalid relocation for flat memory model at 08048535
Error! E2063: file clibs.lib(exit.c): invalid relocation for flat memory model at 08048539
Error! E2063: file clibs.lib(exit.c): invalid relocation for flat memory model at 08048544
Error! E2063: file clibs.lib(exit.c): invalid relocation for flat memory model at 08048548
Error! E2063: file clibs.lib(exit.c): invalid relocation for flat memory model at 08049060
Error! E2063: file clibs.lib(exit.c): invalid relocation for flat memory model at 08049062
I'm not sure how to fix any of that but maybe I'll figure it out.
rdos
Member
Member
Posts: 3296
Joined: Wed Oct 01, 2008 1:55 pm

Re: I need help figuring out how to link my program/os

Post by rdos »

It seems like you are trying to mix 16-bit real mode code with 32-bit flat mode code, which the linker cannot handle.
SomeGuyWithAKeyboard
Member
Member
Posts: 27
Joined: Thu Aug 25, 2022 3:54 pm

Re: I need help figuring out how to link my program/os

Post by SomeGuyWithAKeyboard »

I would rather not mix 16 bit code with 32 bit mode. I want to mix 16 bit assembly code with 16 bit c or c++ code, just wish I could figure out the magic combination of commands.

I did finally make it compile well enough using a g++ cross compiler to the point it can run an empty c++ function but trying to do anything else either doesn't do anything or crashes. I can't print characters to the screen or modify bytes in memory with it. Even if I make the assembly function that the c++ code jumps to only use protected mode compatible assembly and push then pop all relevant registers, it still crashes. I didn't try it on real hardware but I expect it to crash if I try to run that empty function, I'll be sure to try it out and share the results. Name mangling is always a pain when I try to do things this way, I have to name my assembly side references things like _Z10helloWorldv. No one on internet posts ever mentions running into this annoyance so I wonder why I have to do that.
Octocontrabass
Member
Member
Posts: 5562
Joined: Mon Mar 25, 2013 7:01 pm

Re: I need help figuring out how to link my program/os

Post by Octocontrabass »

SomeGuyWithAKeyboard wrote:I actually made a command line system with c++ and g++ but it doesn't work on my 486 for some reason despite working on qemu even with -cpu 486-v1.
Usually when code works on QEMU but not on bare metal, it's because QEMU doesn't enforce segment limits. It could also be something else, such as missing initialization. Try Bochs - it has a log that will show you if the problem is segment limit violations.
SomeGuyWithAKeyboard wrote:It also doesn't work on newer stuff because newer bioses are smart enough to see my bootloader, be like "nah, I ain't booting from that non-standardized trash" and just act like the drive isn't bootable.
If you're trying to boot from USB, your drive needs to either be partitioned like a bootable hard disk or FAT-formatted like a floppy disk. Your current code is neither of those things. (If you're trying to boot from a CF card in a CF-to-IDE adapter, your CF card might identify itself in a way that's incompatible with the BIOS.)
SomeGuyWithAKeyboard wrote:From my understanding, in nasm using global allows you to access an assembly subroutine from c/c++ (if all your linking is set up correctly) and extern is for accessing c/c++ functions from assembly, if your linking stuff is set up correctly.
Not exactly.

Using global makes a symbol available to the linker, so other object files can reference that symbol. Using extern pulls the symbol from the linker, which usually (but not always) comes from another object file. A symbol is a name that the linker will replace with a number when it links your binary together. The number is usually an address, but it's possible to define symbols in your linker script that are not addresses.
SomeGuyWithAKeyboard wrote:After some digging and further studying of the watcom documentation, I'm not entirely sure if watcom can compile to elf or not.
Which version are you using? ELF support is a relatively new feature. It also probably isn't the default object file format - you'll have to tell it you want ELF.
SomeGuyWithAKeyboard wrote:If it truly is possible to use gcc or g++ to make limited real mode code, that might be a rabbit hole worth going doing instead but if it has to jump into protected mode, it probably won't work on the target hardware.
It is truly possible, but there are limitations. Your code (and probably stack?) is limited to a 64kB segment. Your data is not limited to 64kB, but you'll need to set up a #GP handler to enter unreal mode to access any address outside the 64kB real mode segment limit. CS, DS, ES, and SS must all contain the same value. The generated code is really just 32-bit code with appropriate prefixes to run in 16-bit mode. GCC emits code that requires a 386 at minimum; Clang emits code that requires a 486 at minimum. You'll probably need to realign your stack before calling any functions compiled with GCC/Clang.
SomeGuyWithAKeyboard wrote:Name mangling is always a pain when I try to do things this way, I have to name my assembly side references things like _Z10helloWorldv. No one on internet posts ever mentions running into this annoyance so I wonder why I have to do that.
Most people declare functions with extern "C" to prevent name mangling.
SomeGuyWithAKeyboard
Member
Member
Posts: 27
Joined: Thu Aug 25, 2022 3:54 pm

Re: I need help figuring out how to link my program/os

Post by SomeGuyWithAKeyboard »

I'm using the open watcom 2.0 with the 2023-05-09-Build. In the past several days of trying lots of different things, I've never got it to generate an o file that ld or nm will accept. If it really is capable of generating a gnu compatible elf, I can't find how to do it and what little elf related content there is in the documentation either isn't helpful or hasn't worked. Wpp certainly can generate wlink compatible, non-ld compatible o files from c or c++ code which is at least something but I can't overcome all the errors that happen when I try to convert that to a hopefully ld comatible elf with wlink. It's looking more and more like the only way I'm going to get anything besides assembly code running is if I go back to trying to run the system in protected mode with gcc compiled code. This comes with its own issues but I'm at a dead end with the watcom linker errors.

I did get my protected mode c++ version of this system running on bochs and it booted right up once I got the config file set up just right. The only hint as to that's going on is that it spits out exactly 75 errors saying "00006456199i[CPU0 ] math_abort: MSDOS compatibility FPU exception" with different numbers at the left of the letter "i". I did try this on a amd k6-2 and a athlon xp. It didn't work on either of them but instead of freezing, it reboots which means I don't get to see the screen output which means I don't get as much insight into what's actually happening. On my socket 3 486 motherboard it gets really really far into the bootup process before it freezes. It allocates memory, sets up and uses dynamically allocated data types and internally performs quite a few string operations before failing during a memory reallocation operation. I'm going to have to figure out how to insert breakpoints, would be really nice if I could single step it in bochs starting from a few instructions before the part where it crashes on my 486 system but I don't have std or any standard libraries on there.

I also have a pentium 4 dell motherboard with ide ports but it refuses to try to boot off the compact flash card when I try to make it boot from it.

I'll have to clean up my code for the protected mode system so I can post it.

My program does the exact same behavior and crashes at the exact same time whether i'm using the native gcc version on my linux distro or the 486-only musl cross compiler. I've always believed that since I didn't compile the cross compiler on a 486 cpu running a 486 compatible linux distro, invalid opcodes probably leaked in one way or another. Setting up a 486 compatible install of gentoo is going to be such a major undertaking, that's why I gave up and tried to remake this in real mode instead. Seeing as how watcom isn't any easier to deal with and therefore the only way is to do something involving protected mode anyway, I might as well go back to working on this other equally dead ended problem just because the end result if I ever get it working would be better.

edit: ok the bochs breakpoint functionality is useful and easy to use. I already knew which line causes it to crash when running on the irl system but now I've confirmed that with bochs. I don't have it narrowed down to what exact assembly instructions put the system from a non crashed state to a crashed state but this is the contents of the registers when the section of code that crashes a physical system runs in bochs. afaik it's ok if the data segment and code segment both span the same address range but I just want to make sure i'm not misunderstanding something obvious:

Code: Select all

00005402071i[CPU0  ] CPU is in protected mode (active)
00005402071i[CPU0  ] CS.mode = 32 bit
00005402071i[CPU0  ] SS.mode = 32 bit
00005402071i[CPU0  ] EFER   = 0x00000000
00005402071i[CPU0  ] | EAX=000b8000  EBX=00000000  ECX=00000000  EDX=0000e044
00005402071i[CPU0  ] | ESP=0000fe4e  EBP=0000fe52  ESI=00000000  EDI=00000000
00005402071i[CPU0  ] | IOPL=0 id vip vif ac vm rf nt of df if tf sf zf AF pf cf
00005402071i[CPU0  ] | SEG sltr(index|ti|rpl)     base    limit G D
00005402071i[CPU0  ] |  CS:0008( 0001| 0|  0) 00000000 ffffffff 1 1
00005402071i[CPU0  ] |  DS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402071i[CPU0  ] |  SS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402071i[CPU0  ] |  ES:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402071i[CPU0  ] |  FS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402071i[CPU0  ] |  GS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402071i[CPU0  ] | EIP=00007e00 (00007e00)
00005402071i[CPU0  ] | CR0=0x60000011 CR2=0x00000000
00005402071i[CPU0  ] | CR3=0x00000000 CR4=0x00000000
Octocontrabass
Member
Member
Posts: 5562
Joined: Mon Mar 25, 2013 7:01 pm

Re: I need help figuring out how to link my program/os

Post by Octocontrabass »

SomeGuyWithAKeyboard wrote:I'm using the open watcom 2.0 with the 2023-05-09-Build.
That's new enough that ELF support should be present, but I don't know what you have to do differently to compile your code to ELF object files.
SomeGuyWithAKeyboard wrote:It's looking more and more like the only way I'm going to get anything besides assembly code running is if I go back to trying to run the system in protected mode with gcc compiled code.
Again, GCC and Clang are perfectly capable of compiling code that will run in real mode using the "-m16" option. As long as you adhere to all of the limitations, it does work.
SomeGuyWithAKeyboard wrote:The only hint as to that's going on is that it spits out exactly 75 errors saying "00006456199i[CPU0 ] math_abort: MSDOS compatibility FPU exception" with different numbers at the left of the letter "i".
Perhaps you should use "-mgeneral-regs-only" to ensure no FPU instructions are present. (Alternatively, you could initialize the FPU to a reasonable state. You may also wish to set CR0.NE, if you're using a CPU that implements that bit.)
SomeGuyWithAKeyboard wrote:I also have a pentium 4 dell motherboard with ide ports but it refuses to try to boot off the compact flash card when I try to make it boot from it.
Some CF cards identify themselves in a way that some BIOSes will refuse to boot from. You may have better luck with a different card.
SomeGuyWithAKeyboard wrote:I've always believed that since I didn't compile the cross compiler on a 486 cpu running a 486 compatible linux distro, invalid opcodes probably leaked in one way or another.
You can ensure no such opcodes are present using "-march=i486". (It's still a good idea to use a cross-compiler, but you don't need to build it on a 486 or anything silly like that.)
SomeGuyWithAKeyboard wrote:afaik it's ok if the data segment and code segment both span the same address range but I just want to make sure i'm not misunderstanding something obvious:
I don't see anything obviously wrong there.
nullplan
Member
Member
Posts: 1789
Joined: Wed Aug 30, 2017 8:24 am

Re: I need help figuring out how to link my program/os

Post by nullplan »

Octocontrabass wrote:Again, GCC and Clang are perfectly capable of compiling code that will run in real mode using the "-m16" option. As long as you adhere to all of the limitations, it does work.
-m16 makes gcc output the ".code16gcc" directive as first thing in the file, and then proceeds to generate the same code it would for -m32. So it requires all segment bases to be equal (and all pointers to be relative to that common base). There is also a relatively new ia16-gcc that directly generates 16-bit code and does work with segmentation.

Personally I would counsel against the use of C in a boot sector. Get the boot sector to load the next part of the code, switch to PM, and run it in 32-bit mode. Or set up paging and switch to 64-bit mode. But you do you.
Carpe diem!
SomeGuyWithAKeyboard
Member
Member
Posts: 27
Joined: Thu Aug 25, 2022 3:54 pm

Re: I need help figuring out how to link my program/os

Post by SomeGuyWithAKeyboard »

Ok so I found out a lot more about what causes it to crash.

There is this one extremely simple getSize() member function on a data type that returns a const unsigned int and is used as part of a condition for a while loop. Making it into a non const didn't fix the crash but moving the member variable it accesses so that it's public and then accessing that member directly (but only for this 1 while loop) made it not crash.

Further down in the code but in the same function, that member is accessed again. It's in an if statement where its being compared to an unsigned int. If I replace that member function with the non-const version I made earlier, it doesn't crash. Replacing the member function with the member variable isn't necessary here for some reason.

This getSize() function gets called many other times throughout the program returning its const value and doesn't crash. It only seems to crash when getting run in this particular function. But anyway after doing these 2 dubious hack workarounds, there's another part that crashes and is in the same function. It's a line that goes like "*position = i;" where position is an unsigned int* and i is an unsigned int. This entire operation takes exactly 3 assembly instructions:

Code: Select all

0008:0000000000009bfe (unk. ctxt): mov edx, dword ptr ss:[ebp+12] ; 8b550c
0008:0000000000009c01 (unk. ctxt): mov eax, dword ptr ss:[ebp-8] ; 8b45f8
0008:0000000000009c04 (unk. ctxt): mov dword ptr ds:[edx], eax ; 8902


Here is that same thing but with the values of each register from bochs, less readable but it contains more information:

Code: Select all

CPU is in protected mode (active)
00005402124i[CPU0  ] CS.mode = 32 bit
00005402124i[CPU0  ] SS.mode = 32 bit
00005402124i[CPU0  ] EFER   = 0x00000000
00005402124i[CPU0  ] | EAX=000b806d  EBX=00000000  ECX=00000000  EDX=0007f800
00005402124i[CPU0  ] | ESP=0000fe7a  EBP=0000fe92  ESI=00000000  EDI=00000000
00005402124i[CPU0  ] | IOPL=0 id vip vif ac vm rf nt of df if tf sf ZF af PF cf
00005402124i[CPU0  ] | SEG sltr(index|ti|rpl)     base    limit G D
00005402124i[CPU0  ] |  CS:0008( 0001| 0|  0) 00000000 ffffffff 1 1
00005402124i[CPU0  ] |  DS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402124i[CPU0  ] |  SS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402124i[CPU0  ] |  ES:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402124i[CPU0  ] |  FS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402124i[CPU0  ] |  GS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402124i[CPU0  ] | EIP=00009bfe (00009bfe)
00005402124i[CPU0  ] | CR0=0x60000011 CR2=0x00000000
00005402124i[CPU0  ] | CR3=0x00000000 CR4=0x00000000
(0).[5402124] [0x000000009bfe] 0008:0000000000009bfe (unk. ctxt): mov edx, dword ptr ss:[ebp+12] ; 8b550c


CPU is in protected mode (active)
00005402125i[CPU0  ] CS.mode = 32 bit
00005402125i[CPU0  ] SS.mode = 32 bit
00005402125i[CPU0  ] EFER   = 0x00000000
00005402125i[CPU0  ] | EAX=000b806d  EBX=00000000  ECX=00000000  EDX=0000fec2
00005402125i[CPU0  ] | ESP=0000fe7a  EBP=0000fe92  ESI=00000000  EDI=00000000
00005402125i[CPU0  ] | IOPL=0 id vip vif ac vm rf nt of df if tf sf ZF af PF cf
00005402125i[CPU0  ] | SEG sltr(index|ti|rpl)     base    limit G D
00005402125i[CPU0  ] |  CS:0008( 0001| 0|  0) 00000000 ffffffff 1 1
00005402125i[CPU0  ] |  DS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402125i[CPU0  ] |  SS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402125i[CPU0  ] |  ES:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402125i[CPU0  ] |  FS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402125i[CPU0  ] |  GS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402125i[CPU0  ] | EIP=00009c01 (00009c01)
00005402125i[CPU0  ] | CR0=0x60000011 CR2=0x00000000
00005402125i[CPU0  ] | CR3=0x00000000 CR4=0x00000000
(0).[5402125] [0x000000009c01] 0008:0000000000009c01 (unk. ctxt): mov eax, dword ptr ss:[ebp-8] ; 8b45f8


CPU is in protected mode (active)
00005402126i[CPU0  ] CS.mode = 32 bit
00005402126i[CPU0  ] SS.mode = 32 bit
00005402126i[CPU0  ] EFER   = 0x00000000
00005402126i[CPU0  ] | EAX=00000002  EBX=00000000  ECX=00000000  EDX=0000fec2
00005402126i[CPU0  ] | ESP=0000fe7a  EBP=0000fe92  ESI=00000000  EDI=00000000
00005402126i[CPU0  ] | IOPL=0 id vip vif ac vm rf nt of df if tf sf ZF af PF cf
00005402126i[CPU0  ] | SEG sltr(index|ti|rpl)     base    limit G D
00005402126i[CPU0  ] |  CS:0008( 0001| 0|  0) 00000000 ffffffff 1 1
00005402126i[CPU0  ] |  DS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402126i[CPU0  ] |  SS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402126i[CPU0  ] |  ES:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402126i[CPU0  ] |  FS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402126i[CPU0  ] |  GS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00005402126i[CPU0  ] | EIP=00009c04 (00009c04)
00005402126i[CPU0  ] | CR0=0x60000011 CR2=0x00000000
00005402126i[CPU0  ] | CR3=0x00000000 CR4=0x00000000
(0).[5402126] [0x000000009c04] 0008:0000000000009c04 (unk. ctxt): mov dword ptr ds:[edx], eax ; 8902
Weird.
Octocontrabass wrote: That's new enough that ELF support should be present, but I don't know what you have to do differently to compile your code to ELF object files.
I think this new elf functionality is in open watcom wlink. I think you're intended to use wpp compile to a watcom compatible elf file and use wlink to link that into a gnu compatible elf file. The only problem is that wlink spits out relocation errors and I can't find any way to solve those. If that's the only way to make gcc/gnu/ld compatible elf files with open watcom (and it's the only supposed way I've found), then that feature isn't working yet or at least not well enough. It would be really great if wpp could output the right format elf .o files in the first place so I could bypass all that wlink stuff that doesn't work for me but it can't.
Octocontrabass wrote:Perhaps you should use "-mgeneral-regs-only" to ensure no FPU instructions are present. (Alternatively, you could initialize the FPU to a reasonable state. You may also wish to set CR0.NE, if you're using a CPU that implements that bit.)
OK, this is something new that I haven't thought about. Do I have to do anything else besides make sure the CR0 register bits are set correctly in order for the fpu to work? Other than set CR0 bits, I don't touch the fpu at all before jumping out of the assembly code. Maybe I'm supposed to be doing other stuff with the fpu before jumping to the c++ code that I don't know about. Using the "-mgeneral-regs-only" parameter doesn't make the program run any differently on a real system and it still crashes in the same spot.
Octocontrabass
Member
Member
Posts: 5562
Joined: Mon Mar 25, 2013 7:01 pm

Re: I need help figuring out how to link my program/os

Post by Octocontrabass »

SomeGuyWithAKeyboard wrote:Ok so I found out a lot more about what causes it to crash.
You've narrowed it down to three instructions, but none of those instructions could possibly be responsible for the crash. The problem must be somewhere else.
SomeGuyWithAKeyboard wrote:Here is that same thing but with the values of each register from bochs, less readable but it contains more information:
Your stack doesn't appear to be correctly aligned. It's also awfully close to your code - are you sure your stack hasn't outgrown the memory you reserved for it?
SomeGuyWithAKeyboard wrote:Do I have to do anything else besides make sure the CR0 register bits are set correctly in order for the fpu to work?
If you're going to use the FPU, you need to set CR0 appropriately and use FNINIT to set the FPU registers to reasonable defaults.
SomeGuyWithAKeyboard wrote:Using the "-mgeneral-regs-only" parameter doesn't make the program run any differently on a real system and it still crashes in the same spot.
That means the problem has nothing to do with the FPU. You don't need to initialize the FPU when your code doesn't have any FPU instructions.
SomeGuyWithAKeyboard
Member
Member
Posts: 27
Joined: Thu Aug 25, 2022 3:54 pm

Re: I need help figuring out how to link my program/os

Post by SomeGuyWithAKeyboard »

Hmm, the problem might be stack related. I haven't been messing around with the stack any so maybe it's time to start experimenting.

Does the esp stack register point to the absolute 32 bit memory address of where the next, current or previous (can't remember which of the 3 it is) address of the stack is? Or does it point to where the next stack location is in relation to where the stack descriptor starts (or ends)? The reason I'm asking is because I made a dedicated stack descriptor but when I try to change the base address of the stack descriptor to anything other than 0, nothing works. I want to make it 0x10000-0xFFFFFFFF instead of 0x00000000-0xFFFFFFFF for example. Bochs is able to recognize all the information of the stack descriptor correctly when I assign it to the ss register, the descriptor itself is set up and aligned but something is messed up somewhere.

In my bootloader, other than loading ss with the same descriptor as all the other data segment registers, I was doing this:

Code: Select all

movzx esp, sp
mov ebp, esp
i instead changed it to

Code: Select all

mov ebp, 0x10000
movzx esp, sp
Now it still only works if my stack base is zero but in theory my stack should be way out of the way of anything else. It works fine on qemu and bochs but still crashes in the same place when I boot it on a physical system.

How do I properly move the stack to a different memory range in protected mode?
Octocontrabass
Member
Member
Posts: 5562
Joined: Mon Mar 25, 2013 7:01 pm

Re: I need help figuring out how to link my program/os

Post by Octocontrabass »

SomeGuyWithAKeyboard wrote:Does the esp stack register point to the absolute 32 bit memory address of where the next, current or previous (can't remember which of the 3 it is) address of the stack is? Or does it point to where the next stack location is in relation to where the stack descriptor starts (or ends)?
I'm not sure exactly how you define those things, but it points to the most recently pushed item on the stack. Stack alignment requirements are defined in the System V i386 psABI. (This is the specification GCC and Clang follow when compiling your code.)
SomeGuyWithAKeyboard wrote:The reason I'm asking is because I made a dedicated stack descriptor but when I try to change the base address of the stack descriptor to anything other than 0, nothing works.
The System V i386 psABI requires flat addressing, meaning CS, DS, ES, and SS all have to point to the same region of memory. There is no way to force the compiler to use SS for stack accesses, and you can't use segmentation to stop the stack from colliding with your data. The psABI assumes you'll use paging to guard against stack overflows if you need that.

You can try using the "-fstack-usage" option to measure your stack usage, but it can only report what the compiler can figure out through static analysis. You'll have to figure out dynamic stack usage on your own.
SomeGuyWithAKeyboard wrote:How do I properly move the stack to a different memory range in protected mode?
You can't move the stack. Fortunately, you don't need to: you can throw away the stack when you switch to protected mode and set up a completely new stack.
SomeGuyWithAKeyboard
Member
Member
Posts: 27
Joined: Thu Aug 25, 2022 3:54 pm

Re: I need help figuring out how to link my program/os

Post by SomeGuyWithAKeyboard »

So thanks for all the suggestions. I eventually did get it to boot pretty much all the way up. I put the stack way far out of the way of anything it could ever conflict with and studied more about descriptors and changed a few things around. When booted on a physical machine, it still fails to start up maybe 25%-50% of the time and when this happens, it crashes on the function I was having problems with before except it crashes after the function gets run more than once. However all this only happens sometimes. I'm still not using paging, all protected mode segments span 0x00000000-0xFFFFFFFF, the program gets loaded into memory starting at 0x7c00 and is 22.2kb in size. The bootloader sets the stack at 0x2FFFF for now. It seems like setting the stack at 0x1FFFF makes it successfully boot up a little more often when done on real hardware instead of crashing before it finishes starting up.

It's still not fully working the same way on real hardware as it does on emulators though. The system is able to successfully boot up and initialize everything nearly 50% of the time when run on real hardware but once it starts waiting on user input from the keyboard, it crashes right there. On my 486 motherboard it freezes, on anything more advanced than that, it causes a reboot.

Upon further inspection, the function that waits for keyboard keypresses is where it crashes, at least when it doesn't crash during bootup). I can read port 0x60 and 0x64 then display that on the screen no problem but once the system finishes initializing and setting up everything then starts polling user input, it crashes. The point where it crashes is curiously inside a while loop. The section of code where I was having problems before (and where it still crashes on 50% of the time) is also in a while loop. However I have other while loops all throughout my program and it they don't cause any problems.

Things I tried include setting bits 12-13 of the flags register from 00 to 11. I thought that maybe setting the privilege level for io operations to 3 will make it more accepting of io operations but that made no difference.

I also tried implementing assembly code for my own version of iob and outb instead of using the cross compiler's io.h library. I made it as close as possible to the way it works on my real mode version of this. While I finally got the parameters and return values working, this causes the program to run just as perfect and bug free on the emulators as it already did and it still crashes in all the same places on a physical machine.

Here is a link to my project on github: https://github.com/Xeraster/SimpleProtectedModeOS

Maybe I'll try learning about and implementing paging next just because it's a thing I haven't tried yet. Maybe someone else will be able to point out problems in my code I didn't think of yet.
Post Reply