Remove path name from test case
[binutils-gdb.git] / ld / testplug3.c
1 /* Test plugin for the GNU linker. Check non-object IR file and calling
2 release_input_file from onclaim_file.
3 Copyright (C) 2015-2023 Free Software Foundation, Inc.
4
5 This file is part of the GNU Binutils.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
20 MA 02110-1301, USA. */
21
22 #include "sysdep.h"
23 #include "bfd.h"
24 #if BFD_SUPPORTS_PLUGINS
25 #include "plugin-api.h"
26 #include "filenames.h"
27 /* For ARRAY_SIZE macro only - we don't link the library itself. */
28 #include "libiberty.h"
29
30 extern enum ld_plugin_status onload (struct ld_plugin_tv *tv);
31 static enum ld_plugin_status onclaim_file (const struct ld_plugin_input_file *file,
32 int *claimed);
33 static enum ld_plugin_status onall_symbols_read (void);
34 static enum ld_plugin_status oncleanup (void);
35
36 /* Helper for calling plugin api message function. */
37 #define TV_MESSAGE if (tv_message) (*tv_message)
38
39 /* Struct for recording files to claim / files claimed. */
40 typedef struct claim_file
41 {
42 struct claim_file *next;
43 struct ld_plugin_input_file file;
44 bool claimed;
45 struct ld_plugin_symbol *symbols;
46 int n_syms_allocated;
47 int n_syms_used;
48 } claim_file_t;
49
50 /* Types of things that can be added at all symbols read time. */
51 typedef enum addfile_enum
52 {
53 ADD_FILE,
54 ADD_LIB,
55 ADD_DIR
56 } addfile_enum_t;
57
58 /* Struct for recording files to add to final link. */
59 typedef struct add_file
60 {
61 struct add_file *next;
62 const char *name;
63 addfile_enum_t type;
64 } add_file_t;
65
66 /* Helper macro for defining array of transfer vector tags and names. */
67 #define ADDENTRY(tag) { tag, #tag }
68
69 /* Struct for looking up human-readable versions of tag names. */
70 typedef struct tag_name
71 {
72 enum ld_plugin_tag tag;
73 const char *name;
74 } tag_name_t;
75
76 /* Array of all known tags and their names. */
77 static const tag_name_t tag_names[] =
78 {
79 ADDENTRY(LDPT_NULL),
80 ADDENTRY(LDPT_API_VERSION),
81 ADDENTRY(LDPT_GOLD_VERSION),
82 ADDENTRY(LDPT_LINKER_OUTPUT),
83 ADDENTRY(LDPT_OPTION),
84 ADDENTRY(LDPT_REGISTER_CLAIM_FILE_HOOK),
85 ADDENTRY(LDPT_REGISTER_CLAIM_FILE_HOOK_V2),
86 ADDENTRY(LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK),
87 ADDENTRY(LDPT_REGISTER_CLEANUP_HOOK),
88 ADDENTRY(LDPT_ADD_SYMBOLS),
89 ADDENTRY(LDPT_GET_SYMBOLS),
90 ADDENTRY(LDPT_GET_SYMBOLS_V2),
91 ADDENTRY(LDPT_ADD_INPUT_FILE),
92 ADDENTRY(LDPT_MESSAGE),
93 ADDENTRY(LDPT_GET_INPUT_FILE),
94 ADDENTRY(LDPT_GET_VIEW),
95 ADDENTRY(LDPT_RELEASE_INPUT_FILE),
96 ADDENTRY(LDPT_ADD_INPUT_LIBRARY),
97 ADDENTRY(LDPT_OUTPUT_NAME),
98 ADDENTRY(LDPT_SET_EXTRA_LIBRARY_PATH),
99 ADDENTRY(LDPT_GNU_LD_VERSION)
100 };
101
102 /* Function pointers to cache hooks passed at onload time. */
103 static ld_plugin_register_claim_file tv_register_claim_file = 0;
104 static ld_plugin_register_claim_file_v2 tv_register_claim_file_v2 = 0;
105 static ld_plugin_register_all_symbols_read tv_register_all_symbols_read = 0;
106 static ld_plugin_register_cleanup tv_register_cleanup = 0;
107 static ld_plugin_add_symbols tv_add_symbols = 0;
108 static ld_plugin_get_symbols tv_get_symbols = 0;
109 static ld_plugin_get_symbols tv_get_symbols_v2 = 0;
110 static ld_plugin_add_input_file tv_add_input_file = 0;
111 static ld_plugin_message tv_message = 0;
112 static ld_plugin_get_input_file tv_get_input_file = 0;
113 static ld_plugin_get_view tv_get_view = 0;
114 static ld_plugin_release_input_file tv_release_input_file = 0;
115 static ld_plugin_add_input_library tv_add_input_library = 0;
116 static ld_plugin_set_extra_library_path tv_set_extra_library_path = 0;
117
118 /* Other cached info from the transfer vector. */
119 static enum ld_plugin_output_file_type linker_output;
120 static const char *output_name;
121
122 /* Behaviour control flags set by plugin options. */
123 static enum ld_plugin_status onload_ret = LDPS_OK;
124 static enum ld_plugin_status claim_file_ret = LDPS_OK;
125 static enum ld_plugin_status all_symbols_read_ret = LDPS_OK;
126 static enum ld_plugin_status cleanup_ret = LDPS_OK;
127 static bool register_claimfile_hook = true;
128 static bool register_allsymbolsread_hook = false;
129 static bool register_cleanup_hook = false;
130 static bool dumpresolutions = false;
131
132 /* The master list of all claimable/claimed files. */
133 static claim_file_t *claimfiles_list = NULL;
134
135 /* We keep a tail pointer for easy linking on the end. */
136 static claim_file_t **claimfiles_tail_chain_ptr = &claimfiles_list;
137
138 /* The last claimed file added to the list, for receiving syms. */
139 static claim_file_t *last_claimfile = NULL;
140
141 /* The master list of all files to add to the final link. */
142 static add_file_t *addfiles_list = NULL;
143
144 /* We keep a tail pointer for easy linking on the end. */
145 static add_file_t **addfiles_tail_chain_ptr = &addfiles_list;
146
147 /* Add a new claimfile on the end of the chain. */
148 static enum ld_plugin_status
149 record_claim_file (const char *file, off_t filesize)
150 {
151 claim_file_t *newfile;
152
153 newfile = malloc (sizeof *newfile);
154 if (!newfile)
155 return LDPS_ERR;
156 memset (newfile, 0, sizeof *newfile);
157 /* Only setup for now is remembering the name to look for. */
158 newfile->file.name = file;
159 newfile->file.filesize = filesize;
160 /* Chain it on the end of the list. */
161 *claimfiles_tail_chain_ptr = newfile;
162 claimfiles_tail_chain_ptr = &newfile->next;
163 /* Record it as active for receiving symbols to register. */
164 last_claimfile = newfile;
165 return LDPS_OK;
166 }
167
168 /* Add a new addfile on the end of the chain. */
169 static enum ld_plugin_status
170 record_add_file (const char *file, addfile_enum_t type)
171 {
172 add_file_t *newfile;
173
174 newfile = malloc (sizeof *newfile);
175 if (!newfile)
176 return LDPS_ERR;
177 newfile->next = NULL;
178 newfile->name = file;
179 newfile->type = type;
180 /* Chain it on the end of the list. */
181 *addfiles_tail_chain_ptr = newfile;
182 addfiles_tail_chain_ptr = &newfile->next;
183 return LDPS_OK;
184 }
185
186 /* Parse a command-line argument string into a symbol definition.
187 Symbol-strings follow the colon-separated format:
188 NAME:VERSION:def:vis:size:COMDATKEY
189 where the fields in capitals are strings and those in lower
190 case are integers. We don't allow to specify a resolution as
191 doing so is not meaningful when calling the add symbols hook. */
192 static enum ld_plugin_status
193 parse_symdefstr (const char *str, struct ld_plugin_symbol *sym)
194 {
195 int n;
196 long long size;
197 const char *colon1, *colon2, *colon5;
198
199 /* Locate the colons separating the first two strings. */
200 colon1 = strchr (str, ':');
201 if (!colon1)
202 return LDPS_ERR;
203 colon2 = strchr (colon1+1, ':');
204 if (!colon2)
205 return LDPS_ERR;
206 /* Name must not be empty (version may be). */
207 if (colon1 == str)
208 return LDPS_ERR;
209
210 /* The fifth colon and trailing comdat key string are optional,
211 but the intermediate ones must all be present. */
212 colon5 = strchr (colon2+1, ':'); /* Actually only third so far. */
213 if (!colon5)
214 return LDPS_ERR;
215 colon5 = strchr (colon5+1, ':'); /* Hopefully fourth now. */
216 if (!colon5)
217 return LDPS_ERR;
218 colon5 = strchr (colon5+1, ':'); /* Optional fifth now. */
219
220 /* Finally we'll use sscanf to parse the numeric fields, then
221 we'll split out the strings which we need to allocate separate
222 storage for anyway so that we can add nul termination. */
223 n = sscanf (colon2 + 1, "%hhi:%i:%lli", &sym->def, &sym->visibility, &size);
224 if (n != 3)
225 return LDPS_ERR;
226
227 /* Parsed successfully, so allocate strings and fill out fields. */
228 sym->size = size;
229 sym->unused = 0;
230 sym->section_kind = 0;
231 sym->symbol_type = 0;
232 sym->resolution = LDPR_UNKNOWN;
233 sym->name = malloc (colon1 - str + 1);
234 if (!sym->name)
235 return LDPS_ERR;
236 memcpy (sym->name, str, colon1 - str);
237 sym->name[colon1 - str] = '\0';
238 if (colon2 > (colon1 + 1))
239 {
240 sym->version = malloc (colon2 - colon1);
241 if (!sym->version)
242 return LDPS_ERR;
243 memcpy (sym->version, colon1 + 1, colon2 - (colon1 + 1));
244 sym->version[colon2 - (colon1 + 1)] = '\0';
245 }
246 else
247 sym->version = NULL;
248 if (colon5 && colon5[1])
249 {
250 sym->comdat_key = malloc (strlen (colon5 + 1) + 1);
251 if (!sym->comdat_key)
252 return LDPS_ERR;
253 strcpy (sym->comdat_key, colon5 + 1);
254 }
255 else
256 sym->comdat_key = 0;
257 return LDPS_OK;
258 }
259
260 /* Record a symbol to be added for the last-added claimfile. */
261 static enum ld_plugin_status
262 record_claimed_file_symbol (const char *symdefstr)
263 {
264 struct ld_plugin_symbol sym;
265
266 /* Can't add symbols except as belonging to claimed files. */
267 if (!last_claimfile)
268 return LDPS_ERR;
269
270 /* If string doesn't parse correctly, give an error. */
271 if (parse_symdefstr (symdefstr, &sym) != LDPS_OK)
272 return LDPS_ERR;
273
274 /* Check for enough space, resize array if needed, and add it. */
275 if (last_claimfile->n_syms_allocated == last_claimfile->n_syms_used)
276 {
277 int new_n_syms = last_claimfile->n_syms_allocated
278 ? 2 * last_claimfile->n_syms_allocated
279 : 10;
280 last_claimfile->symbols = realloc (last_claimfile->symbols,
281 new_n_syms * sizeof *last_claimfile->symbols);
282 if (!last_claimfile->symbols)
283 return LDPS_ERR;
284 last_claimfile->n_syms_allocated = new_n_syms;
285 }
286 last_claimfile->symbols[last_claimfile->n_syms_used++] = sym;
287
288 return LDPS_OK;
289 }
290
291 /* Records the status to return from one of the registered hooks. */
292 static enum ld_plugin_status
293 set_ret_val (const char *whichval, enum ld_plugin_status retval)
294 {
295 if (!strcmp ("onload", whichval))
296 onload_ret = retval;
297 else if (!strcmp ("claimfile", whichval))
298 claim_file_ret = retval;
299 else if (!strcmp ("allsymbolsread", whichval))
300 all_symbols_read_ret = retval;
301 else if (!strcmp ("cleanup", whichval))
302 cleanup_ret = retval;
303 else
304 return LDPS_ERR;
305 return LDPS_OK;
306 }
307
308 /* Records hooks which should be registered. */
309 static enum ld_plugin_status
310 set_register_hook (const char *whichhook, bool yesno)
311 {
312 if (!strcmp ("claimfile", whichhook))
313 register_claimfile_hook = yesno;
314 else if (!strcmp ("allsymbolsread", whichhook))
315 register_allsymbolsread_hook = yesno;
316 else if (!strcmp ("cleanup", whichhook))
317 register_cleanup_hook = yesno;
318 else
319 return LDPS_ERR;
320 return LDPS_OK;
321 }
322
323 /* Determine type of plugin option and pass to individual parsers. */
324 static enum ld_plugin_status
325 parse_option (const char *opt)
326 {
327 if (!strncmp ("fail", opt, 4))
328 return set_ret_val (opt + 4, LDPS_ERR);
329 else if (!strncmp ("pass", opt, 4))
330 return set_ret_val (opt + 4, LDPS_OK);
331 else if (!strncmp ("register", opt, 8))
332 return set_register_hook (opt + 8, true);
333 else if (!strncmp ("noregister", opt, 10))
334 return set_register_hook (opt + 10, false);
335 else if (!strncmp ("claim:", opt, 6))
336 return record_claim_file (opt + 6, 0);
337 else if (!strncmp ("sym:", opt, 4))
338 return record_claimed_file_symbol (opt + 4);
339 else if (!strncmp ("add:", opt, 4))
340 return record_add_file (opt + 4, ADD_FILE);
341 else if (!strncmp ("lib:", opt, 4))
342 return record_add_file (opt + 4, ADD_LIB);
343 else if (!strncmp ("dir:", opt, 4))
344 return record_add_file (opt + 4, ADD_DIR);
345 else if (!strcmp ("dumpresolutions", opt))
346 dumpresolutions = true;
347 else
348 return LDPS_ERR;
349 return LDPS_OK;
350 }
351
352 /* Handle/record information received in a transfer vector entry. */
353 static enum ld_plugin_status
354 parse_tv_tag (struct ld_plugin_tv *tv)
355 {
356 #define SETVAR(x) x = tv->tv_u.x
357 switch (tv->tv_tag)
358 {
359 case LDPT_OPTION:
360 return parse_option (tv->tv_u.tv_string);
361 case LDPT_NULL:
362 case LDPT_GOLD_VERSION:
363 case LDPT_GNU_LD_VERSION:
364 case LDPT_API_VERSION:
365 default:
366 break;
367 case LDPT_OUTPUT_NAME:
368 output_name = tv->tv_u.tv_string;
369 break;
370 case LDPT_LINKER_OUTPUT:
371 linker_output = tv->tv_u.tv_val;
372 break;
373 case LDPT_REGISTER_CLAIM_FILE_HOOK:
374 SETVAR(tv_register_claim_file);
375 break;
376 case LDPT_REGISTER_CLAIM_FILE_HOOK_V2:
377 SETVAR(tv_register_claim_file_v2);
378 break;
379 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
380 SETVAR(tv_register_all_symbols_read);
381 break;
382 case LDPT_REGISTER_CLEANUP_HOOK:
383 SETVAR(tv_register_cleanup);
384 break;
385 case LDPT_ADD_SYMBOLS:
386 SETVAR(tv_add_symbols);
387 break;
388 case LDPT_GET_SYMBOLS:
389 SETVAR(tv_get_symbols);
390 break;
391 case LDPT_GET_SYMBOLS_V2:
392 tv_get_symbols_v2 = tv->tv_u.tv_get_symbols;
393 break;
394 case LDPT_ADD_INPUT_FILE:
395 SETVAR(tv_add_input_file);
396 break;
397 case LDPT_MESSAGE:
398 SETVAR(tv_message);
399 break;
400 case LDPT_GET_INPUT_FILE:
401 SETVAR(tv_get_input_file);
402 break;
403 case LDPT_GET_VIEW:
404 SETVAR(tv_get_view);
405 break;
406 case LDPT_RELEASE_INPUT_FILE:
407 SETVAR(tv_release_input_file);
408 break;
409 case LDPT_ADD_INPUT_LIBRARY:
410 SETVAR(tv_add_input_library);
411 break;
412 case LDPT_SET_EXTRA_LIBRARY_PATH:
413 SETVAR(tv_set_extra_library_path);
414 break;
415 }
416 #undef SETVAR
417 return LDPS_OK;
418 }
419
420 /* Standard plugin API entry point. */
421 enum ld_plugin_status
422 onload (struct ld_plugin_tv *tv)
423 {
424 enum ld_plugin_status rv;
425
426 /* This plugin does nothing but dump the tv array. It would
427 be an error if this function was called without one. */
428 if (!tv)
429 return LDPS_ERR;
430
431 /* First entry should always be LDPT_MESSAGE, letting us get
432 hold of it easily so we can send output straight away. */
433 if (tv[0].tv_tag == LDPT_MESSAGE)
434 tv_message = tv[0].tv_u.tv_message;
435
436 do
437 if ((rv = parse_tv_tag (tv)) != LDPS_OK)
438 return rv;
439 while ((tv++)->tv_tag != LDPT_NULL);
440
441 /* Register hooks only if instructed by options. */
442 if (register_claimfile_hook)
443 {
444 if (!tv_register_claim_file)
445 {
446 TV_MESSAGE (LDPL_FATAL, "No register_claim_file hook");
447 fflush (NULL);
448 return LDPS_ERR;
449 }
450 (*tv_register_claim_file) (onclaim_file);
451 }
452 if (register_allsymbolsread_hook)
453 {
454 if (!tv_register_all_symbols_read)
455 {
456 TV_MESSAGE (LDPL_FATAL, "No register_all_symbols_read hook");
457 fflush (NULL);
458 return LDPS_ERR;
459 }
460 (*tv_register_all_symbols_read) (onall_symbols_read);
461 }
462 if (register_cleanup_hook)
463 {
464 if (!tv_register_cleanup)
465 {
466 TV_MESSAGE (LDPL_FATAL, "No register_cleanup hook");
467 fflush (NULL);
468 return LDPS_ERR;
469 }
470 (*tv_register_cleanup) (oncleanup);
471 }
472
473 /* Claim testsuite/ld-plugin/func.c, standalone or in a library. Its
474 size must be SIZE_OF_FUNC_C bytes. */
475 #define SIZE_OF_FUNC_C 248
476 if (onload_ret == LDPS_OK
477 && (record_claim_file ("func.c", SIZE_OF_FUNC_C) != LDPS_OK
478 || record_claimed_file_symbol ("func::0:0:0") != LDPS_OK
479 || record_claimed_file_symbol ("_func::0:0:0") != LDPS_OK
480 || record_claim_file ("libfunc.a", SIZE_OF_FUNC_C) != LDPS_OK
481 || record_claimed_file_symbol ("func::0:0:0") != LDPS_OK
482 || record_claimed_file_symbol ("_func::0:0:0") != LDPS_OK))
483 onload_ret = LDPS_ERR;
484
485 return onload_ret;
486 }
487
488 char *
489 xstrdup (const char *s)
490 {
491 size_t len = strlen (s) + 1;
492 char *ret = malloc (len + 1);
493 return (char *) memcpy (ret, s, len);
494 }
495
496 /* Standard plugin API registerable hook. */
497 static enum ld_plugin_status
498 onclaim_file (const struct ld_plugin_input_file *file, int *claimed)
499 {
500 /* Let's see if we want to claim this file. */
501 claim_file_t *claimfile = claimfiles_list;
502 size_t len = strlen (file->name);
503 char *name = xstrdup (file->name);
504 char *p = name + len;
505 bool islib;
506
507 /* Only match the file name without the directory part. */
508 islib = *p == 'a' && *(p - 1) == '.';
509 for (; p != name; p--)
510 if (IS_DIR_SEPARATOR (*p))
511 {
512 p++;
513 break;
514 }
515
516 while (claimfile)
517 {
518 /* Claim the file only if the file name and size match and don't
519 match the whole library. */
520 if (!strcmp (p, claimfile->file.name)
521 && claimfile->file.filesize == file->filesize
522 && (!islib || file->offset != 0))
523 break;
524 claimfile = claimfile->next;
525 }
526
527 free (name);
528
529 /* If we decided to claim it, record that fact, and add any symbols
530 that were defined for it by plugin options. */
531 *claimed = (claimfile != 0);
532 if (claimfile)
533 {
534 char buffer[30];
535 int fd;
536
537 TV_MESSAGE (LDPL_INFO, "Claimed: %s [@%ld/%ld]", file->name,
538 (long)file->offset, (long)file->filesize);
539
540 claimfile->claimed = true;
541 claimfile->file = *file;
542 if (claimfile->n_syms_used && !tv_add_symbols)
543 claim_file_ret = LDPS_ERR;
544 else if (claimfile->n_syms_used)
545 claim_file_ret = (*tv_add_symbols) (claimfile->file.handle,
546 claimfile->n_syms_used,
547 claimfile->symbols);
548
549 fd = claimfile->file.fd;
550 name = xstrdup (claimfile->file.name);
551 claim_file_ret = tv_release_input_file (claimfile->file.handle);
552 if (claim_file_ret != LDPS_OK)
553 {
554 free (name);
555 return claim_file_ret;
556 }
557 if (read (fd, buffer, sizeof (buffer)) >= 0)
558 {
559 claim_file_ret = LDPS_ERR;
560 TV_MESSAGE (LDPL_FATAL, "Unreleased file descriptor on: %s", name);
561 }
562 free (name);
563 }
564
565 return claim_file_ret;
566 }
567
568 /* Standard plugin API registerable hook. */
569 static enum ld_plugin_status
570 onall_symbols_read (void)
571 {
572 static const char *resolutions[] =
573 {
574 "LDPR_UNKNOWN",
575 "LDPR_UNDEF",
576 "LDPR_PREVAILING_DEF",
577 "LDPR_PREVAILING_DEF_IRONLY",
578 "LDPR_PREEMPTED_REG",
579 "LDPR_PREEMPTED_IR",
580 "LDPR_RESOLVED_IR",
581 "LDPR_RESOLVED_EXEC",
582 "LDPR_RESOLVED_DYN",
583 "LDPR_PREVAILING_DEF_IRONLY_EXP",
584 };
585 claim_file_t *claimfile = dumpresolutions ? claimfiles_list : NULL;
586 add_file_t *addfile = addfiles_list;
587 TV_MESSAGE (LDPL_INFO, "hook called: all symbols read.");
588 for ( ; claimfile; claimfile = claimfile->next)
589 {
590 enum ld_plugin_status rv;
591 int n;
592 if (claimfile->n_syms_used && !tv_get_symbols_v2)
593 return LDPS_ERR;
594 else if (!claimfile->n_syms_used)
595 continue;
596 else if (!claimfile->file.handle)
597 continue;
598 rv = tv_get_symbols_v2 (claimfile->file.handle, claimfile->n_syms_used,
599 claimfile->symbols);
600 if (rv != LDPS_OK)
601 return rv;
602 for (n = 0; n < claimfile->n_syms_used; n++)
603 TV_MESSAGE (LDPL_INFO, "Sym: '%s%s%s' Resolution: %s",
604 claimfile->symbols[n].name,
605 claimfile->symbols[n].version ? "@" : "",
606 (claimfile->symbols[n].version
607 ? claimfile->symbols[n].version : ""),
608 resolutions[claimfile->symbols[n].resolution]);
609 }
610 for ( ; addfile ; addfile = addfile->next)
611 {
612 enum ld_plugin_status rv;
613 if (addfile->type == ADD_LIB && tv_add_input_library)
614 rv = (*tv_add_input_library) (addfile->name);
615 else if (addfile->type == ADD_FILE && tv_add_input_file)
616 rv = (*tv_add_input_file) (addfile->name);
617 else if (addfile->type == ADD_DIR && tv_set_extra_library_path)
618 rv = (*tv_set_extra_library_path) (addfile->name);
619 else
620 rv = LDPS_ERR;
621 if (rv != LDPS_OK)
622 return rv;
623 }
624 fflush (NULL);
625 return all_symbols_read_ret;
626 }
627
628 /* Standard plugin API registerable hook. */
629 static enum ld_plugin_status
630 oncleanup (void)
631 {
632 TV_MESSAGE (LDPL_INFO, "hook called: cleanup.");
633 fflush (NULL);
634 return cleanup_ret;
635 }
636 #endif /* BFD_SUPPORTS_PLUGINS */