blob: 87b58263bdaf5479bba6bcc6070cbc2f0335b20d [file] [log] [blame]
njn5c004e42002-11-18 11:04:50 +00001
2/*--------------------------------------------------------------------*/
3/*--- Code that is shared between MemCheck and AddrCheck. ---*/
njn43c799e2003-04-08 00:08:52 +00004/*--- mac_needs.c ---*/
njn5c004e42002-11-18 11:04:50 +00005/*--------------------------------------------------------------------*/
6
7/*
nethercote137bc552003-11-14 17:47:54 +00008 This file is part of MemCheck, a heavyweight Valgrind tool for
9 detecting memory errors, and AddrCheck, a lightweight Valgrind tool
njn5c004e42002-11-18 11:04:50 +000010 for detecting memory errors.
11
nethercotebb1c9912004-01-04 16:43:23 +000012 Copyright (C) 2000-2004 Julian Seward
njn5c004e42002-11-18 11:04:50 +000013 jseward@acm.org
14
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU General Public License as
17 published by the Free Software Foundation; either version 2 of the
18 License, or (at your option) any later version.
19
20 This program is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 General Public License for more details.
24
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
28 02111-1307, USA.
29
30 The GNU General Public License is contained in the file COPYING.
31*/
32
33
njn43c799e2003-04-08 00:08:52 +000034#include "mac_shared.h"
njn5c004e42002-11-18 11:04:50 +000035
njn47363ab2003-04-21 13:24:40 +000036#include "memcheck.h" /* for VG_USERREQ__* */
37
njn5c004e42002-11-18 11:04:50 +000038/*------------------------------------------------------------*/
39/*--- Defns ---*/
40/*------------------------------------------------------------*/
41
42/* These many bytes below %ESP are considered addressible if we're
43 doing the --workaround-gcc296-bugs hack. */
44#define VG_GCC296_BUG_STACK_SLOP 1024
45
46/*------------------------------------------------------------*/
47/*--- Command line options ---*/
48/*------------------------------------------------------------*/
49
njn43c799e2003-04-08 00:08:52 +000050Bool MAC_(clo_partial_loads_ok) = True;
51Int MAC_(clo_freelist_vol) = 1000000;
52Bool MAC_(clo_leak_check) = False;
53VgRes MAC_(clo_leak_resolution) = Vg_LowRes;
54Bool MAC_(clo_show_reachable) = False;
55Bool MAC_(clo_workaround_gcc296_bugs) = False;
njn5c004e42002-11-18 11:04:50 +000056
njn43c799e2003-04-08 00:08:52 +000057Bool MAC_(process_common_cmd_line_option)(Char* arg)
njn5c004e42002-11-18 11:04:50 +000058{
nethercote27fec902004-06-16 21:26:32 +000059 VG_BOOL_CLO("--leak-check", MAC_(clo_leak_check))
60 else VG_BOOL_CLO("--partial-loads-ok", MAC_(clo_partial_loads_ok))
61 else VG_BOOL_CLO("--show-reachable", MAC_(clo_show_reachable))
62 else VG_BOOL_CLO("--workaround-gcc296-bugs",MAC_(clo_workaround_gcc296_bugs))
63
64 else VG_BNUM_CLO("--freelist-vol", MAC_(clo_freelist_vol), 0, 1000000000)
65
njn43c799e2003-04-08 00:08:52 +000066 else if (VG_CLO_STREQ(arg, "--leak-resolution=low"))
67 MAC_(clo_leak_resolution) = Vg_LowRes;
68 else if (VG_CLO_STREQ(arg, "--leak-resolution=med"))
69 MAC_(clo_leak_resolution) = Vg_MedRes;
70 else if (VG_CLO_STREQ(arg, "--leak-resolution=high"))
71 MAC_(clo_leak_resolution) = Vg_HighRes;
njn5c004e42002-11-18 11:04:50 +000072
73 else
njn3e884182003-04-15 13:03:23 +000074 return VG_(replacement_malloc_process_cmd_line_option)(arg);
njn5c004e42002-11-18 11:04:50 +000075
76 return True;
njn43c799e2003-04-08 00:08:52 +000077}
njn5c004e42002-11-18 11:04:50 +000078
njn3e884182003-04-15 13:03:23 +000079void MAC_(print_common_usage)(void)
njn43c799e2003-04-08 00:08:52 +000080{
njn3e884182003-04-15 13:03:23 +000081 VG_(printf)(
82" --partial-loads-ok=no|yes too hard to explain here; see manual [yes]\n"
83" --freelist-vol=<number> volume of freed blocks queue [1000000]\n"
84" --leak-check=no|yes search for memory leaks at exit? [no]\n"
85" --leak-resolution=low|med|high how much bt merging in leak check [low]\n"
86" --show-reachable=no|yes show reachable blocks in leak check? [no]\n"
87" --workaround-gcc296-bugs=no|yes self explanatory [no]\n"
88 );
89 VG_(replacement_malloc_print_usage)();
njn43c799e2003-04-08 00:08:52 +000090}
91
njn3e884182003-04-15 13:03:23 +000092void MAC_(print_common_debug_usage)(void)
njn43c799e2003-04-08 00:08:52 +000093{
njn3e884182003-04-15 13:03:23 +000094 VG_(replacement_malloc_print_debug_usage)();
njn5c004e42002-11-18 11:04:50 +000095}
96
97/*------------------------------------------------------------*/
98/*--- Comparing and printing errors ---*/
99/*------------------------------------------------------------*/
100
101static __inline__
102void clear_AddrInfo ( AddrInfo* ai )
103{
104 ai->akind = Unknown;
105 ai->blksize = 0;
106 ai->rwoffset = 0;
107 ai->lastchange = NULL;
108 ai->stack_tid = VG_INVALID_THREADID;
109 ai->maybe_gcc = False;
110}
111
njn43c799e2003-04-08 00:08:52 +0000112void MAC_(clear_MAC_Error) ( MAC_Error* err_extra )
njn5c004e42002-11-18 11:04:50 +0000113{
114 err_extra->axskind = ReadAxs;
115 err_extra->size = 0;
116 clear_AddrInfo ( &err_extra->addrinfo );
117 err_extra->isWrite = False;
118}
119
120__attribute__ ((unused))
121static Bool eq_AddrInfo ( VgRes res, AddrInfo* ai1, AddrInfo* ai2 )
122{
123 if (ai1->akind != Undescribed
124 && ai2->akind != Undescribed
125 && ai1->akind != ai2->akind)
126 return False;
127 if (ai1->akind == Freed || ai1->akind == Mallocd) {
128 if (ai1->blksize != ai2->blksize)
129 return False;
130 if (!VG_(eq_ExeContext)(res, ai1->lastchange, ai2->lastchange))
131 return False;
132 }
133 return True;
134}
135
136/* Compare error contexts, to detect duplicates. Note that if they
137 are otherwise the same, the faulting addrs and associated rwoffsets
138 are allowed to be different. */
139
140Bool SK_(eq_SkinError) ( VgRes res, Error* e1, Error* e2 )
141{
njn43c799e2003-04-08 00:08:52 +0000142 MAC_Error* e1_extra = VG_(get_error_extra)(e1);
143 MAC_Error* e2_extra = VG_(get_error_extra)(e2);
njn7cc53a82002-11-19 16:19:32 +0000144
145 /* Guaranteed by calling function */
146 sk_assert(VG_(get_error_kind)(e1) == VG_(get_error_kind)(e2));
njn5c004e42002-11-18 11:04:50 +0000147
148 switch (VG_(get_error_kind)(e1)) {
149 case CoreMemErr: {
150 Char *e1s, *e2s;
151 if (e1_extra->isWrite != e2_extra->isWrite) return False;
njn5c004e42002-11-18 11:04:50 +0000152 e1s = VG_(get_error_string)(e1);
153 e2s = VG_(get_error_string)(e2);
154 if (e1s == e2s) return True;
155 if (0 == VG_(strcmp)(e1s, e2s)) return True;
156 return False;
157 }
158
159 case UserErr:
160 case ParamErr:
161 if (e1_extra->isWrite != e2_extra->isWrite) return False;
162 if (VG_(get_error_kind)(e1) == ParamErr
163 && 0 != VG_(strcmp)(VG_(get_error_string)(e1),
164 VG_(get_error_string)(e2))) return False;
165 return True;
166
167 case FreeErr:
168 case FreeMismatchErr:
169 /* JRS 2002-Aug-26: comparing addrs seems overkill and can
170 cause excessive duplication of errors. Not even AddrErr
171 below does that. So don't compare either the .addr field
172 or the .addrinfo fields. */
173 /* if (e1->addr != e2->addr) return False; */
174 /* if (!eq_AddrInfo(res, &e1_extra->addrinfo, &e2_extra->addrinfo))
175 return False;
176 */
177 return True;
178
179 case AddrErr:
180 /* if (e1_extra->axskind != e2_extra->axskind) return False; */
181 if (e1_extra->size != e2_extra->size) return False;
182 /*
183 if (!eq_AddrInfo(res, &e1_extra->addrinfo, &e2_extra->addrinfo))
184 return False;
185 */
186 return True;
187
188 case ValueErr:
189 if (e1_extra->size != e2_extra->size) return False;
190 return True;
191
njn34419c12003-05-02 17:24:29 +0000192 case OverlapErr:
193 return True;
194
njn43c799e2003-04-08 00:08:52 +0000195 case LeakErr:
196 VG_(skin_panic)("Shouldn't get LeakErr in SK_(eq_SkinError),\n"
197 "since it's handled with VG_(unique_error)()!");
198
rjwalshbc0bb832004-06-19 18:12:36 +0000199 case IllegalMempoolErr:
200 return True;
201
njn5c004e42002-11-18 11:04:50 +0000202 default:
203 VG_(printf)("Error:\n unknown error code %d\n",
204 VG_(get_error_kind)(e1));
205 VG_(skin_panic)("unknown error code in SK_(eq_SkinError)");
206 }
207}
208
njn43c799e2003-04-08 00:08:52 +0000209void MAC_(pp_AddrInfo) ( Addr a, AddrInfo* ai )
njn5c004e42002-11-18 11:04:50 +0000210{
211 switch (ai->akind) {
212 case Stack:
213 VG_(message)(Vg_UserMsg,
nethercote3b390c72003-11-13 17:53:43 +0000214 " Address 0x%x is on thread %d's stack",
njn5c004e42002-11-18 11:04:50 +0000215 a, ai->stack_tid);
216 break;
217 case Unknown:
218 if (ai->maybe_gcc) {
219 VG_(message)(Vg_UserMsg,
nethercote3b390c72003-11-13 17:53:43 +0000220 " Address 0x%x is just below %%esp. Possibly a bug in GCC/G++",
njn5c004e42002-11-18 11:04:50 +0000221 a);
222 VG_(message)(Vg_UserMsg,
nethercote3b390c72003-11-13 17:53:43 +0000223 " v 2.96 or 3.0.X. To suppress, use: --workaround-gcc296-bugs=yes");
njn5c004e42002-11-18 11:04:50 +0000224 } else {
225 VG_(message)(Vg_UserMsg,
nethercotef798eee2004-04-13 08:36:35 +0000226 " Address 0x%x is not stack'd, malloc'd or (recently) free'd",a);
njn5c004e42002-11-18 11:04:50 +0000227 }
228 break;
rjwalshbc0bb832004-06-19 18:12:36 +0000229 case Freed: case Mallocd: case UserG: case Mempool: {
njn5c004e42002-11-18 11:04:50 +0000230 UInt delta;
231 UChar* relative;
rjwalshbc0bb832004-06-19 18:12:36 +0000232 UChar* kind;
233 if (ai->akind == Mempool) {
234 kind = "mempool";
235 } else {
236 kind = "block";
237 }
njn5c004e42002-11-18 11:04:50 +0000238 if (ai->rwoffset < 0) {
239 delta = (UInt)(- ai->rwoffset);
240 relative = "before";
241 } else if (ai->rwoffset >= ai->blksize) {
242 delta = ai->rwoffset - ai->blksize;
243 relative = "after";
244 } else {
245 delta = ai->rwoffset;
246 relative = "inside";
247 }
sewardja81709d2002-12-28 12:55:48 +0000248 VG_(message)(Vg_UserMsg,
rjwalshbc0bb832004-06-19 18:12:36 +0000249 " Address 0x%x is %d bytes %s a %s of size %d %s",
250 a, delta, relative, kind,
sewardja81709d2002-12-28 12:55:48 +0000251 ai->blksize,
252 ai->akind==Mallocd ? "alloc'd"
253 : ai->akind==Freed ? "free'd"
254 : "client-defined");
njn5c004e42002-11-18 11:04:50 +0000255 VG_(pp_ExeContext)(ai->lastchange);
256 break;
257 }
258 default:
njn43c799e2003-04-08 00:08:52 +0000259 VG_(skin_panic)("MAC_(pp_AddrInfo)");
260 }
261}
262
263/* This prints out the message for the error types where Memcheck and
264 Addrcheck have identical messages */
265void MAC_(pp_shared_SkinError) ( Error* err )
266{
267 MAC_Error* err_extra = VG_(get_error_extra)(err);
268
269 switch (VG_(get_error_kind)(err)) {
270 case FreeErr:
njn10785452003-05-20 16:38:24 +0000271 VG_(message)(Vg_UserMsg, "Invalid free() / delete / delete[]");
njn43c799e2003-04-08 00:08:52 +0000272 /* fall through */
273 case FreeMismatchErr:
274 if (VG_(get_error_kind)(err) == FreeMismatchErr)
275 VG_(message)(Vg_UserMsg,
276 "Mismatched free() / delete / delete []");
277 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
278 MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
279 break;
280
njn89409f82003-09-26 14:55:31 +0000281 case AddrErr:
282 switch (err_extra->axskind) {
283 case ReadAxs:
284 VG_(message)(Vg_UserMsg, "Invalid read of size %d",
285 err_extra->size );
286 break;
287 case WriteAxs:
288 VG_(message)(Vg_UserMsg, "Invalid write of size %d",
289 err_extra->size );
290 break;
291 case ExecAxs:
292 VG_(message)(Vg_UserMsg, "Jump to the invalid address "
293 "stated on the next line");
294 break;
295 default:
296 VG_(skin_panic)("SK_(pp_SkinError)(axskind)");
297 }
298 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
299 MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
300 break;
301
njnb6cae9f2003-09-04 20:50:47 +0000302 case OverlapErr: {
303 OverlapExtra* ov_extra = (OverlapExtra*)VG_(get_error_extra)(err);
304 if (ov_extra->len == -1)
305 VG_(message)(Vg_UserMsg,
306 "Source and destination overlap in %s(%p, %p)",
307 VG_(get_error_string)(err),
308 ov_extra->dst, ov_extra->src);
309 else
310 VG_(message)(Vg_UserMsg,
311 "Source and destination overlap in %s(%p, %p, %d)",
312 VG_(get_error_string)(err),
313 ov_extra->dst, ov_extra->src, ov_extra->len);
njn66fe05a2003-07-22 09:12:33 +0000314 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
315 break;
njnb6cae9f2003-09-04 20:50:47 +0000316 }
njn43c799e2003-04-08 00:08:52 +0000317 case LeakErr: {
318 /* Totally abusing the types of these spare fields... oh well. */
319 UInt n_this_record = (UInt)VG_(get_error_address)(err);
320 UInt n_total_records = (UInt)VG_(get_error_string) (err);
321
322 MAC_(pp_LeakError)(err_extra, n_this_record, n_total_records);
323 break;
324 }
325
rjwalshbc0bb832004-06-19 18:12:36 +0000326 case IllegalMempoolErr:
327 VG_(message)(Vg_UserMsg, "Illegal memory pool address");
328 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
329 MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo);
330 break;
331
njn43c799e2003-04-08 00:08:52 +0000332 default:
333 VG_(printf)("Error:\n unknown Memcheck/Addrcheck error code %d\n",
334 VG_(get_error_kind)(err));
335 VG_(skin_panic)("unknown error code in MAC_(pp_shared_SkinError)");
njn5c004e42002-11-18 11:04:50 +0000336 }
337}
338
339/*------------------------------------------------------------*/
340/*--- Recording errors ---*/
341/*------------------------------------------------------------*/
342
njn43c799e2003-04-08 00:08:52 +0000343/* Additional description function for describe_addr(); used by
344 MemCheck for user blocks, which Addrcheck doesn't support. */
345Bool (*MAC_(describe_addr_supp)) ( Addr a, AddrInfo* ai ) = NULL;
thughes4ad52d02004-06-27 17:37:21 +0000346
347/* Callback for searching thread stacks */
348static Bool addr_is_in_bounds(Addr stack_min, Addr stack_max, void *ap)
349{
350 Addr a = *(Addr *)ap;
351
352 return (stack_min <= a && a <= stack_max);
353}
354
355/* Callback for searching free'd list */
356static Bool addr_is_in_MAC_Chunk(MAC_Chunk* mc, void *ap)
357{
358 Addr a = *(Addr *)ap;
359
360 return VG_(addr_is_in_block)( a, mc->data, mc->size );
361}
362
363/* Callback for searching malloc'd lists */
364static Bool addr_is_in_HashNode(VgHashNode* sh_ch, void *ap)
365{
366 return addr_is_in_MAC_Chunk( (MAC_Chunk*)sh_ch, ap );
367}
368
njn43c799e2003-04-08 00:08:52 +0000369/* Describe an address as best you can, for error messages,
370 putting the result in ai. */
371static void describe_addr ( Addr a, AddrInfo* ai )
372{
njn3e884182003-04-15 13:03:23 +0000373 MAC_Chunk* sc;
374 ThreadId tid;
njn43c799e2003-04-08 00:08:52 +0000375
njn43c799e2003-04-08 00:08:52 +0000376 /* Perhaps it's a user-def'd block ? (only check if requested, though) */
377 if (NULL != MAC_(describe_addr_supp)) {
378 if (MAC_(describe_addr_supp)( a, ai ))
379 return;
380 }
381 /* Perhaps it's on a thread's stack? */
thughes4ad52d02004-06-27 17:37:21 +0000382 tid = VG_(first_matching_thread_stack)(addr_is_in_bounds, &a);
njn43c799e2003-04-08 00:08:52 +0000383 if (tid != VG_INVALID_THREADID) {
384 ai->akind = Stack;
385 ai->stack_tid = tid;
386 return;
387 }
388 /* Search for a recently freed block which might bracket it. */
thughes4ad52d02004-06-27 17:37:21 +0000389 sc = MAC_(first_matching_freed_MAC_Chunk)(addr_is_in_MAC_Chunk, &a);
njn3e884182003-04-15 13:03:23 +0000390 if (NULL != sc) {
njn43c799e2003-04-08 00:08:52 +0000391 ai->akind = Freed;
njn3e884182003-04-15 13:03:23 +0000392 ai->blksize = sc->size;
393 ai->rwoffset = (Int)a - (Int)sc->data;
394 ai->lastchange = sc->where;
njn43c799e2003-04-08 00:08:52 +0000395 return;
396 }
397 /* Search for a currently malloc'd block which might bracket it. */
thughes4ad52d02004-06-27 17:37:21 +0000398 sc = (MAC_Chunk*)VG_(HT_first_match)(MAC_(malloc_list), addr_is_in_HashNode, &a);
njn43c799e2003-04-08 00:08:52 +0000399 if (NULL != sc) {
400 ai->akind = Mallocd;
njn3e884182003-04-15 13:03:23 +0000401 ai->blksize = sc->size;
402 ai->rwoffset = (Int)(a) - (Int)sc->data;
403 ai->lastchange = sc->where;
njn43c799e2003-04-08 00:08:52 +0000404 return;
405 }
406 /* Clueless ... */
407 ai->akind = Unknown;
408 return;
409}
410
njn5c004e42002-11-18 11:04:50 +0000411/* Is this address within some small distance below %ESP? Used only
412 for the --workaround-gcc296-bugs kludge. */
413static Bool is_just_below_ESP( Addr esp, Addr aa )
414{
415 if ((UInt)esp > (UInt)aa
416 && ((UInt)esp - (UInt)aa) <= VG_GCC296_BUG_STACK_SLOP)
417 return True;
418 else
419 return False;
420}
421
sewardjaf48a602003-07-06 00:54:47 +0000422/* This one called from generated code and non-generated code. */
njn5c004e42002-11-18 11:04:50 +0000423
njn72718642003-07-24 08:45:32 +0000424void MAC_(record_address_error) ( ThreadId tid, Addr a, Int size,
sewardjaf48a602003-07-06 00:54:47 +0000425 Bool isWrite )
njn5c004e42002-11-18 11:04:50 +0000426{
njn43c799e2003-04-08 00:08:52 +0000427 MAC_Error err_extra;
428 Bool just_below_esp;
njn5c004e42002-11-18 11:04:50 +0000429
430 just_below_esp = is_just_below_ESP( VG_(get_stack_pointer)(), a );
431
432 /* If this is caused by an access immediately below %ESP, and the
433 user asks nicely, we just ignore it. */
njn43c799e2003-04-08 00:08:52 +0000434 if (MAC_(clo_workaround_gcc296_bugs) && just_below_esp)
njn5c004e42002-11-18 11:04:50 +0000435 return;
436
njn43c799e2003-04-08 00:08:52 +0000437 MAC_(clear_MAC_Error)( &err_extra );
njn5c004e42002-11-18 11:04:50 +0000438 err_extra.axskind = isWrite ? WriteAxs : ReadAxs;
439 err_extra.size = size;
440 err_extra.addrinfo.akind = Undescribed;
441 err_extra.addrinfo.maybe_gcc = just_below_esp;
njn72718642003-07-24 08:45:32 +0000442 VG_(maybe_record_error)( tid, AddrErr, a, /*s*/NULL, &err_extra );
njn5c004e42002-11-18 11:04:50 +0000443}
444
445/* These ones are called from non-generated code */
446
447/* This is for memory errors in pthread functions, as opposed to pthread API
448 errors which are found by the core. */
njn72718642003-07-24 08:45:32 +0000449void MAC_(record_core_mem_error) ( ThreadId tid, Bool isWrite, Char* msg )
njn5c004e42002-11-18 11:04:50 +0000450{
njn43c799e2003-04-08 00:08:52 +0000451 MAC_Error err_extra;
njn5c004e42002-11-18 11:04:50 +0000452
njn43c799e2003-04-08 00:08:52 +0000453 MAC_(clear_MAC_Error)( &err_extra );
njn5c004e42002-11-18 11:04:50 +0000454 err_extra.isWrite = isWrite;
njn72718642003-07-24 08:45:32 +0000455 VG_(maybe_record_error)( tid, CoreMemErr, /*addr*/0, msg, &err_extra );
njn5c004e42002-11-18 11:04:50 +0000456}
457
njn72718642003-07-24 08:45:32 +0000458void MAC_(record_param_error) ( ThreadId tid, Addr a, Bool isWrite,
njn5c004e42002-11-18 11:04:50 +0000459 Char* msg )
460{
njn43c799e2003-04-08 00:08:52 +0000461 MAC_Error err_extra;
njn5c004e42002-11-18 11:04:50 +0000462
njn72718642003-07-24 08:45:32 +0000463 sk_assert(VG_INVALID_THREADID != tid);
njn43c799e2003-04-08 00:08:52 +0000464 MAC_(clear_MAC_Error)( &err_extra );
njn5c004e42002-11-18 11:04:50 +0000465 err_extra.addrinfo.akind = Undescribed;
466 err_extra.isWrite = isWrite;
njn72718642003-07-24 08:45:32 +0000467 VG_(maybe_record_error)( tid, ParamErr, a, msg, &err_extra );
njn5c004e42002-11-18 11:04:50 +0000468}
469
njn72718642003-07-24 08:45:32 +0000470void MAC_(record_jump_error) ( ThreadId tid, Addr a )
njn5c004e42002-11-18 11:04:50 +0000471{
njn43c799e2003-04-08 00:08:52 +0000472 MAC_Error err_extra;
njn5c004e42002-11-18 11:04:50 +0000473
njn72718642003-07-24 08:45:32 +0000474 sk_assert(VG_INVALID_THREADID != tid);
njn43c799e2003-04-08 00:08:52 +0000475 MAC_(clear_MAC_Error)( &err_extra );
njn5c004e42002-11-18 11:04:50 +0000476 err_extra.axskind = ExecAxs;
nethercote7250b8f2004-04-13 08:47:35 +0000477 err_extra.size = 1; // size only used for suppressions
njn5c004e42002-11-18 11:04:50 +0000478 err_extra.addrinfo.akind = Undescribed;
njn72718642003-07-24 08:45:32 +0000479 VG_(maybe_record_error)( tid, AddrErr, a, /*s*/NULL, &err_extra );
njn5c004e42002-11-18 11:04:50 +0000480}
481
njn72718642003-07-24 08:45:32 +0000482void MAC_(record_free_error) ( ThreadId tid, Addr a )
njn5c004e42002-11-18 11:04:50 +0000483{
njn43c799e2003-04-08 00:08:52 +0000484 MAC_Error err_extra;
njn5c004e42002-11-18 11:04:50 +0000485
njn72718642003-07-24 08:45:32 +0000486 sk_assert(VG_INVALID_THREADID != tid);
njn43c799e2003-04-08 00:08:52 +0000487 MAC_(clear_MAC_Error)( &err_extra );
njn5c004e42002-11-18 11:04:50 +0000488 err_extra.addrinfo.akind = Undescribed;
njn72718642003-07-24 08:45:32 +0000489 VG_(maybe_record_error)( tid, FreeErr, a, /*s*/NULL, &err_extra );
njn5c004e42002-11-18 11:04:50 +0000490}
491
rjwalshbc0bb832004-06-19 18:12:36 +0000492void MAC_(record_illegal_mempool_error) ( ThreadId tid, Addr a )
493{
494 MAC_Error err_extra;
495
496 sk_assert(VG_INVALID_THREADID != tid);
497 MAC_(clear_MAC_Error)( &err_extra );
498 err_extra.addrinfo.akind = Undescribed;
499 VG_(maybe_record_error)( tid, IllegalMempoolErr, a, /*s*/NULL, &err_extra );
500}
501
njn72718642003-07-24 08:45:32 +0000502void MAC_(record_freemismatch_error) ( ThreadId tid, Addr a )
njn5c004e42002-11-18 11:04:50 +0000503{
njn43c799e2003-04-08 00:08:52 +0000504 MAC_Error err_extra;
njn5c004e42002-11-18 11:04:50 +0000505
njn72718642003-07-24 08:45:32 +0000506 sk_assert(VG_INVALID_THREADID != tid);
njn43c799e2003-04-08 00:08:52 +0000507 MAC_(clear_MAC_Error)( &err_extra );
njn5c004e42002-11-18 11:04:50 +0000508 err_extra.addrinfo.akind = Undescribed;
njn72718642003-07-24 08:45:32 +0000509 VG_(maybe_record_error)( tid, FreeMismatchErr, a, /*s*/NULL, &err_extra );
njn5c004e42002-11-18 11:04:50 +0000510}
511
njn66fe05a2003-07-22 09:12:33 +0000512
njn72718642003-07-24 08:45:32 +0000513// This one not passed a ThreadId, so it grabs it itself.
njnb6cae9f2003-09-04 20:50:47 +0000514void MAC_(record_overlap_error) ( Char* function, OverlapExtra* ov_extra )
njn66fe05a2003-07-22 09:12:33 +0000515{
njn72718642003-07-24 08:45:32 +0000516 VG_(maybe_record_error)( VG_(get_current_or_recent_tid)(),
njnb6cae9f2003-09-04 20:50:47 +0000517 OverlapErr, /*addr*/0, /*s*/function, ov_extra );
njn66fe05a2003-07-22 09:12:33 +0000518}
519
520
njnb6cae9f2003-09-04 20:50:47 +0000521/* Updates the copy with address info if necessary (but not for all errors). */
njn43c799e2003-04-08 00:08:52 +0000522UInt SK_(update_extra)( Error* err )
523{
njnb6cae9f2003-09-04 20:50:47 +0000524 switch (VG_(get_error_kind)(err)) {
525 case ValueErr:
526 case CoreMemErr:
527 case AddrErr:
528 case ParamErr:
529 case UserErr:
530 case FreeErr:
rjwalshbc0bb832004-06-19 18:12:36 +0000531 case IllegalMempoolErr:
njnb6cae9f2003-09-04 20:50:47 +0000532 case FreeMismatchErr: {
533 MAC_Error* extra = (MAC_Error*)VG_(get_error_extra)(err);
534 if (extra != NULL && Undescribed == extra->addrinfo.akind) {
535 describe_addr ( VG_(get_error_address)(err), &(extra->addrinfo) );
536 }
537 return sizeof(MAC_Error);
538 }
njn43c799e2003-04-08 00:08:52 +0000539 /* Don't need to return the correct size -- LeakErrs are always shown with
540 VG_(unique_error)() so they're not copied anyway. */
njnb6cae9f2003-09-04 20:50:47 +0000541 case LeakErr: return 0;
542 case OverlapErr: return sizeof(OverlapExtra);
543 default: VG_(skin_panic)("update_extra: bad errkind");
njn43c799e2003-04-08 00:08:52 +0000544 }
njn43c799e2003-04-08 00:08:52 +0000545}
546
547
njn5c004e42002-11-18 11:04:50 +0000548/*------------------------------------------------------------*/
549/*--- Suppressions ---*/
550/*------------------------------------------------------------*/
551
njn43c799e2003-04-08 00:08:52 +0000552Bool MAC_(shared_recognised_suppression) ( Char* name, Supp* su )
553{
554 SuppKind skind;
555
556 if (VG_STREQ(name, "Param")) skind = ParamSupp;
557 else if (VG_STREQ(name, "CoreMem")) skind = CoreMemSupp;
558 else if (VG_STREQ(name, "Addr1")) skind = Addr1Supp;
559 else if (VG_STREQ(name, "Addr2")) skind = Addr2Supp;
560 else if (VG_STREQ(name, "Addr4")) skind = Addr4Supp;
561 else if (VG_STREQ(name, "Addr8")) skind = Addr8Supp;
njnc0616662003-06-12 09:58:41 +0000562 else if (VG_STREQ(name, "Addr16")) skind = Addr16Supp;
njn43c799e2003-04-08 00:08:52 +0000563 else if (VG_STREQ(name, "Free")) skind = FreeSupp;
564 else if (VG_STREQ(name, "Leak")) skind = LeakSupp;
njne9cab142003-09-19 08:10:07 +0000565 else if (VG_STREQ(name, "Overlap")) skind = OverlapSupp;
rjwalshbc0bb832004-06-19 18:12:36 +0000566 else if (VG_STREQ(name, "Mempool")) skind = MempoolSupp;
njn43c799e2003-04-08 00:08:52 +0000567 else
568 return False;
569
570 VG_(set_supp_kind)(su, skind);
571 return True;
572}
573
njn5c004e42002-11-18 11:04:50 +0000574Bool SK_(read_extra_suppression_info) ( Int fd, Char* buf, Int nBuf, Supp *su )
575{
576 Bool eof;
577
578 if (VG_(get_supp_kind)(su) == ParamSupp) {
579 eof = VG_(get_line) ( fd, buf, nBuf );
580 if (eof) return False;
581 VG_(set_supp_string)(su, VG_(strdup)(buf));
582 }
583 return True;
584}
585
sewardj99aac972002-12-26 01:53:45 +0000586Bool SK_(error_matches_suppression)(Error* err, Supp* su)
njn5c004e42002-11-18 11:04:50 +0000587{
sewardj05bcdcb2003-05-18 10:05:38 +0000588 Int su_size;
njn43c799e2003-04-08 00:08:52 +0000589 MAC_Error* err_extra = VG_(get_error_extra)(err);
590 ErrorKind ekind = VG_(get_error_kind )(err);
njn5c004e42002-11-18 11:04:50 +0000591
592 switch (VG_(get_supp_kind)(su)) {
593 case ParamSupp:
594 return (ekind == ParamErr
njn43c799e2003-04-08 00:08:52 +0000595 && VG_STREQ(VG_(get_error_string)(err),
596 VG_(get_supp_string)(su)));
njn5c004e42002-11-18 11:04:50 +0000597
598 case CoreMemSupp:
599 return (ekind == CoreMemErr
njn43c799e2003-04-08 00:08:52 +0000600 && VG_STREQ(VG_(get_error_string)(err),
601 VG_(get_supp_string)(su)));
njn5c004e42002-11-18 11:04:50 +0000602
603 case Value0Supp: su_size = 0; goto value_case;
604 case Value1Supp: su_size = 1; goto value_case;
605 case Value2Supp: su_size = 2; goto value_case;
606 case Value4Supp: su_size = 4; goto value_case;
607 case Value8Supp: su_size = 8; goto value_case;
njnc0616662003-06-12 09:58:41 +0000608 case Value16Supp:su_size =16; goto value_case;
njn5c004e42002-11-18 11:04:50 +0000609 value_case:
610 return (ekind == ValueErr && err_extra->size == su_size);
611
612 case Addr1Supp: su_size = 1; goto addr_case;
613 case Addr2Supp: su_size = 2; goto addr_case;
614 case Addr4Supp: su_size = 4; goto addr_case;
615 case Addr8Supp: su_size = 8; goto addr_case;
njnc0616662003-06-12 09:58:41 +0000616 case Addr16Supp:su_size =16; goto addr_case;
njn5c004e42002-11-18 11:04:50 +0000617 addr_case:
618 return (ekind == AddrErr && err_extra->size == su_size);
619
620 case FreeSupp:
621 return (ekind == FreeErr || ekind == FreeMismatchErr);
622
njn34419c12003-05-02 17:24:29 +0000623 case OverlapSupp:
624 return (ekind = OverlapErr);
625
sewardj4a19e2f2002-12-26 11:50:21 +0000626 case LeakSupp:
njn43c799e2003-04-08 00:08:52 +0000627 return (ekind == LeakErr);
sewardj4a19e2f2002-12-26 11:50:21 +0000628
rjwalshbc0bb832004-06-19 18:12:36 +0000629 case MempoolSupp:
630 return (ekind == IllegalMempoolErr);
631
njn5c004e42002-11-18 11:04:50 +0000632 default:
633 VG_(printf)("Error:\n"
634 " unknown suppression type %d\n",
635 VG_(get_supp_kind)(su));
636 VG_(skin_panic)("unknown suppression type in "
637 "SK_(error_matches_suppression)");
638 }
639}
640
njn43c799e2003-04-08 00:08:52 +0000641Char* SK_(get_error_name) ( Error* err )
642{
643 Char* s;
644 switch (VG_(get_error_kind)(err)) {
645 case ParamErr: return "Param";
646 case UserErr: return NULL; /* Can't suppress User errors */
647 case FreeMismatchErr: return "Free";
rjwalshbc0bb832004-06-19 18:12:36 +0000648 case IllegalMempoolErr: return "Mempool";
njn43c799e2003-04-08 00:08:52 +0000649 case FreeErr: return "Free";
650 case AddrErr:
651 switch ( ((MAC_Error*)VG_(get_error_extra)(err))->size ) {
652 case 1: return "Addr1";
653 case 2: return "Addr2";
654 case 4: return "Addr4";
655 case 8: return "Addr8";
njnc0616662003-06-12 09:58:41 +0000656 case 16: return "Addr16";
njn43c799e2003-04-08 00:08:52 +0000657 default: VG_(skin_panic)("unexpected size for Addr");
658 }
659
660 case ValueErr:
661 switch ( ((MAC_Error*)VG_(get_error_extra)(err))->size ) {
662 case 0: return "Cond";
663 case 1: return "Value1";
664 case 2: return "Value2";
665 case 4: return "Value4";
666 case 8: return "Value8";
njnc0616662003-06-12 09:58:41 +0000667 case 16: return "Value16";
njn43c799e2003-04-08 00:08:52 +0000668 default: VG_(skin_panic)("unexpected size for Value");
669 }
670 case CoreMemErr: return "CoreMem";
njn34419c12003-05-02 17:24:29 +0000671 case OverlapErr: return "Overlap";
njn43c799e2003-04-08 00:08:52 +0000672 case LeakErr: return "Leak";
673 default: VG_(skin_panic)("get_error_name: unexpected type");
674 }
675 VG_(printf)(s);
676}
677
678void SK_(print_extra_suppression_info) ( Error* err )
679{
680 if (ParamErr == VG_(get_error_kind)(err)) {
681 VG_(printf)(" %s\n", VG_(get_error_string)(err));
682 }
683}
njn5c004e42002-11-18 11:04:50 +0000684
685/*------------------------------------------------------------*/
686/*--- Crude profiling machinery. ---*/
687/*------------------------------------------------------------*/
688
689/* Event index. If just the name of the fn is given, this means the
690 number of calls to the fn. Otherwise it is the specified event.
691 Ones marked 'M' are MemCheck only. Ones marked 'A' are AddrCheck only.
692 The rest are shared.
693
694 10 alloc_secondary_map
695
696 20 get_abit
697M 21 get_vbyte
698 22 set_abit
699M 23 set_vbyte
700 24 get_abits4_ALIGNED
701M 25 get_vbytes4_ALIGNED
702
703 30 set_address_range_perms
704 31 set_address_range_perms(lower byte loop)
705 32 set_address_range_perms(quadword loop)
706 33 set_address_range_perms(upper byte loop)
707
708 35 make_noaccess
709 36 make_writable
710 37 make_readable
711A 38 make_accessible
712
713 40 copy_address_range_state
714 41 copy_address_range_state(byte loop)
715 42 check_writable
716 43 check_writable(byte loop)
717 44 check_readable
718 45 check_readable(byte loop)
719 46 check_readable_asciiz
720 47 check_readable_asciiz(byte loop)
721A 48 check_accessible
722A 49 check_accessible(byte loop)
723
724 50 make_noaccess_aligned
725 51 make_writable_aligned
726
727M 60 helperc_LOADV4
728M 61 helperc_STOREV4
729M 62 helperc_LOADV2
730M 63 helperc_STOREV2
731M 64 helperc_LOADV1
732M 65 helperc_STOREV1
733
734A 66 helperc_ACCESS4
735A 67 helperc_ACCESS2
736A 68 helperc_ACCESS1
737
738M 70 rim_rd_V4_SLOWLY
739M 71 rim_wr_V4_SLOWLY
740M 72 rim_rd_V2_SLOWLY
741M 73 rim_wr_V2_SLOWLY
742M 74 rim_rd_V1_SLOWLY
743M 75 rim_wr_V1_SLOWLY
744
745A 76 ACCESS4_SLOWLY
746A 77 ACCESS2_SLOWLY
747A 78 ACCESS1_SLOWLY
748
749 80 fpu_read
750 81 fpu_read aligned 4
751 82 fpu_read aligned 8
752 83 fpu_read 2
jsewardfca60182004-01-04 23:30:55 +0000753 84 fpu_read 10/28/108/512
njn5c004e42002-11-18 11:04:50 +0000754
755M 85 fpu_write
756M 86 fpu_write aligned 4
757M 87 fpu_write aligned 8
758M 88 fpu_write 2
jsewardfca60182004-01-04 23:30:55 +0000759M 89 fpu_write 10/28/108/512
njn5c004e42002-11-18 11:04:50 +0000760
761 90 fpu_access
762 91 fpu_access aligned 4
763 92 fpu_access aligned 8
764 93 fpu_access 2
jsewardfca60182004-01-04 23:30:55 +0000765 94 fpu_access 10/28/108/512
njn5c004e42002-11-18 11:04:50 +0000766
767 100 fpu_access_check_SLOWLY
768 101 fpu_access_check_SLOWLY(byte loop)
njn9b007f62003-04-07 14:40:25 +0000769
770 110 new_mem_stack_4
771 111 new_mem_stack_8
772 112 new_mem_stack_12
773 113 new_mem_stack_16
774 114 new_mem_stack_32
775 115 new_mem_stack
776
777 120 die_mem_stack_4
778 121 die_mem_stack_8
779 122 die_mem_stack_12
780 123 die_mem_stack_16
781 124 die_mem_stack_32
782 125 die_mem_stack
njn5c004e42002-11-18 11:04:50 +0000783*/
784
njn43c799e2003-04-08 00:08:52 +0000785#ifdef MAC_PROFILE_MEMORY
njn5c004e42002-11-18 11:04:50 +0000786
njn43c799e2003-04-08 00:08:52 +0000787UInt MAC_(event_ctr)[N_PROF_EVENTS];
njn5c004e42002-11-18 11:04:50 +0000788
njnb4aee052003-04-15 14:09:58 +0000789static void init_prof_mem ( void )
njn5c004e42002-11-18 11:04:50 +0000790{
791 Int i;
792 for (i = 0; i < N_PROF_EVENTS; i++)
njn43c799e2003-04-08 00:08:52 +0000793 MAC_(event_ctr)[i] = 0;
njn5c004e42002-11-18 11:04:50 +0000794}
795
njnb4aee052003-04-15 14:09:58 +0000796static void done_prof_mem ( void )
njn5c004e42002-11-18 11:04:50 +0000797{
798 Int i;
799 for (i = 0; i < N_PROF_EVENTS; i++) {
800 if ((i % 10) == 0)
801 VG_(printf)("\n");
njn43c799e2003-04-08 00:08:52 +0000802 if (MAC_(event_ctr)[i] > 0)
803 VG_(printf)( "prof mem event %2d: %d\n", i, MAC_(event_ctr)[i] );
njn5c004e42002-11-18 11:04:50 +0000804 }
805 VG_(printf)("\n");
806}
807
808#else
809
njnb4aee052003-04-15 14:09:58 +0000810static void init_prof_mem ( void ) { }
811static void done_prof_mem ( void ) { }
njn5c004e42002-11-18 11:04:50 +0000812
njn5c004e42002-11-18 11:04:50 +0000813#endif
814
815/*------------------------------------------------------------*/
njn3e884182003-04-15 13:03:23 +0000816/*--- Common initialisation + finalisation ---*/
817/*------------------------------------------------------------*/
818
819void MAC_(common_pre_clo_init)(void)
820{
821 MAC_(malloc_list) = VG_(HT_construct)();
rjwalshbc0bb832004-06-19 18:12:36 +0000822 MAC_(mempool_list) = VG_(HT_construct)();
njn3e884182003-04-15 13:03:23 +0000823 init_prof_mem();
824}
825
826void MAC_(common_fini)(void (*leak_check)(void))
827{
828 MAC_(print_malloc_stats)();
829
830 if (VG_(clo_verbosity) == 1) {
831 if (!MAC_(clo_leak_check))
832 VG_(message)(Vg_UserMsg,
833 "For a detailed leak analysis, rerun with: --leak-check=yes");
834
835 VG_(message)(Vg_UserMsg,
836 "For counts of detected errors, rerun with: -v");
837 }
838 if (MAC_(clo_leak_check)) leak_check();
839
840 done_prof_mem();
841}
842
843/*------------------------------------------------------------*/
njn47363ab2003-04-21 13:24:40 +0000844/*--- Common client request handling ---*/
845/*------------------------------------------------------------*/
846
njn72718642003-07-24 08:45:32 +0000847Bool MAC_(handle_common_client_requests)(ThreadId tid, UInt* arg, UInt* ret )
njn47363ab2003-04-21 13:24:40 +0000848{
njnd7994182003-10-02 13:44:04 +0000849 Char* err =
850 "The client requests VALGRIND_MALLOCLIKE_BLOCK and\n"
851 " VALGRIND_FREELIKE_BLOCK have moved. Please recompile your\n"
852 " program to incorporate the updates in the Valgrind header files.\n"
853 " You shouldn't need to change the text of your program at all.\n"
854 " Everything should then work as before. Sorry for the bother.\n";
njn72718642003-07-24 08:45:32 +0000855
856 // Not using 'tid' here because MAC_(new_block)() and MAC_(handle_free)()
857 // grab it themselves. But what they grab should match 'tid', check
858 // this.
859 sk_assert(tid == VG_(get_current_or_recent_tid)());
njn47363ab2003-04-21 13:24:40 +0000860
861 switch (arg[0]) {
njn10785452003-05-20 16:38:24 +0000862 case VG_USERREQ__COUNT_LEAKS: { /* count leaked bytes */
863 UInt** argp = (UInt**)arg;
njne8b5c052003-07-22 22:03:58 +0000864 // MAC_(bytes_leaked) et al were set by the last leak check (or zero
865 // if no prior leak checks performed).
866 *argp[1] = MAC_(bytes_leaked);
867 *argp[2] = MAC_(bytes_dubious);
868 *argp[3] = MAC_(bytes_reachable);
869 *argp[4] = MAC_(bytes_suppressed);
njn47363ab2003-04-21 13:24:40 +0000870 *ret = 0;
871 return True;
njn10785452003-05-20 16:38:24 +0000872 }
njnd7994182003-10-02 13:44:04 +0000873 case VG_USERREQ__MALLOCLIKE_BLOCK__OLD_DO_NOT_USE:
874 case VG_USERREQ__FREELIKE_BLOCK__OLD_DO_NOT_USE:
875 VG_(skin_panic)(err);
876
njn10785452003-05-20 16:38:24 +0000877 case VG_USERREQ__MALLOCLIKE_BLOCK: {
njnd7994182003-10-02 13:44:04 +0000878 Addr p = (Addr)arg[1];
nethercote928a5f72004-11-03 18:10:37 +0000879 SizeT sizeB = arg[2];
njnd7994182003-10-02 13:44:04 +0000880 UInt rzB = arg[3];
881 Bool is_zeroed = (Bool)arg[4];
njn47363ab2003-04-21 13:24:40 +0000882
nethercote57e36b32004-07-10 14:56:28 +0000883 MAC_(new_block) ( p, sizeB, /*ignored*/0, rzB, is_zeroed,
884 MAC_AllocCustom, MAC_(malloc_list) );
njn10785452003-05-20 16:38:24 +0000885 return True;
886 }
887 case VG_USERREQ__FREELIKE_BLOCK: {
njnd7994182003-10-02 13:44:04 +0000888 Addr p = (Addr)arg[1];
889 UInt rzB = arg[2];
njn10785452003-05-20 16:38:24 +0000890
njn72718642003-07-24 08:45:32 +0000891 MAC_(handle_free) ( p, rzB, MAC_AllocCustom );
njn10785452003-05-20 16:38:24 +0000892 return True;
893 }
njnd7994182003-10-02 13:44:04 +0000894
nethercotee3a53722004-05-05 10:46:22 +0000895 case _VG_USERREQ__MEMCHECK_GET_RECORD_OVERLAP:
896 *ret = (Addr)MAC_(record_overlap_error);
897 return True;
898
rjwalshbc0bb832004-06-19 18:12:36 +0000899 case VG_USERREQ__CREATE_MEMPOOL: {
900 Addr pool = (Addr)arg[1];
901 UInt rzB = arg[2];
902 Bool is_zeroed = (Bool)arg[3];
903
904 MAC_(create_mempool) ( pool, rzB, is_zeroed );
905 return True;
906 }
907
908 case VG_USERREQ__DESTROY_MEMPOOL: {
909 Addr pool = (Addr)arg[1];
910
911 MAC_(destroy_mempool) ( pool );
912 return True;
913 }
914
915 case VG_USERREQ__MEMPOOL_ALLOC: {
916 Addr pool = (Addr)arg[1];
917 Addr addr = (Addr)arg[2];
918 UInt size = arg[3];
919
920 MAC_(mempool_alloc) ( pool, addr, size );
921 return True;
922 }
923
924 case VG_USERREQ__MEMPOOL_FREE: {
925 Addr pool = (Addr)arg[1];
926 Addr addr = (Addr)arg[2];
927
928 MAC_(mempool_free) ( pool, addr );
929 return True;
930 }
931
njn47363ab2003-04-21 13:24:40 +0000932 default:
933 return False;
934 }
935}
936
937/*------------------------------------------------------------*/
njn5c004e42002-11-18 11:04:50 +0000938/*--- Syscall wrappers ---*/
939/*------------------------------------------------------------*/
940
941void* SK_(pre_syscall) ( ThreadId tid, UInt syscallno, Bool isBlocking )
942{
943 Int sane = SK_(cheap_sanity_check)();
944 return (void*)sane;
945}
946
947void SK_(post_syscall) ( ThreadId tid, UInt syscallno,
948 void* pre_result, Int res, Bool isBlocking )
949{
950 Int sane_before_call = (Int)pre_result;
951 Bool sane_after_call = SK_(cheap_sanity_check)();
952
953 if ((Int)sane_before_call && (!sane_after_call)) {
954 VG_(message)(Vg_DebugMsg, "post-syscall: ");
955 VG_(message)(Vg_DebugMsg,
956 "probable sanity check failure for syscall number %d\n",
957 syscallno );
958 VG_(skin_panic)("aborting due to the above ... bye!");
959 }
960}
961
njn5c004e42002-11-18 11:04:50 +0000962/*--------------------------------------------------------------------*/
njn43c799e2003-04-08 00:08:52 +0000963/*--- end mac_needs.c ---*/
njn5c004e42002-11-18 11:04:50 +0000964/*--------------------------------------------------------------------*/