TCF Agent: Expressions service: added built-in function $relocate

The function maps link-time address in a file to run-time address in memory.
Can be used to plant breakpoints on link-time addresses.
diff --git a/agent/tcf/services/expressions.c b/agent/tcf/services/expressions.c
index 81de002..6796855 100644
--- a/agent/tcf/services/expressions.c
+++ b/agent/tcf/services/expressions.c
@@ -1171,6 +1171,79 @@
 }
 #endif /* ENABLE_Symbols */
 
+static void expression(int mode, Value * v);
+static uint64_t to_uns(int mode, Value * v);
+static void load_value(Value * v);
+
+static int builtin_identifier(int mode, char * name, Value * v) {
+    Context * ctx = expression_context;
+    for (;;) {
+        RegisterDefinition * def = get_reg_definitions(ctx);
+        if (def != NULL) {
+            while (def->name != NULL) {
+                if (strcmp(name + 1, def->name) == 0) {
+                    int frame = STACK_NO_FRAME;
+                    if (ctx == expression_context) frame = expression_frame;
+                    reg2value(mode, ctx, frame, def, v);
+                    return SYM_CLASS_REFERENCE;
+                }
+                def++;
+            }
+        }
+        ctx = ctx->parent;
+        if (ctx == NULL) break;
+    }
+    if (strcmp(name, "$thread") == 0) {
+        set_string_value(v, expression_context->id);
+        v->constant = 1;
+        return SYM_CLASS_VALUE;
+    }
+    if (strcmp(name, "$relocate") == 0 && text_sy == '(') {
+        unsigned cnt;
+        uint64_t addr = 0;
+        const char * file_name = "";
+        const char * sect_name = "";
+        Symbol * sym = NULL;
+        next_sy();
+        for (cnt = 0;; cnt++) {
+            switch (cnt) {
+            case 0:
+                expression(mode, v);
+                addr = to_uns(mode, v);
+                break;
+            case 1:
+                expression(mode, v);
+                load_value(v);
+                file_name = tmp_strndup((char *)v->value, v->size);
+                break;
+            case 2:
+                expression(mode, v);
+                load_value(v);
+                sect_name = tmp_strndup((char *)v->value, v->size);
+                break;
+            default:
+                error(ERR_INV_EXPRESSION, "Too many arguments");
+            }
+            if (text_sy != ',') break;
+            next_sy();
+        }
+        if (text_sy != ')') error(ERR_INV_EXPRESSION, "Missing ')'");
+        next_sy();
+        ini_value(v);
+        if (mode != MODE_NORMAL) {
+            set_ctx_word_value(v, addr);
+            v->type_class = TYPE_CLASS_POINTER;
+            return SYM_CLASS_VALUE;
+        }
+        if (find_symbol_by_name(expression_context, STACK_NO_FRAME, 0,
+            tmp_printf("$relocate:%s:%s:%" PRIX64, file_name, sect_name, addr), &sym) < 0) {
+            error(errno, "Cannot read symbol data");
+        }
+        return sym2value(mode, sym, v);
+    }
+    return -1;
+}
+
 static int identifier(int mode, Value * scope, char * name, SYM_FLAGS flags, Value * v) {
     ini_value(v);
     if (scope == NULL) {
@@ -1182,28 +1255,8 @@
             exception(ERR_INV_CONTEXT);
         }
         if (name[0] == '$') {
-            Context * ctx = expression_context;
-            for (;;) {
-                RegisterDefinition * def = get_reg_definitions(ctx);
-                if (def != NULL) {
-                    while (def->name != NULL) {
-                        if (strcmp(name + 1, def->name) == 0) {
-                            int frame = STACK_NO_FRAME;
-                            if (ctx == expression_context) frame = expression_frame;
-                            reg2value(mode, ctx, frame, def, v);
-                            return SYM_CLASS_REFERENCE;
-                        }
-                        def++;
-                    }
-                }
-                ctx = ctx->parent;
-                if (ctx == NULL) break;
-            }
-        }
-        if (strcmp(name, "$thread") == 0) {
-            set_string_value(v, expression_context->id);
-            v->constant = 1;
-            return SYM_CLASS_VALUE;
+            int id_class = builtin_identifier(mode, name, v);
+            if (id_class >= 0) return id_class;
         }
     }
 #if ENABLE_Symbols
@@ -2029,8 +2082,6 @@
 #endif
 }
 
-static void expression(int mode, Value * v);
-
 static void primary_expression(int mode, Value * v) {
     if (text_sy == '(') {
         next_sy();
diff --git a/agent/tcf/services/symbols_elf.c b/agent/tcf/services/symbols_elf.c
index 4f241c4..5c42db4 100644
--- a/agent/tcf/services/symbols_elf.c
+++ b/agent/tcf/services/symbols_elf.c
@@ -87,6 +87,7 @@
 #define is_array_type_pseudo_symbol(s) (s->sym_class == SYM_CLASS_TYPE && s->obj == NULL && s->base != NULL)
 #define is_std_type_pseudo_symbol(s) (s->sym_class == SYM_CLASS_TYPE && s->obj == NULL && s->base == NULL)
 #define is_constant_pseudo_symbol(s) (s->sym_class == SYM_CLASS_VALUE && s->obj == NULL && s->base != NULL)
+#define is_pointer_pseudo_symbol(s) (s->sym_class == SYM_CLASS_VALUE && s->obj == NULL && s->base == NULL && s->has_address)
 
 static Context * sym_ctx;
 static int sym_frame;
@@ -1548,6 +1549,66 @@
     }
 }
 
+static void find_relocate(ELF_File * file, const char * name) {
+    /* Psedo-symbol, which helps debugger to map link-time address to run-time address */
+    size_t l = strlen(file->name);
+    size_t i = 0;
+    int ok = 0;
+    if (strncmp(file->name, name, l) == 0 && name[l] == ':') {
+        i = l + 1;
+        ok = 1;
+    }
+    if (!ok) {
+        size_t p = l;
+        while (p > 0 && file->name[p - 1] != '/' && file->name[p - 1] != '\\') p--;
+        if (strncmp(file->name + p, name, l - p) == 0 && name[l - p] == ':') {
+            i = l - p + 1;
+            ok = 1;
+        }
+    }
+    if (ok) {
+        size_t j = i;
+        char * section_name = NULL;
+        while (name[j] != 0 && name[j] != ':') j++;
+        if (j > i) section_name = tmp_strndup(name + i, j - i);
+        if (name[j++] == ':') {
+            ELF_Section * section = NULL;
+            ContextAddress addr = 0;
+            for (;;) {
+                char ch = name[j++];
+                if (ch >= '0' && ch <= '9') addr = (addr << 4) | (ch - '0');
+                else if (ch >= 'A' && ch <= 'F') addr = (addr << 4) | (ch - 'A' + 10);
+                else if (ch >= 'a' && ch <= 'f') addr = (addr << 4) | (ch - 'a' + 10);
+                else break;
+            }
+            if (section_name != NULL) {
+                unsigned n;
+                for (n = 1; n < file->section_cnt; n++) {
+                    ELF_Section * s = file->sections + n;
+                    if (s->name == NULL) continue;
+                    if (strcmp(s->name, section_name) == 0) {
+                        section = s;
+                        break;
+                    }
+                }
+                if (section == NULL) return;
+            }
+            addr = elf_map_to_run_time_address(sym_ctx, file, section, addr);
+            if (errno == 0) {
+                Symbol * sym = alloc_symbol();
+                sym->ctx = context_get_group(sym_ctx, CONTEXT_GROUP_SYMBOLS);
+                sym->frame = STACK_NO_FRAME;
+                sym->sym_class = SYM_CLASS_VALUE;
+                sym->address = addr;
+                sym->size = file->elf64 ? 8 : 4;
+                sym->has_address = 1;
+                assert(is_pointer_pseudo_symbol(sym));
+                add_to_find_symbol_buf(sym);
+            }
+        }
+    }
+}
+
 int find_symbol_by_name(Context * ctx, int frame, ContextAddress ip, const char * name, Symbol ** res) {
     int error = 0;
     ELF_File * curr_file = NULL;
@@ -1691,8 +1752,13 @@
                 Trap trap;
                 if (set_trap(&trap)) {
                     DWARFCache * cache = get_dwarf_cache(get_dwarf_file(file));
-                    find_by_name_in_pub_names(cache, name);
-                    find_by_name_in_sym_table(file, name, 0);
+                    if (strncmp(name, "$relocate:", 10) == 0) {
+                        find_relocate(file, name + 10);
+                    }
+                    else {
+                        find_by_name_in_pub_names(cache, name);
+                        find_by_name_in_sym_table(file, name, 0);
+                    }
                     clear_trap(&trap);
                 }
                 else {
@@ -3193,6 +3259,10 @@
         *type_class = sym->dimension;
         return 0;
     }
+    if (is_pointer_pseudo_symbol(sym)) {
+        *type_class = TYPE_CLASS_POINTER;
+        return 0;
+    }
     if (unpack(sym) < 0) return -1;
     *type_class = TYPE_CLASS_UNKNOWN;
     get_object_type_class(sym->obj, type_class);
@@ -3315,6 +3385,10 @@
         *size = sym->cardinal;
         return 0;
     }
+    if (is_pointer_pseudo_symbol(sym)) {
+        *size = sym->size;
+        return 0;
+    }
     if (unpack(sym) < 0) return -1;
     *size = 0;
     if (obj != NULL) {
@@ -3922,7 +3996,7 @@
 
     assert(sym->magic == SYMBOL_MAGIC);
 
-    if (sym->has_address) {
+    if (sym->has_address && sym->sym_class != SYM_CLASS_VALUE) {
         info->big_endian = big_endian_host();
         add_location_command(info, SFT_CMD_NUMBER)->args.num = sym->address;
         return 0;
@@ -3950,6 +4024,21 @@
         return err_wrong_obj();
     }
 
+    if (is_pointer_pseudo_symbol(sym)) {
+        void const * value = NULL;
+        LocationExpressionCommand * cmd = add_location_command(info, SFT_CMD_PIECE);
+
+        info->big_endian = big_endian_host();
+        cmd->args.piece.bit_size = (unsigned)(sym->size * 8);
+        cmd->args.piece.value = tmp_alloc((size_t)sym->size);
+        value = &sym->address;
+        if (big_endian_host() && sym->size < sizeof(ContextAddress)) {
+            value = (uint8_t *)value + (sizeof(ContextAddress) - sym->size);
+        }
+        memcpy(cmd->args.piece.value, value, (size_t)sym->size);
+        return 0;
+    }
+
     if (unpack(sym) < 0) return -1;
 
     if (obj != NULL) {