TCF Agent: fixed handling of overlapping memory map regions on Linux
diff --git a/agent/system/GNU/Linux/tcf/context-linux.c b/agent/system/GNU/Linux/tcf/context-linux.c
index 786461f..3dfcc58 100644
--- a/agent/system/GNU/Linux/tcf/context-linux.c
+++ b/agent/system/GNU/Linux/tcf/context-linux.c
@@ -1065,7 +1065,7 @@
             &addr0, &addr1, permissions, &offset, &dev_ma, &dev_mi, &inode);
         if (cnt == 0 || cnt == EOF) break;
 
-        for (;;) {
+        for (i = 0;;) {
             int ch = fgetc(file);
             if (ch == '\n' || ch == EOF) break;
             if (i < FILE_PATH_SIZE - 1 && (ch != ' ' || i > 0)) {
@@ -1091,27 +1091,52 @@
         if (map->region_cnt > 0) prev = map->regions + (map->region_cnt - 1);
 
         if (inode != 0 && file_name[0] && file_name[0] != '[') {
-            MemoryRegion * r = map->regions + map->region_cnt++;
-            memset(r, 0, sizeof(MemoryRegion));
-            r->addr = addr0;
-            r->size = addr1 - addr0;
-            r->flags = flags;
-            r->file_offs = offset;
-            r->dev = MKDEV(dev_ma, dev_mi);
-            r->ino = (ino_t)inode;
-            r->file_name = loc_strdup(file_name);
+            if (prev != NULL && (prev->flags & MM_FLAG_X) == 0 &&
+                    prev->file_size == prev->size && prev->dev == MKDEV(dev_ma, dev_mi) && prev->ino == (ino_t)inode &&
+                    prev->file_offs + prev->file_size == offset && prev->addr + prev->size == addr0) {
+                prev->file_size += addr1 - addr0;
+                prev->size += addr1 - addr0;
+                prev->flags |= flags;
+            }
+            else {
+                MemoryRegion * r = map->regions + map->region_cnt++;
+                memset(r, 0, sizeof(MemoryRegion));
+                r->addr = addr0;
+                r->valid |= MM_VALID_ADDR;
+                r->size = addr1 - addr0;
+                r->valid |= MM_VALID_SIZE;
+                r->flags = flags;
+                r->file_offs = offset;
+                r->valid |= MM_VALID_FILE_OFFS;
+                r->file_size = addr1 - addr0;
+                r->valid |= MM_VALID_FILE_SIZE;
+                r->dev = MKDEV(dev_ma, dev_mi);
+                r->ino = (ino_t)inode;
+                r->file_name = loc_strdup(file_name);
+            }
         }
-        else if (file_name[0] == 0 && prev != NULL && prev->addr + prev->size == addr0) {
-            MemoryRegion * r = map->regions + map->region_cnt++;
-            memset(r, 0, sizeof(MemoryRegion));
-            r->bss = 1;
-            r->addr = addr0;
-            r->size = addr1 - addr0;
-            r->flags = flags;
-            r->file_offs = prev->file_offs + prev->size;
-            r->dev = prev->dev;
-            r->ino = prev->ino;
-            r->file_name = loc_strdup(prev->file_name);
+        else if ((file_name[0] == 0 || strcmp(file_name, "[heap]") == 0) &&
+                prev != NULL && prev->addr + prev->size == addr0) {
+            if ((prev->flags & MM_FLAG_X) == 0) {
+                prev->size += addr1 - addr0;
+                prev->flags |= flags;
+            }
+            else {
+                MemoryRegion * r = map->regions + map->region_cnt++;
+                memset(r, 0, sizeof(MemoryRegion));
+                r->bss = 1;
+                r->addr = addr0;
+                r->valid |= MM_VALID_ADDR;
+                r->size = addr1 - addr0;
+                r->valid |= MM_VALID_SIZE;
+                r->flags = flags;
+                r->file_offs = prev->file_offs + prev->size;
+                r->valid |= MM_VALID_FILE_OFFS;
+                r->valid |= MM_VALID_FILE_SIZE;
+                r->dev = prev->dev;
+                r->ino = prev->ino;
+                r->file_name = loc_strdup(prev->file_name);
+            }
         }
     }
     fclose(file);
diff --git a/agent/tcf/services/tcf_elf.c b/agent/tcf/services/tcf_elf.c
index 7517eb6..aa80387 100644
--- a/agent/tcf/services/tcf_elf.c
+++ b/agent/tcf/services/tcf_elf.c
@@ -1387,6 +1387,7 @@
 }
 
 int elf_get_map(Context * ctx, ContextAddress addr0, ContextAddress addr1, MemoryMap * map) {
+    unsigned i;
     map->region_cnt = 0;
     ctx = context_get_group(ctx, CONTEXT_GROUP_PROCESS);
 #if ENABLE_MemoryMap
@@ -1412,6 +1413,30 @@
         }
     }
 #endif
+    for (i = 0; i + 1 < map->region_cnt; i++) {
+        MemoryRegion * m = map->regions + i;
+        MemoryRegion * r = m + 1;
+        if (m->file_size == m->size && m->file_offs < r->file_offs && m->file_offs + m->file_size > r->file_offs &&
+                m->file_name != NULL && r->file_name != NULL && strcmp(m->file_name, r->file_name) == 0) {
+            /* Ambiguity: overlapping regions */
+            ELF_File * elf = elf_open(r->file_name);
+            for (i = 0; i < elf->pheader_cnt; i++) {
+                ELF_PHeader * p = elf->pheaders + i;
+                if (p->type == PT_LOAD && p->offset == m->file_offs && p->mem_size < m->size) {
+                    m->file_size = p->mem_size;
+                    m->size = p->mem_size;
+                    if (m->file_offs + m->size > r->file_offs && p->mem_size == p->file_size) {
+                        uint64_t diff = m->file_offs + m->file_size - r->file_offs;
+                        r->file_offs += diff;
+                        r->file_size -= diff;
+                        r->addr += diff;
+                        r->size -= diff;
+                    }
+                    break;
+                }
+            }
+        }
+    }
     return 0;
 }