return next;
 }
 
+static struct instruction *next_insn_same_func(struct objtool_file *file,
+                                              struct instruction *insn)
+{
+       struct instruction *next = list_next_entry(insn, list);
+       struct symbol *func = insn->func;
+
+       if (!func)
+               return NULL;
+
+       if (&next->list != &file->insn_list && next->func == func)
+               return next;
+
+       /* Check if we're already in the subfunction: */
+       if (func == func->cfunc)
+               return NULL;
+
+       /* Move to the subfunction: */
+       return find_insn(file, func->cfunc->sec, func->cfunc->offset);
+}
+
+#define func_for_each_insn_all(file, func, insn)                       \
+       for (insn = find_insn(file, func->sec, func->offset);           \
+            insn;                                                      \
+            insn = next_insn_same_func(file, insn))
+
 #define func_for_each_insn(file, func, insn)                           \
        for (insn = find_insn(file, func->sec, func->offset);           \
             insn && &insn->list != &file->insn_list &&                 \
                        if (!strcmp(func->name, global_noreturns[i]))
                                return 1;
 
-       if (!func->sec)
+       if (!func->len)
                return 0;
 
-       func_for_each_insn(file, func, insn) {
+       insn = find_insn(file, func->sec, func->offset);
+       if (!insn->func)
+               return 0;
+
+       func_for_each_insn_all(file, func, insn) {
                empty = false;
 
                if (insn->type == INSN_RETURN)
         * case, the function's dead-end status depends on whether the target
         * of the sibling call returns.
         */
-       func_for_each_insn(file, func, insn) {
-               if (insn->sec != func->sec ||
-                   insn->offset >= func->offset + func->len)
-                       break;
-
+       func_for_each_insn_all(file, func, insn) {
                if (insn->type == INSN_JUMP_UNCONDITIONAL) {
                        struct instruction *dest = insn->jump_dest;
-                       struct symbol *dest_func;
 
                        if (!dest)
                                /* sibling call to another file */
                                return 0;
 
-                       if (dest->sec != func->sec ||
-                           dest->offset < func->offset ||
-                           dest->offset >= func->offset + func->len) {
-                               /* local sibling call */
-                               dest_func = find_symbol_by_offset(dest->sec,
-                                                                 dest->offset);
-                               if (!dest_func)
-                                       continue;
+                       if (dest->func && dest->func->pfunc != insn->func->pfunc) {
 
+                               /* local sibling call */
                                if (recursion == 5) {
                                        /*
                                         * Infinite recursion: two functions
                                        return 0;
                                }
 
-                               return __dead_end_function(file, dest_func,
+                               return __dead_end_function(file, dest->func,
                                                           recursion + 1);
                        }
                }
                        if (!ignore_func(file, func))
                                continue;
 
-                       func_for_each_insn(file, func, insn)
+                       func_for_each_insn_all(file, func, insn)
                                insn->ignore = true;
                }
        }
        return ret;
 }
 
-static int add_switch_table(struct objtool_file *file, struct symbol *func,
-                           struct instruction *insn, struct rela *table,
-                           struct rela *next_table)
+static int add_switch_table(struct objtool_file *file, struct instruction *insn,
+                           struct rela *table, struct rela *next_table)
 {
        struct rela *rela = table;
        struct instruction *alt_insn;
                if (rela == next_table)
                        break;
 
-               if (rela->sym->sec != insn->sec ||
-                   rela->addend <= func->offset ||
-                   rela->addend >= func->offset + func->len)
+               alt_insn = find_insn(file, rela->sym->sec, rela->addend);
+               if (!alt_insn)
                        break;
 
-               alt_insn = find_insn(file, insn->sec, rela->addend);
-               if (!alt_insn) {
-                       WARN("%s: can't find instruction at %s+0x%x",
-                            file->rodata->rela->name, insn->sec->name,
-                            rela->addend);
-                       return -1;
-               }
+               /* Make sure the jmp dest is in the function or subfunction: */
+               if (alt_insn->func->pfunc != insn->func->pfunc)
+                       break;
 
                alt = malloc(sizeof(*alt));
                if (!alt) {
        struct rela *rela, *prev_rela = NULL;
        int ret;
 
-       func_for_each_insn(file, func, insn) {
+       func_for_each_insn_all(file, func, insn) {
                if (!last)
                        last = insn;
 
                 * the beginning of another switch table in the same function.
                 */
                if (prev_jump) {
-                       ret = add_switch_table(file, func, prev_jump, prev_rela,
-                                              rela);
+                       ret = add_switch_table(file, prev_jump, prev_rela, rela);
                        if (ret)
                                return ret;
                }
        }
 
        if (prev_jump) {
-               ret = add_switch_table(file, func, prev_jump, prev_rela, NULL);
+               ret = add_switch_table(file, prev_jump, prev_rela, NULL);
                if (ret)
                        return ret;
        }
        while (1) {
                next_insn = next_insn_same_sec(file, insn);
 
-
-               if (file->c_file && func && insn->func && func != insn->func) {
+               if (file->c_file && func && insn->func && func != insn->func->pfunc) {
                        WARN("%s() falls through to next function %s()",
                             func->name, insn->func->name);
                        return 1;
                }
 
-               if (insn->func)
-                       func = insn->func;
+               func = insn->func ? insn->func->pfunc : NULL;
 
                if (func && insn->ignore) {
                        WARN_FUNC("BUG: why am I validating an ignored function?",
 
                                i = insn;
                                save_insn = NULL;
-                               func_for_each_insn_continue_reverse(file, func, i) {
+                               func_for_each_insn_continue_reverse(file, insn->func, i) {
                                        if (i->save) {
                                                save_insn = i;
                                                break;
                case INSN_JUMP_UNCONDITIONAL:
                        if (insn->jump_dest &&
                            (!func || !insn->jump_dest->func ||
-                            func == insn->jump_dest->func)) {
+                            insn->jump_dest->func->pfunc == func)) {
                                ret = validate_branch(file, insn->jump_dest,
                                                      state);
                                if (ret)
 
        for_each_sec(file, sec) {
                list_for_each_entry(func, &sec->symbol_list, list) {
-                       if (func->type != STT_FUNC)
+                       if (func->type != STT_FUNC || func->pfunc != func)
                                continue;
 
                        insn = find_insn(file, sec, func->offset);
 
        return NULL;
 }
 
+struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
+{
+       struct section *sec;
+       struct symbol *sym;
+
+       list_for_each_entry(sec, &elf->sections, list)
+               list_for_each_entry(sym, &sec->symbol_list, list)
+                       if (!strcmp(sym->name, name))
+                               return sym;
+
+       return NULL;
+}
+
 struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
 {
        struct symbol *sym;
 
 static int read_symbols(struct elf *elf)
 {
-       struct section *symtab;
-       struct symbol *sym;
+       struct section *symtab, *sec;
+       struct symbol *sym, *pfunc;
        struct list_head *entry, *tmp;
        int symbols_nr, i;
+       char *coldstr;
 
        symtab = find_section_by_name(elf, ".symtab");
        if (!symtab) {
                hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx);
        }
 
+       /* Create parent/child links for any cold subfunctions */
+       list_for_each_entry(sec, &elf->sections, list) {
+               list_for_each_entry(sym, &sec->symbol_list, list) {
+                       if (sym->type != STT_FUNC)
+                               continue;
+                       sym->pfunc = sym->cfunc = sym;
+                       coldstr = strstr(sym->name, ".cold.");
+                       if (coldstr) {
+                               coldstr[0] = '\0';
+                               pfunc = find_symbol_by_name(elf, sym->name);
+                               coldstr[0] = '.';
+
+                               if (!pfunc) {
+                                       WARN("%s(): can't find parent function",
+                                            sym->name);
+                                       goto err;
+                               }
+
+                               sym->pfunc = pfunc;
+                               pfunc->cfunc = sym;
+                       }
+               }
+       }
+
        return 0;
 
 err: