Softwareportierung auf PPC - wie?

Herakles

Profifragensteller
Moin!

Ich möchte ein eigenes Programm, was ich für x86 entwickelt habe, auf den PowerPC portieren. Kennt jemand ein Howto oder andere einschlägige Links, was ich für so etwas zu tun habe?

Danke im Voraus, Herakles
 
Sofern du einen Compiler für die entsprechende Programmiersprache da hast, solltest du das doch einfach darauf übersetzen lassen können.

Die Binarys selbst kannst du natürlich nicht portieren.
:)
 
Das Programm ist in C geschrieben und es handhabt WLAN-Verbindungen. Der Haken ist, dass ich einige Bitshifts im Programm habe und schon fundiert portieren möchte. Eigentlich kann ich mir gar nicht vorstellen, dass einfach die Verwendung eines PPC-Compilers alles Probleme löst - denn dann könnte man ja einfach jede Software auf jede X-beliebige Plattform mit minimalem Aufwand portieren. Und das bezweifle ich eigentlich (ganz ohne Vorwissen)...
 
Du musst Dir halt bei Werten, die nicht in Host Byte Order vorliegen, d.h. z.B. Daten die über's Netz rein kommen, Gedanken machen. Ein "HowTo" oder "Tutorial" gibt's dafür nicht, Du musst halt wissen was Du tust.

Als ganz (für mich) ganz gute Vorgehensweise hat sich erwiesen:

  • Lege Dich auf eine externe Endianness fest. Eine gute Wahl wäre in Deinem Fall wohl die Netzwerk Byte Order.
  • Datenaustauschpunkte identifizieren: Finde all die Stellen im Programm, die Daten von extern lesen oder nach extern weitergeben. Netzwerkdaten, Binärdateien auf Festplatte, etc. sind solche Daten.
  • Konverterschicht einfügen: Umschließe die o.g. Datenaustauschpunkte mit Programmcode, der die Endianess konvertiert, und zwar von Externer Byte Order zu Host Byte Order bzw.
    umgekehrt. Diese Schicht ist natürlich architekturabhängig.
  • Testen, Fehler finden, zurück zu Punkt 2.

Wenn Du Dich darauf fest legst, zwischen externer und interner Byte Order zu trennen, brauchst Du intern nicht mehr umdenken. Ein

Code:
uint16_t
foo(uint16_t bar) {
    return (bar & 0xF0);
}

macht dann eigentlich immer das selbe. Um diese Beispielfunktion zu exportieren, kannst Du folgendes Schema verwenden:

Code:
struct interface {
    uint16_t (*foo)(uint16_t);
};

#if NEEDS_CONVERTER
static struct interface exported_interface = {
    .foo = &foo_converter
}
#else
static struct interface exported_interface = {
    .foo = &foo
}
#endif

uint16_t
foo_converter(uint16_t bar) {
    return(htons(foo(ntohs(bar))));
}

Externer Code darf dann natürlich nur noch so auf die Funktion "foo" zugreifen:

Code:
extern struct interface exported_interface;

int
main(int argc, char **argv) {
    exported_interface.foo(0x1234);
}
 
Das Programm ist in C geschrieben und es handhabt WLAN-Verbindungen. Der Haken ist, dass ich einige Bitshifts im Programm habe und schon fundiert portieren möchte. Eigentlich kann ich mir gar nicht vorstellen, dass einfach die Verwendung eines PPC-Compilers alles Probleme löst - denn dann könnte man ja einfach jede Software auf jede X-beliebige Plattform mit minimalem Aufwand portieren. Und das bezweifle ich eigentlich (ganz ohne Vorwissen)...

Moin mein Lieber :)

also schau Dir mal QNX an, mit diesem Unix ist es möglich Software zu entwickeln um die nahezu auf jede Plattform zu portieren.

Berühmtes Beispiel Unreal Tournemant wurde so auf alle gängigen Plattformen portiert :)

Schau mal mit der Suchmaschine deines Vertrauens nach QNX und dessen Möglichkeiten QNX bringt schon per default ein eigenes Entwicklungsstudio ja ist mehr als nur ne Umgebung mit :) * saugeil * würde mein Junior jetzt sagen :D

Schade muss zur Arbeit aber kannst mir ne PM schreiben bin so gegen 02:00 Uhr wieder allways on my Satelit :)

peace rudy :) immer senkrecht bleiben
 
Morgen

Eifache lösung währe, dein C code in C++ umzuschreiben und das Qt zu verwenden. Wenn du dich strikt an die Qt eigenheiten hälst, sollte es auf allen Plattformen kompatibel sein.
Bezüglich Hardware, naja, wenn du Hardware spezifisch entickelst, wirst allgemein probleme haben diese Cross-Platform fähig zu machen, ausser eine LIB hält die konsistenz, dass sie auch auf anderen plattformen funktioniert, wie bsp. von Qt die QIODevice.

Gruss
bsdagent
 
Wenn du dich strikt an die Qt eigenheiten hälst, sollte es auf allen Plattformen kompatibel sein.

Stimmt nicht ganz. Sobald Du in C oder C++ mit (Binär-)Daten von mehreren Bytes Länge (z.B. int, long, etc.) zu tun hast, und diese Daten von extern, also z.B. über's Netzwerk oder von Platte, kommen dann hilft auch QT nicht.
 
Stimmt nicht ganz. Sobald Du in C oder C++ mit (Binär-)Daten von mehreren Bytes Länge (z.B. int, long, etc.) zu tun hast, und diese Daten von extern, also z.B. über's Netzwerk oder von Platte, kommen dann hilft auch QT nicht.

Abend

Mittels Streams (Bsp. QDataStream) ist die Codierung völlig unabhängig von CPU, Betriebsystem und Byte-Anordnung.

Gruss
bsdagent
 
Die Klasse sieht ganz interessant aus, aber ich glaube für rohe Binärdaten die einer bestimmten Byte-Anordnung folgen müssen, also Dinge die man in C üblicherweise einfach in einen Buffer liest und dann als Pointer auf eine Struktur casted (z.B. Header von Netzwerkpaketen) ist sie etwas zu aufwändig.
 
Die Klasse sieht ganz interessant aus, aber ich glaube für rohe Binärdaten die einer bestimmten Byte-Anordnung folgen müssen, also Dinge die man in C üblicherweise einfach in einen Buffer liest und dann als Pointer auf eine Struktur casted (z.B. Header von Netzwerkpaketen) ist sie etwas zu aufwändig.

Abend

Das lesen eines Buffers ist ja simpel (QBuffer), damit kannst bsp. ein QByteArray eifach lesen/schreiben ohne jegliche anderen sorgen. Man muss natürlich auch beachten:

Nicht ohne grund hat Qt jeden mist implementiert. Denn sobald man anfängt, andere Framework sachen einfach mit Qt's Framework zu mischen gibts probleme. Deshalb baut man generell, Klassen, die spezielle Codefragmente anderer Frameworks wie bsp. OpenSSL für Kryptographie Qt kompatibel macht und somit eben Cross-Plattform sind.

Mal ein simples bsp., bei Qt gibts quint8, quint16, quint32 und quint64. Qt garantiert das diese Datentypen auf allen Plattformen gleich gross sind. Demnach wäre es etwas naiv, eifach statt quint16 den "unsigned int" zu verwenden, da ja generell bekannt ist, dass ein int ein 16 oder 32 bit langer datentyp sein kann.

Gruss
bsdagent
 
Ich glaub wir reden ein bisschen aneinander vorbei. Es geht eigentlich so langsam immer weiter weg vom Thema, aber ich würd's Dir trotzdem gern erklären was ich meine.

Das lesen eines Buffers ist ja simpel (QBuffer), damit kannst bsp. ein QByteArray eifach lesen/schreiben ohne jegliche anderen sorgen. Man muss natürlich auch beachten:

Wenn Du einen Daten-Block irgendwoher einliest, es ist wirklich egal woher er kommt, bei dem das Layout vorbestimmt ist, dann wird das (so wie ich das im Moment überlicke) schnell unübersichtlich mit den QT Klassen. Das liegt erst einmal da dran, dass man nicht ohne weiteres einen Datenblock in ein Objekt casten kann.

Nimm an, Du würdest an der Adresse 0x00010002 den Config Header des zweiten Geräts am ersten PCI Bus auslesen können. Nimm weiterhin an, Du bist an Device- und Vendor-ID interessiert sowie an der Adresse des Expansion ROMs.

Eventuell musst Du dem Compiler noch sagen, dass die Struktur gepackt werden muss, aber im Prinzip würdest Du In C so etwas schreiben:

Code:
struct pci_config_space {
  uint16_t vendor_id;
  uint16_t device_id;
  /* Ein paar andere Felder */
  uint32_t exp_rom_base_addr;
  /* Noch mehr Felder */
};

int
main (int argc, char **argv) {
    struct pci_config_space *ptr = (void *) 0x00010002;

    printf("vendor=%#x\n", ptr->vendor_id);
    printf("device=%#x\n", ptr->device_id);
    printf("rom=%#x\n", ptr->exp_rom_base_addr);

   return (0);
}

Der Code wird das richtige Ergebnis auf einer Little Endian Maschine liefern, auf einer Big Endian Architektur werden die Werte allerdings "falsch herum" ausgegeben.

Du könntest die Adresse des Headers natürlich auch einem QDataStream Objekt indirekt über ein QByteArray übergeben; irgendwie wird das Framework schon auf rohen Daten operieren können. Du müsstest dann allerdings wissen, dass die Vendor ID bei Offset 0h, die Device ID bei Offset 2h und die Expansion ROM Base Address bei Offset 30h steht.

Auf was ich hinaus will: Die QT Klassen lösen ein völlig anderes Problem als das des Threadstarters - jedenfalls habe ich das so interpretiert.

Mal ein simples bsp., bei Qt gibts quint8, quint16, quint32 und quint64. Qt garantiert das diese Datentypen auf allen Plattformen gleich gross sind. Demnach wäre es etwas naiv, eifach statt quint16 den "unsigned int" zu verwenden, da ja generell bekannt ist, dass ein int ein 16 oder 32 bit langer datentyp sein kann.

Wenn ich's recht weiß ist die Breite eines int komplett abhängig von Compiler und Prozessor, ist aber auch egal.

Was ich sagen wollte: {,u}int{8,16,32,64}_t (C99 Typen) sind auch auf allen Plattformen genau 8/16/32/64 Bits breit - das ist also kein Argument. Allerdings gebe ich Dir Recht, dass wenn man sich in einem Framework bewegt, dass man es dann bitteschön richtig macht und dann auch die entsprechenden Datentypen verwendet.
 
Ich glaub wir reden ein bisschen aneinander vorbei. Es geht eigentlich so langsam immer weiter weg vom Thema, aber ich würd's Dir trotzdem gern erklären was ich meine.



Wenn Du einen Daten-Block irgendwoher einliest, es ist wirklich egal woher er kommt, bei dem das Layout vorbestimmt ist, dann wird das (so wie ich das im Moment überlicke) schnell unübersichtlich mit den QT Klassen. Das liegt erst einmal da dran, dass man nicht ohne weiteres einen Datenblock in ein Objekt casten kann.

Nimm an, Du würdest an der Adresse 0x00010002 den Config Header des zweiten Geräts am ersten PCI Bus auslesen können. Nimm weiterhin an, Du bist an Device- und Vendor-ID interessiert sowie an der Adresse des Expansion ROMs.

Eventuell musst Du dem Compiler noch sagen, dass die Struktur gepackt werden muss, aber im Prinzip würdest Du In C so etwas schreiben:

Code:
struct pci_config_space {
  uint16_t vendor_id;
  uint16_t device_id;
  /* Ein paar andere Felder */
  uint32_t exp_rom_base_addr;
  /* Noch mehr Felder */
};

int
main (int argc, char **argv) {
    struct pci_config_space *ptr = (void *) 0x00010002;

    printf("vendor=%#x\n", ptr->vendor_id);
    printf("device=%#x\n", ptr->device_id);
    printf("rom=%#x\n", ptr->exp_rom_base_addr);

   return (0);
}

Der Code wird das richtige Ergebnis auf einer Little Endian Maschine liefern, auf einer Big Endian Architektur werden die Werte allerdings "falsch herum" ausgegeben.

Du könntest die Adresse des Headers natürlich auch einem QDataStream Objekt indirekt über ein QByteArray übergeben; irgendwie wird das Framework schon auf rohen Daten operieren können. Du müsstest dann allerdings wissen, dass die Vendor ID bei Offset 0h, die Device ID bei Offset 2h und die Expansion ROM Base Address bei Offset 30h steht.

Auf was ich hinaus will: Die QT Klassen lösen ein völlig anderes Problem als das des Threadstarters - jedenfalls habe ich das so interpretiert.



Wenn ich's recht weiß ist die Breite eines int komplett abhängig von Compiler und Prozessor, ist aber auch egal.

Was ich sagen wollte: {,u}int{8,16,32,64}_t (C99 Typen) sind auch auf allen Plattformen genau 8/16/32/64 Bits breit - das ist also kein Argument. Allerdings gebe ich Dir Recht, dass wenn man sich in einem Framework bewegt, dass man es dann bitteschön richtig macht und dann auch die entsprechenden Datentypen verwendet.

Abend

Ich gebe dir recht, bei System spezifischen eigenheiten ists klar, dass solch ein Framework grenzen findet. Aber das is generell so, wenn du auf Hardware oder System ebene agierst. Deshalb hat man auch das D-Bus System entwickelt, wo es Dienste/Objekte auf D-Bus ebene existieren, die via bsp. Qt direkt angesprochen werden. Somit währe das Cross-Plattform.
Doch is klar, das System programmierung oder HW programmierung nie mit einem Cross-Plattform vermischt werden sollen, da es ja klar ist, dass es auf System ebene wie auch Hardware eigenheiten existieren bzw. existieren werden.
Doch muss der entwickler, wo auf System ebene wie auch HW eben programmiert, dass halt in kenntnis nehmen.
Wenn er es aber durch D-Bus verfügbar macht, kann man, egal ob Qt, gibt auch andere Frameworks, auf diese HW zugreifen. Ist halt ne frage der implementierung.

Gruss
bsdagen
 
Vielleicht hab ich das Problem nicht gut genug erklärt. Deshalb ohne Dir zu Nahe treten zu wollen: Ich glaube Du verstehst nicht, um was es geht. Das Beispiel mit dem PCI Config Header war frei gewählt, es hätte ebenso der Section-Header einer ELF Datei oder sonstwas sei können. Das Grundproblem hat mit Hardware wenig bis nichts zu tun und mit D-Bus noch weniger.
 
Vielleicht hab ich das Problem nicht gut genug erklärt. Deshalb ohne Dir zu Nahe treten zu wollen: Ich glaube Du verstehst nicht, um was es geht. Das Beispiel mit dem PCI Config Header war frei gewählt, es hätte ebenso der Section-Header einer ELF Datei oder sonstwas sei können. Das Grundproblem hat mit Hardware wenig bis nichts zu tun und mit D-Bus noch weniger.

Abend

Vincent Vega, joar, dass stimmt mit sicherheit au. Deshalb währe ich dir dankbar, würdest mir ein konkretes beispiel geben, wo die Importabilität existiert?
Dabei gehts mir, an welchem punkt du das importabel sieht, würde sicher au andere interresieren. Worallem bischen ausführlicher bzw. ein eifacheres Bsp.
Den mit PCI oder ELF bsp. hab ich nie programmiert.

Hoff das ich da nicht zuviel verlange, aber es interresiert mich wirklich, inwiefern du die inportabilität in einiegen bereichen sieht. Währe toll, wenn da mal ein simples bsp. also net grad komplex demonstrieren würdest. Somit könnte ich nämlich bei mir, wo ich generell Cross-Platform entwickle, dass gerade gut gebrauchen um keine fehler zu machen.

Danke im voraus :D.

Gruss
bsdagent
 
Vincent Vega, joar, dass stimmt mit sicherheit au. Deshalb währe ich dir dankbar, würdest mir ein konkretes beispiel geben, wo die Importabilität existiert?

Ok.

Schau Dir dieses Bild nochmal genau an. Das Bild beschreibt den Aufbau einer binären Datenstruktur, deren Felder in Little Endian Byte Order vorliegen. In diesem Fall ist es der PCI Config Header. Es könnte aber auch eine beliebige Datenstruktur sein, es ist völlig unerheblich das dieses eine Beispiel mit Hardware zu tun hat. Wichtig ist: Es ist eine binäre Datenstruktur und es gilt eine Konvention über die Byte Order der darin enthaltenen Daten.

Zum Verständnis des Bildes: Jede Zeile repräsentiert vier Bytes, gezählt wird von rechts nach links. Die Beschriftungen rechts der Zeilen geben den Offset vom Start der Struktur an, gezählt wird in hexadezimalen Zahlen. Ein Beispiel: Das Feld "Expansion ROM Base Address" ist also vier Bytes breit und beginnt ab Offset 0x30 (also 48, dezimal gezählt), gemessen vom Start der Struktur. Die gesamte Struktur ist 64 Bytes lang.

Jetzt schau Dir nochmal das Codebeispiel von weiter oben an, der Einfachheit halber hab ich's hier nochmal reinkopiert.

Code:
struct pci_config_space {
  uint16_t vendor_id;
  uint16_t device_id;
  /* Ein paar andere Felder */
  uint32_t exp_rom_base_addr;
  /* Noch mehr Felder */
};

int
main (int argc, char **argv) {
    struct pci_config_space *ptr = (void *) 0x00010002;

    printf("vendor=%#x\n", ptr->vendor_id);
    printf("device=%#x\n", ptr->device_id);
    printf("rom=%#x\n", ptr->exp_rom_base_addr);

   return (0);
}

Du weisst jetzt, woher auch immer, dass die Struktur an Adresse 0x00010002 beginnt. Da sie 64 Bytes lang ist, wird sie bis Adresse 0x00010041 (inkl.) gehn. Du weisst, da Du den Aufbau der Struktur kennst, dass die Adresse des Expansion ROM in den Bytes von 0x00010032 bis 0x00010035 liegt, und zwar in Little Endian Byte Order.

In C kannst Du einfach Code wie im Beispiel oben schreiben. Du musst jetzt allerdings aufpassen, dass wenn Du auf ptr->exp_rom_base_addr zugreifst, dass die dort abgelegte Zahl in Little Endian Byte Order vorliegt. Wenn Du auf einer Big Endian Architektur unterwegs bist, dann musst Du, bevor Du den Zahlenwert verwendest, eine Konvertierung von Little nach Big Endian vornehmen.

Ich kann Dir kein getestetes Beispiel geben, wie das selbe in Qt funktioniert. Wenn aber ich die Dokumentation richtig interpretiere, dann müsstest Du erst einen Buffer anlegen, der den Inhalt des Speichers von 0x00010002 bis 0x00010041 enthält.

Code:
QByteArray byteArray;
byteArray.setRawData(0x00010002, 0x40);

QDataStream stream(byteArray, IO_ReadOnly);

/* ... */

byteArray.resetRawData(0x00010002, 0x40);

Da wo Code fehlt, könntest Du mit dem >> Operator die Datenstruktur feldweise auslesen. Du musst ausserdem Qt noch sagen, dass der Inhalt von byteArray in Little Endian Byte Order vorliegt, Du die Daten aber in Host Byte Order auslesen willst. Wie das genau geht, musst Du selbst heraus finden.

Da Du aber vermutlich nicht nur an einem einzigen Wert der Struktur interessiert bist, würdest Du vermutlich ein Objekt oder eine Struktur aus den Daten erzeugen wollen. Ich denke, sauber (in C++) würde man das tun indem man den >> Operator überladen würde, so dass Du in ein Objekt einer eigenen Klasse oder in eine Struktur schreiben würdest. Der Operator müsste also in etwa so eine Signatur haben:

Code:
QDataStream &QDataStream::operator>> (struct pci_config_space & ptr);

Den musst Du dann halt noch implementieren. In der Implementierung musst Du aufpassen, dass Du die einzelnen Felder in der richtigen Reihenfolge ausliest und ausserdem musst Du aufpassen, dass Du die einzelnen Felder von Little Endian Byte Order in Host Byte Order konvertierst.

Punkt ist: Bestimmt irgendwie möglich, aber mit viel mehr Code.
 
Zurück
Oben