/* 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; }