2012-04-26 6 views
8

ich ein ELF-Analysator schreibe, aber ich einige Probleme, richtig konvertieren Endian. Ich habe Funktionen, um die Endianess des Analysators und die Endianness der Objektdatei zu bestimmen.Endianness Swap ohne ntohs

Grundsätzlich gibt es vier mögliche Szenarien:

  1. Ein Big-Endian zusammengestellt Analysator läuft auf einer Big-Endian-Objektdatei
    • Bedürfnisse nichts
    • umgewandelt
  2. Ein Big-Endian zusammengestellt Analysator läuft auf eine little-endian-Objektdatei
    • die Byte-Reihenfolge Bedarf getauscht, aber ntohs/l() und htons/l() sind beide null Makros o In einer großen Endian-Maschine werden sie die Byte-Reihenfolge nicht vertauschen. Das ist das Problem
  3. A Little-Endian zusammengestellt Analysator läuft auf einer Big-Endian-Objektdatei
    • der Byte-Reihenfolge Bedarf ausgetauscht, so verwendet htons(), um die Byte-Reihenfolge
  4. zu tauschen Ein mit Little Endian kompilierter Analysator wird auf einer kleinen Endian-Objektdatei ausgeführt.
    • nichts Bedürfnisse umgewandelt

Gibt es eine Funktion, die ich verwenden kann, um explizit tauschen Byte-Reihenfolge/Wechsel endianness, da ntohs/l() und htons/l() nimmt die endianness des Host-Rechnung und manchmal nicht konvertieren? Oder muss ich meine eigene Swap-Byte-Order-Funktion finden/schreiben?

Antwort

3

Muss ich finden müssen/meine eigene Swap-Byte-Reihenfolge Funktion schreiben?

Ja Sie tun. Aber, es einfach zu machen, verweise ich Sie auf diese Frage: How do I convert between big-endian and little-endian values in C++?, die eine Liste von Funktionen Compiler spezifische Byte-Reihenfolge Swap gibt, sowie einige Implementierungen von Byte-Reihenfolge Swap-Funktionen.

7

In Linux there are several conversion functions in endian.h, die zwischen beliebiger endianness erlauben zu konvertieren:

uint16_t htobe16(uint16_t host_16bits); 
uint16_t htole16(uint16_t host_16bits); 
uint16_t be16toh(uint16_t big_endian_16bits); 
uint16_t le16toh(uint16_t little_endian_16bits); 

uint32_t htobe32(uint32_t host_32bits); 
uint32_t htole32(uint32_t host_32bits); 
uint32_t be32toh(uint32_t big_endian_32bits); 
uint32_t le32toh(uint32_t little_endian_32bits); 

uint64_t htobe64(uint64_t host_64bits); 
uint64_t htole64(uint64_t host_64bits); 
uint64_t be64toh(uint64_t big_endian_64bits); 
uint64_t le64toh(uint64_t little_endian_64bits); 

Edited, weniger zuverlässige Lösung. Sie können union verwenden, um auf die Bytes in beliebiger Reihenfolge zuzugreifen. Es ist sehr bequem:

union { 
    short number; 
    char bytes[sizeof(number)]; 
}; 
+0

Technisch undefiniertes Verhalten in C++ obwohl. – bames53

+0

Aber woher wissen wir die richtige Reihenfolge? –

+0

@BoPersson OP weiß, wann er Bytes tauschen will. Ich habe meine Antwort bearbeitet, um die passendere Lösung zu finden. –

1

Die ntoh Funktionen können zwischen mehr als nur große und kleine Endian tauschen. Einige Systeme sind auch "Middle Endian", bei denen die Bytes nicht auf die eine oder andere Weise geordnet sind.

Wie dem auch sei, wenn alle um dich kümmern sind groß und Little-Endian, dann alles, was Sie wissen müssen, ist, wenn der Host und die Objektdatei Endianess unterscheiden. Sie haben Ihre eigene Funktion, die bedingungslos die Byte-Reihenfolge vertauscht, und Sie rufen sie an oder nicht, je nachdem, ob host_endianess()==objectfile_endianess() oder nicht.

0

Wenn ich über eine Cross-Plattform-Lösung denken würde, die auf Windows oder Linux arbeiten würde, würde ich schreiben so etwas wie:

#include <algorithm> 

// dataSize is the number of bytes to convert. 
char le[dataSize];// little-endian 
char be[dataSize];// big-endian 

// Fill contents in le here... 
std::reverse_copy(le, le + dataSize, be); 
10

Ich denke, es lohnt sich hier The Byte Order Fallacy Artikel Anheben von Rob Pyke (eines Go's Autor).

Wenn Sie Dinge richtig machen - dh Sie nicht irgendetwas über Ihre Plattformen Byte-Reihenfolge annehmen - dann wird es einfach funktionieren. Alles, was Sie beachten müssen, ist, ob ELF-Dateien in Little Endian oder Big Endian Modus sind.

Aus dem Artikel:

Lassen Sie sich sagen, dass Ihr Datenstrom eine Little-Endian-codierte ganze Zahl 32-Bit hat. Hier ist, wie es zu extrahieren (vorausgesetzt, unsigned Bytes):

i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24); 

Wenn es Big-Endian, hier ist, wie es zu extrahieren:

i = (data[3]<<0) | (data[2]<<8) | (data[1]<<16) | (data[0]<<24); 

Und lassen Sie nur den Compiler Sorgen über das Beste daraus zu machen.

+0

AFAIK-Compiler verwenden nur dann einen optimierten Byte-Order-Swap, wenn Sie mit einem Wort beginnen. –

+0

@AndrewDunn: Ziemlich wahrscheinlich, aber wie immer, zweimal messen, einmal optimieren. –