/* Chrysalide - Outil d'analyse de fichiers binaires
* leb128.c - support des valeurs encodées au format LEB128.
*
* Copyright (C) 2010-2018 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* Chrysalide is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Chrysalide is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chrysalide. If not, see .
*/
#include "leb128.h"
#include
#include "io.h"
/**
* Quantité maximale d'octets de représentation.
*
* sizeof([u]leb128_t) / 7 = 9.142857142857142
*
*/
#define MAX_LEB128_BYTES 9
/******************************************************************************
* *
* Paramètres : target = lieu d'enregistrement de la lecture. [OUT] *
* data = flux de données à analyser. *
* pos = position courante dans ce flux. [OUT] *
* len = taille totale des données à analyser. *
* *
* Description : Lit un nombre non signé encodé au format LEB128. *
* *
* Retour : Bilan de l'opération : true en cas de succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool read_uleb128(uleb128_t *target, const bin_t *data, phys_t *pos, phys_t len)
{
int shift; /* Décalage à appliquer */
phys_t i; /* Boucle de parcours */
shift = 0;
*target = 0;
for (i = 0; i < 8; i++)
{
/* On évite les débordements... */
if (*pos >= len) return false;
*target |= (data[*pos] & 0x7f) << shift;
shift += 7;
(*pos)++;
if ((data[*pos - 1] & 0x80) == 0x00) break;
}
return (i < 8);
}
/******************************************************************************
* *
* Paramètres : target = lieu d'enregistrement de la lecture. [OUT] *
* data = flux de données à analyser. *
* pos = position courante dans ce flux. [OUT] *
* len = taille totale des données à analyser. *
* *
* Description : Lit un nombre signé encodé au format LEB128. *
* *
* Retour : Bilan de l'opération : true en cas de succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool read_leb128(leb128_t *target, const bin_t *data, phys_t *pos, phys_t len)
{
int shift; /* Décalage à appliquer */
phys_t i; /* Boucle de parcours */
shift = 0;
*target = 0;
for (i = 0; i < 8; i++)
{
/* On évite les débordements... */
if (*pos >= len) return false;
*target |= (data[*pos] & 0x7f) << shift;
shift += 7;
if ((data[(*pos)++] & 0x80) == 0x00) break;
}
if (shift < (8 * sizeof(int64_t)) && (data[*pos - 1] & 0x40) == 0x40)
*target |= - (1 << shift);
return (i < 8);
}
/******************************************************************************
* *
* Paramètres : value = valeur à constituer. [OUT] *
* fd = flux ouvert en lecture à consulter. *
* *
* Description : Restaure un nombre non signé encodé au format LEB128. *
* *
* Retour : Bilan de l'opération : true en cas de succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool load_uleb128(uleb128_t *value, int fd)
{
bool result; /* Bilan à retourner */
unsigned int shift; /* Décalage à appliquer */
uint8_t byte; /* Octet à transposer */
result = false;
*value = 0;
shift = 0;
while (true)
{
/* Encodage sur trop d'octets ? */
if (shift > (7 * MAX_LEB128_BYTES))
{
result = false;
break;
}
result = safe_read(fd, &byte, sizeof(uint8_t));
if (!result) break;
*value |= ((byte & 0x7f) << shift);
result = true;
if ((byte & 0x80) == 0x00)
break;
shift += 7;
}
return result;
}
/******************************************************************************
* *
* Paramètres : value = valeur à consigner. *
* fd = flux ouvert en écriture. *
* *
* Description : Sauvegarde un nombre non signé encodé au format LEB128. *
* *
* Retour : Bilan de l'opération : true en cas de succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool store_uleb128(const uleb128_t *value, int fd)
{
bool result; /* Bilan à retourner */
uleb128_t tmp; /* Valeur modifiable */
uint8_t byte; /* Octet à transposer */
tmp = *value;
do
{
byte = (tmp & 0x7f);
tmp >>= 7;
if (tmp != 0)
byte |= 0x80;
result = safe_write(fd, &byte, sizeof(uint8_t));
}
while (result && tmp != 0);
return result;
}
/******************************************************************************
* *
* Paramètres : value = valeur à consigner. *
* len = taille du tampon de données à constitué. [OUT] *
* *
* Description : Encode un nombre non signé encodé au format LEB128. *
* *
* Retour : Bilan de l'opération : true en cas de succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
void *pack_uleb128(const uleb128_t *value, size_t *len)
{
uint8_t *result; /* Données à retourner */
uleb128_t tmp; /* Valeur modifiable */
uint8_t *byte; /* Octet à transposer */
/* Calcul de la quantité d'octets nécessaires */
*len = 0;
tmp = *value;
do
{
tmp >>= 7;
(*len)++;
}
while (tmp != 0);
/* Exportation */
result = malloc(*len * sizeof(uint8_t));
byte = result;
tmp = *value;
do
{
*byte = (tmp & 0x7f);
tmp >>= 7;
if (tmp != 0)
*byte |= 0x80;
byte++;
}
while (tmp != 0);
return result;
}
/******************************************************************************
* *
* Paramètres : value = valeur à consigner. *
* len = taille du tampon de données à constitué. [OUT] *
* *
* Description : Encode un nombre signé encodé au format LEB128. *
* *
* Retour : Bilan de l'opération : true en cas de succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
void *pack_leb128(const leb128_t *value, size_t *len)
{
uint8_t *result; /* Données à retourner */
bool negative; /* Nature de la valeur */
uleb128_t tmp; /* Valeur modifiable */
bool more; /* Poursuite des traitements */
uint8_t byte; /* Octet à transposer */
uint8_t *iter; /* Boucle de parcours */
negative = (*value < 0);
/* Calcul de la quantité d'octets nécessaires */
*len = 0;
tmp = *value;
more = true;
while (more)
{
byte = (tmp & 0x7f);
tmp >>= 7;
/**
* Propagation forcée du bit de signe pour les implémentations de
* décalage basées sur une opération logique et non arithmétique.
*/
if (negative)
tmp |= (~0llu << (LEB128_BITS_COUNT - 7));
/**
* Le bit de signe n'est pas le bit de poids fort ici :
* On travaille sur 7 bits, donc le masque est 0x40 !
*/
if ((tmp == 0 && (byte & 0x40) == 0x00) || (tmp == -1 && (byte & 0x40) == 0x40))
more = false;
(*len)++;
}
/* Exportation */
result = malloc(*len * sizeof(uint8_t));
iter = result;
tmp = *value;
more = true;
while (more)
{
*iter = (tmp & 0x7f);
tmp >>= 7;
/**
* Propagation forcée du bit de signe pour les implémentations de
* décalage basées sur une opération logique et non arithmétique.
*/
if (negative)
tmp |= (~0llu << (LEB128_BITS_COUNT - 7));
/**
* Le bit de signe n'est pas le bit de poids fort ici :
* On travaille sur 7 bits, donc le masque est 0x40 !
*/
if ((tmp == 0 && (*iter & 0x40) == 0x00) || (tmp == -1 && (*iter & 0x40) == 0x40))
more = false;
else
*iter |= 0x80;
iter++;
}
return result;
}
/******************************************************************************
* *
* Paramètres : value = valeur à constituer. [OUT] *
* pos = tête de lecture à faire évoluer. [OUT] *
* max = position maximale liée à la fin des données. *
* *
* Description : Décode un nombre non signé encodé au format LEB128. *
* *
* Retour : Bilan de l'opération : true en cas de succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool unpack_uleb128(uleb128_t *value, const void **pos, const void *max)
{
bool result; /* Bilan à retourner */
unsigned int shift; /* Décalage à appliquer */
uint8_t *byte; /* Octet à transposer */
result = false;
*value = 0;
shift = 0;
byte = *(uint8_t **)pos;
do
{
/* Encodage sur trop d'octets ? */
if (shift > (7 * MAX_LEB128_BYTES))
{
result = false;
break;
}
/* Atteinte de la fin des données ? */
if ((void *)byte >= max)
{
result = false;
break;
}
*value |= ((*byte & 0x7f) << shift);
result = true;
shift += 7;
}
while ((*byte++ & 0x80) == 0x80);
*pos = byte;
return result;
}
/******************************************************************************
* *
* Paramètres : value = valeur à constituer. [OUT] *
* pos = tête de lecture à faire évoluer. [OUT] *
* max = position maximale liée à la fin des données. *
* *
* Description : Décode un nombre signé encodé au format LEB128. *
* *
* Retour : Bilan de l'opération : true en cas de succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool unpack_leb128(leb128_t *value, const void **pos, const void *max)
{
bool result; /* Bilan à retourner */
unsigned int shift; /* Décalage à appliquer */
uint8_t *byte; /* Octet à transposer */
result = false;
*value = 0;
shift = 0;
byte = *(uint8_t **)pos;
do
{
/* Encodage sur trop d'octets ? */
if (shift > (7 * MAX_LEB128_BYTES))
{
result = false;
break;
}
/* Atteinte de la fin des données ? */
if ((void *)byte >= max)
{
result = false;
break;
}
*value |= ((*byte & 0x7f) << shift);
result = true;
shift += 7;
}
while ((*byte++ & 0x80) == 0x80);
/**
* Le bit de signe n'est pas le bit de poids fort ici :
* On travaille sur 7 bits, donc le masque est 0x40 !
*/
if (result)
{
if (shift < LEB128_BITS_COUNT && (byte[-1] & 0x40) == 0x40)
*value |= (~0llu << shift);
}
*pos = byte;
return result;
}