2012-04-26 18 views
8

Estoy escribiendo un analizador ELF, pero estoy teniendo algunos problemas para convertir el endianness correctamente. Tengo funciones para determinar el endianness del analizador y la endiannness del archivo del objeto.Intercambio de endianness sin ntohs

Básicamente, hay cuatro escenarios posibles:

  1. Un endian compilado gran analizador de ejecución en un archivo de objeto big endian
    • nada necesidades convierten
  2. Un endian compilado gran analizador de carrera y un pequeño archivo de objeto endian
    • el orden de bytes necesita ser intercambiado, pero ntohs/l() y htons/l() son macros nulos o n una gran máquina endian, por lo que no cambiarán el orden de bytes. Este es el problema
  3. Un poco endian compilado analizador de ejecución en un archivo de objeto big endian
    • las necesidades de orden de bytes intercambiados, a fin de utilizar htons() para cambiar el orden de bytes
  4. Un pequeño analista compilado endian se ejecuta en un pequeño archivo de objeto endian.
    • nada necesidades convierten

¿Hay una función que pueda usar para explícitamente intercambiar byte de orden/cambio de orden de bits, ya que ntohs/l() y htons/l() tomar endianness del huésped en cuenta y a veces no se convierten? ¿O necesito encontrar/escribir mi propia función de orden de bytes de intercambio?

Respuesta

3

¿Es necesario buscar/escribir mi propia función de orden de bytes de intercambio?

Sí, lo hace. Pero, para hacerlo más fácil, le remito a esta pregunta: How do I convert between big-endian and little-endian values in C++? que proporciona una lista de funciones de intercambio de orden de bytes específicas del compilador, así como algunas implementaciones de funciones de intercambio de orden de bytes.

7

En Linux there are several conversion functions en endian.h, que permite convertir entre endianness arbitraria:

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); 

Editado, solución menos confiable. Puede usar la unión para acceder a los bytes en cualquier orden. Es muy conveniente:

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

Comportamiento técnicamente indefinido en C++. – bames53

+0

Pero, ¿cómo sabemos el orden correcto? –

+0

@BoPersson OP sabe, cuando quiere intercambiar bytes. He editado mi respuesta para exponer la solución más adecuada. –

1

Las funciones ntoh pueden intercambiar entre algo más que un endian grande y pequeño. Algunos sistemas también son 'middle endian' donde los bytes se mezclan en lugar de simplemente ordenarse de una forma u otra.

De todos modos, si todo lo que te importa es grande y pequeño endian, entonces todo lo que necesitas saber es si el host y la endianess del archivo objeto difieren. Tendrás tu propia función que cambia incondicionalmente el orden de los bytes y lo llamarás o no según host_endianess()==objectfile_endianess() o no.

0

Si pensaba en una solución multiplataforma que funciona en Windows o Linux, me gustaría escribir algo como:

#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

creo que vale la pena elevar The Byte Order Fallacy artículo aquí, por Rob Pyke (uno de Autor de Go).

Si haces las cosas bien, es decir, no asumes nada sobre el orden de bytes de tu plataforma, entonces simplemente funcionará. Todo lo que debe preocuparse es si los archivos de formato ELF están en Little Endian o modo Big Endian.

Desde el artículo:

Supongamos que su flujo de datos tiene un entero de 32 bits little-endian-codificado. Así es como para extraerlo (suponiendo bytes sin signo):

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

Si se trata de big-endian, aquí es cómo extraerlo:

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

y dejar que el compilador preocupación acerca optimizando el diablo.

+0

Los compiladores de AFAIK solo usarán un intercambio de orden de bytes optimizado si comienzas con una palabra en primer lugar. –

+0

@AndrewDunn: Es muy posible, pero como de costumbre, medir dos veces, optimizar una vez. –