Sure I can cross compile Linux, but what about 386BSD? This had long been a thorn in my side, as the GCC/Binutil toolchain that is used in this early era is not GNU pure, they had been modified in all kinds of ways. One of which was a builtin memcpy that doesn’t work the same as a normal memcpy, and the other being that the C compiler & pre-processor rely in YACC to build the tokens. I had been using bison before, however even though bison didn’t generate any errors it build the compiler wrong enough that the majority of the kernel wouldn’t compile.
As it stands right now, the only things that do not compile is locore
to post process the kernel, symorder is used along with dbsym, although neither do any processing to the kernel file itself, so they aren’t needed to get a working system.
386BSD Release 0.1 by William and Lynne Jolitz.
Copyright (c) 1989,1990,1991,1992 William F. Jolitz. All rights reserved.
Based in part on work by the 386BSD User Community and the
BSD Networking Software, Release 2 by UCB EECS Department.
386BSD 0.1.2018 (GENERICISA) 02/02/18 15:01
Other than that, yeah it’s great, compile a kernel in under 15 seconds.
Anyone that cares, the initial release is here: 386bsd01.7z
Inspired by Building and using a 29-year-old compiler on a modern system, i thought I too could get this ancient version of GCC working. At the time I never had bothered with the older version as I had always assumed that there were many fixes and adaptations to GCC for it to run on MS-DOS via GO32/DJGPP. However after doing this, its obvious that GO32/DJGPP was rather built around GCC’s stock output, which would sure make a lot more sense.
And it turns out that the target machine being an i386 Sequent running BSD is the best match, both in turns of underscores, and debugging format. At first I had tried the AT&T SYSV i386 target, however it couldn’t link anything from the standard libraries that DJGPP has as they all have a leading underscore. After starting to mess with internal macros to turn things on and off, and re-define how various portions of assembly are emittied, I found the Sequent target and went with that and everything was fine, and using the existing build infrastructure for GCC 1.39 I now could actually run hello world!
gcc_v1 -v -E hello.c -o hello.i
gcc version 1.39
cpp_v1 -v -undef -D__GNUC__ -DGNUDOS -Dunix -Di386 -D__unix__ -D__i386__ hello.c -o hello.i
GNU CPP version 1.39
gcc_v1 -v -S hello.i -o hello.s
gcc version 1.39
cc1_v1 hello.i -quiet -version -o hello.s
GNU C version 1.27 (80386, BSD syntax) compiled by GNU C version 5.1.0.
gcc_v1 -v -c hello.s -o hello.o
gcc version 1.39
as -o hello.o hello.s
gcc_v1 -v -o hello hello.o
gcc version 1.39
ld -o hello C:/dos/xdjgpp.v1/lib/crt0.o hello.o -lc
go32 version 1.12.maint3 Copyright (C) 1994 DJ Delorie
hello from DJGPP v1/GCC 1.39!
Wasn’t that great? Then going through my ‘test’ programs I went to try to build the infocom interpreter, and that is when things went off the rails.
funcs.o: Undefined symbol __udivsi3 referenced from text segment
options.o: Undefined symbol __divsi3 referenced from text segment
options.o: Undefined symbol __divsi3 referenced from text segment
print.o: Undefined symbol __divsi3 referenced from text segment
print.o: Undefined symbol __udivsi3 referenced from text segment
support.o: Undefined symbol __divsi3 referenced from text segment
gcc_v1: Program ld got fatal signal 1.
I’ve had some issues with GCC and these ‘built in’ functions before. This was an early major stumbling block back in the x68000 GCC days, where after a lot of searching I was able to find 68000 versions of various math routines that were in the native Hudson Soft assembler to link in. While GCC 1.x does have a libgnu/gnulib to include these functions it warns you over and over to not use GCC to build them, but rather the native CC. But the problem is that I don’t have a native CC.
But I managed to save myself after googling around by finding srt0.c from 386BSD. Namely these two:
I ended up having to removing a single underscore, but now I could link infocom, and even better it runs!
Wanting to try something far more exciting, I went ahead and tried to build DooM. However GCC 1.27 has an issue with m_fixed.c I fired up GDB to at least take a look, although I’m not sure where the fault lies.
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
Breakpoint 1, 0x752c5ad5 in msvcrt!_exit () from C:\WINDOWS\System32\msvcrt.dll
#0 0x752c5ad5 in msvcrt!_exit () from C:\WINDOWS\System32\msvcrt.dll
#1 0x752bb379 in msvcrt!abort () from C:\WINDOWS\System32\msvcrt.dll
#2 0x0045805c in final (first=0xe066a0, file=0x75312688 <msvcrt!_iob+128>, write_symbols=NO_DEBUG, optimize=0)
#3 0x00403198 in rest_of_compilation (decl=0x722718) at toplev.c:1296
#4 0x0040fbce in finish_function () at c-decl.c:3272
#5 0x004040c0 in yyparse () at c-parse.y:224
#6 0x0040239d in compile_file (name=0xe00def "C:/dos/xdjgpp.v1/tmp/cca02992.cpp") at toplev.c:837
#7 0x00403a33 in main (argc=11, argv=0xe00f90, envp=0xe01598) at toplev.c:1556
With the code being:
if (! constrain_operands (insn_code_number))
So I assume some error with constrain_operands? Not that it makes it any better. However I know this one file compiles fine with 1.39, and since we are on the i386 another alternative is just to used the assembly version that was hiding in the readme..
DooM mostly built by GCC 1.27
And much to my amazement, it works! Keeping in mind that this is using an a.out tool chain, along with the last DPMI enabled GO32 extender.
Considering the compiler dates back from September 5th, 1988 it’s really great to see it running.
I’ll have to upload source (GCC 1.27/DooM) & binaries later. But I imagine it should also run on EMX/RSX for a Win32 version.
Happy 25th birthday, Linux! Here’s your f-ing cake, go ahead and compile it yourself.
So it’s always a fun time for me to push my old project Ancient Linux on Windows. And what makes this so special? Well it’s a cross compiler for the ancient Linux kernels, along with source to the kernels so you can easily edit, compile and run early Linux from Windows!
As always the kernels I have built and done super basic testing on are:
All of these are a.out kernels, like things were back in the old days. You can edit stuff in notepad if you so wish, or any other editor. A MSYS environment is included, so you can just type in ‘make’ and a kernel can be built, and it also can be tested in the included Qemu. I’ve updated a few things, first with better environment variables, and only tested on Windows 10. Although building a standalone linux EXE still requires a bit of work, it isn’t my goal here as this whole thing is instead geared around building kernels from source. I included bison in this build, so more of GCC is generated on the host. Not that I think it matters too much, although it ended up being an issue doing DooM on GCC 1.39.
So for people who want to relive the good old bad days of Linux, and want to do so from the comfort of Windows, this is your chance!
Around the time of the x68000 port of DooM, I was cutting down the DooM source for a null/portable version. I never could get it to actually run either using EMX or DJGPP 1.03, as I couldn’t get it to link to save my life with a constant never ending battle of unresolved symbols. After a while I just used what I had towards the x68000 version and concentrated on getting it up and running, and just shelved the null/portable effort.
Later on I wanted to get it running again as part of messing with another cross compiler, as DooM isn’t a trivial application to port and verify correct operation. And in the process of trying to get the null version to build and run on Windows using TDM GCC, I wanted to make sure it at least kept compiling with GCC v1.x.
Once more again I was able to compile individual files but unable to link. But this time, I just looked at the diffs for binutils, I thought it should be somewhat easy to get hosted on Windows. Although versions may point to binutils 1.0, I had to use binutils-1.9.tar.gz even though the diffs are against Mar 24 1991, and the source for 1.9 is dated April 17 1991.
My first effort gave me a linker that would happily link, but go32 would either refuse to run the executable, or just crash. I was going to give up again, but I found mention in another file that DJGPP actually uses the linker from G++, the C++ compiler which was a separate thing in the late ’80s and early’90’s. This time it worked, and I could link a trivial hello world style application!
Now that I finally had a cross linker actually working, I didn’t want to compile under emulation, so looking at the other diffs, they didn’t look too extensive. I went ahead ,and took DJGPP v1.06 and patched up the compiler & assembler to get a full cross toolchain. And in no time, I had a null version of DooM running on MS-DOS well at least tested on DOSBox.
This was fun, and all but I didn’t see any easy way to do fun things like hook interrupts so I could get the keyboard & clock like any good MS-DOS program. DPMI greatly eased this kind of stuff, so looking at the DJGPP history, DJGPP v1 version 1.10 actually adds preliminary DPMI support! And in the next version, DPMI was much more better supported, however the binary format had changed from a.out to COFF as part of the move to v1.11. I was able to take the memory, and DPMI portions from the final v1.12 libc, and manually build and run them against the v1.06 library / dev tools.
And much to my surprise, it actually worked! At least having the wrong format didn’t have any effect on how GO32 worked for me.
So feeling lazy, I snagged some of the support code from Maraakate’s revamp of DooM, just to make sure of the timer code, and the keyboard code, and again verified that I can build with the keyboard & timer ISR and I’m able to play the v1.9 shareware & commercial levels fine. I haven’t done a thing to clean up or update the DooM source itself against all the dozens of bugs and issues with Ultimate DooM, or other games like Chex Quest etc.
I’m sure 99% of people wouldn’t care but you can download it here:
Although I’m using DPMI to drive realtime events, if I looked further at the GO32 v1.06 environments I could either figure out how it operates it’s timer, or modify the extender directly to drive the PIC timer and keyboard as I need. But overlooking that, the vintage 1991 software is more than capable of running DooM.
When gperf wasn’t installed, the compilation script ran the command anyway but generated a blank ./gcc/cp/cfns.h. Since this file was newer than the source (./gcc/cp/cfns.gperf) the makefile left it alone and never regenerated the ‘real’ file when you actually had gperf. To continue, run rm ./gcc/cp/cfns.h and try again.
to dealing with duplicate inlines exact_log2 from an include gone wrong. Not to mention more and more headers not generating. But in the end it actually works. As always it feels so much faster to run on OS X than Windows. I’m sure there is stuff out there for newer versions of GCC, but I wanted to use the older toolchain and libs for some other reason.
Yes, I know there are others. Newer versions of GCC too!.. but I was more so curious to see if I could do it. I know there were GCC 1.x ports to the Amiga but I can’t find source anywhere. And for some reason the Amiga and Atari ST seem to have never been mainlined into GCC. I would have thought 1990-1992 they would have had far more users than say SUN-2/SUN-3.
I’ve just tested a hello world type executable. I’m more so amazed that it linked and executed, ‘file’ detects the objects as
x.o: raw G3 data, byte-padded
But at least the executables look right:
hi: AmigaOS loadseg()ble executable/binary
I had to hack all kinds of crap compiling eamiga.c
and eamiga_bss.c as neither generated correctly, and both had all kinds of missing and undefined things. I’m sure on bigger projects it’d just explode, but right now I’m just amazed the linker could pick up my object, plus the 21 year old objects + libraries from that aforementioned ancient GCC port.
To start this fun voyage, I used HCC, the first usable port of Sozobon C to the Amiga I could track down. From it’s description:
Amiga port of Sozobon, Limited’s C Compiler. Can completely compile itself, supports 32 bit ints, and optimizer can ‘registerize’ variables. Includes compiler, optimizer, tool for creating interface code for Amiga system calls, startup code, C library, include files, and library routines that work with Motorola FFP format. Uses assembler A68k, linker BLink, and provided run-time shared C library CClib.library.
From the readme
And isn’t that great? It even supports 32 bit integers! I had to massage things in Visual C++, as there was some weird instances of return codes missing, and the optimizer not actually mallocing it’s memory, but just blindly using pointers. As always if you can see what is going on in a debugger it’s not too hard to make some wild guesses and get it running, and if you get lucky it may even work too…
Running the compiler
With the compiler and optimizer running (it is actually needed to run to further massage the assembly output into something the Amiga a68k assembler can read), it was time to look at an assembler. For the heck of it, I did try a68k, and to my amazement it did actually work, once I had updated the file output call.
hcc\hcc -L hanoi.c
hcc: version 2.0 Copyright (c) 1988,1989,1991 by Sozobon, Limited.
Amiga Version 1.1 by Detlef W³rkner.
top\top -v hanoi.s h2.s
top Version 2.00 Copyright (c) 1988-1991 by Sozobon, Limited.
Amiga Version 1.1 by Detlef W³rkner.
Peephole changes (1): 8
Peephole changes (2): 1
Peephole changes (3): 0
Instructions deleted: 3
Variables registered: 0
Loop rotations : 0
Branch reversals : 0
Branches removed : 4
a68k\a68k -q100 h2.s
68000 Assembler - version 2.61 (January 11, 1990)
Copyright 1985 by Brian R. Anderson
AmigaDOS conversion copyright 1989 by Charlie Gibbs.
PASS 1 line 59
PASS 2 line 59
End of assembly - no errors were found.
Heap usage: -w2047,80
Total hunk sizes: 94 code, 10 data, 0 BSS
Wow wasn’t that fun! I haven’t seen the source code to the BLINK linker, so I just end up using a native linker, BLINK.
Much to my amazement, the a68k assembler functions just fine as a cross assembler, and I only had to copy the object file into the emulator, and I could happily link.
The syntax for BLINK was a little strange, mostly because I really don’t know what I’m doing.
BLink LIB:HCC.o+hanoi.o LIB LIB:HCC.lib+LIB:stubs.lib TO hanoi SC SD VERBOSE
Now to try something bigger, like the ancient 1987 vintage InfoTaskForce. I had to add in the include files from the DICE compiler, and surprisingly, in no time, it was all compiled, and assembled the only step remaining was to run the BLINK linker. This time it was slightly different as now we had a bunch of object files:
BLink LIB:HCC.o+fileo.o+funcso.o+infocomo.o+inito.o+inputo.o+interpo.o+ioo.o+jumpo.o+objecto.o+optionso.o+pageo.o+printo.o+propertyo.o+supporto.o+variableo.o LIB LIB:HCC.lib+LIB:stubs.lib TO infocom SC SD VERBOSE
Running that as a single line (or better in a command file) got me my executable.
And it linked without any unresolved externals.
Running under WinUAE
And even better, it worked. Here it is running Planetfall!
I can’t imagine it being all that useful for anyone, as Sozobon C is K&R C, and well this is for the Commodore Amiga, not exactly a mainstay in this day & age.
HCC_Sozobon_win32cross.7z This link will take you to the sourceforge page, and the archive contains both source, and executables. As mentioned I didn’t see any Amiga linker that has source code, it seems everyone use BLINK, and the team that write BLINK went on to re-write all the ‘c’ commands in AmigaDOS from BCPL/asm into C.
I just discovered vlink after writing this, and now I can link a working executable under Windows 10! Since I made zero changes to vlink, and I’m not charging money, I am free to redistribute this so I’ve updated my archive, and included it.
I know it’s utterly pointless… But yeah GCC 2.8.1 + EMX 0.9d, hosted (running) on Win32. The main reason is that I wanted to be able use use my substantially faster Win64 machines to build stuff for OS/2. And since I have a 4 core (+4 hyper thread), I want to be able to use make with the -j 16 flag, and say compile QuakeWorld/2 in under two seconds.
I was able to get the binutils 2.6 derived stuff to compile, along with the ‘ancient’ binutils which is notably the linker that EMX depends on. I would imagine this ought to be able to compile PDOS, although my own simple attempt at InfoTaskForce met with spectacular failure. While it does compile fine using an older EMX 0.8h based release.
I had a request to help get a GCC+Binutils running as native win32 exe’s something comperable to the ancient ‘ultra’ N64 toolchain done by Kyoto Microcomputer (resume pdf). One interesting thing about their toolchain is that they used a common object format for MS-DOS, DOS/V and MS-DOS on the PC-98 format, along with Win32. However the Win32 runtime doesn’t like Win64 environments. On Win64 the exew32 driver just complains:
Can’t allocate memory (Error Code=487)
However the stubs in all the exe’s reference exegcc98 exegccv DOS extender’s along with a exegcc. However googling around yields nothing.
Running on a x86 version of Windows, however the tools run and report gcc 2.7.2 release 1.2 and the binutils version is simply 2.6 with BFD version 2.6. So going with this, and the request to keep it 1997 vintage I went ahead with Gcc 220.127.116.11 and Binutils 2.8.1 as they are the end of the line in both trains of code.
To configure is really a snap, as both support the Windows NT platform directly
sh configure --host=i386-winnt3.5 --target=mips-elf
Binutils 2.8.1 notes:
make sure this uses MS-DOS rb wb type constraints!
There is no sbrk on my MinGW32 … so comment out all the sbrk stuff.
My sed LOVES UNIX style text files, so this one shouldn’t be in MS-DOS CRLF format.
mkdir only accepts the path on Win32. Also there is now chown.
Gcc 18.104.22.168 notes:
‘__inline’ for is_reserved_word needs to be commented out.
Set like the following for both ASM_FINAL_SPEC to prevent the t-mips from trying to be run.
#define ASM_FINAL_SPEC “\
Just because we are on Windows NT, doesn’t mean we want an .obj object suffix.
__spawnv : __spawnvp work better as _spawnv : _spawnvp
*((void **)__o->next_free)++ = ((void *)datum);
confuses newer compilers, with this error message:
obstack.h:341:32: error: lvalue required as increment operand
replace it with with:
*(__o->next_free)++ = ((void *)datum);
So at the end I have a cross compiler, and I can generate object files, and link files that the final tool MILD can then use and produce N64 ROM images. It’s not a 100% solution, as I don’t see any mention of MILD being GNU, however the compiler and binutils is running on Windows 10 x64!
I’m using Slackware 4.0 as a starting point, so it’s Binutils 2.9.1 and GCC 22.214.171.124 .. I verified that I can build a static hello world executable, and it runs! …
However Linux 2.0.40 has the same issue, it starts to decompress, and triggers a reboot in both Qemu and PCem. Going in circles I guess. I suppose the next step is to use the exact version they have in Slackware to see if Qemu can actually run that pre-built kernel, and if I can create one via cross compiling.
I should add that on Debian 7.1 I got GCC 126.96.36.199 running, and it too produces the exact same thing.