System Files:QFILEMCD

From Try-AS/400
Revision as of 22:38, 8 August 2026 by Friedkiwi (talk | contribs) (Correct the header field at 0x26: it carries the system serial number, not a build id)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

QFILEMCD is the larger of the two container files found on AS/400 SAVSYS and Licensed Internal Code installation media. It is a backup of SLIC itself — the System Licensed Internal Code that sits below OS/400 — stored as a small number of very large memory segments. Its internal format is called COPYDIR after the eyecatcher at offset zero.

Practically, this is the file to reach for if you want to read the machine's own code without touching a running system. Its segments are a byte-for-byte image of what the link loader maps into memory, so an address recovered from a crash, a trace or a system reference code can be looked up directly in a file on your workstation. The companion file System Files:QFILEIML carries the loadable IOP, adapter and service processor microcode instead.

Both files are containers of LIDs, but at different granularities, and the relationship between them is described under Two directories, two granularities below.

The figures quoted here come from one V4R4 image of 472,846,336 bytes (451 MiB). Treat structure as general and numbers as release-specific; the format is unchanged from V4R4 through V7R4, across install media and SAVSYS output alike.

Header

Offset Contents
0x00 Eyecatcher COPYDIR  in EBCDIC (C3 D6 D7 E8 C4 C9 D9 40), a zero byte, then the two EBCDIC characters 00
0x1C 32-bit total size of the payload area — see below. Confirmed on three independent samples
0x26 Release level, then the system serial number, EBCDIC
0x50 Licensed Internal Code banner, EBCDIC, in 48-character chunks
0x1C0 Start of the directory

The string in a V4R4 image reads:

V4R4M044-C6295  2606071417

and in a V7R4 one taken by SAVSYS from a running machine:

V7R4M021-7886W

The first field is the release and modification level. The five characters after the hyphen are the system serial number. That is not an inference: the owner of the V7R4 machine confirms its serial is 7886W.

On installation media the same field carries a build or media tag instead — a V7R4 install ISO reads V7R4M021-821BV — so what the field means depends on how the image was produced. Both forms are five characters, which makes them easy to confuse.

Note! A LIC backup is therefore not anonymous: it records the serial number of the machine it came from. The serial is also one of the values the system password is keyed on (see CISC AS/400 LIC Tapes), so treat these files as identifying if you share them.

In the V4R4 sample the ten digits after the serial parse naturally as a timestamp of the form YYMMDDHHMM, making that backup 2026-06-07 14:17. That part is an inference from the shape of the field, and the V7R4 sample carries binary rather than digits in the same place, so the layout after the serial is not fixed.

Note! The header is not a fixed 192 (0xC0) bytes, despite older format notes saying so. The IBM legal banner runs on well past that point, and in this V4R4 image the directory does not begin until 0x1C0. A parser should locate the directory by scanning for the first 64-byte-aligned block that has the shape of an entry, rather than seeking to a constant.

The 32-bit field at 0x1C holds 0x1C2F0000, which is exactly the end of the last segment's data. It is a total-payload-size field and makes a good consistency check after parsing the directory: accumulate the entries and you should arrive at the same number.

The directory

The directory is a run of 64-byte entries beginning at 0x1C0.

Offset Size Field
+0x00 8 Always zero in observed data
+0x08 8 Flags. See the bit-numbering warning below
+0x10 8 SLS address of the saved segment
+0x18 8 Segment attributes; the top 16 bits carry the useful value
+0x20 8 Always zero in observed data
+0x28 8 Offset to the segment data, absolute from the start of the file
+0x30 4 Segment length, in raw bytes — not blocks
+0x34 4 Always zero in observed data
+0x38 8 Always zero in observed data

Two details cause most of the trouble people have with this structure, and both were originally got wrong here:

  • Flags use IBM's MSB-first bit numbering. Older notes describe the field as bbt 0:3, and a reader used to LSB-first numbering will look in the bottom nibble and find nothing. Bit 0 is the most significant bit, so the value lives in the top nibble of the 8-byte field. A normal entry reads 0x1000000000000000; the end-of-directory marker reads 0xF000000000000000.
  • The data offset is absolute. It is measured from the start of the file, not from the end of the directory. Reading it as relative shifts every extracted segment by the size of the directory, which produces code that disassembles almost plausibly and is wrong everywhere.

In this image the directory holds 32 entries, with the terminator at 0x9C0 and the directory ending at 0xA00. The first entry has a data offset of zero, which means the header and directory physically occupy the first 0xA00 bytes of the first segment's stored image. This is expected rather than a bug, but a tool that extracts that segment will see the container's own header at the front of it.

Segment lengths are consistently a whole number of 4 KiB pages less than a power-of-two boundary: 0xFFF000 is 16 MiB minus one page, 0x7FF000 is 8 MiB minus one page, and so on.

Contents of a typical backup

The 32 segments of this image, with names from a community-maintained SLS address reference. Where the name column is blank, the segment's purpose has not been established.

SLS address Length Name
FFFFFFFFB1 000000 16 MiB
FFFFFFFFB2 000000 16 MiB
FFFFFFFFB3 000000 16 MiB
FFFFFFFFB5 000000 16 MiB
FFFFFFFFC1 000000 16 MiB Link Loader — LIC Code
FFFFFFFFC2 000000 16 MiB Link Loader — LIC Data
FFFFFFFFC3 000000 16 MiB Link Loader — LIC Code (2)
FFFFFFFFC4 000000 16 MiB Link Loader — LIC Code (3)
FFFFFFFFC5 000000 16 MiB Link Loader — LIC Code (4)
FFFFFFFFC6 000000 8 MiB Link Loader — LIC Code (5)
FFFFFFFFC7 000000 16 MiB Mixed — component code and NLS text pools
FFFFFFFFC8 000000 8 MiB Mixed — bignum/JVM code and NLS text pools
FFFFFFFFF1 000000 4 MiB Link Loader TOC
FFFFFFFFF2 000000 16 MiB Service LID
FFFFFFFFF4 000000 16 MiB Service LID (2)
FFFFFFFFFE 000000 15 MiB Pageable BLA
FFFFFFFFD1 000000 16 MiB Link Loader Information
FFFFFFFFD2 000000 16 MiB Link Loader Information, Code, Data
FFFFFFFFD3 000000 16 MiB Link Loader Information (2)
FFFFFFFFD4 000000 16 MiB Mixed — JVM strings and DST debug symbol table
FFFFFFFF90 010000 60 KiB LID Directory
FFFFFFFF91 000000 16 MiB LID NUC2 A-Side
FFFFFFFF92 000000 16 MiB LID NUC2 B-Side
FFFFFFFF93 000000 16 MiB LID NUC1 A-Side
FFFFFFFF94 000000 16 MiB LID NUC1 B-Side
FFFFFFFF95 000000 16 MiB
FFFFFFFF96 000000 16 MiB
FFFFFFFF9A 000000 8 MiB
FFFFFFFF9B 000000 8 MiB
FFFFFFFFA1 000000 16 MiB LID Manager
FFFFFFFFA2 000000 16 MiB
FFFFFFFF69 000000 16 MiB IDE

So a backup of this shape is the SLIC boot and loader nucleus: link loader code and data, the loader's table of contents, the LID subsystem's own bookkeeping, and both IPL sides of the two nucleus LIDs. It is not the whole of the machine's code.

A and B sides

Several segments appear twice, distinguished as A-side and B-side. An AS/400 keeps two copies of its Licensed Internal Code so that an update can be applied to one while the other remains a fallback. A normal, unattended IPL runs the B side, so B is what a running machine is executing and B is what static analysis should be done against. Because the two sides are normally near-identical, a byte comparison against the A side is not evidence that A is the live one — the test only means something on a segment that actually differs between them.

Two directories, two granularities

A common source of confusion is that QFILEMCD appears to have two completely different directory formats. It does, and they nest:

  • The coarse level is COPYDIR's own directory, documented above: 64-byte entries, one per 16 MiB-class memory segment, 32 of them.
  • The fine level is a catalogue of individual LIDs, thousands of entries, using the 32-byte record described at Data Structures:LID.

The fine level is not a second header in the file. It is simply the contents of one particular segment — the one named LID Directory, at FFFFFFFF90 010000. Extract that segment and you find, after a preamble of about 1536 bytes, an EBCDIC Directory. eyecatcher, a version label, and then the records. In this image the label reads v4r4m01204.0.03, which is the same label carried by System Files:QFILEIML — a quick way to confirm that two files came off the same media.

That is also why tools which dump this file emit two dissimilar-looking listings: a short table of large memory extents, and a long list of LIDs. They are two levels of one structure, not two formats.

Extracting a segment

The practical recipe, and the reason this file is worth knowing about:

  1. Read the COPYDIR eyecatcher at offset 0 to confirm the format.
  2. Find the directory by scanning 64-byte-aligned offsets for the first entry-shaped block, rather than assuming 0xC0 or 0x1C0.
  3. Walk entries until the flags field has 0xF in its top nibble.
  4. For the segment you want, seek to the data offset as an absolute file offset and read length raw bytes.
  5. Load the result at its SLS address in a disassembler. The bytes are what the link loader maps, so addresses match.

Step 5 is the payoff. A 64-bit address such as FFFFFFFFC6 1F1690 can be typed straight into the DST Display/Alter storage panel on a live machine of the same build, and the sixteen rows it displays will match the extracted file byte for byte. Static and live analysis share one address space, which means you can do the slow work offline and use the machine only for confirmation.

Two caveats on addresses. They are per-build, not per-release, so comparisons are only meaningful between identical builds; the same class sits at a quite different address on a V5R4 machine. And the SLS address recorded in the directory is the address the segment was saved from, which is not always where it runs — the resident nucleus segment, for instance, is stored in the container under the SID of the B-side nucleus LID rather than its runtime SID.

Reading the code

Once a segment is loaded, two structures make it navigable, and both are worth knowing because they turn addresses into names:

  • Traceback trailers. Every compilation unit ends its code with a TBTB trailer, and many carry the module's name. A trailer names a whole compilation unit rather than a single function, so a hit means "this address is inside module X", not "this address is function X".
  • Link-loader descriptor blocks. Considerably richer: one block per procedure, carrying the entry address at +0x08 and the name in EBCDIC at +0x38. Names beginning with # are SLIC-internal modules; the rest are C++ mangled symbols.

Merging both across every segment of this V4R4 image yields roughly 184,000 named records, which is enough to name a substantial fraction of any address you encounter. See SRC for how to use that to get from a reference code back to a module.

Calls between modules do not go through a conventional dispatch table, which surprises people disassembling SLIC for the first time. An external call is a two-instruction sequence — ori r11,r13,ordinal followed by bla into a transfer vector at the very top of the address space — and the 16-byte stub it lands on computes the target arithmetically. There is no table to read off; the ordinal is the callee's word offset within a window, and the stub supplies the window. The stub constants cannot be derived and must be read from a dump of the transfer vector.

The trailing page

The payload area ends at 0x1C2F0000, but the file is one 4 KiB page longer than that. The final page is not padding: it holds 468 non-zero 8-byte records, which appear to be address-and-value pairs.

They fall into two clear populations. The first 110 are in ascending address order with a value of 8, addressing the Link Loader — LIC Data segment among others. The remaining 256 are identical to each other: the same address 0xFE911220, in the Pageable BLA segment, paired with the value 0x48000002 — which as PowerPC is a branch-absolute to zero, the shape of a deliberately poisoned entry.

This has the flavour of a fixup or relocation list applied after the segments are restored, but that is a guess from shape alone. It is recorded here because it is real, reproducible and unexplained, and because a parser that assumes the file ends with the payload will silently ignore it.

Open questions

  • The purpose of the trailing page.
  • The header bytes other than the eyecatcher, size field, release string and banner.
  • The meaning of the segment attribute field's top 16 bits, which take values 0x0001, 0x0040, 0x0080 and 0x0100 and correlate loosely with segment size.
  • Names for the nine segments left blank in the table above.

See also

Footnotes