Skip to content

PsyQ Lib File Format

Antonio Davide edited this page Dec 7, 2018 · 1 revision

This document is in draft state, it may be wrong, incomplete and bad written.


PsyQ Lib File Format

PsyQ static libraries are shipped with the official PS1 SDK and provides all needed APIs for develop games on this console. Some fields are still unknown but it's possible to extract all function signatures, names and relocation info.

NOTE: All std::string in structs are parsed from a PsyQString type.

The "LIB" Header

A PsyQ Lib begin with "LIB" signature and its version.

struct PSYQFile { 
  char signature[3]; // "LIB"
  u8 version; // Always 1
 };

PsyQ Strings

The PsyQ string is represented in this way:

struct PSYQString { 
  u8 length;
  char data[1];
};

If the length field is 0, the data one doesn't exist.

NOTE: those strings are are not null terminated.

Modules

Modules are the main data structure of LIB files, they contains one or more function definitions, sections, relocations and data/function references, the size of this structure is variable, so it must be read sequencially.

By using C++, the structure is more or less this:

struct PSYQModule {
    std::string name;
    u32 unk, offsetlink, offsetnext;
    std::list<std::string> names;
    PSYQLink link;
};
  • name: Max length 8.
  • offsetlink: Relative offset to "LNK" structure (see below).
  • offsetnext: Relative offset to the next module.
  • names: PSYQString array of definitions.
  • link: "LNK" structure.

NOTE: Offsets are relative to the beginning of PSYQModule.

"LNK" structure

Every module provides a "LNK" structure which contains all useful data of a LIB file, this structure is not fixed.

struct PSYQLink {
    char signature[3]; // "LNK"
    u8 version, processorType;
    std::unordered_map<u16, PSYQSection> sections;
    std::unordered_map<u16, PSYQDefinition> definitions;
    std::unordered_map<u16, PSYQReference> references;
    std::unordered_map<u16, PSYQBss> bss;
    std::list<PSYQPatch> patches;
};
  • signature: "LNK".
  • version: Always 2.
  • processorType: Always 7 (MIPS?).

After the processorType field the structure is variable: sections, definition, references, bss and patches can be available in every order. Before every structure there is an hint byte which help to understand which data is following, there are the known values:

enum PSYQState: u8 {
    SectionEOF      = 0,
    SectionCode     = 2,
    SectionSwitch   = 6,
    SectionBss      = 8,
    SectionPatch    = 10,
    SymbolRef       = 14,
    SymbolDef       = 12,
    SectionSymbol   = 16,
    Processor       = 46,
    SymbolBss       = 48,
};
  • SectionEOF: This module is terminated.
  • SectionCode: Here we have the section's bytes.
  • SectionSwitch: We are switching section, all information that follows belongs to this one.
  • SectionBSS: Size of unallocated data.
  • SectionPatch: Relocations.
  • SymbolRef: Imported/External symbols.
  • SymbolDef: Function declaration.
  • SectionSymbol: The section header (with symbolnumber, group and alignment).
  • Processor: A single byte which is always 7.
  • SymbolBss: Variable references.

"SectionCode" state

We can get the function signatures from here!

struct PSYQSectionCode {
  u16 length;
  u8 data[1];
}

"SectionSwitch" state

A unsigned 16-bit integer with the section number

"SectionBSS" state

An unsigned 32-bit integer which contains size of the current BSS section.

"SectionPatch" state

This section contains the relocations for this module, the structure is variable and contains some kind of substates

struct PSYQPatch {
    u8 type;
    u16 offset;

    struct {
        u8 unk1;
        u32 value;
    } patchvalue;

    struct {
        u16 symbolnumber;
        u16 sectionnumber;
    };
};

These are the relocation states.
Names are guessed, so they may be wrong:

enum PSYQPatchState: u8 {
    PatchToReference = 2,
    PatchToSection   = 4,
    PatchToValue     = 44,
    PatchToDiff      = 46,
};

Every relocation begins with type and offset fieldd and is followed by a PSYQPatchState unsgined 8-bit value which explains how to read the rest of the relocation:

  • PatchToReference: Unsigned 16-bit integer which represents the symbol number.
  • PatchToSection: Unsigned 16-bit integer which represents the section number.
  • PatchToValue: An unknown 8-bit value followed by a 32-bit integer with the relocation value.
  • PatchToDiff: Same as PatchToValue.

"SymbolRef" state

This state contains all external references with the symbol number (useful for relocations) and its name.

struct PSYQReference {
    u16 symbolnumber;
    std::string name;
};

"SymbolDef" state

This is a symbol defined inside the library with its code/data.

struct PSYQDefinition {
    u16 symbolnumber, sectionnumber;
    u32 offset;
    std::string name;
};
  • symbolnumber: A unique id for this symbol.
  • sectionnumber: The section that contains this symbol.
  • offset: A section relative offset where you can find the data of this symbol (function bytes, etc.)
  • name: The name of this symbol.

"SectionSymbol" state

This state contains the header of a single section:

struct PSYQSection {
    u16 symbolnumber, group;
    u8 alignment;
    std::string name;
    std::vector<u8> code;
    // u32 sizebss;
};
  • symbolnumber: The unique id for this section (used in relocations and symbol definitions).
  • group: It seems to be always 0.
  • alignment: Always 8.
  • name: The name of this section (.text, .data, .bss, etc).
  • code: The section bytes (if not bss).
  • sizebss: The size of BSS section (0 if code is valid).

"Processor" state

An 8-bit integer which is always 7.

"SymbolBSS" state

struct PSYQBss {
    u16 symbolnumber, sectionnumber;
    u32 size;
    std::string name;
};
  • symbolnumber: The unique id (used in relocations and symbol definitions).
  • sectionnumber: The section that contains this symbol.
  • size: Size, in bytes.
  • name: Name.

Reference