From 5cad06b6a52e17d5649e0152c15745a96f7c0efa Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Tue, 29 Dec 2015 16:08:17 +0100
Subject: Handled ELF overlapping program and section headers.

---
 ChangeLog                 |  13 ++++
 src/arch/vmpa.c           |  42 +++++++++++++
 src/arch/vmpa.h           |   3 +
 src/format/elf/elf.c      | 150 +++++++++++++++++++++++++++++++++++++++++-----
 src/glibext/gbinportion.c |  57 ++++++++++++++++--
 5 files changed, 244 insertions(+), 21 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 25e74e3..fcec4f1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,18 @@
 15-12-29  Cyrille Bagard <nocbos@gmail.com>
 
+	* src/arch/vmpa.c:
+	* src/arch/vmpa.h:
+	Detect intersections between two ranges.
+
+	* src/format/elf/elf.c:
+	Handle ELF overlapping program and section headers.
+
+	* src/glibext/gbinportion.c:
+	Create a new layer for the portion which is overlapping the ones of the
+	current layer.
+
+15-12-29  Cyrille Bagard <nocbos@gmail.com>
+
 	* src/arch/processor.c:
 	* src/arch/processor.h:
 	Make it possible to find instructions by their addresses in a flexible way.
diff --git a/src/arch/vmpa.c b/src/arch/vmpa.c
index a2430b5..e2da876 100644
--- a/src/arch/vmpa.c
+++ b/src/arch/vmpa.c
@@ -990,6 +990,48 @@ bool mrange_contains_addr_inclusive(const mrange_t *range, const vmpa2t *addr)
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : range = zone mémoire à consulter.                            *
+*                other = autre zone mémoire à manipuler.                      *
+*                                                                             *
+*  Description : Détermine si deux zones mémoire se chevauchent ou non.       *
+*                                                                             *
+*  Retour      : Bilan de la consultation.                                    *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool mrange_intersects_mrange(const mrange_t *range, const mrange_t *other)
+{
+    bool result;                            /* Bilan à retourner           */
+    vmpa2t end;                             /* Fin d'une zone mémoire      */
+
+    result = false;
+
+    result |= mrange_contains_addr(range, &other->addr);
+    result |= mrange_contains_addr(other, &range->addr);
+
+    if (get_mrange_length(other) > 0)
+    {
+        compute_mrange_end_addr(other, &end);
+        deminish_vmpa(&end, 1);
+        result |= mrange_contains_addr(range, &end);
+    }
+
+    if (get_mrange_length(range) > 0)
+    {
+        compute_mrange_end_addr(range, &end);
+        deminish_vmpa(&end, 1);
+        result |= mrange_contains_addr(other, &end);
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : range = zone mémoire à consulter.                            *
 *                addr  = localisation mémoire à déterminer.                   *
 *                                                                             *
 *  Description : Calcule la position extérieure finale d'une couverture.      *
diff --git a/src/arch/vmpa.h b/src/arch/vmpa.h
index 4470676..eb63324 100644
--- a/src/arch/vmpa.h
+++ b/src/arch/vmpa.h
@@ -198,6 +198,9 @@ bool mrange_contains_addr(const mrange_t *, const vmpa2t *);
 /* Indique si une localisation est incluse dans une zone ou non. */
 bool mrange_contains_addr_inclusive(const mrange_t *, const vmpa2t *);
 
+/* Détermine si deux zones mémoire se chevauchent ou non. */
+bool mrange_intersects_mrange(const mrange_t *, const mrange_t *);
+
 /* Calcule la position extérieure finale d'une couverture. */
 void compute_mrange_end_addr(const mrange_t *, vmpa2t *);
 
diff --git a/src/format/elf/elf.c b/src/format/elf/elf.c
index 3491c71..a48e3b3 100644
--- a/src/format/elf/elf.c
+++ b/src/format/elf/elf.c
@@ -365,9 +365,11 @@ static const char *g_elf_format_get_target_machine(const GElfFormat *format)
 static void g_elf_format_refine_portions(const GElfFormat *format, GPortionLayer *main)
 {
     GPortionLayer *layer;                   /* Couche à mettre en place    */
+    uint16_t max;                           /* Décompte d'éléments traités */
+    elf_phdr *sorted_phdrs;                 /* Liste de segments triée     */
     uint16_t i;                             /* Boucle de parcours          */
     off_t offset;                           /* Début de part de programme  */
-    elf_phdr phdr;                          /* En-tête de programme ELF    */
+    elf_phdr *phdr;                         /* En-tête de programme ELF    */
     uint32_t p_flags;                       /* Droits associés à une partie*/
     const char *background;                 /* Fond signigicatif           */
     GBinPortion *new;                       /* Nouvelle portion définie    */
@@ -376,25 +378,87 @@ static void g_elf_format_refine_portions(const GElfFormat *format, GPortionLayer
     PortionAccessRights rights;             /* Droits d'une portion        */
     elf_shdr strings;                       /* Section des descriptions    */
     bool has_strings;                       /* Section trouvée ?           */
-    elf_shdr section;                       /* En-tête de section ELF      */
+    elf_shdr *sorted_shdrs;                 /* Liste de sections triée     */
+    elf_shdr *section;                      /* En-tête de section ELF      */
     uint64_t sh_flags;                      /* Droits associés à une partie*/
     const char *name;                       /* Nom trouvé ou NULL          */
 
-    /* Côté segments basiques */
+    /**
+     * La copie des différents en-têtes cherche à reproduire l'inclusion native
+     * du format :
+     *
+     *  EXIDX          0x001178 0x00009178 0x00009178 0x00008 0x00008 R   0x4
+     *  PHDR           0x000034 0x00008034 0x00008034 0x00120 0x00120 R E 0x4
+     *  INTERP         0x000154 0x00008154 0x00008154 0x00019 0x00019 R   0x1
+     *  LOAD           0x000000 0x00008000 0x00008000 0x01184 0x01184 R E 0x8000
+     *
+     */
+
+    /**
+     * Côté segments basiques.
+     */
 
     layer = g_portion_layer_new(NO_LENGTH_YET, _("Segment"));
 
     g_portion_layer_attach_sub(main, layer);
 
-    for (i = 0; i < ELF_HDR(format, format->header, e_phnum); i++)
+    /* Constitution d'une liste de travail */
+
+    max = ELF_HDR(format, format->header, e_phnum);
+
+    sorted_phdrs = (elf_phdr *)calloc(max, sizeof(elf_phdr));
+
+    for (i = 0; i < max; i++)
     {
         offset = ELF_HDR(format, format->header, e_phoff)
             + ELF_HDR(format, format->header, e_phentsize) * i;
 
-        if (!read_elf_program_header(format, offset, &phdr))
+        if (!read_elf_program_header(format, offset, &sorted_phdrs[i]))
+        {
+            if (format->is_32b)
+                sorted_phdrs[i].phdr32.p_type = PT_NULL;
+            else
+                sorted_phdrs[i].phdr64.p_type = PT_NULL;
+        }
+
+    }
+
+    /* Tri de cette liste */
+
+    int sort_phdr(elf_phdr *a, elf_phdr *b)
+    {
+        uint64_t filesz_a;                  /* Taille de l'en-tête 'a'     */
+        uint64_t filesz_b;                  /* Taille de l'en-tête 'b'     */
+        int status;                         /* Bilan d'une comparaison     */
+
+        filesz_a = ELF_PHDR(format, *a, p_filesz);
+        filesz_b = ELF_PHDR(format, *b, p_filesz);
+
+        if (filesz_a < filesz_b)
+            status = 1;
+
+        else if (filesz_a > filesz_b)
+            status = -1;
+
+        else
+            status = 0;
+
+        return status;
+
+    }
+
+    qsort(sorted_phdrs, max, sizeof(elf_phdr), (__compar_fn_t)sort_phdr);
+
+    /* Inclusion de ces en-têtes */
+
+    for (i = 0; i < max; i++)
+    {
+        phdr = &sorted_phdrs[i];
+
+        if (ELF_PHDR(format, *phdr, p_type) == PT_NULL)
             continue;
 
-        p_flags = ELF_PHDR(format, phdr, p_flags);
+        p_flags = ELF_PHDR(format, *phdr, p_flags);
 
         if (p_flags & PF_X) background = BPC_CODE;
         else if (p_flags & PF_W) background = BPC_DATA;
@@ -404,12 +468,12 @@ static void g_elf_format_refine_portions(const GElfFormat *format, GPortionLayer
 
         snprintf(desc, MAX_PORTION_DESC, "%s \"%s\"",
                  _("Segment"),
-                 get_elf_program_type_desc(ELF_PHDR(format, phdr, p_type)));
+                 get_elf_program_type_desc(ELF_PHDR(format, *phdr, p_type)));
 
         g_binary_portion_set_desc(new, desc);
 
-        init_vmpa(&addr, ELF_PHDR(format, phdr, p_offset), ELF_PHDR(format, phdr, p_vaddr));
-        g_binary_portion_set_values(new, &addr, ELF_PHDR(format, phdr, p_filesz));
+        init_vmpa(&addr, ELF_PHDR(format, *phdr, p_offset), ELF_PHDR(format, *phdr, p_vaddr));
+        g_binary_portion_set_values(new, &addr, ELF_PHDR(format, *phdr, p_filesz));
 
         rights = PAC_NONE;
         if (p_flags & PF_R) rights |= PAC_READ;
@@ -422,7 +486,11 @@ static void g_elf_format_refine_portions(const GElfFormat *format, GPortionLayer
 
     }
 
-    /* Inclusion des sections, si possible... */
+    free(sorted_phdrs);
+
+    /**
+     * Inclusion des sections, si possible...
+     */
 
     has_strings = find_elf_section_by_index(format,
                                             ELF_HDR(format, format->header, e_shstrndx),
@@ -432,12 +500,60 @@ static void g_elf_format_refine_portions(const GElfFormat *format, GPortionLayer
 
     g_portion_layer_attach_sub(main, layer);
 
-    for (i = 0; i < ELF_HDR(format, format->header, e_shnum); i++)
+    /* Constitution d'une liste de travail */
+
+    max = ELF_HDR(format, format->header, e_shnum);
+
+    sorted_shdrs = (elf_shdr *)calloc(max, sizeof(elf_shdr));
+
+    for (i = 0; i < max; i++)
+    {
+        if (!find_elf_section_by_index(format, i, &sorted_shdrs[i]))
+        {
+            if (format->is_32b)
+                sorted_shdrs[i].shdr32.sh_offset = 0;
+            else
+                sorted_shdrs[i].shdr64.sh_offset = 0;
+        }
+
+    }
+
+    /* Tri de cette liste */
+
+    int sort_shdr(elf_shdr *a, elf_shdr *b)
+    {
+        uint64_t size_a;                    /* Taille de l'en-tête 'a'     */
+        uint64_t size_b;                    /* Taille de l'en-tête 'b'     */
+        int status;                         /* Bilan d'une comparaison     */
+
+        size_a = ELF_SHDR(format, *a, sh_size);
+        size_b = ELF_SHDR(format, *b, sh_size);
+
+        if (size_a < size_b)
+            status = 1;
+
+        else if (size_a > size_b)
+            status = -1;
+
+        else
+            status = 0;
+
+        return status;
+
+    }
+
+    qsort(sorted_shdrs, max, sizeof(elf_shdr), (__compar_fn_t)sort_shdr);
+
+    /* Inclusion de ces en-têtes */
+
+    for (i = 0; i < max; i++)
     {
-        if (!find_elf_section_by_index(format, i, &section))
+        section = &sorted_shdrs[i];
+
+        if (ELF_SHDR(format, *section, sh_offset) == 0)
             continue;
 
-        sh_flags = ELF_SHDR(format, section, sh_flags);
+        sh_flags = ELF_SHDR(format, *section, sh_flags);
 
         if ((sh_flags & SHF_ALLOC) == 0)
             continue;
@@ -450,7 +566,7 @@ static void g_elf_format_refine_portions(const GElfFormat *format, GPortionLayer
 
         if (has_strings)
             name = extract_name_from_elf_string_section(format, &strings,
-                                                        ELF_SHDR(format, section, sh_name));
+                                                        ELF_SHDR(format, *section, sh_name));
         else name = NULL;
 
         if (name != NULL)
@@ -460,8 +576,8 @@ static void g_elf_format_refine_portions(const GElfFormat *format, GPortionLayer
 
         g_binary_portion_set_desc(new, desc);
 
-        init_vmpa(&addr, ELF_SHDR(format, section, sh_offset), ELF_SHDR(format, section, sh_addr));
-        g_binary_portion_set_values(new, &addr, ELF_SHDR(format, section, sh_size));
+        init_vmpa(&addr, ELF_SHDR(format, *section, sh_offset), ELF_SHDR(format, *section, sh_addr));
+        g_binary_portion_set_values(new, &addr, ELF_SHDR(format, *section, sh_size));
 
         rights = PAC_NONE;
         if (sh_flags & SHF_ALLOC) rights |= PAC_READ;
@@ -474,6 +590,8 @@ static void g_elf_format_refine_portions(const GElfFormat *format, GPortionLayer
 
     }
 
+    free(sorted_shdrs);
+
 }
 
 
diff --git a/src/glibext/gbinportion.c b/src/glibext/gbinportion.c
index 790614a..abdec60 100644
--- a/src/glibext/gbinportion.c
+++ b/src/glibext/gbinportion.c
@@ -898,14 +898,61 @@ void g_portion_layer_attach_sub(GPortionLayer *layer, GPortionLayer *sub)
 
 void g_portion_layer_include(GPortionLayer *layer, GBinPortion *portion)
 {
-    layer->portions = (GBinPortion **)realloc(layer->portions,
-                                              ++layer->count * sizeof(GBinPortion *));
+    GPortionLayer *sub;                     /* Sous couche indispensable   */
+    bool conflict;                          /* Conflit dû aux débordements */
+    const mrange_t *range;                  /* Emplacement de la portion   */
+    size_t i;                               /* Boucle de parcours          */
+    const mrange_t *other;                  /* Emplacements déjà occupés   */
+
+    /**
+     * On prend ici en compte le genre de situations suivantes :
+     *
+     *  [21] .bss                NOBITS          00088240 07823c 0018c8 00  WA  0   0  8
+     *  [22] __libc_freeres_ptrs NOBITS          00089b08 07823c 000018 00  WA  0   0  4
+     *  [23] .comment            PROGBITS        00000000 07823c 000022 01  MS  0   0  1
+     *
+     * Pendant le désassemblage, la procédure n'aime pas trop les intersections
+     * de zones mémoire.
+     */
+
+    conflict = false;
+
+    range = g_binary_portion_get_range(portion);
+
+    for (i = 0; i < layer->count && !conflict; i++)
+    {
+        other = g_binary_portion_get_range(layer->portions[i]);
 
-    layer->portions[layer->count - 1] = portion;
+        conflict = mrange_intersects_mrange(range, other);
 
-    g_binary_portion_set_level(portion, &layer->level);
+    }
+
+    /* La portion recouvre-t-elle une portion déjà existante ? */
+    if (conflict)
+    {
+        if (layer->sub_layer == NULL)
+        {
+            sub = g_portion_layer_new(layer->length, layer->name);
+            g_portion_layer_attach_sub(layer, sub);
+        }
 
-    qsort(layer->portions, layer->count, sizeof(GBinPortion *), (__compar_fn_t)g_binary_portion_compare);
+        g_portion_layer_include(layer->sub_layer, portion);
+
+    }
+
+    /* Sinon on l'intègre dans la couche courante */
+    else
+    {
+        layer->portions = (GBinPortion **)realloc(layer->portions,
+                                                  ++layer->count * sizeof(GBinPortion *));
+
+        layer->portions[layer->count - 1] = portion;
+
+        g_binary_portion_set_level(portion, &layer->level);
+
+        qsort(layer->portions, layer->count, sizeof(GBinPortion *), (__compar_fn_t)g_binary_portion_compare);
+
+    }
 
 }
 
-- 
cgit v0.11.2-87-g4458