TCF Agent: added support for debugging Linux kernel modules
diff --git a/agent/tcf/services/memorymap.c b/agent/tcf/services/memorymap.c
index 71c8a65..fbc0c95 100644
--- a/agent/tcf/services/memorymap.c
+++ b/agent/tcf/services/memorymap.c
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2009, 2014 Wind River Systems, Inc. and others.
+ * Copyright (c) 2009, 2015 Wind River Systems, Inc. and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * and Eclipse Distribution License v1.0 which accompany this distribution.
@@ -351,14 +351,18 @@
     MemoryRegionAttribute * x = m->attrs;
 
     write_stream(out, '{');
-    json_write_string(out, "Addr");
-    write_stream(out, ':');
-    json_write_uint64(out, m->addr);
-    write_stream(out, ',');
-    json_write_string(out, "Size");
-    write_stream(out, ':');
-    json_write_uint64(out, m->size);
-    write_stream(out, ',');
+    if (m->addr != 0) {
+        json_write_string(out, "Addr");
+        write_stream(out, ':');
+        json_write_uint64(out, m->addr);
+        write_stream(out, ',');
+    }
+    if (m->size != 0) {
+        json_write_string(out, "Size");
+        write_stream(out, ':');
+        json_write_uint64(out, m->size);
+        write_stream(out, ',');
+    }
     json_write_string(out, "Flags");
     write_stream(out, ':');
     json_write_ulong(out, m->flags);
@@ -367,13 +371,14 @@
         json_write_string(out, "FileName");
         write_stream(out, ':');
         json_write_string(out, m->file_name);
-        write_stream(out, ',');
         if (m->sect_name != NULL) {
+            write_stream(out, ',');
             json_write_string(out, "SectionName");
             write_stream(out, ':');
             json_write_string(out, m->sect_name);
         }
-        else {
+        else if (m->file_offs != 0) {
+            write_stream(out, ',');
             json_write_string(out, "Offs");
             write_stream(out, ':');
             json_write_uint64(out, m->file_offs);
diff --git a/agent/tcf/services/tcf_elf.c b/agent/tcf/services/tcf_elf.c
index a10f2c5..890fc33 100644
--- a/agent/tcf/services/tcf_elf.c
+++ b/agent/tcf/services/tcf_elf.c
@@ -38,6 +38,7 @@
 #include <tcf/framework/events.h>
 #include <tcf/framework/cache.h>
 #include <tcf/framework/trace.h>
+#include <tcf/framework/json.h>
 #include <tcf/services/tcf_elf.h>
 #include <tcf/services/memorymap.h>
 #include <tcf/services/dwarfcache.h>
@@ -61,12 +62,34 @@
 #define MAX_FILE_AGE 60
 #define MAX_FILE_CNT 100
 
+#ifndef ARCH_SHF_SMALL
+#define ARCH_SHF_SMALL 0
+#endif
+
 typedef struct FileINode {
     struct FileINode * next;
     char * name;
     ino_t ino;
 } FileINode;
 
+typedef struct ElfListState {
+    Context * ctx;
+    unsigned pos;
+    MemoryMap map;
+    struct ElfListState * next;
+} ElfListState;
+
+typedef struct KernelModuleAddress {
+    U8_T module_init;
+    U8_T module_core;
+    U8_T init_size;
+    U8_T core_size;
+    U8_T init_text_size;
+    U8_T core_text_size;
+    U8_T init_ro_size;
+    U8_T core_ro_size;
+} KernelModuleAddress;
+
 static ELF_File * files = NULL;
 static FileINode * inodes = NULL;
 static ELFOpenListener * openlisteners = NULL;
@@ -77,13 +100,6 @@
 static unsigned closelisteners_max = 0;
 static int elf_cleanup_posted = 0;
 static ino_t elf_ino_cnt = 0;
-typedef struct ElfListState {
-    Context * ctx;
-    unsigned pos;
-    MemoryMap map;
-    struct ElfListState * next;
-} ElfListState;
-
 static ElfListState * elf_list_state = NULL;
 
 #if ENABLE_DebugContext
@@ -1125,12 +1141,144 @@
     return NULL;
 }
 
-static void add_region(MemoryMap * map, MemoryRegion * r) {
+static MemoryRegion * add_region(MemoryMap * map) {
+    MemoryRegion * r = NULL;
     if (map->region_cnt >= map->region_max) {
         map->region_max += 8;
-        map->regions = (MemoryRegion *)loc_realloc(map->regions, sizeof(MemoryRegion ) * map->region_max);
+        map->regions = (MemoryRegion *)loc_realloc(map->regions, sizeof(MemoryRegion) * map->region_max);
     }
-    map->regions[map->region_cnt++] = *r;
+    r = map->regions + map->region_cnt++;
+    memset(r, 0, sizeof(MemoryRegion));
+    return r;
+}
+
+static void program_headers_ranges(ELF_File * file, ContextAddress addr0, ContextAddress addr1, MemoryMap * res) {
+    unsigned j;
+    for (j = 0; j < file->pheader_cnt; j++) {
+        ELF_PHeader * p = file->pheaders + j;
+        if (p->mem_size == 0) continue;
+        if (p->type != PT_LOAD) continue;
+        if (p->address <= addr1 && p->address + p->mem_size - 1 >= addr0) {
+            MemoryRegion * x = add_region(res);
+            x->addr = (ContextAddress)p->address;
+            x->size = (ContextAddress)p->mem_size;
+            x->dev = file->dev;
+            x->ino = file->ino;
+            x->file_name = file->name;
+            x->file_offs = p->offset;
+            x->file_size = p->file_size;
+            x->bss = p->file_size == 0 && p->mem_size != 0;
+            if (p->flags & PF_R) x->flags |= MM_FLAG_R;
+            if (p->flags & PF_W) x->flags |= MM_FLAG_W;
+            if (p->flags & PF_X) x->flags |= MM_FLAG_X;
+        }
+    }
+}
+
+static void read_module_struct(InputStream * inp, const char * name, void * args) {
+    KernelModuleAddress * module = (KernelModuleAddress *)args;
+    if (strcmp(name, "Init") == 0) module->module_init = json_read_uint64(inp);
+    else if (strcmp(name, "Core") == 0) module->module_core = json_read_uint64(inp);
+    else if (strcmp(name, "InitSize") == 0) module->init_size = json_read_uint64(inp);
+    else if (strcmp(name, "CoreSize") == 0) module->core_size = json_read_uint64(inp);
+    else if (strcmp(name, "InitTextSize") == 0) module->init_text_size = json_read_uint64(inp);
+    else if (strcmp(name, "CoreTextSize") == 0) module->core_text_size = json_read_uint64(inp);
+    else if (strcmp(name, "InitROSize") == 0) module->init_ro_size = json_read_uint64(inp);
+    else if (strcmp(name, "CoreROSize") == 0) module->core_ro_size = json_read_uint64(inp);
+    else json_skip_object(inp);
+}
+
+static void linux_kernel_module_ranges(ELF_File * file, KernelModuleAddress * module, ContextAddress addr0, ContextAddress addr1, MemoryMap * res) {
+    unsigned i, m;
+    U8_T * sec_addr = (U8_T *)tmp_alloc_zero(file->section_cnt * 8);
+    static const U4_T masks[][2] = {
+            { SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL },
+            { SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL },
+            { SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL },
+            { ARCH_SHF_SMALL | SHF_ALLOC, 0 }
+    };
+
+    if (module->module_core != 0) {
+        /* Core section allocation */
+        U8_T addr = module->module_core;
+        for (m = 0; m < 4; ++m) {
+            for (i = 0; i < file->section_cnt; i++) {
+                ELF_Section * s = file->sections + i;
+                if (sec_addr[i] != 0) continue;
+                if (s->flags & masks[m][1]) continue;
+                if ((s->flags & masks[m][0]) != masks[m][0]) continue;
+                if (strcmp(s->name, ".data..percpu") == 0) continue;
+                if (strcmp(s->name, ".modinfo") == 0) continue;
+                if (strncmp(s->name, ".init", 5) == 0) continue;
+                if (s->alignment > 1) {
+                    U8_T alignment = s->alignment - 1;
+                    if (addr & alignment) addr = (addr | alignment) + 1;
+                }
+                sec_addr[i] = addr;
+                addr += s->size;
+            }
+            switch (m) {
+            case 0: /* text */
+                addr = module->module_core + module->core_text_size;
+                break;
+            case 1: /* read only */
+                addr = module->module_core + module->core_ro_size;
+                break;
+            }
+        }
+    }
+
+    if (module->module_init != 0) {
+        /* Init section allocation */
+        U8_T addr = module->module_init;
+        for (m = 0; m < 4; ++m) {
+            for (i = 0; i < file->section_cnt; i++) {
+                ELF_Section * s = file->sections + i;
+                if (sec_addr[i] != 0) continue;
+                if (s->flags & masks[m][1]) continue;
+                if ((s->flags & masks[m][0]) != masks[m][0]) continue;
+                if (strcmp(s->name, ".data..percpu") == 0) continue;
+                if (strcmp(s->name, ".modinfo") == 0) continue;
+                if (strncmp(s->name, ".init", 5) != 0) continue;
+                if (s->alignment > 1) {
+                    U8_T alignment = s->alignment - 1;
+                    if (addr & alignment) addr = (addr | alignment) + 1;
+                }
+                sec_addr[i] = addr;
+                addr += s->size;
+            }
+            switch (m) {
+            case 0: /* text */
+                addr = module->module_init + module->init_text_size;
+                break;
+            case 1: /* read only */
+                addr = module->module_init + module->init_ro_size;
+                break;
+            }
+        }
+    }
+
+    for (i = 0; i < file->section_cnt; i++) {
+        if (sec_addr[i] != 0) {
+            ELF_Section * s = file->sections + i;
+            if (s->size > 0) {
+                ContextAddress s_addr0 = (ContextAddress)sec_addr[i];
+                ContextAddress s_addr1 = (ContextAddress)(sec_addr[i] + s->size - 1);
+                if (s_addr0 <= addr1 && s_addr1 >= addr0) {
+                    MemoryRegion * r = add_region(res);
+                    r->addr = s_addr0;
+                    r->size = (ContextAddress)s->size;
+                    r->sect_name = s->name;
+                    r->file_name = file->name;
+                    r->dev = file->dev;
+                    r->ino = file->ino;
+                    r->flags |= MM_FLAG_R;
+                    if (s->flags & SHF_WRITE) r->flags |= MM_FLAG_W;
+                    if (s->flags & SHF_EXECINSTR) r->flags |= MM_FLAG_X;
+                }
+            }
+        }
+    }
 }
 
 static void search_regions(MemoryMap * map, ContextAddress addr0, ContextAddress addr1, MemoryMap * res) {
@@ -1141,27 +1289,26 @@
         if (r->addr == 0 && r->size == 0 && r->file_offs == 0 && r->file_size == 0 && r->sect_name == NULL) {
             ELF_File * file = elf_open_memory_region_file(r, NULL);
             if (file != NULL) {
-                unsigned j;
-                for (j = 0; j < file->pheader_cnt; j++) {
-                    ELF_PHeader * p = file->pheaders + j;
-                    if (p->type != PT_LOAD) continue;
-                    if (p->address <= addr1 && p->address + p->mem_size > addr0) {
-                        MemoryRegion x;
-                        memset(&x, 0, sizeof(x));
-                        x.addr = (ContextAddress)p->address;
-                        x.size = (ContextAddress)p->mem_size;
-                        x.dev = file->dev;
-                        x.ino = file->ino;
-                        x.file_name = file->name;
-                        x.file_offs = p->offset;
-                        x.file_size = p->file_size;
-                        x.bss = p->file_size == 0 && p->mem_size != 0;
-                        if (p->flags & PF_R) x.flags |= MM_FLAG_R;
-                        if (p->flags & PF_W) x.flags |= MM_FLAG_W;
-                        if (p->flags & PF_X) x.flags |= MM_FLAG_X;
-                        add_region(res, &x);
+                KernelModuleAddress * module = NULL;
+                if (r->attrs != NULL) {
+                    MemoryRegionAttribute * a = r->attrs;
+                    while (a != NULL) {
+                        if (strcmp(a->name, "KernelModule") == 0) {
+                            ByteArrayInputStream buf;
+                            InputStream * inp = create_byte_array_input_stream(&buf, a->value, strlen(a->value));
+                            module = (KernelModuleAddress *)tmp_alloc_zero(sizeof(KernelModuleAddress));
+                            json_read_struct(inp, read_module_struct, module);
+                            break;
+                        }
+                        a = a->next;
                     }
                 }
+                if (module != NULL) {
+                    linux_kernel_module_ranges(file, module, addr0, addr1, res);
+                }
+                else {
+                    program_headers_ranges(file, addr0, addr1, res);
+                }
             }
         }
         else if (r->size == 0 && r->file_size == 0 && r->sect_name == NULL) {
@@ -1186,17 +1333,15 @@
                             ContextAddress p_addr0 = (ContextAddress)p->address + base_addr;
                             ContextAddress p_addr1 = (ContextAddress)(p->address + p->mem_size - 1) + base_addr;
                             if (p_addr0 <= addr1 && p_addr1 >= addr0) {
-                                MemoryRegion x;
-                                memset(&x, 0, sizeof(x));
-                                x.addr = p_addr0;
-                                x.size = (ContextAddress)p->mem_size;
-                                x.dev = file->dev;
-                                x.ino = file->ino;
-                                x.file_name = file->name;
-                                x.file_offs = p->offset;
-                                x.file_size = p->file_size;
-                                x.flags = MM_FLAG_R | MM_FLAG_W | MM_FLAG_X;
-                                add_region(res, &x);
+                                MemoryRegion * x = add_region(res);
+                                x->addr = p_addr0;
+                                x->size = (ContextAddress)p->mem_size;
+                                x->dev = file->dev;
+                                x->ino = file->ino;
+                                x->file_name = file->name;
+                                x->file_offs = p->offset;
+                                x->file_size = p->file_size;
+                                x->flags = MM_FLAG_R | MM_FLAG_W | MM_FLAG_X;
                             }
                         }
                     }
@@ -1209,26 +1354,24 @@
                 unsigned j;
                 for (j = 0; j < file->section_cnt; j++) {
                     ELF_Section * s = file->sections + j;
-                    if (s == NULL || s->name == NULL) continue;
+                    if (s == NULL || s->name == NULL || s->size == 0) continue;
                     if (strcmp(s->name, r->sect_name)) continue;
-                    if (r->addr + s->size > addr0) {
-                        MemoryRegion x;
-                        memset(&x, 0, sizeof(x));
-                        x.addr = r->addr;
-                        x.size = (ContextAddress)s->size;
-                        x.dev = file->dev;
-                        x.ino = file->ino;
-                        x.file_name = file->name;
-                        x.sect_name = r->sect_name;
-                        x.flags = r->flags;
-                        if (x.flags == 0) x.flags = MM_FLAG_R | MM_FLAG_W | MM_FLAG_X;
-                        add_region(res, &x);
+                    if (r->addr + s->size - 1 >= addr0) {
+                        MemoryRegion * x = add_region(res);
+                        x->addr = r->addr;
+                        x->size = (ContextAddress)s->size;
+                        x->dev = file->dev;
+                        x->ino = file->ino;
+                        x->file_name = file->name;
+                        x->sect_name = r->sect_name;
+                        x->flags = r->flags;
+                        if (x->flags == 0) x->flags = MM_FLAG_R | MM_FLAG_W | MM_FLAG_X;
                     }
                 }
             }
         }
         else if (r->addr <= addr1 && r->addr + r->size > addr0) {
-            add_region(res, r);
+            *add_region(res) = *r;
         }
     }
 }