blob: 8e1a7b8a4e39fe7e005997eaca0df19b1f8d282d [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;
346
njn43c799e2003-04-08 00:08:52 +0000347/* Describe an address as best you can, for error messages,
348 putting the result in ai. */
349static void describe_addr ( Addr a, AddrInfo* ai )
350{
njn3e884182003-04-15 13:03:23 +0000351 MAC_Chunk* sc;
352 ThreadId tid;
njn43c799e2003-04-08 00:08:52 +0000353
354 /* Nested functions, yeah. Need the lexical scoping of 'a'. */
njn3e884182003-04-15 13:03:23 +0000355
njn43c799e2003-04-08 00:08:52 +0000356 /* Closure for searching thread stacks */
357 Bool addr_is_in_bounds(Addr stack_min, Addr stack_max)
358 {
359 return (stack_min <= a && a <= stack_max);
360 }
njn3e884182003-04-15 13:03:23 +0000361 /* Closure for searching free'd list */
362 Bool addr_is_in_MAC_Chunk(MAC_Chunk* mc)
njn43c799e2003-04-08 00:08:52 +0000363 {
njn3e884182003-04-15 13:03:23 +0000364 return VG_(addr_is_in_block)( a, mc->data, mc->size );
365 }
366 /* Closure for searching malloc'd lists */
367 Bool addr_is_in_HashNode(VgHashNode* sh_ch)
368 {
369 return addr_is_in_MAC_Chunk( (MAC_Chunk*)sh_ch );
njn43c799e2003-04-08 00:08:52 +0000370 }
371
372 /* Perhaps it's a user-def'd block ? (only check if requested, though) */
373 if (NULL != MAC_(describe_addr_supp)) {
374 if (MAC_(describe_addr_supp)( a, ai ))
375 return;
376 }
377 /* Perhaps it's on a thread's stack? */
378 tid = VG_(first_matching_thread_stack)(addr_is_in_bounds);
379 if (tid != VG_INVALID_THREADID) {
380 ai->akind = Stack;
381 ai->stack_tid = tid;
382 return;
383 }
384 /* Search for a recently freed block which might bracket it. */
njn3e884182003-04-15 13:03:23 +0000385 sc = MAC_(first_matching_freed_MAC_Chunk)(addr_is_in_MAC_Chunk);
386 if (NULL != sc) {
njn43c799e2003-04-08 00:08:52 +0000387 ai->akind = Freed;
njn3e884182003-04-15 13:03:23 +0000388 ai->blksize = sc->size;
389 ai->rwoffset = (Int)a - (Int)sc->data;
390 ai->lastchange = sc->where;
njn43c799e2003-04-08 00:08:52 +0000391 return;
392 }
393 /* Search for a currently malloc'd block which might bracket it. */
njn3e884182003-04-15 13:03:23 +0000394 sc = (MAC_Chunk*)VG_(HT_first_match)(MAC_(malloc_list), addr_is_in_HashNode);
njn43c799e2003-04-08 00:08:52 +0000395 if (NULL != sc) {
396 ai->akind = Mallocd;
njn3e884182003-04-15 13:03:23 +0000397 ai->blksize = sc->size;
398 ai->rwoffset = (Int)(a) - (Int)sc->data;
399 ai->lastchange = sc->where;
njn43c799e2003-04-08 00:08:52 +0000400 return;
401 }
402 /* Clueless ... */
403 ai->akind = Unknown;
404 return;
405}
406
njn5c004e42002-11-18 11:04:50 +0000407/* Is this address within some small distance below %ESP? Used only
408 for the --workaround-gcc296-bugs kludge. */
409static Bool is_just_below_ESP( Addr esp, Addr aa )
410{
411 if ((UInt)esp > (UInt)aa
412 && ((UInt)esp - (UInt)aa) <= VG_GCC296_BUG_STACK_SLOP)
413 return True;
414 else
415 return False;
416}
417
sewardjaf48a602003-07-06 00:54:47 +0000418/* This one called from generated code and non-generated code. */
njn5c004e42002-11-18 11:04:50 +0000419
njn72718642003-07-24 08:45:32 +0000420void MAC_(record_address_error) ( ThreadId tid, Addr a, Int size,
sewardjaf48a602003-07-06 00:54:47 +0000421 Bool isWrite )
njn5c004e42002-11-18 11:04:50 +0000422{
njn43c799e2003-04-08 00:08:52 +0000423 MAC_Error err_extra;
424 Bool just_below_esp;
njn5c004e42002-11-18 11:04:50 +0000425
426 just_below_esp = is_just_below_ESP( VG_(get_stack_pointer)(), a );
427
428 /* If this is caused by an access immediately below %ESP, and the
429 user asks nicely, we just ignore it. */
njn43c799e2003-04-08 00:08:52 +0000430 if (MAC_(clo_workaround_gcc296_bugs) && just_below_esp)
njn5c004e42002-11-18 11:04:50 +0000431 return;
432
njn43c799e2003-04-08 00:08:52 +0000433 MAC_(clear_MAC_Error)( &err_extra );
njn5c004e42002-11-18 11:04:50 +0000434 err_extra.axskind = isWrite ? WriteAxs : ReadAxs;
435 err_extra.size = size;
436 err_extra.addrinfo.akind = Undescribed;
437 err_extra.addrinfo.maybe_gcc = just_below_esp;
njn72718642003-07-24 08:45:32 +0000438 VG_(maybe_record_error)( tid, AddrErr, a, /*s*/NULL, &err_extra );
njn5c004e42002-11-18 11:04:50 +0000439}
440
441/* These ones are called from non-generated code */
442
443/* This is for memory errors in pthread functions, as opposed to pthread API
444 errors which are found by the core. */
njn72718642003-07-24 08:45:32 +0000445void MAC_(record_core_mem_error) ( ThreadId tid, Bool isWrite, Char* msg )
njn5c004e42002-11-18 11:04:50 +0000446{
njn43c799e2003-04-08 00:08:52 +0000447 MAC_Error err_extra;
njn5c004e42002-11-18 11:04:50 +0000448
njn43c799e2003-04-08 00:08:52 +0000449 MAC_(clear_MAC_Error)( &err_extra );
njn5c004e42002-11-18 11:04:50 +0000450 err_extra.isWrite = isWrite;
njn72718642003-07-24 08:45:32 +0000451 VG_(maybe_record_error)( tid, CoreMemErr, /*addr*/0, msg, &err_extra );
njn5c004e42002-11-18 11:04:50 +0000452}
453
njn72718642003-07-24 08:45:32 +0000454void MAC_(record_param_error) ( ThreadId tid, Addr a, Bool isWrite,
njn5c004e42002-11-18 11:04:50 +0000455 Char* msg )
456{
njn43c799e2003-04-08 00:08:52 +0000457 MAC_Error err_extra;
njn5c004e42002-11-18 11:04:50 +0000458
njn72718642003-07-24 08:45:32 +0000459 sk_assert(VG_INVALID_THREADID != tid);
njn43c799e2003-04-08 00:08:52 +0000460 MAC_(clear_MAC_Error)( &err_extra );
njn5c004e42002-11-18 11:04:50 +0000461 err_extra.addrinfo.akind = Undescribed;
462 err_extra.isWrite = isWrite;
njn72718642003-07-24 08:45:32 +0000463 VG_(maybe_record_error)( tid, ParamErr, a, msg, &err_extra );
njn5c004e42002-11-18 11:04:50 +0000464}
465
njn72718642003-07-24 08:45:32 +0000466void MAC_(record_jump_error) ( ThreadId tid, Addr a )
njn5c004e42002-11-18 11:04:50 +0000467{
njn43c799e2003-04-08 00:08:52 +0000468 MAC_Error err_extra;
njn5c004e42002-11-18 11:04:50 +0000469
njn72718642003-07-24 08:45:32 +0000470 sk_assert(VG_INVALID_THREADID != tid);
njn43c799e2003-04-08 00:08:52 +0000471 MAC_(clear_MAC_Error)( &err_extra );
njn5c004e42002-11-18 11:04:50 +0000472 err_extra.axskind = ExecAxs;
nethercote7250b8f2004-04-13 08:47:35 +0000473 err_extra.size = 1; // size only used for suppressions
njn5c004e42002-11-18 11:04:50 +0000474 err_extra.addrinfo.akind = Undescribed;
njn72718642003-07-24 08:45:32 +0000475 VG_(maybe_record_error)( tid, AddrErr, a, /*s*/NULL, &err_extra );
njn5c004e42002-11-18 11:04:50 +0000476}
477
njn72718642003-07-24 08:45:32 +0000478void MAC_(record_free_error) ( ThreadId tid, Addr a )
njn5c004e42002-11-18 11:04:50 +0000479{
njn43c799e2003-04-08 00:08:52 +0000480 MAC_Error err_extra;
njn5c004e42002-11-18 11:04:50 +0000481
njn72718642003-07-24 08:45:32 +0000482 sk_assert(VG_INVALID_THREADID != tid);
njn43c799e2003-04-08 00:08:52 +0000483 MAC_(clear_MAC_Error)( &err_extra );
njn5c004e42002-11-18 11:04:50 +0000484 err_extra.addrinfo.akind = Undescribed;
njn72718642003-07-24 08:45:32 +0000485 VG_(maybe_record_error)( tid, FreeErr, a, /*s*/NULL, &err_extra );
njn5c004e42002-11-18 11:04:50 +0000486}
487
rjwalshbc0bb832004-06-19 18:12:36 +0000488void MAC_(record_illegal_mempool_error) ( ThreadId tid, Addr a )
489{
490 MAC_Error err_extra;
491
492 sk_assert(VG_INVALID_THREADID != tid);
493 MAC_(clear_MAC_Error)( &err_extra );
494 err_extra.addrinfo.akind = Undescribed;
495 VG_(maybe_record_error)( tid, IllegalMempoolErr, a, /*s*/NULL, &err_extra );
496}
497
njn72718642003-07-24 08:45:32 +0000498void MAC_(record_freemismatch_error) ( ThreadId tid, Addr a )
njn5c004e42002-11-18 11:04:50 +0000499{
njn43c799e2003-04-08 00:08:52 +0000500 MAC_Error err_extra;
njn5c004e42002-11-18 11:04:50 +0000501
njn72718642003-07-24 08:45:32 +0000502 sk_assert(VG_INVALID_THREADID != tid);
njn43c799e2003-04-08 00:08:52 +0000503 MAC_(clear_MAC_Error)( &err_extra );
njn5c004e42002-11-18 11:04:50 +0000504 err_extra.addrinfo.akind = Undescribed;
njn72718642003-07-24 08:45:32 +0000505 VG_(maybe_record_error)( tid, FreeMismatchErr, a, /*s*/NULL, &err_extra );
njn5c004e42002-11-18 11:04:50 +0000506}
507
njn66fe05a2003-07-22 09:12:33 +0000508
njn72718642003-07-24 08:45:32 +0000509// This one not passed a ThreadId, so it grabs it itself.
njnb6cae9f2003-09-04 20:50:47 +0000510void MAC_(record_overlap_error) ( Char* function, OverlapExtra* ov_extra )
njn66fe05a2003-07-22 09:12:33 +0000511{
njn72718642003-07-24 08:45:32 +0000512 VG_(maybe_record_error)( VG_(get_current_or_recent_tid)(),
njnb6cae9f2003-09-04 20:50:47 +0000513 OverlapErr, /*addr*/0, /*s*/function, ov_extra );
njn66fe05a2003-07-22 09:12:33 +0000514}
515
516
njnb6cae9f2003-09-04 20:50:47 +0000517/* Updates the copy with address info if necessary (but not for all errors). */
njn43c799e2003-04-08 00:08:52 +0000518UInt SK_(update_extra)( Error* err )
519{
njnb6cae9f2003-09-04 20:50:47 +0000520 switch (VG_(get_error_kind)(err)) {
521 case ValueErr:
522 case CoreMemErr:
523 case AddrErr:
524 case ParamErr:
525 case UserErr:
526 case FreeErr:
rjwalshbc0bb832004-06-19 18:12:36 +0000527 case IllegalMempoolErr:
njnb6cae9f2003-09-04 20:50:47 +0000528 case FreeMismatchErr: {
529 MAC_Error* extra = (MAC_Error*)VG_(get_error_extra)(err);
530 if (extra != NULL && Undescribed == extra->addrinfo.akind) {
531 describe_addr ( VG_(get_error_address)(err), &(extra->addrinfo) );
532 }
533 return sizeof(MAC_Error);
534 }
njn43c799e2003-04-08 00:08:52 +0000535 /* Don't need to return the correct size -- LeakErrs are always shown with
536 VG_(unique_error)() so they're not copied anyway. */
njnb6cae9f2003-09-04 20:50:47 +0000537 case LeakErr: return 0;
538 case OverlapErr: return sizeof(OverlapExtra);
539 default: VG_(skin_panic)("update_extra: bad errkind");
njn43c799e2003-04-08 00:08:52 +0000540 }
njn43c799e2003-04-08 00:08:52 +0000541}
542
543
njn5c004e42002-11-18 11:04:50 +0000544/*------------------------------------------------------------*/
545/*--- Suppressions ---*/
546/*------------------------------------------------------------*/
547
njn43c799e2003-04-08 00:08:52 +0000548Bool MAC_(shared_recognised_suppression) ( Char* name, Supp* su )
549{
550 SuppKind skind;
551
552 if (VG_STREQ(name, "Param")) skind = ParamSupp;
553 else if (VG_STREQ(name, "CoreMem")) skind = CoreMemSupp;
554 else if (VG_STREQ(name, "Addr1")) skind = Addr1Supp;
555 else if (VG_STREQ(name, "Addr2")) skind = Addr2Supp;
556 else if (VG_STREQ(name, "Addr4")) skind = Addr4Supp;
557 else if (VG_STREQ(name, "Addr8")) skind = Addr8Supp;
njnc0616662003-06-12 09:58:41 +0000558 else if (VG_STREQ(name, "Addr16")) skind = Addr16Supp;
njn43c799e2003-04-08 00:08:52 +0000559 else if (VG_STREQ(name, "Free")) skind = FreeSupp;
560 else if (VG_STREQ(name, "Leak")) skind = LeakSupp;
njne9cab142003-09-19 08:10:07 +0000561 else if (VG_STREQ(name, "Overlap")) skind = OverlapSupp;
rjwalshbc0bb832004-06-19 18:12:36 +0000562 else if (VG_STREQ(name, "Mempool")) skind = MempoolSupp;
njn43c799e2003-04-08 00:08:52 +0000563 else
564 return False;
565
566 VG_(set_supp_kind)(su, skind);
567 return True;
568}
569
njn5c004e42002-11-18 11:04:50 +0000570Bool SK_(read_extra_suppression_info) ( Int fd, Char* buf, Int nBuf, Supp *su )
571{
572 Bool eof;
573
574 if (VG_(get_supp_kind)(su) == ParamSupp) {
575 eof = VG_(get_line) ( fd, buf, nBuf );
576 if (eof) return False;
577 VG_(set_supp_string)(su, VG_(strdup)(buf));
578 }
579 return True;
580}
581
sewardj99aac972002-12-26 01:53:45 +0000582Bool SK_(error_matches_suppression)(Error* err, Supp* su)
njn5c004e42002-11-18 11:04:50 +0000583{
sewardj05bcdcb2003-05-18 10:05:38 +0000584 Int su_size;
njn43c799e2003-04-08 00:08:52 +0000585 MAC_Error* err_extra = VG_(get_error_extra)(err);
586 ErrorKind ekind = VG_(get_error_kind )(err);
njn5c004e42002-11-18 11:04:50 +0000587
588 switch (VG_(get_supp_kind)(su)) {
589 case ParamSupp:
590 return (ekind == ParamErr
njn43c799e2003-04-08 00:08:52 +0000591 && VG_STREQ(VG_(get_error_string)(err),
592 VG_(get_supp_string)(su)));
njn5c004e42002-11-18 11:04:50 +0000593
594 case CoreMemSupp:
595 return (ekind == CoreMemErr
njn43c799e2003-04-08 00:08:52 +0000596 && VG_STREQ(VG_(get_error_string)(err),
597 VG_(get_supp_string)(su)));
njn5c004e42002-11-18 11:04:50 +0000598
599 case Value0Supp: su_size = 0; goto value_case;
600 case Value1Supp: su_size = 1; goto value_case;
601 case Value2Supp: su_size = 2; goto value_case;
602 case Value4Supp: su_size = 4; goto value_case;
603 case Value8Supp: su_size = 8; goto value_case;
njnc0616662003-06-12 09:58:41 +0000604 case Value16Supp:su_size =16; goto value_case;
njn5c004e42002-11-18 11:04:50 +0000605 value_case:
606 return (ekind == ValueErr && err_extra->size == su_size);
607
608 case Addr1Supp: su_size = 1; goto addr_case;
609 case Addr2Supp: su_size = 2; goto addr_case;
610 case Addr4Supp: su_size = 4; goto addr_case;
611 case Addr8Supp: su_size = 8; goto addr_case;
njnc0616662003-06-12 09:58:41 +0000612 case Addr16Supp:su_size =16; goto addr_case;
njn5c004e42002-11-18 11:04:50 +0000613 addr_case:
614 return (ekind == AddrErr && err_extra->size == su_size);
615
616 case FreeSupp:
617 return (ekind == FreeErr || ekind == FreeMismatchErr);
618
njn34419c12003-05-02 17:24:29 +0000619 case OverlapSupp:
620 return (ekind = OverlapErr);
621
sewardj4a19e2f2002-12-26 11:50:21 +0000622 case LeakSupp:
njn43c799e2003-04-08 00:08:52 +0000623 return (ekind == LeakErr);
sewardj4a19e2f2002-12-26 11:50:21 +0000624
rjwalshbc0bb832004-06-19 18:12:36 +0000625 case MempoolSupp:
626 return (ekind == IllegalMempoolErr);
627
njn5c004e42002-11-18 11:04:50 +0000628 default:
629 VG_(printf)("Error:\n"
630 " unknown suppression type %d\n",
631 VG_(get_supp_kind)(su));
632 VG_(skin_panic)("unknown suppression type in "
633 "SK_(error_matches_suppression)");
634 }
635}
636
njn43c799e2003-04-08 00:08:52 +0000637Char* SK_(get_error_name) ( Error* err )
638{
639 Char* s;
640 switch (VG_(get_error_kind)(err)) {
641 case ParamErr: return "Param";
642 case UserErr: return NULL; /* Can't suppress User errors */
643 case FreeMismatchErr: return "Free";
rjwalshbc0bb832004-06-19 18:12:36 +0000644 case IllegalMempoolErr: return "Mempool";
njn43c799e2003-04-08 00:08:52 +0000645 case FreeErr: return "Free";
646 case AddrErr:
647 switch ( ((MAC_Error*)VG_(get_error_extra)(err))->size ) {
648 case 1: return "Addr1";
649 case 2: return "Addr2";
650 case 4: return "Addr4";
651 case 8: return "Addr8";
njnc0616662003-06-12 09:58:41 +0000652 case 16: return "Addr16";
njn43c799e2003-04-08 00:08:52 +0000653 default: VG_(skin_panic)("unexpected size for Addr");
654 }
655
656 case ValueErr:
657 switch ( ((MAC_Error*)VG_(get_error_extra)(err))->size ) {
658 case 0: return "Cond";
659 case 1: return "Value1";
660 case 2: return "Value2";
661 case 4: return "Value4";
662 case 8: return "Value8";
njnc0616662003-06-12 09:58:41 +0000663 case 16: return "Value16";
njn43c799e2003-04-08 00:08:52 +0000664 default: VG_(skin_panic)("unexpected size for Value");
665 }
666 case CoreMemErr: return "CoreMem";
njn34419c12003-05-02 17:24:29 +0000667 case OverlapErr: return "Overlap";
njn43c799e2003-04-08 00:08:52 +0000668 case LeakErr: return "Leak";
669 default: VG_(skin_panic)("get_error_name: unexpected type");
670 }
671 VG_(printf)(s);
672}
673
674void SK_(print_extra_suppression_info) ( Error* err )
675{
676 if (ParamErr == VG_(get_error_kind)(err)) {
677 VG_(printf)(" %s\n", VG_(get_error_string)(err));
678 }
679}
njn5c004e42002-11-18 11:04:50 +0000680
681/*------------------------------------------------------------*/
682/*--- Crude profiling machinery. ---*/
683/*------------------------------------------------------------*/
684
685/* Event index. If just the name of the fn is given, this means the
686 number of calls to the fn. Otherwise it is the specified event.
687 Ones marked 'M' are MemCheck only. Ones marked 'A' are AddrCheck only.
688 The rest are shared.
689
690 10 alloc_secondary_map
691
692 20 get_abit
693M 21 get_vbyte
694 22 set_abit
695M 23 set_vbyte
696 24 get_abits4_ALIGNED
697M 25 get_vbytes4_ALIGNED
698
699 30 set_address_range_perms
700 31 set_address_range_perms(lower byte loop)
701 32 set_address_range_perms(quadword loop)
702 33 set_address_range_perms(upper byte loop)
703
704 35 make_noaccess
705 36 make_writable
706 37 make_readable
707A 38 make_accessible
708
709 40 copy_address_range_state
710 41 copy_address_range_state(byte loop)
711 42 check_writable
712 43 check_writable(byte loop)
713 44 check_readable
714 45 check_readable(byte loop)
715 46 check_readable_asciiz
716 47 check_readable_asciiz(byte loop)
717A 48 check_accessible
718A 49 check_accessible(byte loop)
719
720 50 make_noaccess_aligned
721 51 make_writable_aligned
722
723M 60 helperc_LOADV4
724M 61 helperc_STOREV4
725M 62 helperc_LOADV2
726M 63 helperc_STOREV2
727M 64 helperc_LOADV1
728M 65 helperc_STOREV1
729
730A 66 helperc_ACCESS4
731A 67 helperc_ACCESS2
732A 68 helperc_ACCESS1
733
734M 70 rim_rd_V4_SLOWLY
735M 71 rim_wr_V4_SLOWLY
736M 72 rim_rd_V2_SLOWLY
737M 73 rim_wr_V2_SLOWLY
738M 74 rim_rd_V1_SLOWLY
739M 75 rim_wr_V1_SLOWLY
740
741A 76 ACCESS4_SLOWLY
742A 77 ACCESS2_SLOWLY
743A 78 ACCESS1_SLOWLY
744
745 80 fpu_read
746 81 fpu_read aligned 4
747 82 fpu_read aligned 8
748 83 fpu_read 2
jsewardfca60182004-01-04 23:30:55 +0000749 84 fpu_read 10/28/108/512
njn5c004e42002-11-18 11:04:50 +0000750
751M 85 fpu_write
752M 86 fpu_write aligned 4
753M 87 fpu_write aligned 8
754M 88 fpu_write 2
jsewardfca60182004-01-04 23:30:55 +0000755M 89 fpu_write 10/28/108/512
njn5c004e42002-11-18 11:04:50 +0000756
757 90 fpu_access
758 91 fpu_access aligned 4
759 92 fpu_access aligned 8
760 93 fpu_access 2
jsewardfca60182004-01-04 23:30:55 +0000761 94 fpu_access 10/28/108/512
njn5c004e42002-11-18 11:04:50 +0000762
763 100 fpu_access_check_SLOWLY
764 101 fpu_access_check_SLOWLY(byte loop)
njn9b007f62003-04-07 14:40:25 +0000765
766 110 new_mem_stack_4
767 111 new_mem_stack_8
768 112 new_mem_stack_12
769 113 new_mem_stack_16
770 114 new_mem_stack_32
771 115 new_mem_stack
772
773 120 die_mem_stack_4
774 121 die_mem_stack_8
775 122 die_mem_stack_12
776 123 die_mem_stack_16
777 124 die_mem_stack_32
778 125 die_mem_stack
njn5c004e42002-11-18 11:04:50 +0000779*/
780
njn43c799e2003-04-08 00:08:52 +0000781#ifdef MAC_PROFILE_MEMORY
njn5c004e42002-11-18 11:04:50 +0000782
njn43c799e2003-04-08 00:08:52 +0000783UInt MAC_(event_ctr)[N_PROF_EVENTS];
njn5c004e42002-11-18 11:04:50 +0000784
njnb4aee052003-04-15 14:09:58 +0000785static void init_prof_mem ( void )
njn5c004e42002-11-18 11:04:50 +0000786{
787 Int i;
788 for (i = 0; i < N_PROF_EVENTS; i++)
njn43c799e2003-04-08 00:08:52 +0000789 MAC_(event_ctr)[i] = 0;
njn5c004e42002-11-18 11:04:50 +0000790}
791
njnb4aee052003-04-15 14:09:58 +0000792static void done_prof_mem ( void )
njn5c004e42002-11-18 11:04:50 +0000793{
794 Int i;
795 for (i = 0; i < N_PROF_EVENTS; i++) {
796 if ((i % 10) == 0)
797 VG_(printf)("\n");
njn43c799e2003-04-08 00:08:52 +0000798 if (MAC_(event_ctr)[i] > 0)
799 VG_(printf)( "prof mem event %2d: %d\n", i, MAC_(event_ctr)[i] );
njn5c004e42002-11-18 11:04:50 +0000800 }
801 VG_(printf)("\n");
802}
803
804#else
805
njnb4aee052003-04-15 14:09:58 +0000806static void init_prof_mem ( void ) { }
807static void done_prof_mem ( void ) { }
njn5c004e42002-11-18 11:04:50 +0000808
njn5c004e42002-11-18 11:04:50 +0000809#endif
810
811/*------------------------------------------------------------*/
njn3e884182003-04-15 13:03:23 +0000812/*--- Common initialisation + finalisation ---*/
813/*------------------------------------------------------------*/
814
815void MAC_(common_pre_clo_init)(void)
816{
817 MAC_(malloc_list) = VG_(HT_construct)();
rjwalshbc0bb832004-06-19 18:12:36 +0000818 MAC_(mempool_list) = VG_(HT_construct)();
njn3e884182003-04-15 13:03:23 +0000819 init_prof_mem();
820}
821
822void MAC_(common_fini)(void (*leak_check)(void))
823{
824 MAC_(print_malloc_stats)();
825
826 if (VG_(clo_verbosity) == 1) {
827 if (!MAC_(clo_leak_check))
828 VG_(message)(Vg_UserMsg,
829 "For a detailed leak analysis, rerun with: --leak-check=yes");
830
831 VG_(message)(Vg_UserMsg,
832 "For counts of detected errors, rerun with: -v");
833 }
834 if (MAC_(clo_leak_check)) leak_check();
835
836 done_prof_mem();
837}
838
839/*------------------------------------------------------------*/
njn47363ab2003-04-21 13:24:40 +0000840/*--- Common client request handling ---*/
841/*------------------------------------------------------------*/
842
njn72718642003-07-24 08:45:32 +0000843Bool MAC_(handle_common_client_requests)(ThreadId tid, UInt* arg, UInt* ret )
njn47363ab2003-04-21 13:24:40 +0000844{
njnd7994182003-10-02 13:44:04 +0000845 Char* err =
846 "The client requests VALGRIND_MALLOCLIKE_BLOCK and\n"
847 " VALGRIND_FREELIKE_BLOCK have moved. Please recompile your\n"
848 " program to incorporate the updates in the Valgrind header files.\n"
849 " You shouldn't need to change the text of your program at all.\n"
850 " Everything should then work as before. Sorry for the bother.\n";
njn72718642003-07-24 08:45:32 +0000851
852 // Not using 'tid' here because MAC_(new_block)() and MAC_(handle_free)()
853 // grab it themselves. But what they grab should match 'tid', check
854 // this.
855 sk_assert(tid == VG_(get_current_or_recent_tid)());
njn47363ab2003-04-21 13:24:40 +0000856
857 switch (arg[0]) {
njn10785452003-05-20 16:38:24 +0000858 case VG_USERREQ__COUNT_LEAKS: { /* count leaked bytes */
859 UInt** argp = (UInt**)arg;
njne8b5c052003-07-22 22:03:58 +0000860 // MAC_(bytes_leaked) et al were set by the last leak check (or zero
861 // if no prior leak checks performed).
862 *argp[1] = MAC_(bytes_leaked);
863 *argp[2] = MAC_(bytes_dubious);
864 *argp[3] = MAC_(bytes_reachable);
865 *argp[4] = MAC_(bytes_suppressed);
njn47363ab2003-04-21 13:24:40 +0000866 *ret = 0;
867 return True;
njn10785452003-05-20 16:38:24 +0000868 }
njnd7994182003-10-02 13:44:04 +0000869 case VG_USERREQ__MALLOCLIKE_BLOCK__OLD_DO_NOT_USE:
870 case VG_USERREQ__FREELIKE_BLOCK__OLD_DO_NOT_USE:
871 VG_(skin_panic)(err);
872
njn10785452003-05-20 16:38:24 +0000873 case VG_USERREQ__MALLOCLIKE_BLOCK: {
njnd7994182003-10-02 13:44:04 +0000874 Addr p = (Addr)arg[1];
875 UInt sizeB = arg[2];
876 UInt rzB = arg[3];
877 Bool is_zeroed = (Bool)arg[4];
njn47363ab2003-04-21 13:24:40 +0000878
rjwalshbc0bb832004-06-19 18:12:36 +0000879 MAC_(new_block) ( p, sizeB, rzB, is_zeroed, MAC_AllocCustom,
880 MAC_(malloc_list) );
njn10785452003-05-20 16:38:24 +0000881 return True;
882 }
883 case VG_USERREQ__FREELIKE_BLOCK: {
njnd7994182003-10-02 13:44:04 +0000884 Addr p = (Addr)arg[1];
885 UInt rzB = arg[2];
njn10785452003-05-20 16:38:24 +0000886
njn72718642003-07-24 08:45:32 +0000887 MAC_(handle_free) ( p, rzB, MAC_AllocCustom );
njn10785452003-05-20 16:38:24 +0000888 return True;
889 }
njnd7994182003-10-02 13:44:04 +0000890
nethercotee3a53722004-05-05 10:46:22 +0000891 case _VG_USERREQ__MEMCHECK_GET_RECORD_OVERLAP:
892 *ret = (Addr)MAC_(record_overlap_error);
893 return True;
894
rjwalshbc0bb832004-06-19 18:12:36 +0000895 case VG_USERREQ__CREATE_MEMPOOL: {
896 Addr pool = (Addr)arg[1];
897 UInt rzB = arg[2];
898 Bool is_zeroed = (Bool)arg[3];
899
900 MAC_(create_mempool) ( pool, rzB, is_zeroed );
901 return True;
902 }
903
904 case VG_USERREQ__DESTROY_MEMPOOL: {
905 Addr pool = (Addr)arg[1];
906
907 MAC_(destroy_mempool) ( pool );
908 return True;
909 }
910
911 case VG_USERREQ__MEMPOOL_ALLOC: {
912 Addr pool = (Addr)arg[1];
913 Addr addr = (Addr)arg[2];
914 UInt size = arg[3];
915
916 MAC_(mempool_alloc) ( pool, addr, size );
917 return True;
918 }
919
920 case VG_USERREQ__MEMPOOL_FREE: {
921 Addr pool = (Addr)arg[1];
922 Addr addr = (Addr)arg[2];
923
924 MAC_(mempool_free) ( pool, addr );
925 return True;
926 }
927
njn47363ab2003-04-21 13:24:40 +0000928 default:
929 return False;
930 }
931}
932
933/*------------------------------------------------------------*/
njn5c004e42002-11-18 11:04:50 +0000934/*--- Syscall wrappers ---*/
935/*------------------------------------------------------------*/
936
937void* SK_(pre_syscall) ( ThreadId tid, UInt syscallno, Bool isBlocking )
938{
939 Int sane = SK_(cheap_sanity_check)();
940 return (void*)sane;
941}
942
943void SK_(post_syscall) ( ThreadId tid, UInt syscallno,
944 void* pre_result, Int res, Bool isBlocking )
945{
946 Int sane_before_call = (Int)pre_result;
947 Bool sane_after_call = SK_(cheap_sanity_check)();
948
949 if ((Int)sane_before_call && (!sane_after_call)) {
950 VG_(message)(Vg_DebugMsg, "post-syscall: ");
951 VG_(message)(Vg_DebugMsg,
952 "probable sanity check failure for syscall number %d\n",
953 syscallno );
954 VG_(skin_panic)("aborting due to the above ... bye!");
955 }
956}
957
njn5c004e42002-11-18 11:04:50 +0000958/*--------------------------------------------------------------------*/
njn43c799e2003-04-08 00:08:52 +0000959/*--- end mac_needs.c ---*/
njn5c004e42002-11-18 11:04:50 +0000960/*--------------------------------------------------------------------*/