Categorie
Uncategorized

Hands on #2

Nell’esempio di savefile descritto nel precedente post abbiamo analizzato solo la prima pagina contenente la intestazione del savefile. Seguono 3 pagine (da 4KB ciascuna) per cui la struttura complessiva è la seguente:

-------------------------------------------------
| Header                               | PAGE 1 |
-------------------------------------------------
| Load/dump object descriptor (0x19db) | PAGE 2 |
-------------------------------------------------
| System Object Structure     (0x19db) | PAGE 3 |
| Associated space (IBM i work area)   | PAGE 4 |
-------------------------------------------------

Come dicevamo in precedenza la documentazione fornita da Frank Soltis ci è utile per comprendere la pagina 3 (System Object Structure).

I primi 32 byte della stessa contengono la Segment header e forniscono informazioni descrittive del segmento di memoria di cui è stato effettuato il dump. Le due pagine 3 e 4 sono esattamente la copia del contenuto di due pagine presenti in memoria (si tratta di un dump) anche se non si tratta di un oggetto utente ma di una entità di servizio ad uso del sistema operativo.

Questa è la segment header:

    00 01 00 10 03 c0 00 90 3a aa 40 cc fa 00 00 00
    00 01 00 00 00 00 00 00 3a aa 40 cc fa 00 10 00

Il valore 0x3aaa40ccfa (ossia i primi 40 bit dei due indirizzi nelle posizioni 9-16 e 25-32) identificano univocamente un segmento di memoria IBM i. Il primo indirizzo è complessivamente un puntatore alla posizione iniziale del segmento (offset 0x000000), il secondo è un puntatore alla pagina successiva (offset 0x001000).


Un segmento si compone sempre di un multiplo intero di pagine di memoria (4KB) logicamente contigue ( ma non necessariamente contigue anche nella memoria fisica ).

Il secondo indirizzo ci dice che a partire dalla posizione 0x001000 (fatto 0 l’inizio della header) inizia il cosiddetto Associated Space che altro non è se non l’area di lavoro utilizzata dal sistema operativo IBM i. Il sistema operativo IBM i (ex OS/400 -e ancor prima ex CPF-) esiste ad un livello di architettura superiore al License Internal Code : “all users of the MI, including OS/400 itself, are not allowed below the MI” (op. cit. Soltis, page 129) .

Le informazioni che ci interessano, ossia quelle che descrivono gli oggetti come visti/gestiti dal sistema operativo, si trovano dunque all’inizio della pagina 4.

Sapendo che all’interno del segmento di memoria 0x3aaa40ccfa la sola pagina gestita dal sistema operativo IBM i (e di nostro interesse) inizia all’indirizzo 0x1000, decidiamo di far precedere al dump esadecimale un offset relativo partendo da 0x00000000 anziché dall’offset relativo al segmento:

000000000 02 49 c5 d4 d7 e3 e8 40 40 40 40 40 40 40 40 40
000000010 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40
000000020 04 01 a6 d7 9a 5b 0d 5a e0 01 00 00 00 01 00 00
000000030 00 00 01 40 40 ** ** ** ** ** ** ** 00 00 5c e2 
000000040 e8 e2 e5 c1 d3 40 40 40 00 00 00 00 d5 00 01 00 000000050 5c e2 e8 e2 c2 c1 e2 40 40 40 00 5c d5 d6 d5 c5 000000060 40 40 40 40 40 ** ** ** ** ** ** ** ** 00 00 00

La prima sezione (byte 1-112) ha dimensione fissa è descrive l’operazione di salvataggio (operazione, versione sistema operativo, nome libreria, tipo, sottotipo, timestamp, ecc.).

000000070 c5 d4 d7 e3 e8 40 40 40 40 40 40 40 40 40 40 40
000000080 40 40 40 40 40 40 40 40 40 40 40 40 40 40 04 01
000000090 c1 d5 c4 d9 c5 c1 d9 c9 c2 40 40 40 40 40 40 40
0000000a0 40 40 40 40 40 40 40 40 40 40 40 40 40 40 00 00
0000000b0 00 00 80 00 01 c0 00 00 00 01 10 00 00 01 60 00
0000000c0 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000000d0 00 00 00 00 00 00 00
0000000d7                      00 00 00 00 00 00 00 00 00

La seconda è costituita da una struttura di elementi che si ripete: una struct di lunghezza 151 byte (0xd7) per ogni oggetto salvato (nel nostro caso ne è presente una sola).
All’interno di questa struttura risiedono due riferimenti a posizioni successive che contengono dati addizionali relative all’elemento salvato.

I valori 0x00000110 e 0x00000160 fungono da puntatori locali all’interno della pagina.

000000110 00 00 00 00 00 00 00 40 00 00 00 00 02 e2 00 00
000000120 ff 1c 3f 10 60 00 00 00 40 00 00 00 00 00 00 00
000000130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*

Alla posizione 0x00000110 (in questo esempio) si trovano informazioni generali valide per ogni tipo di oggetto.

Mentre alla posizione 0x00000160 (sempre in questo esempio) troviamo una serie di strutture precedute dalla loro
lunghezza e che hanno un significato posizionale.

Espandiamo la sezione di dump seguente per facilitarne la comprensione:

000000160 00 00 00 00 00 00 00 a8 40 40 40 40 40 40 40 40
000000170 40 40 40 40 40 40 40 40 40 40 40 40 40 f1 40 40
000000180 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40
000000190 40 40 40 e5 f7 d9 f4 d4 f0 f1 f2 f1 f0 f8 f2 f6
0000001a0 f1 f8 f5 f3 f0 f9 40 40 40 40 40 40 40 40 40 40
0000001b0 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40
*
0000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
000000200 40 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000000210 00 00 00 1a c1 d5 c4 d9 c5 c1 d9 c9 c2 40 ** **
000000220 ** ** ** ** ** ** 00 00 00 00 00 00 00 00 00 00
000000230 00 00 00 00 00 0b d7 d9 d6 c4 40 40 40 40 40 40
000000240 f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000000250 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

interpretando 4 byte alla volta come lunghezza che precede ciascun elemento:

  • Primo elemento (assente, lunghezza 0)
000000160 00 00 00 00
  • Secondo elemento
000000164             00 00 00 a8
000000168                         40 40 40 40 40 40 40 40
000000170 40 40 40 40 40 40 40 40 40 40 40 40 40 f1 40 40
000000180 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40
000000190 40 40 40 e5 f7 d9 f4 d4 f0 f1 f2 f1 f0 f8 f2 f6
0000001a0 f1 f8 f5 f3 f0 f9 40 40 40 40 40 40 40 40 40 40
0000001b0 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40
0000001c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000000200 40 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  • Terzo elemento

Avanzando di 0xa8 byte: 0x168 + 0xa8 = 0x210

000000210 00 00 00 1a
000000214             c1 d5 c4 d9 c5 c1 d9 c9 c2 40 ** **
000000220 ** ** ** ** ** ** 00 00 00 00 00 00 00 00

Quarto elemento (assente, lunghezza 0)

00000022e                                           00 00
000000230 00 00

Quinto elemento (attributo; valore “PROD”)

000000232       00 00 00 0b
000000236                   d7 d9 d6 c4 40 40 40 40 40 40
000000240 f0

Sesto elemento (assente, lunghezza 0)

000000241    00 00 00 00

Settimo elemento (assente, lunghezza 0)

000000245                 00 00 00 00

Mettendo a confronto il savefile con l’output del comando DMPOBJ OBJ(EMPTY) OBJTYPE(*LIB) si potranno correlare i campi con la dicitura adottata nella stampa QPSRVDMP.

Categorie
Uncategorized

Hands on #1

Partiamo dal caso più semplice in assoluto: il salvataggio di una libreria vuota.

CRTLIB LIB(EMPTY)
È stata creata la libreria EMPTY.
CRTSAVF FILE(EMPTY)
File EMPTY creato nella libreria QGPL.
SAVLIB LIB(EMPTY) DEV(*SAVF) SAVF(EMPTY)
1 oggetti salvati dalla libreria EMPTY.

Una volta trasferito la dimensione risultante del savefile è 16896 byte.
Come detto in precedenza la lunghezza record di un savefile è 528.
Una prima conferma sulla correttezza delle nostre ipotesi è che 528 * 32 = 16896.

Verifichiamo anche che 8 * 4 = 32: il nostro savefile è di fatto il backup di sole 4 pagine di memoria.

La prima di queste quattro pagine contiene una intestazione che impiega una porzione risibile dei 4KB utilizzati. Stiamo parlando dei soli primi 64 byte (64 su 4096… meno dell’1.6%):

a6 d7 2f 42 b7 82 70 00 00 00 00 20 30 00 30 00 
f4 f1 c7 40 f9 f0 f0 f9 40 40 40 40 40 40 40 40 
40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 
00 00 46 00 00 00 10 00 00 00 00 00 00 00 00 20 

Nel prosieguo indicheremo il primo byte con 1 e gli altri di conseguenza.

I byte dal 17 al 48 sono in EBCDIC e descrivono il tipo macchina e il modello su cui è stato effettuato il backup: nel caso mostrato vale 41G 9009 seguito da 24 blank (0x40).

I byte da 1 a 16 e da 49 a 64 sono composti di unsigned (big endian) di dimensioni diverse; rispettivamente:

  • (8, 4, 2, 2) e
  • (2, 2, 4, 8).

Faremo dunque riferimento a 9 campi:

  • da 1 a 8
  • da 9 a 12
  • da 13 a 14
  • da 15 a 16
  • da 17 a 48: tipo macchina e modello
  • da 49 a 50
  • da 51 a 52
  • da 53 a 56
  • da 57a 64

Con un poco di osservazioni su svariati savefile si verifica facilmente che 0x00000020 (byte 9-12) della prima riga e 0x0000000000000020 della quarta (byte 57-64) altro non sono che la lunghezza del savefile espressa in numero di record. Infatti 0x20 = 32 corrisponde a 16896/528 come rilevato in precedenza.

Generando nuovi savefile i primi 8 byte sono sempre diversi ma crescenti e sono grosso modo sincronizzati tra macchine diverse… sono infatti un timestamp nel formato interno di sistema. Tale formato è documentato come *DTS nelle Usage Notes della API QWCCVTDT.

Il campo 53-56 sembra specificare la dimensione in byte di una pagina di memoria: 0x00001000 = 4096 (ai tempi della architettura CISC il valore era infatti 0x00000200).

  • 1-8 timestamp nel formato *DTS (uint64_t)
  • 9-12 lunghezza savefile in numero record (uint32_t)
  • 13-14
  • 15-16
  • 17-48 tipo macchina e modello
  • 49-50
  • 51-52
  • 53-56 dimensione pagina di memoria in byte (uint32_t)
  • 57-64 lunghezza savefile in numero record (uint64_t)

I campi rimanenti hanno a che fare con la versione del formato savefile utilizzata e con la versione del sistema operativo abilitato al ripristino.

Per ora tuttavia ignoreremo i qualificatori per i formati del savefile (non cambiano dai tempi della introduzione RISC: X’3000′) e ci limitiamo ad una piccola precisazione circa il valore registrato nei byte 51-52.

Se salvassimo con valori diversi per l’opzione TGTRLS:

  • TGTRLS(V7R2M0)
  • TGTRLS(V7R3M0)
  • TGTRLS(V7R4M0)

otterremmo rispettivamente i valori X’4400′, X’4500′ e X’4600′.

In punti successivi nel savefile troveremo le sequenze X’46004400′, X’46004500′ e X’46004600′ rispettivamente. E’ il secondo di tali valori che -ove non rispettato- fa generare al sistema l’errore CPF3743.


Tale campo specifica a partire da quale versione sia possibile effettuare il ripristino o la visualizzazione del savefile.
Se un savefile è stato generato su un sistema a versione V7R4M0 senza specificare un diverso target release (TGTRLS) non sarà possibile effettuarne il ripristino in un sistema a V7R3M0 o V7R2M0 (come tristemente noto agli utenti principianti!).

Categorie
Uncategorized

Primi passi

La fonte pubblica principale di informazioni sui dettagli implementativi relativi alla memorizzazione di oggetti nel sistema operativo IBM i resta -per quanto io sia a conoscenza- il testo di Frank Soltis intitolato FORTRESS ROCHESTER
la cui pubblicazione risale al 2001.

Nell’ottavo capitolo -dedicato agli oggetti- esistono preziose considerazioni per guidarci alla scoperta dei savefile. Non che si faccia alcuna menzione ad essi nel testo ma semplicemente perché la System Object Strutture descritta a partire dalla pagina 125 è illuminante per capire il tracciato dei savefile stessi.

Le informazioni relative ad un singolo oggetto una volta trasferito su un savefile risiedono in punti diversi. Il fatto stesso che un oggetto venga salvato introduce alcune entità intermedie che servono a legare il dump dell’oggetto al nuovo contesto in cui esso viene ad essere incastonato.

A tal fine concorre l’esistenza di un particolare oggetto avente come tipo e sottotipo i valori esadecimali X’19DB’ e che, nella documentazione pubblicata online da IBM, corrisponde al tipo *SRDS definito come Save/restore descriptor space.

In funzione della numerosità e del contenuto di informazioni relative agli oggetti coinvolti in uno specifico salvataggio, un savefile potrà attivare il dump di più oggetti *SRDS. Nella serializzazione risultante avremo quindi la sequenza dei dump degli oggetti formalmente salvati aperta ed eventualmente intervallata dai dump degli oggetti *SRDS necessari a veicolare le informazioni descrittive più generali che li riguardano.

Per correttezza dobbiamo osservare che alcuni oggetti non hanno necessità di un dump dedicato in quanto le informazioni che li riguardano rientrano completamente nel contenuto dell’oggetto *SRDS che li menziona. Il caso più classico di questa situazione è l’oggetto *LIB.

Un altro aspetto macroscopico interessante è che la lunghezza record “utile” di un savefile è di 512 byte: questi diventano 528 “effettivi” concatenando 4 byte per il numero sequenza e 12 per un particolare CRC di controllo.

Ai tempi della architettura CISC, 512 era anche la dimensione di una pagina del sistema operativo: col passaggio alla architettura RISC la pagina è diventata di 4KB ma la lunghezza record del savefile non è stata modificata.

La conseguenza è che (senza compressione) alcuni record di un savefile contengono semplicemente zeri esadecimali fino al raggiungimento dei 4KB utili.

Questa “espansione” nella occupazione di memoria -tra l’altro- era oggetto di indagini preliminari ai tempi delle migrazioni CISC-RISC perché le stime precedentemente utilizzate per dimensionare un nuovo server non erano più valide.

Riassumendo, ci muoveremo all’interno di un savefile (non compresso) spostandoci di 8 record per volta per ricostruire le pagine di memoria come 512 * 8 = 4096 byte contigui.