From 9a080bc3f8184a5663ce42c3c74ae80e1ae598a0 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Sat, 20 Feb 2021 23:38:22 +0100
Subject: Pack and unpack LEB128 values on demand.

---
 plugins/pychrysalide/common/Makefile.am |   1 +
 plugins/pychrysalide/common/leb128.c    | 384 ++++++++++++++++++++++++++++++++
 plugins/pychrysalide/common/leb128.h    |  45 ++++
 plugins/pychrysalide/common/module.c    |   3 +
 src/common/leb128.c                     | 207 +++++++++++++++++
 src/common/leb128.h                     |  16 +-
 tests/common/leb128.py                  | 108 +++++++++
 7 files changed, 763 insertions(+), 1 deletion(-)
 create mode 100644 plugins/pychrysalide/common/leb128.c
 create mode 100644 plugins/pychrysalide/common/leb128.h
 create mode 100644 tests/common/leb128.py

diff --git a/plugins/pychrysalide/common/Makefile.am b/plugins/pychrysalide/common/Makefile.am
index 1a9e347..66e7622 100644
--- a/plugins/pychrysalide/common/Makefile.am
+++ b/plugins/pychrysalide/common/Makefile.am
@@ -4,6 +4,7 @@ noinst_LTLIBRARIES = libpychrysacommon.la
 libpychrysacommon_la_SOURCES =			\
 	bits.h bits.c						\
 	fnv1a.h fnv1a.c						\
+	leb128.h leb128.c					\
 	module.h module.c					\
 	packed.h packed.c					\
 	pathname.h pathname.c
diff --git a/plugins/pychrysalide/common/leb128.c b/plugins/pychrysalide/common/leb128.c
new file mode 100644
index 0000000..49ef533
--- /dev/null
+++ b/plugins/pychrysalide/common/leb128.c
@@ -0,0 +1,384 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * leb128.c - équivalent Python du fichier "common/leb128.c"
+ *
+ * Copyright (C) 2018-2020 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include "leb128.h"
+
+
+#include <assert.h>
+#include <pygobject.h>
+
+
+#include <common/leb128.h>
+
+
+#include "packed.h"
+#include "../access.h"
+#include "../helpers.h"
+
+
+
+/* Encode un nombre non signé encodé au format LEB128. */
+static PyObject *py_leb128_pack_uleb128(PyObject *, PyObject *);
+
+/* Encode un nombre signé encodé au format LEB128. */
+static PyObject *py_leb128_pack_leb128(PyObject *, PyObject *);
+
+/* Décode un nombre non signé encodé au format LEB128. */
+static PyObject *py_leb128_unpack_uleb128(PyObject *, PyObject *);
+
+/* Décode un nombre signé encodé au format LEB128. */
+static PyObject *py_leb128_unpack_leb128(PyObject *, PyObject *);
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = objet Python concerné par l'appel.                    *
+*                args = tampon de données à constituer.                       *
+*                                                                             *
+*  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   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_leb128_pack_uleb128(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Valeur à retourner          */
+    uleb128_t value;                        /* Valeur à manipuler          */
+    packed_buffer *pbuf;                    /* Tampon de données à employer*/
+    int ret;                                /* Bilan de lecture des args.  */
+    bool status;                            /* Bilan de l'opération        */
+
+#define LEB128_PACK_ULEB128_METHOD PYTHON_METHOD_DEF                \
+(                                                                   \
+    pack_uleb128, "value, pbuf",                                    \
+    METH_VARARGS, py_leb128,                                        \
+    "Pack an unsigned LEB128 value into a data buffer.\n"           \
+    "\n"                                                            \
+    "The *value* is an integer value. The *pbuf* argument has to"   \
+    " be a pychrysalide.common.PackedBuffer instance where data"    \
+    " will be appended.\n"                                          \
+    "\n"                                                            \
+    "The returned value is the operation status: *True* for"        \
+    " success, *False* for failure."                                \
+)
+
+    ret = PyArg_ParseTuple(args, "O&O&", convert_to_uleb128_value, &value, convert_to_packed_buffer, &pbuf);
+    if (!ret) return NULL;
+
+    status = pack_uleb128(&value, pbuf);
+
+    result = status ? Py_True : Py_False;
+    Py_INCREF(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = objet Python concerné par l'appel.                    *
+*                args = tampon de données à constituer.                       *
+*                                                                             *
+*  Description : Encode un nombre signé encodé au format LEB128.              *
+*                                                                             *
+*  Retour      : Bilan de l'opération : True en cas de succès, False sinon.   *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_leb128_pack_leb128(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Valeur à retourner          */
+    leb128_t value;                         /* Valeur à manipuler          */
+    packed_buffer *pbuf;                    /* Tampon de données à employer*/
+    int ret;                                /* Bilan de lecture des args.  */
+    bool status;                            /* Bilan de l'opération        */
+
+#define LEB128_PACK_LEB128_METHOD PYTHON_METHOD_DEF                 \
+(                                                                   \
+    pack_leb128, "value, pbuf",                                     \
+    METH_VARARGS, py_leb128,                                        \
+    "Pack a signed LEB128 value into a data buffer.\n"              \
+    "\n"                                                            \
+    "The *value* is an integer value. The *pbuf* argument has to"   \
+    " be a pychrysalide.common.PackedBuffer instance where data"    \
+    " will be appended.\n"                                          \
+    "\n"                                                            \
+    "The returned value is the operation status: *True* for"        \
+    " success, *False* for failure."                                \
+)
+
+    ret = PyArg_ParseTuple(args, "O&O&", convert_to_leb128_value, &value, convert_to_packed_buffer, &pbuf);
+    if (!ret) return NULL;
+
+    status = pack_leb128(&value, pbuf);
+
+    result = status ? Py_True : Py_False;
+    Py_INCREF(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = objet Python concerné par l'appel.                    *
+*                args = tampon de données à consulter.                        *
+*                                                                             *
+*  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   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_leb128_unpack_uleb128(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Valeur à retourner          */
+    packed_buffer *pbuf;                    /* Tampon de données à employer*/
+    int ret;                                /* Bilan de lecture des args.  */
+    uleb128_t value;                        /* Valeur à manipuler          */
+    bool status;                            /* Bilan de l'opération        */
+
+#define LEB128_UNPACK_ULEB128_METHOD PYTHON_METHOD_DEF              \
+(                                                                   \
+    unpack_uleb128, "pbuf",                                         \
+    METH_VARARGS, py_leb128,                                        \
+    "Unpack an unsigned LEB128 value into a data buffer.\n"         \
+    "\n"                                                            \
+    "The *pbuf* argument has to be a"                               \
+    " pychrysalide.common.PackedBuffer instance from where data"    \
+    " will be read.\n"                                              \
+    "\n"                                                            \
+    "The returned value depends on the operation status: *None*"    \
+    " for failure or a integer value for success."                  \
+)
+
+    ret = PyArg_ParseTuple(args, "O&", convert_to_packed_buffer, &pbuf);
+    if (!ret) return NULL;
+
+    status = unpack_uleb128(&value, pbuf);
+
+    if (status)
+        result = PyLong_FromUnsignedLongLong(value);
+
+    else
+    {
+        result = Py_None;
+        Py_INCREF(result);
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = objet Python concerné par l'appel.                    *
+*                args = tampon de données à consulter.                        *
+*                                                                             *
+*  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   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_leb128_unpack_leb128(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Valeur à retourner          */
+    packed_buffer *pbuf;                    /* Tampon de données à employer*/
+    int ret;                                /* Bilan de lecture des args.  */
+    leb128_t value;                        /* Valeur à manipuler          */
+    bool status;                            /* Bilan de l'opération        */
+
+#define LEB128_UNPACK_LEB128_METHOD PYTHON_METHOD_DEF               \
+(                                                                   \
+    unpack_leb128, "pbuf",                                          \
+    METH_VARARGS, py_leb128,                                        \
+    "Unpack a signed LEB128 value into a data buffer.\n"            \
+    "\n"                                                            \
+    "The *pbuf* argument has to be a"                               \
+    " pychrysalide.common.PackedBuffer instance from where data"    \
+    " will be read.\n"                                              \
+    "\n"                                                            \
+    "The returned value depends on the operation status: *None*"    \
+    " for failure or a integer value for success."                  \
+)
+
+    ret = PyArg_ParseTuple(args, "O&", convert_to_packed_buffer, &pbuf);
+    if (!ret) return NULL;
+
+    status = unpack_leb128(&value, pbuf);
+
+    if (status)
+        result = PyLong_FromLongLong(value);
+
+    else
+    {
+        result = Py_None;
+        Py_INCREF(result);
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : -                                                            *
+*                                                                             *
+*  Description : Définit une extension du module 'common' à compléter.        *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool populate_common_module_with_leb128(void)
+{
+    bool result;                            /* Bilan à retourner           */
+    PyObject *module;                       /* Module à recompléter        */
+
+    static PyMethodDef py_queue_methods[] = {
+        LEB128_PACK_ULEB128_METHOD,
+        LEB128_PACK_LEB128_METHOD,
+        LEB128_UNPACK_ULEB128_METHOD,
+        LEB128_UNPACK_LEB128_METHOD,
+        { NULL }
+    };
+
+    module = get_access_to_python_module("pychrysalide.common");
+
+    result = register_python_module_methods(module, py_queue_methods);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : arg = argument quelconque à tenter de convertir.             *
+*                dst = destination des valeurs récupérées en cas de succès.   *
+*                                                                             *
+*  Description : Tente de convertir en valeur LEB128 non signée.              *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_uleb128_value(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+    unsigned long long value;               /* Valeur récupérée            */
+
+    result = PyObject_IsInstance(arg, (PyObject *)&PyLong_Type);
+
+    switch (result)
+    {
+        case -1:
+            /* L'exception est déjà fixée par Python */
+            result = 0;
+            break;
+
+        case 0:
+            PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to ULEB128 value");
+            break;
+
+        case 1:
+            value = PyLong_AsUnsignedLongLong(arg);
+            *((uleb128_t *)dst) = value;
+            break;
+
+        default:
+            assert(false);
+            break;
+
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : arg = argument quelconque à tenter de convertir.             *
+*                dst = destination des valeurs récupérées en cas de succès.   *
+*                                                                             *
+*  Description : Tente de convertir en valeur LEB128 signée.                  *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_leb128_value(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+    long long value;                        /* Valeur récupérée            */
+
+    result = PyObject_IsInstance(arg, (PyObject *)&PyLong_Type);
+
+    switch (result)
+    {
+        case -1:
+            /* L'exception est déjà fixée par Python */
+            result = 0;
+            break;
+
+        case 0:
+            PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to LEB128 value");
+            break;
+
+        case 1:
+            value = PyLong_AsLongLong(arg);
+            *((leb128_t *)dst) = value;
+            break;
+
+        default:
+            assert(false);
+            break;
+
+    }
+
+    return result;
+
+}
diff --git a/plugins/pychrysalide/common/leb128.h b/plugins/pychrysalide/common/leb128.h
new file mode 100644
index 0000000..f8b1034
--- /dev/null
+++ b/plugins/pychrysalide/common/leb128.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * leb128.h - prototypes pour l'équivalent Python du fichier "common/leb128.h"
+ *
+ * Copyright (C) 2021 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_COMMON_LEB128_H
+#define _PLUGINS_PYCHRYSALIDE_COMMON_LEB128_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Définit une extension du module 'common' à compléter. */
+bool populate_common_module_with_leb128(void);
+
+/* Tente de convertir en valeur LEB128 non signée. */
+int convert_to_uleb128_value(PyObject *, void *);
+
+/* Tente de convertir en valeur LEB128 signée. */
+int convert_to_leb128_value(PyObject *, void *);
+
+
+
+#endif  /* _PLUGINS_PYCHRYSALIDE_COMMON_LEB128_H */
diff --git a/plugins/pychrysalide/common/module.c b/plugins/pychrysalide/common/module.c
index 7f7cbb7..a5cf957 100644
--- a/plugins/pychrysalide/common/module.c
+++ b/plugins/pychrysalide/common/module.c
@@ -27,6 +27,7 @@
 
 #include "bits.h"
 #include "fnv1a.h"
+#include "leb128.h"
 #include "packed.h"
 #include "pathname.h"
 #include "../helpers.h"
@@ -94,6 +95,8 @@ bool populate_common_module(void)
 
     result = true;
 
+    if (result) result = populate_common_module_with_leb128();
+
     if (result) result = ensure_python_bitfield_is_registered();
     if (result) result = ensure_python_fnv1a_is_registered();
     if (result) result = ensure_python_packed_buffer_is_registered();
diff --git a/src/common/leb128.c b/src/common/leb128.c
index c3f7466..c8d2ace 100644
--- a/src/common/leb128.c
+++ b/src/common/leb128.c
@@ -109,3 +109,210 @@ bool read_leb128(leb128_t *target, const bin_t *data, phys_t *pos, phys_t len)
     return (i < 8);
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : value = valeur à consigner.                                  *
+*                pbuf  = tampon de données à constituer. [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   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool pack_uleb128(const uleb128_t *value, packed_buffer_t *pbuf)
+{
+    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 = extend_packed_buffer(pbuf, &byte, sizeof(uint8_t), false);
+
+    }
+    while (result && tmp != 0);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : value = valeur à consigner.                                  *
+*                pbuf  = tampon de données à constituer. [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   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool pack_leb128(const leb128_t *value, packed_buffer_t *pbuf)
+{
+
+    bool result;                            /* Bilan à retourner           */
+    uleb128_t tmp;                          /* Valeur modifiable           */
+    bool more;                              /* Poursuite des traitements   */
+    bool negative;                          /* Nature de la valeur         */
+    uint8_t byte;                           /* Octet à transposer          */
+
+    tmp = *value;
+
+    more = true;
+    negative = (*value < 0);
+
+    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;
+
+        else
+            byte |= 0x80;
+
+        result = extend_packed_buffer(pbuf, &byte, sizeof(uint8_t), false);
+
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : value = valeur à constituer. [OUT]                           *
+*                pbuf  = tampon de données à consulter.                       *
+*                                                                             *
+*  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, packed_buffer_t *pbuf)
+{
+    bool result;                            /* Bilan à retourner           */
+    unsigned int shift;                     /* Décalage à appliquer        */
+    uint8_t byte;                           /* Octet à transposer          */
+
+    result = true;
+
+    *value = 0;
+
+    shift = 0;
+
+    while (true)
+    {
+        /* Encodage sur trop d'octets ? */
+        if (shift > (7 * sizeof(uleb128_t)))
+        {
+            result = false;
+            break;
+        }
+
+        result = extract_packed_buffer(pbuf, &byte, sizeof(uint8_t), false);
+        if (!result) break;
+
+        *value |= ((byte & 0x7f) << shift);
+
+        if ((byte & 0x80) == 0x00)
+            break;
+
+        shift += 7;
+
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : value = valeur à constituer. [OUT]                           *
+*                pbuf  = tampon de données à consulter.                       *
+*                                                                             *
+*  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, packed_buffer_t *pbuf)
+{
+    bool result;                            /* Bilan à retourner           */
+    unsigned int shift;                     /* Décalage à appliquer        */
+    uint8_t byte;                           /* Octet à transposer          */
+
+    result = true;
+
+    *value = 0;
+
+    shift = 0;
+
+    do
+    {
+        /* Encodage sur trop d'octets ? */
+        if (shift > (7 * sizeof(leb128_t)))
+        {
+            result = false;
+            break;
+        }
+
+        result = extract_packed_buffer(pbuf, &byte, sizeof(uint8_t), false);
+        if (!result) break;
+
+        *value |= ((byte & 0x7f) << shift);
+
+        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 (shift < LEB128_BITS_COUNT && (byte & 0x40) == 0x40)
+        *value |= (~0llu << shift);
+
+    return result;
+
+}
diff --git a/src/common/leb128.h b/src/common/leb128.h
index 2093ead..ae1078a 100644
--- a/src/common/leb128.h
+++ b/src/common/leb128.h
@@ -29,6 +29,7 @@
 #include <stdlib.h>
 
 
+#include "packed.h"
 #include "../arch/archbase.h"
 #include "../arch/vmpa.h"
 
@@ -39,10 +40,12 @@ typedef uint64_t uleb128_t;
 typedef int64_t leb128_t;
 
 
+/* Quantité de bits utilisés */
+#define LEB128_BITS_COUNT (sizeof(leb128_t) * 8)
+
 /* Récupération de la valeur absolue */
 #define leb128_abs(v) llabs(v)
 
-
 /* Valeurs minimales et maximales */
 #define ULEB128_MIN UINT64_MIN
 #define ULEB128_MAX UINT64_MAX
@@ -56,6 +59,17 @@ bool read_uleb128(uleb128_t *, const bin_t *, phys_t *, phys_t);
 /* Lit un nombre signé encodé au format LEB128. */
 bool read_leb128(leb128_t *, const bin_t *, phys_t *, phys_t);
 
+/* Encode un nombre non signé encodé au format LEB128. */
+bool pack_uleb128(const uleb128_t *, packed_buffer_t *);
+
+/* Encode un nombre signé encodé au format LEB128. */
+bool pack_leb128(const leb128_t *, packed_buffer_t *);
+
+/* Décode un nombre non signé encodé au format LEB128. */
+bool unpack_uleb128(uleb128_t *, packed_buffer_t *);
+
+/* Décode un nombre signé encodé au format LEB128. */
+bool unpack_leb128(leb128_t *, packed_buffer_t *);
 
 
 #endif  /* _COMMON_LEB128_H */
diff --git a/tests/common/leb128.py b/tests/common/leb128.py
new file mode 100644
index 0000000..db3013e
--- /dev/null
+++ b/tests/common/leb128.py
@@ -0,0 +1,108 @@
+
+from chrysacase import ChrysalideTestCase
+from pychrysalide.common import pack_uleb128, unpack_uleb128, pack_leb128, unpack_leb128
+from pychrysalide.common import PackedBuffer
+
+
+class TestLEB128Values(ChrysalideTestCase):
+    """TestCase for common LEB128 features*"""
+
+    def testUnsignedLeb128Encoding(self):
+        """Pack and unpack unsigned LEB128 values."""
+
+        cases = {
+            624485: b'\xe5\x8e\x26',
+            127: b'\x7f',
+            128: b'\x80\x01',
+        }
+
+        for value, encoding in cases.items():
+
+            pbuf = PackedBuffer()
+
+            status = pack_uleb128(value, pbuf)
+            self.assertTrue(status)
+
+            self.assertEqual(pbuf.payload_length, len(encoding))
+
+            pbuf.rewind()
+
+            got = pbuf.extract(len(encoding))
+
+            self.assertEqual(got, encoding)
+
+            self.assertFalse(pbuf.more_data)
+
+        for value, encoding in cases.items():
+
+            pbuf = PackedBuffer()
+            pbuf.extend(encoding, False)
+
+            pbuf.rewind()
+
+            got = unpack_uleb128(pbuf)
+            self.assertIsNotNone(got)
+
+            self.assertEqual(got, value)
+
+
+    def testSignedLeb128Encoding(self):
+        """Pack and unpack signed LEB128 values."""
+
+        cases = {
+            -123456: b'\xc0\xbb\x78',
+            -42: b'\x56',
+            -9001: b'\xd7\xb9\x7f',
+        }
+
+        for value, encoding in cases.items():
+
+            pbuf = PackedBuffer()
+
+            status = pack_leb128(value, pbuf)
+            self.assertTrue(status)
+
+            self.assertEqual(pbuf.payload_length, len(encoding))
+
+            pbuf.rewind()
+
+            got = pbuf.extract(len(encoding))
+
+            self.assertEqual(got, encoding)
+
+            self.assertFalse(pbuf.more_data)
+
+        for value, encoding in cases.items():
+
+            pbuf = PackedBuffer()
+            pbuf.extend(encoding, False)
+
+            pbuf.rewind()
+
+            got = unpack_leb128(pbuf)
+            self.assertIsNotNone(got)
+
+            self.assertEqual(got, value)
+
+
+    def testTooBigLeb128Encodings(self):
+        """Prevent overflow for LEB128 values."""
+
+        pbuf = PackedBuffer()
+        pbuf.extend(b'\x80' * 10 + b'\x7f', False)
+
+        pbuf.rewind()
+
+        got = unpack_uleb128(pbuf)
+
+        self.assertIsNone(got)
+
+        pbuf = PackedBuffer()
+        pbuf.extend(b'\x80' * 10 + b'\x7f', False)
+
+        pbuf.rewind()
+
+        got = unpack_leb128(pbuf)
+
+        self.assertIsNone(got)
+
-- 
cgit v0.11.2-87-g4458