Third milestone - mikpe/pdp10-tools GitHub Wiki
Today the project reached its third milestone: using my PDP-10 port of GNU binutils to generate an executable in industry-standard ELF format, then using my elf2boot
utility to generate a DEC EXE format executable, and finally booting that in the KLH10 PDP-10 full-system emulator.
The PDP-10 is a 36-bit word-oriented computer, halfwords are 18 bits, and the closest we can come to a working definition of "byte" is the 9-bit nonet. ELF and GNU binutils on the other hand assume byte means octet (8-bits) and primitive types are a power-of-2 multiple of that, so 8, 32, or 64 bits.
Teaching GNU binutils about 9-bit bytes and 36-bit words seemed impossible, so long ago I started the pdp10-tools
project to reimplement GNU binutils using a variant of ELF based on nonets, aka ELF-36. This progressed quite far (working assembler, readelf, archiver, and basic linker) but it got very tedious reimplementing the wheel and trying to match GNU binutils on features and correctness.
Then in 2024 I completed a port of GNU binutils and GCC to the CDP-1802, a very simple 8-bit microprocessor, with ELF as object file format. That process taught me a lot about GNU binutils internals and what's required to get a fully functional ELF assembler and linker -- as it turns out, not very much. Armed with that experience I decided to try again to port GNU binutils to the PDP-10.
The core idea is to "embed" PDP-10 code and data in ELF in such a way that each nonet, halfword, and word is addressable on an octet boundary. This is achieved by zero-extending each individual nonet (9 bits) to two octets (16 bits). Conversions between the two views happen:
- When assembling instructions. This is entirely in the port's own code (in
gas
). - When emitting data. To avoid breaking most of the test suite the port adds PDP-10 specific directives (
.pdp10_word
etc). They are handled by generic code, with port-specific wrappers and callbacks to ensure that PDP-10 specific relocations are used. Finally the data and relocation is passed to the port's own code for final layout (this is ingas
). - When finalizing relocations. This is driven by declarative
HOWTO
structures that tell generic code where the relocated data is stored and how it's transformed. Unfortunately it's limited to simple masking and shifting, which isn't enough for us, so the port extends that structure with two optional callback functions for reading and writing relocated data, and it's in those callbacks our conversions occur (this is inbfd
). - When dissembling code. This is entirely in the port's own code (in
opcodes
).
Everywhere else it's just standard ELF-64 and addresses that happen to always be even. The main benefit is that we don't have to do anything PDP-10 specific to get a fully functional generic ELF port. And the GNU binutils test suite is clean with no unexpected failures. Yes the object files grow a bit, but that's a price I'm willing to pay.
kn10-kl
comes from KLH10, elf2boot
from my pdp10-tools, and the remaining binary utilities from my GNU binutils PDP-10 port.
> cat kernel.s
.text
.globl _start
.type _start,@function
_start:
halt 0604460 # "PDP" in DEC SIXBIT
.size _start,.-_start
> pdp10-unknown-elf-as --version
GNU assembler (GNU Binutils) 2.45.50.20250804
Copyright (C) 2025 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or later.
This program has absolutely no warranty.
This assembler was configured for a target of `pdp10-unknown-elf'.
> pdp10-unknown-elf-as -o kernel.o kernel.s
> pdp10-unknown-elf-ld -V
GNU ld (GNU Binutils) 2.45.50.20250804
Supported emulations:
pdp10elf
> pdp10-unknown-elf-ld -o kernel kernel.o
> pdp10-unknown-elf-readelf --wide -a kernel
ELF Header:
Magic: 7f 45 4c 46 02 02 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, big endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Digital Equipment Corp. PDP-10
Version: 0x1
Entry point address: 0x401078
Start of program headers: 64 (bytes into file)
Start of section headers: 336 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 1
Size of section headers: 64 (bytes)
Number of section headers: 5
Section header string table index: 4
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 0000000000401078 000078 000008 00 AX 0 0 1
[ 2] .symtab SYMTAB 0000000000000000 000080 000090 18 3 2 8
[ 3] .strtab STRTAB 0000000000000000 000110 000019 00 0 0 1
[ 4] .shstrtab STRTAB 0000000000000000 000129 000021 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
D (mbind), p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x0000000000401000 0x0000000000401000 0x000080 0x000080 R E 0x1000
Section to Segment mapping:
Segment Sections...
00 .text
There is no dynamic section in this file.
There are no relocations in this file.
The decoding of unwind sections for machine type Digital Equipment Corp. PDP-10 is not currently supported.
Symbol table '.symtab' contains 6 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000401078 0 SECTION LOCAL DEFAULT 1 .text
2: 0000000000401078 8 FUNC GLOBAL DEFAULT 1 _start
3: 0000000000402080 0 NOTYPE GLOBAL DEFAULT 1 __bss_start
4: 0000000000402080 0 NOTYPE GLOBAL DEFAULT 1 _edata
5: 0000000000402080 0 NOTYPE GLOBAL DEFAULT 1 _end
No version information found in this file.
> pdp10-unknown-elf-objdump -d kernel
kernel: file format elf64-pdp10
Disassembly of section .text:
0000000000401078 <_start>:
401078: 00 ac 00 80 halt 604460
40107c: 01 84 01 30
> elf2boot -o kernel.exe kernel
> kn10-kl
KLH10 2.0l (MyKL) built Aug 26 2023 16:49:35
Copyright © 2002 Kenneth L. Harrenstien -- All Rights Reserved.
This program comes "AS IS" with ABSOLUTELY NO WARRANTY.
Compiled for unknown-linux-gnu on x86_64 with word model USEINT
Emulated config:
CPU: KL10-extend SYS: T20 Pager: KL APRID: 3600
Memory: 8192 pages of 512 words (SHARED)
Time interval: INTRP Base: OSGET
Interval default: 60Hz
Internal clock: OSINT
Other: MCA25 CIRC JPC DEBUG PCCACHE CTYINT EVHINT
Devices: DTE RH20 RPXX(DP) TM03(DP) NI20(DP)
[MEM: Allocating 8192 pages shared memory, clearing...done]
KLH10# load kernel.exe
Using word format "c36"...
Loaded "kernel.exe":
Format: DEC-PEXE
Data: 0, Symwds: 0, Low: 01000000, High: 0, Startaddress: 01000
Entvec: 06 wds at 01000
KLH10# trace
Tracing now ON
KLH10# go
Starting KN10 at loc 01000...
1000: JRST 5,1003 E = 1003
2001017: JRST 4,604460 E = 604460
[HALTED: Program Halt, PC = 2604460]
KLH10>