Add support for precise unwind info in epilogs on amd64:
authorZoltan Varga <vargaz@gmail.com>
Sat, 5 Jul 2014 02:43:03 +0000 (04:43 +0200)
committerZoltan Varga <vargaz@gmail.com>
Sat, 5 Jul 2014 14:10:13 +0000 (16:10 +0200)
* Implement DW_CFA_remember_state/restore_state opcodes.
* Add a DW_CFA_mono_advance_loc opcode to advance to a location stored outside
  of the unwind info. Use this to allow the sharing of unwind info between
  methods.

This is need to prevent crashes during unwinding when the ip of the last frame is in the epilog. This can be reproduced by
running sgen-new-threads-dont-join-stw.exe in a loop.

14 files changed:
mono/mini/aot-compiler.c
mono/mini/aot-runtime.c
mono/mini/exceptions-amd64.c
mono/mini/exceptions-arm.c
mono/mini/exceptions-mips.c
mono/mini/exceptions-ppc.c
mono/mini/exceptions-s390x.c
mono/mini/exceptions-x86.c
mono/mini/mini-amd64.c
mono/mini/mini-exceptions.c
mono/mini/mini-unwind.h
mono/mini/mini.c
mono/mini/mini.h
mono/mini/unwind.c

index 9802f3ef8e4c6feecc81b7399469881a9ccb9ba8..2a0547f729dfad71bc4923b02266b385db622ac7 100644 (file)
@@ -5325,14 +5325,27 @@ emit_exception_debug_info (MonoAotCompile *acfg, MonoCompile *cfg)
        if (use_unwind_ops) {
                guint32 encoded_len;
                guint8 *encoded;
+               guint32 unwind_desc;
 
                /* 
                 * This is a duplicate of the data in the .debug_frame section, but that
                 * section cannot be accessed using the dl interface.
                 */
                encoded = mono_unwind_ops_encode (cfg->unwind_ops, &encoded_len);
-               encode_value (get_unwind_info_offset (acfg, encoded, encoded_len), p, &p);
-               g_free (encoded);
+
+               unwind_desc = get_unwind_info_offset (acfg, encoded, encoded_len);
+               g_assert (unwind_desc < 0xffff);
+               if (cfg->has_unwind_info_for_epilog) {
+                       /*
+                        * The lower 16 bits identify the unwind descriptor, the upper 16 bits contain the offset of
+                        * the start of the epilog from the end of the method.
+                        */
+                       g_assert (cfg->code_size - cfg->epilog_begin < 0xffff);
+                       encode_value (((cfg->code_size - cfg->epilog_begin) << 16) | unwind_desc, p, &p);
+                       g_free (encoded);
+               } else {
+                       encode_value (unwind_desc, p, &p);
+               }
        } else {
                encode_value (jinfo->used_regs, p, &p);
        }
index dc046daf20eede642d6f19017b60642e4a5eac72..3c9064b885d26a203dc19b01836b11e696780383 100644 (file)
@@ -2700,7 +2700,8 @@ mono_aot_get_unwind_info (MonoJitInfo *ji, guint32 *unwind_info_len)
                mono_aot_unlock ();
        }
 
-       p = amodule->unwind_info + ji->used_regs;
+       /* The upper 16 bits of ji->used_regs might contain the epilog offset */
+       p = amodule->unwind_info + (ji->used_regs & 0xffff);
        *unwind_info_len = decode_value (p, &p);
        return p;
 }
index 212416e06cf3b6d31a38dcbb1eda8268afb44370..5a030740915820434c31baadb39063bd851f263c 100644 (file)
@@ -590,6 +590,7 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls,
                guint8 *cfa;
                guint32 unwind_info_len;
                guint8 *unwind_info;
+               guint8 *epilog;
 
                frame->type = FRAME_TYPE_MANAGED;
 
@@ -602,6 +603,7 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls,
                printf ("%s %p %p\n", ji->d.method->name, ji->code_start, ip);
                mono_print_unwind_info (unwind_info, unwind_info_len);
                */
+               epilog = (guint8*)ji->code_start + ji->code_size - (ji->used_regs >> 16);
  
                regs [AMD64_RAX] = new_ctx->rax;
                regs [AMD64_RBX] = new_ctx->rbx;
@@ -619,7 +621,7 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls,
 
                mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, 
                                                   (guint8*)ji->code_start + ji->code_size,
-                                                  ip, regs, MONO_MAX_IREGS + 1, 
+                                                  ip, &epilog, regs, MONO_MAX_IREGS + 1,
                                                   save_locations, MONO_MAX_IREGS, &cfa);
 
                new_ctx->rax = regs [AMD64_RAX];
index 020794582b168b1e40a22bd89c41131f7d3e1cd2..d7fd3318f3d1e45a5a27693114a4ab2dbbf551c6 100644 (file)
@@ -424,7 +424,7 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls,
 
                mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, 
                                                   (guint8*)ji->code_start + ji->code_size,
-                                                  ip, regs, MONO_MAX_IREGS + 8,
+                                                  ip, NULL, regs, MONO_MAX_IREGS + 8,
                                                   save_locations, MONO_MAX_IREGS, &cfa);
 
                for (i = 0; i < 16; ++i)
index ad6f2552c0e7a4d3986d0435a30c338b3b761a12..34197145048468a709de54e097cead753f06a2f0 100644 (file)
@@ -413,7 +413,7 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls,
 
                mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, 
                                                   (guint8*)ji->code_start + ji->code_size,
-                                                  ip, regs, MONO_MAX_IREGS,
+                                                  ip, NULL, regs, MONO_MAX_IREGS,
                                                   save_locations, MONO_MAX_IREGS, &cfa);
 
                for (i = 0; i < MONO_MAX_IREGS; ++i)
index d2429325d94a5acbfa9ab68d17e904c4ebaa78f2..6c3878e97b8e4df6102126330851adae5cea167f 100644 (file)
@@ -550,7 +550,7 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls,
 
                        mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, 
                                                           (guint8*)ji->code_start + ji->code_size,
-                                                          ip, regs, ppc_lr + 1,
+                                                          ip, NULL, regs, ppc_lr + 1,
                                                           save_locations, MONO_MAX_IREGS, &cfa);
 
                        /* we substract 4, so that the IP points into the call instruction */
index 4820c52a355ef53a0b853f69f25f30726127700a..74c8a0f37a3dfc9fb29813eb87029ad251f7421f 100644 (file)
@@ -478,9 +478,9 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls,
 
                memcpy(&regs, &ctx->uc_mcontext.gregs, sizeof(regs));
                mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start,
-                               (guint8 *) ji->code_start + ji->code_size,
-                               ip, regs, 16, save_locations, 
-                               MONO_MAX_IREGS, &cfa);
+                                                  (guint8 *) ji->code_start + ji->code_size,
+                                                  ip, NULL, regs, 16, save_locations,
+                                                  MONO_MAX_IREGS, &cfa);
                memcpy (&new_ctx->uc_mcontext.gregs, &regs, sizeof(regs));
                MONO_CONTEXT_SET_IP(new_ctx, regs[14] - 2);
                MONO_CONTEXT_SET_BP(new_ctx, cfa);
index c2ff867dfd6bea563b8d8c90d7210794db18f928..d335d7ea5c6c3fa92c97b57f891722683410c673 100644 (file)
@@ -816,7 +816,7 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls,
 
                mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, 
                                                   (guint8*)ji->code_start + ji->code_size,
-                                                  ip, regs, MONO_MAX_IREGS + 1,
+                                                  ip, NULL, regs, MONO_MAX_IREGS + 1,
                                                   save_locations, MONO_MAX_IREGS, &cfa);
 
                new_ctx->eax = regs [X86_EAX];
index c72e39c868db2c5cc78baa644de85d522155d3c9..fbebe0ce71c451fe367554c2fef8f3a550aea231 100644 (file)
@@ -7151,7 +7151,7 @@ mono_arch_emit_epilog (MonoCompile *cfg)
        CallInfo *cinfo;
        gint32 lmf_offset = cfg->lmf_var ? ((MonoInst*)cfg->lmf_var)->inst_offset : -1;
        gint32 save_area_offset = cfg->arch.reg_save_area_offset;
-       
+
        max_epilog_size = get_max_epilog_size (cfg);
 
        while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
@@ -7162,6 +7162,14 @@ mono_arch_emit_epilog (MonoCompile *cfg)
 
        code = cfg->native_code + cfg->code_len;
 
+       cfg->has_unwind_info_for_epilog = TRUE;
+
+       /* Mark the start of the epilog */
+       mono_emit_unwind_op_mark_loc (cfg, code, 0);
+
+       /* Save the uwind state which is needed by the out-of-line code */
+       mono_emit_unwind_op_remember_state (cfg, code);
+
        if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
                code = mono_arch_instrument_epilog (cfg, mono_trace_leave_method, code, TRUE);
 
@@ -7199,8 +7207,11 @@ mono_arch_emit_epilog (MonoCompile *cfg)
        for (i = 0; i < AMD64_NREG; ++i) {
                if (AMD64_IS_CALLEE_SAVED_REG (i) && (cfg->arch.saved_iregs & (1 << i))) {
                        /* Restore only used_int_regs, not arch.saved_iregs */
-                       if (cfg->used_int_regs & (1 << i))
+                       if (cfg->used_int_regs & (1 << i)) {
                                amd64_mov_reg_membase (code, i, cfg->frame_reg, save_area_offset, 8);
+                               mono_emit_unwind_op_same_value (cfg, code, i);
+                               async_exc_point (code);
+                       }
                        save_area_offset += 8;
                }
        }
@@ -7231,14 +7242,20 @@ mono_arch_emit_epilog (MonoCompile *cfg)
        }
 
        if (cfg->arch.omit_fp) {
-               if (cfg->arch.stack_alloc_size)
+               if (cfg->arch.stack_alloc_size) {
                        amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, cfg->arch.stack_alloc_size);
+               }
        } else {
                amd64_leave (code);
+               mono_emit_unwind_op_same_value (cfg, code, AMD64_RBP);
        }
+       mono_emit_unwind_op_def_cfa (cfg, code, AMD64_RSP, 8);
        async_exc_point (code);
        amd64_ret (code);
 
+       /* Restore the unwind state to be the same as before the epilog */
+       mono_emit_unwind_op_restore_state (cfg, code);
+
        cfg->code_len = code - cfg->native_code;
 
        g_assert (cfg->code_len < cfg->code_size);
index 833704485b39b37e0295977b63ecdf320db61a7b..706577b5024dec77a2384c32505fb4e3290860d4 100644 (file)
@@ -2747,5 +2747,6 @@ mono_jinfo_get_unwind_info (MonoJitInfo *ji, guint32 *unwind_info_len)
        if (ji->from_aot)
                return mono_aot_get_unwind_info (ji, unwind_info_len);
        else
-               return mono_get_cached_unwind_info (ji->used_regs, unwind_info_len);
+               /* The upper 16 bits of ji->used_regs might contain the epilog offset */
+               return mono_get_cached_unwind_info (ji->used_regs & 0xffff, unwind_info_len);
 }
index 6b6ba7ad2f94fb6989412733dfd1a098e2fd5e82..a2c9f49805dbf617c500af6139e3cdf5c9bdc99e 100644 (file)
 #define DW_CFA_lo_user           0x1c
 #define DW_CFA_hi_user           0x3f
 
+/*
+ * Mono extension, advance loc to a location stored outside the unwind info.
+ * This is required to make the unwind descriptors sharable, since otherwise each one would contain
+ * an advance_loc with a different offset just before the unwind ops for the epilog.
+ */
+#define DW_CFA_mono_advance_loc DW_CFA_lo_user
+
 /* Represents one unwind instruction */
 typedef struct {
        guint8 op; /* One of DW_CFA_... */
@@ -84,6 +91,16 @@ typedef struct {
 #define mono_emit_unwind_op_same_value(cfg,ip,reg) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_same_value, (reg), 0)
 /* Reg is saved at cfa+offset */
 #define mono_emit_unwind_op_offset(cfg,ip,reg,offset) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_offset, (reg), (offset))
+/* Save the unwind state into an implicit stack */
+#define mono_emit_unwind_op_remember_state(cfg,ip) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_remember_state, 0, 0)
+/* Restore the unwind state from the state stack */
+#define mono_emit_unwind_op_restore_state(cfg,ip) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_restore_state, 0, 0)
+/*
+ * Mark the current location as a location stored outside the unwind info, which will be passed
+ * explicitly to mono_unwind_frame () in the MARK_LOCATIONS argument. This allows the unwind info
+ * to be shared among multiple methods.
+ */
+#define mono_emit_unwind_op_mark_loc(cfg,ip,n) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_mono_advance_loc, 0, (n))
 
 /* Similar macros usable when a cfg is not available, like for trampolines */
 #define mono_add_unwind_op_def_cfa(op_list,code,buf,reg,offset) do { (op_list) = g_slist_append ((op_list), mono_create_unwind_op ((code) - (buf), DW_CFA_def_cfa, (reg), (offset))); } while (0)
@@ -127,7 +144,8 @@ mono_unwind_ops_encode (GSList *unwind_ops, guint32 *out_len) MONO_INTERNAL;
 
 void
 mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len, 
-                                  guint8 *start_ip, guint8 *end_ip, guint8 *ip, mgreg_t *regs, int nregs,
+                                  guint8 *start_ip, guint8 *end_ip, guint8 *ip, guint8 **mark_locations,
+                                  mgreg_t *regs, int nregs,
                                   mgreg_t **save_locations, int save_locations_len,
                                   guint8 **out_cfa) MONO_INTERNAL;
 
index 0610353aaa266721813a0ea9c31507c1ac4c6528..cae9683bdcb6de6bafd2074d1ea58be56dc58b50 100644 (file)
@@ -4638,8 +4638,21 @@ create_jit_info (MonoCompile *cfg, MonoMethod *method_to_compile)
        } else if (cfg->unwind_ops) {
                guint32 info_len;
                guint8 *unwind_info = mono_unwind_ops_encode (cfg->unwind_ops, &info_len);
+               guint32 unwind_desc;
 
-               jinfo->used_regs = mono_cache_unwind_info (unwind_info, info_len);
+               unwind_desc = mono_cache_unwind_info (unwind_info, info_len);
+
+               if (cfg->has_unwind_info_for_epilog) {
+                       /*
+                        * The lower 16 bits identify the unwind descriptor, the upper 16 bits contain the offset of
+                        * the start of the epilog from the end of the method.
+                        */
+                       g_assert (unwind_desc < 0xffff);
+                       g_assert (cfg->code_size - cfg->epilog_begin < 0xffff);
+                       jinfo->used_regs = ((cfg->code_size - cfg->epilog_begin) << 16) | unwind_desc;
+               } else {
+                       jinfo->used_regs = unwind_desc;
+               }
                g_free (unwind_info);
        }
 
index 97c715d7033eb7796477e9f6eb03e06a407ba7ee..707061026416b9672d2d36544927aa44c9c817bf 100644 (file)
@@ -1499,6 +1499,7 @@ typedef struct {
        guint            has_atomic_exchange_i4 : 1;
        guint            has_atomic_cas_i4 : 1;
        guint            check_pinvoke_callconv : 1;
+       guint            has_unwind_info_for_epilog : 1;
        gpointer         debug_info;
        guint32          lmf_offset;
     guint16          *intvars;
index 8a7d46eae49127b91fc61b1926694b968ac027b9..d9f4778c0e769c95e21f03c67ecd54e901020ac2 100644 (file)
@@ -307,10 +307,23 @@ mono_print_unwind_info (guint8 *unwind_info, int unwind_info_len)
                                offset = decode_sleb128 (p, &p) * DWARF_DATA_ALIGN;
                                printf ("CFA: [%x] offset_extended_sf: %s at cfa-0x%x\n", pos, mono_arch_regname (mono_dwarf_reg_to_hw_reg (reg)), -offset);
                                break;
+                       case DW_CFA_same_value:
+                               reg = decode_uleb128 (p, &p);
+                               printf ("CFA: [%x] same_value: %s\n", pos, mono_arch_regname (mono_dwarf_reg_to_hw_reg (reg)));
+                               break;
                        case DW_CFA_advance_loc4:
                                pos += read32 (p);
                                p += 4;
                                break;
+                       case DW_CFA_remember_state:
+                               printf ("CFA: [%x] remember_state\n", pos);
+                               break;
+                       case DW_CFA_restore_state:
+                               printf ("CFA: [%x] restore_state\n", pos);
+                               break;
+                       case DW_CFA_mono_advance_loc:
+                               printf ("CFA: [%x] mono_advance_loc\n", pos);
+                               break;
                        default:
                                g_assert_not_reached ();
                        }
@@ -348,9 +361,31 @@ mono_unwind_ops_encode (GSList *unwind_ops, guint32 *out_len)
                /* Convert the register from the hw encoding to the dwarf encoding */
                reg = mono_hw_reg_to_dwarf_reg (op->reg);
 
+               if (op->op == DW_CFA_mono_advance_loc) {
+                       /* This advances loc to its location */
+                       loc = op->when;
+               }
+
                /* Emit an advance_loc if neccesary */
                while (op->when > loc) {
-                       if (op->when - loc < 32) {
+                       if (op->when - loc > 65536) {
+                               *p ++ = DW_CFA_advance_loc4;
+                               *(guint32*)p = (guint32)(op->when - loc);
+                               g_assert (read32 (p) == (guint32)(op->when - loc));
+                               p += 4;
+                               loc = op->when;
+                       } else if (op->when - loc > 256) {
+                               *p ++ = DW_CFA_advance_loc2;
+                               *(guint16*)p = (guint16)(op->when - loc);
+                               g_assert (read16 (p) == (guint32)(op->when - loc));
+                               p += 2;
+                               loc = op->when;
+                       } else if (op->when - loc >= 32) {
+                               *p ++ = DW_CFA_advance_loc1;
+                               *(guint8*)p = (guint8)(op->when - loc);
+                               p += 1;
+                               loc = op->when;
+                       } else if (op->when - loc < 32) {
                                *p ++ = DW_CFA_advance_loc | (op->when - loc);
                                loc = op->when;
                        } else {
@@ -373,6 +408,10 @@ mono_unwind_ops_encode (GSList *unwind_ops, guint32 *out_len)
                        *p ++ = op->op;
                        encode_uleb128 (reg, p, &p);
                        break;
+               case DW_CFA_same_value:
+                       *p ++ = op->op;
+                       encode_uleb128 (reg, p, &p);
+                       break;
                case DW_CFA_offset:
                        if (reg > 63) {
                                *p ++ = DW_CFA_offset_extended_sf;
@@ -383,6 +422,15 @@ mono_unwind_ops_encode (GSList *unwind_ops, guint32 *out_len)
                                encode_uleb128 (op->val / DWARF_DATA_ALIGN, p, &p);
                        }
                        break;
+               case DW_CFA_remember_state:
+               case DW_CFA_restore_state:
+                       *p ++ = op->op;
+                       break;
+               case DW_CFA_mono_advance_loc:
+                       /* Only one location is supported */
+                       g_assert (op->val == 0);
+                       *p ++ = op->op;
+                       break;
                default:
                        g_assert_not_reached ();
                        break;
@@ -416,6 +464,12 @@ print_dwarf_state (int cfa_reg, int cfa_offset, int ip, int nregs, Loc *location
        printf ("\n");
 }
 
+typedef struct {
+       Loc locations [NUM_REGS];
+       guint8 reg_saved [NUM_REGS];
+       int cfa_reg, cfa_offset;
+} UnwindState;
+
 /*
  * Given the state of the current frame as stored in REGS, execute the unwind 
  * operations in unwind_info until the location counter reaches POS. The result is 
@@ -423,11 +477,13 @@ print_dwarf_state (int cfa_reg, int cfa_offset, int ip, int nregs, Loc *location
  * If SAVE_LOCATIONS is non-NULL, it should point to an array of size SAVE_LOCATIONS_LEN.
  * On return, the nth entry will point to the address of the stack slot where register
  * N was saved, or NULL, if it was not saved by this frame.
+ * MARK_LOCATIONS should contain the locations marked by mono_emit_unwind_op_mark_loc (), if any.
  * This function is signal safe.
  */
 void
 mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len, 
-                                  guint8 *start_ip, guint8 *end_ip, guint8 *ip, mgreg_t *regs, int nregs,
+                                  guint8 *start_ip, guint8 *end_ip, guint8 *ip, guint8 **mark_locations,
+                                  mgreg_t *regs, int nregs,
                                   mgreg_t **save_locations, int save_locations_len,
                                   guint8 **out_cfa)
 {
@@ -436,6 +492,8 @@ mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len,
        int i, pos, reg, cfa_reg, cfa_offset, offset;
        guint8 *p;
        guint8 *cfa_val;
+       UnwindState state_stack [1];
+       int state_stack_pos;
 
        memset (reg_saved, 0, sizeof (reg_saved));
 
@@ -443,6 +501,7 @@ mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len,
        pos = 0;
        cfa_reg = -1;
        cfa_offset = -1;
+       state_stack_pos = 0;
        while (pos <= ip - start_ip && p < unwind_info + unwind_info_len) {
                int op = *p & 0xc0;
 
@@ -489,10 +548,42 @@ mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len,
                                locations [reg].loc_type = LOC_OFFSET;
                                locations [reg].offset = offset * DWARF_DATA_ALIGN;
                                break;
+                       case DW_CFA_same_value:
+                               reg = decode_uleb128 (p, &p);
+                               locations [reg].loc_type = LOC_SAME;
+                               break;
+                       case DW_CFA_advance_loc1:
+                               pos += *p;
+                               p += 1;
+                               break;
+                       case DW_CFA_advance_loc2:
+                               pos += read16 (p);
+                               p += 2;
+                               break;
                        case DW_CFA_advance_loc4:
                                pos += read32 (p);
                                p += 4;
                                break;
+                       case DW_CFA_remember_state:
+                               g_assert (state_stack_pos == 0);
+                               memcpy (&state_stack [0].locations, &locations, sizeof (locations));
+                               memcpy (&state_stack [0].reg_saved, &reg_saved, sizeof (reg_saved));
+                               state_stack [0].cfa_reg = cfa_reg;
+                               state_stack [0].cfa_offset = cfa_offset;
+                               state_stack_pos ++;
+                               break;
+                       case DW_CFA_restore_state:
+                               g_assert (state_stack_pos == 1);
+                               state_stack_pos --;
+                               memcpy (&locations, &state_stack [0].locations, sizeof (locations));
+                               memcpy (&reg_saved, &state_stack [0].reg_saved, sizeof (reg_saved));
+                               cfa_reg = state_stack [0].cfa_reg;
+                               cfa_offset = state_stack [0].cfa_offset;
+                               break;
+                       case DW_CFA_mono_advance_loc:
+                               g_assert (mark_locations [0]);
+                               pos = mark_locations [0] - start_ip;
+                               break;
                        default:
                                g_assert_not_reached ();
                        }