Third milestone - mikpe/pdp10-tools GitHub Wiki

Third milstone

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.

GNU binutils port to pdp10-elf

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 in gas).
  • 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 in bfd).
  • 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.

Putting it all together

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>
⚠️ **GitHub.com Fallback** ⚠️