Merge remote branch 'upstream/master'
[mono.git] / mono / mini / exceptions-mips.c
1 /*
2  * exceptions-mips.c: exception support for MIPS
3  *
4  * Authors:
5  *   Mark Mason (mason@broadcom.com)
6  *
7  * Based on exceptions-ppc.c by:
8  *   Dietmar Maurer (dietmar@ximian.com)
9  *   Paolo Molaro (lupus@ximian.com)
10  *
11  * (C) 2006 Broadcom
12  * (C) 2001 Ximian, Inc.
13  */
14
15 #include <config.h>
16 #include <glib.h>
17 #include <signal.h>
18 #include <string.h>
19 #include <ucontext.h>
20
21 #include <mono/arch/mips/mips-codegen.h>
22 #include <mono/metadata/appdomain.h>
23 #include <mono/metadata/tabledefs.h>
24 #include <mono/metadata/threads.h>
25 #include <mono/metadata/debug-helpers.h>
26 #include <mono/metadata/exception.h>
27 #include <mono/metadata/mono-debug.h>
28
29 #include "mini.h"
30 #include "mini-mips.h"
31
32 #define GENERIC_EXCEPTION_SIZE 256
33
34 /* XXX */
35 #if 1
36 #define restore_regs_from_context(ctx_reg,ip_reg,tmp_reg) do {  \
37         } while (0)
38 #else
39 #define restore_regs_from_context(ctx_reg,pc,tmp_reg) do {      \
40                 int reg;        \
41                 ppc_lwz (code, pc, G_STRUCT_OFFSET (MonoContext, sc_pc), ctx_reg);      \
42                 ppc_lmw (code, ppc_r13, ctx_reg, G_STRUCT_OFFSET (MonoContext, sc_regs));       \
43                 for (reg = 0; reg < MONO_SAVED_FREGS; ++reg) {  \
44                         ppc_lfd (code, (14 + reg), G_STRUCT_OFFSET(MonoLMF, sc_fpregs) + reg * sizeof (gdouble), ctx_reg);      \
45                 }       \
46         } while (0)
47 #endif
48
49 /* nothing to do */
50 #define setup_context(ctx) do { \
51                 memset ((ctx), 0, sizeof(*(ctx)));      \
52         } while (0);
53
54 /*
55  * mono_arch_get_restore_context:
56  *
57  * Returns a pointer to a method which restores a previously saved MonoContext.
58  * The first argument in a0 is the pointer to the MonoContext.
59  */
60 gpointer
61 mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot)
62 {
63         int i;
64         guint8 *code;
65         static guint8 start [128];
66         static int inited = 0;
67         guint32 iregs_to_restore;
68
69         g_assert (!aot);
70         if (info)
71                 *info = NULL;
72
73         if (inited)
74                 return start;
75         inited = 1;
76         code = start;
77
78         iregs_to_restore = (MONO_ARCH_CALLEE_SAVED_REGS \
79                             | (1 << mips_sp) | (1 << mips_ra));
80         for (i = 0; i < MONO_SAVED_GREGS; ++i) {
81                 if (iregs_to_restore & (1 << i)) {
82                         MIPS_LW (code, i, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[i]));
83                 }
84         }
85
86         /* Get the address to return to */
87         mips_lw (code, mips_t9, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_pc));
88
89         /* jump to the saved IP */
90         mips_jr (code, mips_t9);
91         mips_nop (code);
92
93         /* never reached */
94         mips_break (code, 0xff);
95
96         g_assert ((code - start) < sizeof(start));
97         mono_arch_flush_icache (start, code - start);
98         return start;
99 }
100
101 /*
102  * mono_arch_get_call_filter:
103  *
104  * Returns a pointer to a method which calls an exception filter. We
105  * also use this function to call finally handlers (we pass NULL as 
106  * @exc object in this case).
107  *
108  * This function is invoked as
109  *      call_handler (MonoContext *ctx, handler)
110  *
111  * Where 'handler' is a function to be invoked as:
112  *      handler (void)
113  */
114 gpointer
115 mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
116 {
117         static guint8 start [320];
118         static int inited = 0;
119         guint8 *code;
120         int alloc_size;
121         int offset;
122
123         g_assert (!aot);
124         if (info)
125                 *info = NULL;
126
127         if (inited)
128                 return start;
129
130         inited = 1;
131         code = start;
132
133         alloc_size = 64;
134         g_assert ((alloc_size & (MIPS_STACK_ALIGNMENT-1)) == 0);
135
136         mips_addiu (code, mips_sp, mips_sp, -alloc_size);
137         mips_sw (code, mips_ra, mips_sp, alloc_size + MIPS_RET_ADDR_OFFSET);
138
139         /* Save global registers on stack (s0 - s7) */
140         offset = 16;
141         MIPS_SW (code, mips_s0, mips_sp, offset); offset += IREG_SIZE;
142         MIPS_SW (code, mips_s1, mips_sp, offset); offset += IREG_SIZE;
143         MIPS_SW (code, mips_s2, mips_sp, offset); offset += IREG_SIZE;
144         MIPS_SW (code, mips_s3, mips_sp, offset); offset += IREG_SIZE;
145         MIPS_SW (code, mips_s4, mips_sp, offset); offset += IREG_SIZE;
146         MIPS_SW (code, mips_s5, mips_sp, offset); offset += IREG_SIZE;
147         MIPS_SW (code, mips_s6, mips_sp, offset); offset += IREG_SIZE;
148         MIPS_SW (code, mips_s7, mips_sp, offset); offset += IREG_SIZE;
149         MIPS_SW (code, mips_fp, mips_sp, offset); offset += IREG_SIZE;
150
151         /* Restore global registers from MonoContext, including the frame pointer */
152         MIPS_LW (code, mips_s0, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s0]));
153         MIPS_LW (code, mips_s1, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s1]));
154         MIPS_LW (code, mips_s2, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s2]));
155         MIPS_LW (code, mips_s3, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s3]));
156         MIPS_LW (code, mips_s4, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s4]));
157         MIPS_LW (code, mips_s5, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s5]));
158         MIPS_LW (code, mips_s6, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s6]));
159         MIPS_LW (code, mips_s7, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s7]));
160         MIPS_LW (code, mips_fp, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_fp]));
161
162         /* a1 is the handler to call */
163         mips_move (code, mips_t9, mips_a1);
164
165         /* jump to the saved IP */
166         mips_jalr (code, mips_t9, mips_ra);
167         mips_nop (code);
168
169         /* restore all regs from the stack */
170         offset = 16;
171         MIPS_LW (code, mips_s0, mips_sp, offset); offset += IREG_SIZE;
172         MIPS_LW (code, mips_s1, mips_sp, offset); offset += IREG_SIZE;
173         MIPS_LW (code, mips_s2, mips_sp, offset); offset += IREG_SIZE;
174         MIPS_LW (code, mips_s3, mips_sp, offset); offset += IREG_SIZE;
175         MIPS_LW (code, mips_s4, mips_sp, offset); offset += IREG_SIZE;
176         MIPS_LW (code, mips_s5, mips_sp, offset); offset += IREG_SIZE;
177         MIPS_LW (code, mips_s6, mips_sp, offset); offset += IREG_SIZE;
178         MIPS_LW (code, mips_s7, mips_sp, offset); offset += IREG_SIZE;
179         MIPS_LW (code, mips_fp, mips_sp, offset); offset += IREG_SIZE;
180
181         /* epilog */
182         mips_lw (code, mips_ra, mips_sp, alloc_size + MIPS_RET_ADDR_OFFSET);
183         mips_addiu (code, mips_sp, mips_sp, alloc_size);
184         mips_jr (code, mips_ra);
185         mips_nop (code);
186
187         g_assert ((code - start) < sizeof(start));
188         mono_arch_flush_icache (start, code - start);
189         return start;
190 }
191
192 static void
193 throw_exception (MonoObject *exc, unsigned long eip, unsigned long esp, gboolean rethrow)
194 {
195         static void (*restore_context) (MonoContext *);
196         MonoContext ctx;
197
198 #ifdef DEBUG_EXCEPTIONS
199         g_print ("throw_exception: exc=%p eip=%p esp=%p rethrow=%d\n",
200                  exc, (void *)eip, (void *) esp, rethrow);
201 #endif
202
203         if (!restore_context)
204                 restore_context = mono_get_restore_context ();
205
206         /* adjust eip so that it point into the call instruction */
207         eip -= 8;
208
209         setup_context (&ctx);
210
211         /*g_print  ("stack in throw: %p\n", esp);*/
212         memcpy (&ctx.sc_regs, (void *)(esp + MIPS_STACK_PARAM_OFFSET),
213                 sizeof (gulong) * MONO_SAVED_GREGS);
214         memset (&ctx.sc_fpregs, 0, sizeof (mips_freg) * MONO_SAVED_FREGS);
215         MONO_CONTEXT_SET_IP (&ctx, eip);
216
217         if (mono_object_isinst (exc, mono_defaults.exception_class)) {
218                 MonoException *mono_ex = (MonoException*)exc;
219                 if (!rethrow)
220                         mono_ex->stack_trace = NULL;
221         }
222         mono_handle_exception (&ctx, exc, (void *)eip, FALSE);
223 #ifdef DEBUG_EXCEPTIONS
224         g_print ("throw_exception: restore to pc=%p sp=%p fp=%p ctx=%p\n",
225                  (void *) ctx.sc_pc, (void *) ctx.sc_regs[mips_sp],
226                  (void *) ctx.sc_regs[mips_fp], &ctx);
227 #endif
228         restore_context (&ctx);
229
230         g_assert_not_reached ();
231 }
232
233 /**
234  * arch_get_throw_exception_generic:
235  *
236  * Returns a function pointer which can be used to raise 
237  * exceptions. The returned function has the following 
238  * signature: void (*func) (MonoException *exc); or
239  * void (*func) (char *exc_name);
240  *
241  */
242 static gpointer 
243 mono_arch_get_throw_exception_generic (guint8 *start, int size, int corlib, gboolean rethrow)
244 {
245         guint8 *code;
246         int alloc_size, pos, i;
247
248         code = start;
249
250         //g_print ("mono_arch_get_throw_exception_generic: code=%p\n", code);
251
252         pos = 0;
253         /* XXX - save all the FP regs on the stack ? */
254
255         pos += MONO_MAX_IREGS * sizeof(guint32);
256
257         alloc_size = MIPS_MINIMAL_STACK_SIZE + pos + 64;
258         // align to MIPS_STACK_ALIGNMENT bytes
259         alloc_size += MIPS_STACK_ALIGNMENT - 1;
260         alloc_size &= ~(MIPS_STACK_ALIGNMENT - 1);
261
262         g_assert ((alloc_size & (MIPS_STACK_ALIGNMENT-1)) == 0);
263         mips_addiu (code, mips_sp, mips_sp, -alloc_size);
264         mips_sw (code, mips_ra, mips_sp, alloc_size + MIPS_RET_ADDR_OFFSET);
265
266         /* Save all the regs on the stack */
267         for (i = 0; i < MONO_MAX_IREGS; i++) {
268                 if (i != mips_sp)
269                         MIPS_SW (code, i, mips_sp, i*IREG_SIZE + MIPS_STACK_PARAM_OFFSET);
270                 else {
271                         mips_addiu (code, mips_at, mips_sp, alloc_size);
272                         MIPS_SW (code, mips_at, mips_sp, i*IREG_SIZE + MIPS_STACK_PARAM_OFFSET);
273                 }
274         }
275
276         if (corlib) {
277                 mips_move (code, mips_a1, mips_a0);
278                 mips_load (code, mips_a0, mono_defaults.corlib);
279                 mips_load (code, mips_t9, mono_exception_from_token);
280                 mips_jalr (code, mips_t9, mips_ra);
281                 mips_nop (code);
282                 mips_move (code, mips_a0, mips_v0);
283         }
284         /* call throw_exception (exc, ip, sp, rethrow) */
285
286         /* exc is already in place in a0 */
287
288         /* pointer to ip */
289         if (corlib)
290                 mips_lw (code, mips_a1, mips_sp, alloc_size + MIPS_RET_ADDR_OFFSET);
291         else
292                 mips_move (code, mips_a1, mips_ra);
293
294         /* current sp & rethrow */
295         mips_move (code, mips_a2, mips_sp);
296         mips_addiu (code, mips_a3, mips_zero, rethrow);
297
298         mips_load (code, mips_t9, throw_exception);
299         mips_jr (code, mips_t9);
300         mips_nop (code);
301         /* we should never reach this breakpoint */
302         mips_break (code, 0xfe);
303
304         g_assert ((code - start) < size);
305         mono_arch_flush_icache (start, code - start);
306         return start;
307 }
308
309 /**
310  * mono_arch_get_rethrow_exception:
311  *
312  * Returns a function pointer which can be used to rethrow 
313  * exceptions. The returned function has the following 
314  * signature: void (*func) (MonoException *exc); 
315  *
316  */
317 gpointer
318 mono_arch_get_rethrow_exception (MonoTrampInfo **info, gboolean aot)
319 {
320         static guint8 start [GENERIC_EXCEPTION_SIZE];
321         static int inited = 0;
322
323         g_assert (!aot);
324         if (info)
325                 *info = NULL;
326
327         if (inited)
328                 return start;
329         mono_arch_get_throw_exception_generic (start, sizeof (start), FALSE, TRUE);
330         inited = 1;
331         return start;
332 }
333
334 /**
335  * arch_get_throw_exception:
336  *
337  * Returns a function pointer which can be used to raise 
338  * exceptions. The returned function has the following 
339  * signature: void (*func) (MonoException *exc); 
340  * For example to raise an arithmetic exception you can use:
341  *
342  * x86_push_imm (code, mono_get_exception_arithmetic ()); 
343  * x86_call_code (code, arch_get_throw_exception ()); 
344  *
345  */
346 gpointer
347 mono_arch_get_throw_exception (MonoTrampInfo **info, gboolean aot)
348 {
349         static guint8 start [GENERIC_EXCEPTION_SIZE];
350         static int inited = 0;
351
352         g_assert (!aot);
353         if (info)
354                 *info = NULL;
355
356         if (inited)
357                 return start;
358         mono_arch_get_throw_exception_generic (start, sizeof (start), FALSE, FALSE);
359         inited = 1;
360         return start;
361 }
362
363 gpointer 
364 mono_arch_get_throw_exception_by_name (void)
365 {
366         guint8 *start, *code;
367         int size = 64;
368
369         /* Not used on MIPS */  
370         start = code = mono_global_codeman_reserve (size);
371         mips_break (code, 0xfd);
372         mono_arch_flush_icache (start, code - start);
373         return start;
374 }
375
376 /**
377  * mono_arch_get_throw_corlib_exception:
378  *
379  * Returns a function pointer which can be used to raise 
380  * corlib exceptions. The returned function has the following 
381  * signature: void (*func) (guint32 ex_token, guint32 offset); 
382  * On MIPS, the offset argument is missing.
383  */
384 gpointer
385 mono_arch_get_throw_corlib_exception (MonoTrampInfo **info, gboolean aot)
386 {
387         static guint8 start [GENERIC_EXCEPTION_SIZE];
388         static int inited = 0;
389
390         g_assert (!aot);
391         if (info)
392                 *info = NULL;
393
394         if (inited)
395                 return start;
396         mono_arch_get_throw_exception_generic (start, sizeof (start), TRUE, FALSE);
397         inited = 1;
398         return start;
399 }       
400
401 static MonoArray *
402 glist_to_array (GList *list, MonoClass *eclass) 
403 {
404         MonoDomain *domain = mono_domain_get ();
405         MonoArray *res;
406         int len, i;
407
408         if (!list)
409                 return NULL;
410
411         len = g_list_length (list);
412         res = mono_array_new (domain, eclass, len);
413
414         for (i = 0; list; list = list->next, i++)
415                 mono_array_set (res, gpointer, i, list->data);
416
417         return res;
418 }
419
420 /*
421  * mono_arch_find_jit_info:
422  *
423  * This function is used to gather information from @ctx, and store it in @frame_info.
424  * It unwinds one stack frame, and stores the resulting context into @new_ctx. @lmf
425  * is modified if needed.
426  * Returns TRUE on success, FALSE otherwise.
427  */
428 gboolean
429 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, 
430                                                          MonoJitInfo *ji, MonoContext *ctx, 
431                                                          MonoContext *new_ctx, MonoLMF **lmf, 
432                                                          mgreg_t **save_locations,
433                                                          StackFrameInfo *frame)
434 {
435         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
436         gpointer fp = MONO_CONTEXT_GET_BP (ctx);
437         guint32 sp;
438
439         memset (frame, 0, sizeof (StackFrameInfo));
440         frame->ji = ji;
441         frame->managed = FALSE;
442
443         *new_ctx = *ctx;
444
445         if (ji != NULL) {
446                 int i;
447                 gint32 address;
448                 int offset = 0;
449
450                 frame->type = FRAME_TYPE_MANAGED;
451
452                 if (!ji->method->wrapper_type || ji->method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD)
453                         frame->managed = TRUE;
454
455                 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
456                         /* remove any unused lmf */
457                         *lmf = (*lmf)->previous_lmf;
458                 }
459
460                 address = (char *)ip - (char *)ji->code_start;
461
462                 /* My stack frame */
463                 fp = MONO_CONTEXT_GET_BP (ctx);
464
465                 /* Compute the previous stack frame */
466                 sp = (guint32)(fp) - (short)(*(guint32 *)(ji->code_start));
467
468                 /* Sanity check the frame */
469                 if (!sp || (sp == 0xffffffff)
470                     || (sp & 0x07) || (sp < 64*1024)) {
471 #ifdef DEBUG_EXCEPTIONS
472                         g_print ("mono_arch_find_jit_info: bad stack sp=%p\n", (void *) sp);
473 #endif
474                         return FALSE;
475                 }
476
477                 if (ji->method->save_lmf && 0) {
478                         /* only enable this when prologue stops emitting
479                          * normal save of s-regs when save_lmf is true.
480                          * Will have to sync with prologue code at that point.
481                          */
482                         memcpy (&new_ctx->sc_fpregs,
483                                 (char*)sp - sizeof (float) * MONO_SAVED_FREGS,
484                                 sizeof (float) * MONO_SAVED_FREGS);
485                         memcpy (&new_ctx->sc_regs,
486                                 (char*)sp - sizeof (float) * MONO_SAVED_FREGS - sizeof (gulong) * MONO_SAVED_GREGS,
487                                 sizeof (gulong) * MONO_SAVED_GREGS);
488                 } else if (ji->used_regs) {
489                         guint32 *insn;
490                         guint32 mask = ji->used_regs;
491
492                         /* these all happen before adjustment of fp */
493                         /* Look for sw ??, ????(sp) */
494                         insn = ((guint32 *)ji->code_start) + 1;
495                         while (!*insn || ((*insn & 0xffe00000) == 0xafa00000) || ((*insn & 0xffe00000) == 0xffa00000)) {
496                                 int reg = (*insn >> 16) & 0x1f;
497                                 guint32 addr = (((guint32)fp) + (short)(*insn & 0x0000ffff));
498
499                                 mask &= ~(1 << reg);
500                                 if ((*insn & 0xffe00000) == 0xafa00000)
501                                         new_ctx->sc_regs [reg] = *(guint32 *)addr;
502                                 else
503                                         new_ctx->sc_regs [reg] = *(guint64 *)addr;
504                                 insn++;
505                         }
506                         MONO_CONTEXT_SET_SP (new_ctx, sp);
507                         MONO_CONTEXT_SET_BP (new_ctx, sp);
508                         /* assert that we found all registers we were supposed to */
509                         g_assert (!mask);
510                 }
511                 /* we substract 8, so that the IP points into the call instruction */
512                 MONO_CONTEXT_SET_IP (new_ctx, new_ctx->sc_regs[mips_ra] - 8);
513
514                 /* Sanity check -- we should have made progress here */
515                 g_assert (new_ctx->sc_pc != ctx->sc_pc);
516                 return TRUE;
517         } else if (*lmf) {
518                 if (!(*lmf)->method) {
519 #ifdef DEBUG_EXCEPTIONS
520                         g_print ("mono_arch_find_jit_info: bad lmf @ %p\n", (void *) *lmf);
521 #endif
522                         return FALSE;
523                 }
524                 g_assert (((*lmf)->magic == MIPS_LMF_MAGIC1) || ((*lmf)->magic == MIPS_LMF_MAGIC2));
525
526                 ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->eip, NULL);
527                 if (!ji) {
528                         // FIXME: This can happen with multiple appdomains (bug #444383)
529                         return FALSE;
530                 }
531
532                 frame->ji = ji;
533                 frame->type = FRAME_TYPE_MANAGED_TO_NATIVE;
534
535                 memcpy (&new_ctx->sc_regs, (*lmf)->iregs, sizeof (gulong) * MONO_SAVED_GREGS);
536                 memcpy (&new_ctx->sc_fpregs, (*lmf)->fregs, sizeof (float) * MONO_SAVED_FREGS);
537                 MONO_CONTEXT_SET_IP (new_ctx, (*lmf)->eip);
538                 /* ensure that we've made progress */
539                 g_assert (new_ctx->sc_pc != ctx->sc_pc);
540                 *lmf = (*lmf)->previous_lmf;
541
542                 return TRUE;
543         }
544
545         return FALSE;
546 }
547
548 void
549 mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
550 {
551         int i;
552         struct sigcontext *ctx = (struct sigcontext *)sigctx;
553
554         mctx->sc_pc = ctx->sc_pc;
555         for (i = 0; i < 32; ++i) {
556                 mctx->sc_regs[i] = ctx->sc_regs[i];
557                 mctx->sc_fpregs[i] = ctx->sc_fpregs[i];
558         }
559 }
560
561 void
562 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
563 {
564         int i;
565         struct sigcontext *ctx = (struct sigcontext *)sigctx;
566
567         ctx->sc_pc = mctx->sc_pc;
568         for (i = 0; i < 32; ++i) {
569                 ctx->sc_regs[i] = mctx->sc_regs[i];
570                 ctx->sc_fpregs[i] = mctx->sc_fpregs[i];
571         }
572 }       
573
574 gpointer
575 mono_arch_ip_from_context (void *sigctx)
576 {
577         struct sigcontext *ctx = (struct sigcontext *)sigctx;
578         return (gpointer)(guint32)ctx->sc_pc;
579 }
580
581 /*
582  * This is the function called from the signal handler
583  */
584 gboolean
585 mono_arch_handle_exception (void *ctx, gpointer obj, gboolean test_only)
586 {
587         MonoContext mctx;
588         gboolean result;
589         
590         mono_arch_sigctx_to_monoctx (ctx, &mctx);
591 #ifdef DEBUG_EXCEPTIONS
592         g_print ("mono_arch_handle_exception: pc=%p\n", (void *) mctx.sc_pc);
593 #endif
594         mono_handle_exception (&mctx, obj, (gpointer)mctx.sc_pc, test_only);
595         result = TRUE;
596
597 #ifdef DEBUG_EXCEPTIONS
598         g_print ("mono_arch_handle_exception: restore pc=%p\n", (void *)mctx.sc_pc);
599 #endif
600         /* restore the context so that returning from the signal handler
601          * will invoke the catch clause 
602          */
603         mono_arch_monoctx_to_sigctx (&mctx, ctx);
604
605         return result;
606 }
607
608
609 gboolean
610 mono_arch_has_unwind_info (gconstpointer addr)
611 {
612         return FALSE;
613 }
614