2009-05-17 11 views
4

Ich schreibe ein Programm auf Linux in C, um Kerndateien zu analysieren, die von einem eingebetteten System erzeugt werden. Die Kerndateien können Little Endian (ARM) oder Big Endian (MIPS) sein, und das zu analysierende Programm läuft möglicherweise auf einem kleinen Endian-Host (x86) oder Big-Endian (PowerPC).Unterstützende Byte-Reihenfolge im Linux-Benutzerbereich

Mit Blick auf die Header weiß ich, ob der Kern LE oder BE ist. Ich würde lieber mein Programm nicht müssen wissen, ob der Host, auf dem es läuft, ist kleine oder große Endian, würde ich gerne eine API verwenden, um es für mich zu behandeln. Wenn es keine bessere Option gibt, werde ich mich auf #ifdef __BIG_ENDIAN__ verlassen.

Im Linux-Kernel haben wir cpu_to_le32 et al. Von nativer Byteordnung in Little Endian usw. konvertieren. Im Userspace gibt es htonl et al, die von nativ zu big endian konvertieren, aber kein Äquivalent für native zu Little Endian Ich kann finden.

Kann jemand eine geeignete API für Benutzerraum vorschlagen?

Bearbeiten: Nur um klar zu sein, ich bin auf der Suche nach einer API, die bereits weiß, ob meine CPU ist groß oder Little Endian und Swaps Byes entsprechend. Ich möchte meinen Code dafür nicht mit #ifdefs einstreuen müssen. Ich suche nicht nur Code-Snippets, um Bytes zu tauschen. Danke für die, aber das war nicht der Punkt.

+0

Aber mit diesen Codeschnipsel können Sie trivialerweise ein Makro schreiben, das das tut, also Ihre eigene "API" schaffend, nein? – orip

+0

Was für ein eingebettetes System ist das? Können Sie nicht einfach einige cross gdb verwenden, um die Kerndateien zu betrachten? – sigjuice

+0

Könnte ich meine eigene API schreiben? Sicher. Ich möchte nur vermeiden, #ifdef __BIG_ENDIAN__, wenn es eine sauberere, wartungsfreundlichere Möglichkeit gibt, es über eine vorhandene API zu tun, die bereits weiß, ob meine CPU groß oder Little Endian ist. – DGentry

Antwort

3

Basierend auf dem, was Sie tatsächlich tun (ELF Core Dump-Dateien w Ohne dass ich mich um endianische Probleme kümmern müsste, wäre ich der Meinung, dass die Verwendung von libelf, verfügbar here mit einem netten Tutorial here, eine gute Wahl wäre.

Diese Bibliothek arbeitet transparent mit ELF-Dateien für große und kleine Endiane und läuft unter Linux trotz ihrer FreeBSD-Herkunft gut (die übliche "./configure" - und "make" -Sequenz ist alles, was Sie zum Aufbau benötigen) .Für Grins habe ich versucht, das "Lesen eines Programms Header-Tabelle" Beispiel (mit geringfügigen Änderungen, um es zu bauen) auf eine x86-Core-Datei sowie eine MIPS Big-Endian-Core-Datei scheint es "einfach zu arbeiten".

+0

Ich habe libbfd verwendet, um den Kern zu öffnen und jeden Abschnitt zu finden, aber für den Inhalt reicht es einfach eine große Reihe von unsignierten Zeichen (überlasse es mir, die Endianness von zu behandeln alles darin). Ich werde stattdessen libelf untersuchen, vielleicht ist es in dieser Hinsicht hilfreicher. – DGentry

+0

Die Funktionen elf32_xlatetom() und elf32_xlatetof() könnten für diesen Zweck interessant sein, die API ist z. bei http://docs.sun.com/app/docs/doc/816-5172/6mbb7btp9?a=view –

1

Warum benötigen Sie eine API, um es zu tun? Schreiben Sie einfach Ihre eigene Funktion zum Aufruf htonl() (oder was auch immer erzeugt BE) dann einfach die Bytes umkehren. Das hört sich nicht so schwer an.

Etwas wie:

union { 
    struct { 
     unsigned char c0; 
     unsigned char c1; 
     unsigned char c2; 
     unsigned char c3; 
    } ch; 
    uint32_t ui; 
} u; 
unsigned char t; 

u.ui = htonl (hostlong); 
t = u.ch.c0; u.ch.c0 = u.ch.c3 ; u.ch.c3 = t; 
t = u.ch.c1; u.ch.c1 = u.ch.c2 ; u.ch.c2 = t; 
1

Werfen Sie einen Blick auf die Kernel bereitgestellten Header in/usr/include/linux/byteorder/wie __cpu_to_be32() und __be32_to_cpu()

Nehmen Sie auch einen Blick in der Datei /usr/include/linux/types.h, wo Sie Typen als explizite Big/Little-Endian-Ganzzahlen definieren können, die sehr hilfreich sind, da jede Nichtübereinstimmung zur Kompilierungszeit erkannt wird.

+1

Schöner Fund. Von einem schnellen Blick scheint es jedoch, dass Sie noch etwas wie ein "#ifdef __BIG_ENDIAN__" benötigen, um entweder linux/byteorder/little_endian.h oder linux/byteorder/big_endian.h bedingt einzuschließen. Nicht eine große Sache, aber immer noch ... –

1

Da endian-esss is easy wechseln, habe ich immer mit benutzerdefinierten Code wie folgt, eine strenge Regel darüber, welche Darstellung ich im Code verwenden, und die Handhabung von Endianity an den Endpunkten (Eingabe und Ausgabe).

1

Sie nur Ihre eigenen schreiben könnte (diese basieren auf Apples Routinen):

static inline uint16_t Swap16(uint16_t x) 
{ 
    return ((x << 8) | (x >> 8)); 
} 

static inline uint32_t Swap32(uint32_t x) 
{ 
    return ((((x^(x >> 16 | (x << 16))) & 0xff00ffff) >> 8)^(x >> 8 | data << 24)); 
} 

Dann können Sie conditional Makros definieren:

#ifdef __BIG_ENDIAN__ 
# define htols(x) Swap16(x) 
# define htoll(x) Swap32(x) 
#else 
# define htols(x) (x) 
# define htoll(x) (x) 
#endif 

Wenn Sie mit Intel Assembler-Code zufrieden sind, Sie können dies auch tun:

// Swap16 is unchanged 

static inline uint32_t Swap32(uint32_t x) 
{ 
    __asm__ ("bswap %0" : "+r" (x)); 
    return (x); 
} 
#ifdef __i386__ 
static inline uint64_t Swap64(uint64_t x) 
{ 
    __asm__ ("bswap %%eax\n\t" 
      "bswap %%edx\n\t" 
      "xchgl %%eax, %%edx" 
      : "+A" (x)); 
    return (x); 
} 
#elif defined(__x86_64__) 
static inline uint64_t Swap64(uint64_t x) 
{ 
    __asm__ ("bswap %0" : "+r" (x)); 
    return (x); 
} 
#endif 
1

können Sie die Standard-Netzwerk verwenden Funktionen in apa/inet.h bytswapping:

#include <arpa/inet.h> 
uint32_t htonl(uint32_t hostlong); // Host to network 
uint16_t htons(uint16_t hostshort); // Host to network 
uint32_t ntohl(uint32_t netlong); // Network to host 
uint16_t ntohs(uint16_t netshort); // Network to host 

Netzwerk-Byte-Reihenfolge ist Big-Endian. Also, diese Funktionen bedeuten:

hton*: Host endian to big endian 
ntoh*: Big endian to host endian 

Hoffe das hilft.

19

#include <arpa/inet.h> ist schön und tragbar, aber garantiert nur {ntoh,hton}{s,l}. Wenn Sie Conversions für 64-Bit-Werte benötigen oder Endian auf Big-Endian (wobei ntoh und hton nichts tun), reicht dies nicht aus.

Unter Linux (glibc) bietet #include <endian.h> Folgendes, das für die aktuelle Maschine definiert ist.

htobe16 be16toh htole16 le16toh 
htobe32 be32toh htole32 le32toh 
htobe64 be64toh htole64 le64toh 

On * BSD, #include <sys/endian.h> bietet die gleichen Makros.

3

Wenn Sie die Datei als Byte-Array behandeln, dann steuern Sie, welche Reihenfolge Sie die Bytes auswählen, und die Endian-Ness Ihrer CPU ist eigentlich egal.

Dies ist auch sehr nützlich im Hinblick auf die Ausrichtungsprobleme. Ihr Core-Dump könnte möglicherweise nicht ausgerichtete Referenzen enthalten. Ich weiß, das ist sehr unwahrscheinlich, aber es könnte auch an Korruption liegen. Dies ist ein weiteres Problem, das vermieden wird, indem die Datei als Byte-Array behandelt wird.

Ich habe diesen Ansatz bei der Implementierung von jhead verwendet.

5

Wenn Sie Zugriff auf den Neon-Coprozessor haben und der Speicher zusammenhängend ist (Beispiel ein Videoframe), können Sie swap16 auf dem Frame mit den q-Registern (128 Bytes) auf diese Weise ausführen; natürlich müssen Sie für Ausrichtungsprobleme

void swap16(void *__restrict src16) 
{ 
    const void *start = src16; 
    const void *end = src16 + FRAME_SIZE; 
    asm volatile (
     "1: pld  [%0, #0x100]\n" 
     "vld2.8   {q0,q1}, [%0]\n" 
     "vmov   q2,q0\n" 
     "vst2.8   {q1,q2}, [%0]!\n" 
     "cmp   %1,%0\n" 
     "bne   1b\n" 
     : /* empty output operands */ 
     : "r" (start), "r" (end) 
     : "cc", "memory" 
     ); 
} 
sehen
+0

nice code snippet :) – mishmashru

Verwandte Themen