HomeMagiCVFAT-XFS in MagiCN.AES

11.23 Shared Libraries

Gemeinsame Bibliotheken (Shared Libraries) in MagiC ab V6.00

Hinweis: Shared Libraries wurden bereits mit MagiC Version 5.20 eingeführt. Aufgrund eines Designfehlers mußte das Format der Bibliothek ab Version 6.00 ändern. Da unterschiedliche "magische" Kennungen verwendet werden, kann es aber nicht zu Abstürzen kommen, sondern die Bibliotheken für 6.00 werden lediglich von der alten Version 5.20 nicht erkannt und umgekehrt. Aufgrund mehrerer Unzulänglichkeiten in der Version 5.20 sollten ohnehin nur Bibliotheken für 6.00 erstellt werden.

11.23.1 Was sind Shared Libraries?

Meist werden Bibliotheken mit häufig genutzten Prozeduren in einem eigenen Objektmodul zusammengefaßt und mit mehreren Programmen zusammengelinkt. Dabei erhält jedes Programm eine Kopie der Bibliothek, die fest in die PRG-Datei integriert wird. Shared Libraries dagegen existieren als eigene Dateien nur ein einziges Mal auf der Festplatte und werden von mehreren Programmen, auch gleichzeitig, verwendet.

11.23.2 Wozu Shared Libraries?

Gegenüber fest eingelinkten Bibliotheken ergeben sich eine Reihe von Vorteilen:

Natürlich gibt es auch Nachteile:

11.23.3 Wie verwende ich eine Shared Libraries?

Zunächst benötigt man zum Aufruf der beiden neuen DOS-Funktionen das Objektmodul SLB_BIND, dazu muß man die Datei SLB.H einbinden.

Für jede benutzte Bibliothek deklariert man einen Deskriptor vom Typ SHARED_LIB und einen Funktionszeiger vom Typ SLB_EXEC.

Jede Bibliothek wird mit Slbopen() geöffnet (das Öffnen und Schließen sollte nur im User-Modus erfolgen), dabei werden folgende Parameter übergeben:

char *name Der Name der Bibliothek, in Großschrift, inkl. Extension (".SLB"). Der Bibliotheksname ist gleichzeitig Dateiname.
char *path Wenn dieser Parameter nicht NULL ist, wird hier zuerst nach der Bibliothek gesucht (der Pfad muß in MagiC 5.20 mit '\' abgeschlossen sein; dies ist in MagiC 6 nicht mehr nötig). Der Pfad sollte absolut sein, damit die Shared Library weiß, wo sie liegt. Wenn der Parameter NULL ist oder die Bibliothek im angegebenen Pfad nicht gefunden wurde, wird im XTENSION-Ordner gesucht.
Ab MagiC 6 wird die Environment-Variable SLBPATH ausgewertet. Sie enthält wie PATH eine Liste der Suchpfade, jeweils durch ';' getrennt. Ist die Variable definiert, wird nicht mehr extra im XTENSION-Ordner gesucht.
LONG min_ver Minimal notwendige Versionsnummer der Bibliothek.
Wenn ein Programm etwa die Version 3 benötigt, die Bibliothek aber erst Version 2 ist, wird ERANGE zurückgegeben. Die tatsächliche Versionsnummer der Bibliothek wird als Rückgabewert geliefert.
SHARED_LIB *sl Zeiger auf den Deskriptor. Wenn die Bibliothek geöffnet wurde, wird hier der Deskriptor eingetragen.
SLB_EXEC *fn Zeiger auf den Funktionszeiger. Wenn die Bibliothek geöffnet wurde, wird hier der Funktionszeiger eingetragen.

Rückgabewert kann sein:

  >= 0        Alles OK, Versionsnummer der Bibliothek.
  ERANGE      Versionsnummer zu niedrig
  EACCDN      Bibliothek schon von diesem Prozeß geöffnet
  EFILNF      Bibliothek nicht gefunden
  ENSMEM      zuwenig Speicher
  ...         andere Fehlercodes.

Die Bibliothek kann nun verwendet und schließlich mit Slbclose() wieder geschlossen werden. Dies ist nicht unbedingt notwendig, da alle geöffneten Bibliotheken bei Programmende automatisch geschlossen werden, aber es ist guter Programmierstil. Auf keinen Fall darf eine Bibliothek mehrmals geschlossen werden, der Kernel kann solche Fehler nicht erkennen.

Einige Bibliotheken, wie z.B. EDITOBJC.SLB, installieren neue Systemaufrufe, in diesem Fall die AES-Aufrufe 210..217. Für diese Bibliotheken wird der Funktionszeiger nicht benötigt. Ansonsten werden alle Funktionen der Bibliothek über den Funktionszeiger aufgerufen. Die Bibliotheks-Aufruf-Funktion ist folgendermaßen deklariert:

 typedef LONG (cdecl *SLB_EXEC)( SHARED_LIB *sl, LONG fn,
        WORD nargs, ... );

Da leider PureC hier einen Fehler hat, mußte ich die Funktion notgedrungen deklarieren als "typedef LONG (*SLB_EXEC)( void , ... );" Damit sind leider alle Typüberprüfungen verbaut. Also Vorsicht!

Der Aufruf erwartet folgende Parameter:

     Den Deskriptor der Bibliothek
     Ein Langwort (!) für die Funktionsnummer
     Ein WORD, das die Anzahl der Argumente in WORDs
               (d.h. alle "..."-Argumente) angibt
     weitere Argumente je nach Funktion

Am besten geschieht der Aufruf über ein Makro, das in einer Headerdatei für die Bibliothek definiert werden sollte, z.B.:

JPEG.H:

#define SLBJPEG_WANDELN (a,b) (*slbjpeg_exec)(slbjpeg, 7L, 4, a, b)

Hierbei werden und bei Slbopen() ermittelt, 7L ist die Funktionsnummer für den Aufruf WANDELN, 4 bezeichnet die Länge der folgenden Argumente (a und b sind zwei Zeiger => 2*4 Bytes => 4 WORDs), a und b sind die Argumente der Funktion WANDELN.

Wenn die Funktion nicht vorhanden ist (die Bibliothek enthält für diese Funktion einen Nullzeiger, oder die Funktionsnummer ist höher als die Anzahl der tatsächlich vorhandenen Funktionen), erhält man EINVFN als Funktionsergebnis (tatsächlich klappt das erst ab MagiC 6 korrekt).

11.23.4 Wie schreibe ich eine Shared Libraries?

Auch dazu gibt es eine Beispielbibliothek SLB_DEMO, die alle notwendigen Elemente und Beschreibungen enthält. Am besten, man kopiert SLB_DEMO.C, LIBHEAD.S und SLB_DEMO.PRJ und modifiziert die Dateien entsprechend. Es muß dringend darauf geachtet werden, daß Bit 3 der Flags im Programmheader einer Bibliothek gesetzt ist, dazu kann man PH_BIT3.TTP verwenden.

LIBHEAD ist der Header für eine Shared Library. Der Zeiger auf die Funktionsnamen kann entfallen, ansonsten zeigt er auf eine Tabelle von Zeigern mit den Namen der Bibliotheksfunktionen. Die Anzahl der Funktionen muß korrekt festgelegt werden, ebenso die Tabelle der Funktionen und der Bibliotheksname, welcher mit dem Dateinamen identisch ist. Beim Hinzufügen von Funktionen muß darauf geachtet werden, die Funktionsanzahl entsprechend anzupassen und ggf. die Versionsnummer zu erhöhen.

Bei öffentlich zugänglichen Shared Libraries ist sicherzustellen, daß dokumentierte Funktionsaufrufe nie geändert werden! Entweder sind neue Parameter zu ergänzen (die aufgerufene Funktion kann die Anzahl der tatsächlich übergebenen Parameter abfragen), oder es ist eine neue Funktionsnummer zu verwenden.

Für die Funktionszeiger sind auch Nullzeiger zulässig, sie geben beim Aufruf der Funktion ein EINVFN.

Folgende Funktionen zur (De-) Initialisierung sind obligatorisch:

slb_init()/slb_exit()

Werden beim Laden bzw. Entfernen der Bibliothek aufgerufen, und zwar im Supervisor-Modus und im Kontext (Prozeß) der Bibliothek. Typischerweise lädt slb_init() eine Konfigurationsdatei, alloziert globalen Speicher für die Bibliothek und öffnet eine virtuelle VDI- Workstation. slb_exit() schreibt die Konfigurationsdatei zurück, gibt den Speicher wieder frei und schließt die VDI-Workstation.

Falls slb_init() eine Datei öffnet, darf auf das Handle erst wieder bei slb_exit() zugegriffen werden, da alle anderen Aufrufe der Bibliothek im Kontext des Aufrufers ablaufen.

Ab MagiC 6 erhält die Bibliothek in der Kommandozeilen-Struktur der Basepage eine normale 'C'-Zeichenkette übergeben, welche den vollständigen Pfad der SharedLibrary enthält. Falls die SharedLibrary Konfigurations- oder RSC-Dateien laden muß, kann der Pfad extrahiert und der Dateiname der Konfigurationsdatei entsprechend zusammengebastelt werden.

Falls slb_init() z.B. aufgrund eines Busfehlers beendet wird, erhält der Aufrufer EXCPT als Ergebnis des Slbopen()-Aufrufs. Um die unplanmäßige Terminierung der Bibliothek abzufangen, installiert der Kernel vor Aufruf von slb_init()/exit() einen etv_term-Handler für die Bibliothek.

slb_open()/slb_close()

Werden beim Öffnen bzw. Schließen der Bibliothek aufgerufen. Wenn die Bibliothek nur einmal geöffnet wird, ist die Reihenfolge:

  slb_init()
  slb_open()
  slb_close()
  slb_exit()

Im Gegensatz zu slb_init()/slb_exit() laufen slb_open()/slb_close() im Kontext des Aufrufers und im Usermodus mit dem Userstack des Aufrufers ab, auch dann, wenn der Slbopen()-Aufruf im Supervisor-Modus erfolgt ist.

Die Bibliothek kann auch bei slb_open Speicher allozieren, dieser gehört jedoch dem Aufrufer und sollte bei slb_close() wieder freigegeben werden. Um die Zuordnung von alloziertem Speicher zum Aufrufer zu ermöglichen, wird der Bibliothek bei slb_open(), slb_close() und bei jedem Funktionsaufruf der aktuelle Prozeß- Deskriptor mit übergeben.

Achtung: Die Übergabe des PD an slb_open() und slb_close() geht aufgrund eines Bugs in 5.20 erst ab MagiC 6.

Der Kernel stellt sicher, daß die open/close Aufrufe korrekt geschachtelt sind, d.h. ein Prozeß kann eine Bibliothek nicht mehrmals öffnen.

Funktionen

Funktionen sind nicht obligatorisch, so kann eine Bibliothek auch Systemaufrufe über AES oder DOS einhängen, die nach Beendigung wieder entfernt werden, i.a. werden jedoch Funktionen zur Verfügung gestellt.

Eine Funktion wird mit folgenden Parametern auf dem Stack aufgerufen:

PD *pd Prozeß-Deskriptor des Aufrufers, korrespondiert mit dem zugehörigen slb_open()/close()
LONG fn Funktionsnummer. Praktisch, wenn mehrere Funktionen zusammengelegt sind (identische Funktionszeiger in LIBHEAD)
WORD nargs Anzahl der folgenden Argumente, in WORDs. Hat eine Funktion eine variable Anzahl von Parametern, kann die tatsächliche Anzahl ermittelt werden. Sehr praktisch bei Erweiterungen, ohne neue Funktionen einzubauen. Beispiel: Erwartet eine Funktion immer einen Zeiger, optional aber noch ein WORD, erhält sie entweder 2 oder 3 als .
... die übrigen Parameter

Die Funktionen werden im Kontext des Aufrufers und mit dessen Stack ausgeführt. Da dieser Aufruf i.a. im User-Modus erfolgt, wird das Multitasking auch bei längeren Aktionen nicht unterbrochen. Das Funktions-Ergebnis kann je nach Funktion LONG, WORD, void usw. sein.

Eine Funktion darf die Register d0-d2 und a0-a1 ändern, alle anderen Register müssen ggf. gerettet werden. Insbesondere darf Register a2 nicht verändert werden, damit Routinen von PureC aus aufgerufen werden können.

11.23.5 SLB_DEMO.C

/*
*
* Rumpf einer "shared library"
*
* Andreas Kromke
* 22.10.97
*
*/

#include 
#include 
#include 
#pragma warn -par

typedef void *PD;

char *mem;      /* hier globalen Speicher */

/*****************************************************************
*
* Die init-Funktion wird einmal beim Laden der Bibliothek
* aufgerufen. Dabei läuft sie im Prozeß der Bibliothek,
* d.h. es können Dateien geöffnet und Speicher angefordert
* werden, die jeweils der Bibliothek gehören.
* Achtung: Auf die dabei geöffneten Dateien darf durch die
*          Bibliotheksfunktionen _NICHT_ zugegriffen werden,
*          weil diese im Kontext des Aufrufers laufen.
*
* Achtung: Die init-Funktion läuft im Supervisormode, da eine
*          Bibliothek i.a. keinen Userstack hat.
*          Daher darf sie nicht zuviel Stack benutzen (max. 1kB)
*          und nicht zu lange laufen (weil das Multitasking
*          im Supervisormode blockiert ist).
*          Ggf. kann aber ein Userstack alloziert und in den
*          Usermode gewechselt werden.
*
*****************************************************************/

extern LONG cdecl slb_init( void )
{
  mem = Malloc(4096L);
  if  (mem)
    return(E_OK);
  else  return(ENSMEM);
}

/*****************************************************************
*
* Die exit-Funktion wird einmal beim Freigeben der Bibliothek
* aufgerufen. Dabei läuft sie im Prozeß der Bibliothek,
* d.h. es können Dateien geöffnet und Speicher angefordert
* werden, die jeweils der Bibliothek gehören.
*
* Achtung: Die exit-Funktion läuft im Supervisormode, da eine
*          Bibliothek i.a. keinen Userstack hat.
*          Daher darf sie nicht zuviel Stack benutzen (max. 1kB)
*          und nicht zu lange laufen (weil das Multitasking
*          im Supervisormode blockiert ist).
*          Ggf. kann aber ein Userstack alloziert und in den
*          Usermode gewechselt werden.
*
*****************************************************************/

extern void cdecl slb_exit( void )
{
  Mfree(mem);
}


/*****************************************************************
*
* Die open-Funktion wird einmal beim Öffnen der Bibliothek
* durch einen Anwenderprozeß aufgerufen. Dabei läuft sie im
* Prozeß des Aufrufers, d.h. es können Dateien geöffnet und
* Speicher angefordert werden, die jeweils dem Aufrufer gehören.
*
* Durch den Kernel ist sichergestellt, daß jeder Prozeß die
* Bibliothek nicht mehrmals öffnet und daß die Bibliothek immer
* ordnungsgemäß geschlossen wird.
*
* Achtung: Die open-Funktion läuft im Usermode, und zwar mit dem
*          Userstack des Aufrufers. Das heißt, daß der Aufrufer,
*          auch wenn er im Supervisormode läuft, immer einen
*          ausreichend großen usp zur Verfügung stellen muß.
*
*****************************************************************/

extern LONG cdecl slb_open( PD *pd )
{
  return(E_OK);
}


/*****************************************************************
*
* Die close-Funktion wird einmal beim Schließen der Bibliothek
* durch einen Anwenderprozeß aufgerufen. Dabei läuft sie im
* Prozeß des Aufrufers, d.h. es können Dateien geöffnet bzw.
* geschlossen und Speicher angefordert und freigegeben  werden,
* die jeweils dem Aufrufer gehören.
*
* Achtung: Die close-Funktion läuft im Usermode, und zwar mit dem
*          Userstack des Aufrufers. Das heißt, daß der Aufrufer,
*          auch wenn er im Supervisormode läuft, immer einen
*          ausreichend großen usp zur Verfügung stellen muß.
*
*****************************************************************/

extern void cdecl slb_close( PD *pd )
{
}


/*****************************************************************
*
* Eine Beispiel-Bibliotheksfunktion.
* Sie wird im Kontext des Aufrufers ausgeführt, und zwar mit dem
* Stack des Aufrufers (je nach Status usp oder ssp).
*
* Es wird dringend empfohlen, die Funktionen einer SLB nur im
* Usermode aufzurufen, um die Kompatibilität zu späteren
* Implementationen zu wahren.
*
*****************************************************************/

extern LONG cdecl slb_fn0( PD *pd, LONG fn, WORD nargs, char *s )
{
  Cconws(s);
  Cconws("\r\nTaste: ");
  Cconin();
  return(E_OK);
}

11.23.6 LIBHEAD.S

/*
*
* Header für eine "shared library"
* Wird statt des Start-Codes von PureC verwendet
*
* Andreas Kromke
* 10.2.98
*
*/

  XREF slb_init
  XREF slb_exit
  XREF slb_open
  XREF slb_close
  XREF slb_fn0

  TEXT

DC.L    $70004afc         ; magischer Wert (5.20: $42674e41)
DC.L    name              ; Zeiger auf Namen der Bibliothek
DC.L    1                 ; Versionsnummer
DC.L    0                 ; Flags, z.Zt. 0L
DC.L    slb_init          ; wird nach dem Laden aufgerufen
DC.L    slb_exit          ; wird vor dem Entfernen aufgerufen
DC.L    slb_open          ; wird beim Öffnen aufgerufen
DC.L    slb_close         ; wird beim Schließen aufgerufen
DC.L    0                 ; Zeiger auf Prozedurnamen (optional)
DC.L    0,0,0,0,0,0,0,0   ; unbenutzt, immer NULL
DC.L    1                 ; Anzahl der Funktionen (5.20: .W)
DC.L    slb_fn0           ; Funktion #0

name:   DC.B  'DEMO.SLB',0

END

11.23.7 SLB_DEMO.PRJ

slb_demo.slb
.L[-S=256]     ; minimaler Stack

=

libhead.s      ; startup code for SLBs
slb_demo.c (slb.h)

pctoslib.lib
pcstdlib.lib

11.23.8 PH_BIT3.TTP

/*
*
* Programm zur Manipulation des Bit 3 im Programmheader.
* MagiC benötigt dieses Bit, das i.a. Null ist, dafür, um
* dem Programm nur den minimal notwendigen Speicher zuzuweisen,
* d.h. nur Basepage+Text+Daten+Symbol+Bss.
* Wird insbesondere für alle SharedLibraries benötigt.
*
* Andreas Kromke
* 25.10.97
*
*/

#include 
#include 
#include 
#include 

/* ProgramHeader, Programmkopf für ausführbare Dateien                  */
/************************************************************************/

typedef struct {
 WORD ph_branch;         /* 0x00: muß 0x601a sein            */
 LONG ph_tlen;           /* 0x02: Länge  des TEXT - Segments */
 LONG ph_dlen;           /* 0x06: Länge  des DATA - Segments */
 LONG ph_blen;           /* 0x0a: Länge  des BSS  - Segments */
 LONG ph_slen;           /* 0x0e: Länge  der Symboltabelle   */
 LONG ph_res1;           /* 0x12: von PureC benötigt         */
 LONG ph_prgflags;       /* 0x16:  Bit 0: Heap nicht löschen */
                         /*        Bit 1: Laden ins FastRAM  */
                         /*        Bit 2: Malloc aus FastRAM */
                         /*        Bit 3: nur t+d+b+s (MagiC 5.20) */
                         /*        Bit 4,5,6,7: Speicherschutz (MiNT) */
                         /*        Bit 8: unbenutzt          */
                         /*        Bit 9: unbenutzt          */
                         /*        Bit 10: unbenutzt         */
                         /*        Bit 11: SharedText (MiNT) */
                         /*        Bit 12: unbenutzt         */
                         /*        Bit 13: unbenutzt         */
                         /*        Bit 14: unbenutzt         */
                         /*        Bit 15: unbenutzt         */
                         /*        Bits 31..28: TPA-Size     */
                         /*         (mal 128k + 128k: Mindestgr. Heap */
 WORD ph_absflag;        /* 0x1a: ungleich Null => nicht relozieren */
} PH;



WORD main( WORD argc, char *argv[] )
{
     PH ph;
     WORD ret = 0;
     LONG err;
     WORD f;

     for  (argc--,argv++; argc; argc--,argv++)
          {
          Cconws("Datei: ");
          Cconws(*argv);
          err = Fopen(*argv, RMODE_RW); /* zum Lesen+Schreiben öffnen */
          f = (WORD) err;     /* Datei-Handle */
          err = Fread(f, sizeof(PH), &ph);
          if   ((err != sizeof(PH)) || (ph.ph_branch != 0x601a))
               {
               err = EPLFMT;
               goto nextone;
               }
          Fseek(0L, f, 0);         /* Dateizeiger an den Anfang */
          ph.ph_prgflags |= 8;     /* Bit 3 setzen */
          err = Fwrite(f, sizeof(PH), &ph);

      nextone:
          if   (f > 0)
               Fclose(f);
          if   (err < 0)
               {
               printf(" => Fehler %ld", err);
               ret = (WORD) err;
               }
          Cconws("\r\n");
          }
     return(ret);
}

11.23.9 SLB_BIND

Achtung: Die Funktion Slbopen enthält einen zusätzliche Parameter param. Dieser taucht in der SLB.H nicht mehr auf. Am besten einfach ignorieren.

/*
*
* Binding für die Benutzung einer "shared library"
*
* Andreas Kromke
* 22.10.97
*
*/

#include 

/*****************************************************************
*
* Öffnet eine "shared lib".
*
* Eingabe:
* name      Name der Bibliothek inkl. Extension.
* path      Suchpfad mit '\', optional
* min_ver   Minimale benötigte Versionsnummer
* Rückgabe:
* sl        Bibliotheks-Deskriptor
* fn        Funktion zum Aufruf einer Bibliotheksfunktion
*      tatsächliche Versionsnummer oder Fehlercode
*
*****************************************************************/

LONG Slbopen( char *name, char *path, LONG min_ver,
        SHARED_LIB *sl, SLB_EXEC *fn,
        LONG param )
{
  return(gemdos(0x16, name, path, min_ver, sl, fn, param));
}


/*****************************************************************
*
* Schließt eine "shared lib".
*
* Rückgabe:
*    EACCDN, falls Lib nicht geöffnet
*
*****************************************************************/

extern LONG Slbclose( SHARED_LIB *sl )

{
  return(gemdos(0x17, sl));
}

11.23.10 SLB.H

/*
*
* Binding für Verwendung von "shared libraries"
*
* Andreas Kromke
* 22.10.97
*
*/

#ifndef LONG
#include 
#endif

typedef void *SHARED_LIB;
typedef LONG (*SLB_EXEC)( void , ... );
/*
  das geht leider in PureC nicht, weil PureC hier einen
  Fehler (!!!) hat: cdecl wird ignoriert, wenn die Funktion
  eine variable Anzahl von Parametern hat.

typedef LONG (cdecl *SLB_EXEC)( SHARED_LIB *sl, LONG fn, WORD nargs, ... );
*/

extern LONG Slbopen( char *name, char *path, LONG min_ver,
        SHARED_LIB *sl, SLB_EXEC *fn );
extern LONG Slbclose( SHARED_LIB *sl );

HomeMagiCVFAT-XFS in MagiCN.AES