OSDN Git Service

2010-10-11 Kai Tietz <kai.tietz@onevision.com>
[pf3gnuchains/gcc-fork.git] / lto-plugin / lto-plugin.c
1 /* LTO plugin for gold and/or GNU ld.
2    Copyright (C) 2009, 2010 Free Software Foundation, Inc.
3    Contributed by Rafael Avila de Espindola (espindola@google.com).
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING3.  If not see
17 <http://www.gnu.org/licenses/>.  */
18
19 /* The plugin has only one external function: onload. Gold passes it an array of
20    function that the plugin uses to communicate back to gold.
21
22    With the functions provided by gold, the plugin can be notified when
23    gold first analyzes a file and pass a symbol table back to gold. The plugin
24    is also notified when all symbols have been read and it is time to generate
25    machine code for the necessary symbols.
26
27    More information at http://gcc.gnu.org/wiki/whopr/driver.
28
29    This plugin should be passed the lto-wrapper options and will forward them.
30    It also has 2 options of its own:
31    -debug: Print the command line used to run lto-wrapper.
32    -nop: Instead of running lto-wrapper, pass the original to the plugin. This
33    only works if the input files are hybrid.  */
34
35 #include <assert.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <inttypes.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 #include <fcntl.h>
43 #include <sys/types.h>
44 #include <sys/wait.h>
45 #include <stdbool.h>
46 #include <libiberty.h>
47 #include <hashtab.h>
48 #include "../gcc/lto/common.h"
49
50 /* Common definitions for/from the object format dependent code.  */
51 #include "lto-plugin.h"
52
53 static char *arguments_file_name;
54 static ld_plugin_register_claim_file register_claim_file;
55 static ld_plugin_register_all_symbols_read register_all_symbols_read;
56 static ld_plugin_get_symbols get_symbols;
57 static ld_plugin_register_cleanup register_cleanup;
58 static ld_plugin_add_input_file add_input_file;
59 static ld_plugin_add_input_library add_input_library;
60 static ld_plugin_message message;
61
62 /* These are not static because the object format dependent
63    claim_file hooks in lto-plugin-{coff,elf}.c need them.  */
64 ld_plugin_add_symbols add_symbols;
65
66 struct plugin_file_info *claimed_files = NULL;
67 unsigned int num_claimed_files = 0;
68
69 static char **output_files = NULL;
70 static unsigned int num_output_files = 0;
71
72 static char **lto_wrapper_argv;
73 static int lto_wrapper_num_args;
74
75 static char **pass_through_items = NULL;
76 static unsigned int num_pass_through_items;
77
78 static bool debug;
79 static bool nop;
80 static char *resolution_file = NULL;
81
82 void
83 check (bool gate, enum ld_plugin_level level, const char *text)
84 {
85   if (gate)
86     return;
87
88   if (message)
89     message (level, text);
90   else
91     {
92       /* If there is no nicer way to inform the user, fallback to stderr. */
93       fprintf (stderr, "%s\n", text);
94       if (level == LDPL_FATAL)
95         abort ();
96     }
97 }
98
99 /* Parse an entry of the IL symbol table. The data to be parsed is pointed
100    by P and the result is written in ENTRY. The slot number is stored in SLOT.
101    Returns the address of the next entry. */
102
103 char *
104 parse_table_entry (char *p, struct ld_plugin_symbol *entry, 
105                    struct sym_aux *aux)
106 {
107   unsigned char t;
108   enum ld_plugin_symbol_kind translate_kind[] =
109     {
110       LDPK_DEF,
111       LDPK_WEAKDEF,
112       LDPK_UNDEF,
113       LDPK_WEAKUNDEF,
114       LDPK_COMMON
115     };
116
117   enum ld_plugin_symbol_visibility translate_visibility[] =
118     {
119       LDPV_DEFAULT,
120       LDPV_PROTECTED,
121       LDPV_INTERNAL,
122       LDPV_HIDDEN
123     };
124
125   entry->name = xstrdup (p);
126   while (*p)
127     p++;
128   p++;
129
130   entry->version = NULL;
131
132   entry->comdat_key = p;
133   while (*p)
134     p++;
135   p++;
136
137   if (strlen (entry->comdat_key) == 0)
138     entry->comdat_key = NULL;
139   else
140     entry->comdat_key = xstrdup (entry->comdat_key);
141
142   t = *p;
143   check (t <= 4, LDPL_FATAL, "invalid symbol kind found");
144   entry->def = translate_kind[t];
145   p++;
146
147   t = *p;
148   check (t <= 3, LDPL_FATAL, "invalid symbol visibility found");
149   entry->visibility = translate_visibility[t];
150   p++;
151
152   entry->size = *(uint64_t *) p;
153   p += 8;
154
155   aux->slot = *(uint32_t *) p;
156   p += 4;
157
158   entry->resolution = LDPR_UNKNOWN;
159
160   aux->next_conflict = -1;
161
162   return p;
163 }
164
165 /* Translate the IL symbol table located between DATA and END. Append the
166    slots and symbols to OUT. */
167
168 void
169 translate (char *data, char *end, struct plugin_symtab *out)
170 {
171   struct sym_aux *aux;
172   struct ld_plugin_symbol *syms = NULL;
173   int n, len;
174
175   /* This overestimates the output buffer sizes, but at least 
176      the algorithm is O(1) now. */
177
178   len = (end - data)/8 + out->nsyms + 1;
179   syms = xrealloc (out->syms, len * sizeof (struct ld_plugin_symbol));
180   aux = xrealloc (out->aux, len * sizeof (struct sym_aux));
181   
182   for (n = out->nsyms; data < end; n++) 
183     { 
184       aux[n].id = out->id; 
185       data = parse_table_entry (data, &syms[n], &aux[n]);
186     }
187
188   assert(n < len);
189
190   out->nsyms = n;
191   out->syms = syms;
192   out->aux = aux;
193 }
194
195 /* Free all memory that is no longer needed after writing the symbol
196    resolution. */
197
198 static void
199 free_1 (void)
200 {
201   unsigned int i;
202   for (i = 0; i < num_claimed_files; i++)
203     {
204       struct plugin_file_info *info = &claimed_files[i];
205       struct plugin_symtab *symtab = &info->symtab;
206       unsigned int j;
207       for (j = 0; j < symtab->nsyms; j++)
208         {
209           struct ld_plugin_symbol *s = &symtab->syms[j];
210           free (s->name);
211           if (s->comdat_key)
212             free (s->comdat_key);
213         }
214       free (symtab->syms);
215       symtab->syms = NULL;
216     }
217 }
218
219 /* Free all remaining memory. */
220
221 static void
222 free_2 (void)
223 {
224   unsigned int i;
225   for (i = 0; i < num_claimed_files; i++)
226     {
227       struct plugin_file_info *info = &claimed_files[i];
228       struct plugin_symtab *symtab = &info->symtab;
229       free (symtab->aux);
230       free (info->name);
231     }
232
233   for (i = 0; i < num_output_files; i++)
234     free (output_files[i]);
235   free (output_files);
236
237   free (claimed_files);
238   claimed_files = NULL;
239   num_claimed_files = 0;
240
241   if (arguments_file_name)
242     free (arguments_file_name);
243   arguments_file_name = NULL;
244 }
245
246 /* Dump SYMTAB to resolution file F. */
247
248 static void
249 dump_symtab (FILE *f, struct plugin_symtab *symtab)
250 {
251   unsigned j;
252
253   for (j = 0; j < symtab->nsyms; j++)
254     {
255       uint32_t slot = symtab->aux[j].slot;
256       unsigned int resolution = symtab->syms[j].resolution;
257       
258       assert (resolution != LDPR_UNKNOWN);
259
260       fprintf (f, "%u %x %s %s\n", (unsigned int) slot, symtab->aux[j].id,
261                lto_resolution_str[resolution], 
262                symtab->syms[j].name);
263     }
264 }
265
266 /* Finish the conflicts' resolution information after the linker resolved
267    the original symbols */
268
269 static void
270 finish_conflict_resolution (struct plugin_symtab *symtab, 
271                            struct plugin_symtab *conflicts)
272 {
273   int i, j;
274
275   if (conflicts->nsyms == 0)
276     return;
277
278   for (i = 0; i < symtab->nsyms; i++)
279     { 
280       int resolution = LDPR_UNKNOWN;
281
282       if (symtab->aux[i].next_conflict == -1)
283         continue;
284
285       switch (symtab->syms[i].def) 
286         {
287         case LDPK_DEF:
288         case LDPK_COMMON: /* ??? */
289           resolution = LDPR_RESOLVED_IR; 
290           break;
291         case LDPK_WEAKDEF:
292           resolution = LDPR_PREEMPTED_IR;
293           break;
294         case LDPK_UNDEF:
295         case LDPK_WEAKUNDEF:
296           resolution = symtab->syms[i].resolution;
297           break;
298         default:
299           assert (0);
300         }
301
302       assert (resolution != LDPR_UNKNOWN);
303
304       for (j = symtab->aux[i].next_conflict; 
305            j != -1; 
306            j = conflicts->aux[j].next_conflict)
307         conflicts->syms[j].resolution = resolution;
308     }
309 }
310
311 /* Free symbol table SYMTAB. */
312
313 static void
314 free_symtab (struct plugin_symtab *symtab)
315 {
316   free (symtab->syms);
317   symtab->syms = NULL;
318   free (symtab->aux);
319   symtab->aux = NULL;
320 }
321
322 /*  Writes the relocations to disk. */
323
324 static void
325 write_resolution (void)
326 {
327   unsigned int i;
328   FILE *f;
329
330   check (resolution_file, LDPL_FATAL, "resolution file not specified");
331   f = fopen (resolution_file, "w");
332   check (f, LDPL_FATAL, "could not open file");
333
334   fprintf (f, "%d\n", num_claimed_files);
335
336   for (i = 0; i < num_claimed_files; i++)
337     {
338       struct plugin_file_info *info = &claimed_files[i];
339       struct plugin_symtab *symtab = &info->symtab;
340       struct ld_plugin_symbol *syms = symtab->syms;
341
342       get_symbols (info->handle, symtab->nsyms, syms);
343
344       finish_conflict_resolution (symtab, &info->conflicts);
345
346       fprintf (f, "%s %d\n", info->name, symtab->nsyms + info->conflicts.nsyms);
347       dump_symtab (f, symtab);
348       if (info->conflicts.nsyms)
349         {
350           dump_symtab (f, &info->conflicts);
351           free_symtab (&info->conflicts);
352         }
353     }
354   fclose (f);
355 }
356
357 /* Pass files generated by the lto-wrapper to the linker. FD is lto-wrapper's
358    stdout. */
359
360 static void
361 add_output_files (FILE *f)
362 {
363   for (;;)
364     {
365       const unsigned piece = 32;
366       char *buf, *s = xmalloc (piece);
367       size_t len;
368
369       buf = s;
370 cont:
371       if (!fgets (buf, piece, f))
372         break;
373       len = strlen (s);
374       if (s[len - 1] != '\n')
375         {
376           s = xrealloc (s, len + piece);
377           buf = s + len;
378           goto cont;
379         }
380       s[len - 1] = '\0';
381
382       num_output_files++;
383       output_files
384         = xrealloc (output_files, num_output_files * sizeof (char *));
385       output_files[num_output_files - 1] = s;
386       add_input_file (output_files[num_output_files - 1]);
387     }
388 }
389
390 /* Execute the lto-wrapper. ARGV[0] is the binary. The rest of ARGV is the
391    argument list. */
392
393 static void
394 exec_lto_wrapper (char *argv[])
395 {
396   int t, i;
397   int status;
398   char *at_args;
399   FILE *args;
400   FILE *wrapper_output;
401   char *new_argv[3];
402   struct pex_obj *pex;
403   const char *errmsg;
404
405   /* Write argv to a file to avoid a command line that is too long. */
406   arguments_file_name = make_temp_file ("");
407   check (arguments_file_name, LDPL_FATAL,
408          "Failed to generate a temorary file name");
409
410   args = fopen (arguments_file_name, "w");
411   check (args, LDPL_FATAL, "could not open arguments file");
412
413   t = writeargv (&argv[1], args);
414   check (t == 0, LDPL_FATAL, "could not write arguments");
415   t = fclose (args);
416   check (t == 0, LDPL_FATAL, "could not close arguments file");
417
418   at_args = concat ("@", arguments_file_name, NULL);
419   check (at_args, LDPL_FATAL, "could not allocate");
420
421   for (i = 1; argv[i]; i++)
422     {
423       char *a = argv[i];
424       if (a[0] == '-' && a[1] == 'v' && a[2] == '\0')
425         {
426           for (i = 0; argv[i]; i++)
427             fprintf (stderr, "%s ", argv[i]);
428           fprintf (stderr, "\n");
429           break;
430         }
431     }
432
433   new_argv[0] = argv[0];
434   new_argv[1] = at_args;
435   new_argv[2] = NULL;
436
437   if (debug)
438     {
439       for (i = 0; new_argv[i]; i++)
440         fprintf (stderr, "%s ", new_argv[i]);
441       fprintf (stderr, "\n");
442     }
443
444
445   pex = pex_init (PEX_USE_PIPES, "lto-wrapper", NULL);
446   check (pex != NULL, LDPL_FATAL, "could not pex_init lto-wrapper");
447
448   errmsg = pex_run (pex, 0, new_argv[0], new_argv, NULL, NULL, &t);
449   check (errmsg == NULL, LDPL_FATAL, "could not run lto-wrapper");
450   check (t == 0, LDPL_FATAL, "could not run lto-wrapper");
451
452   wrapper_output = pex_read_output (pex, 0);
453   check (wrapper_output, LDPL_FATAL, "could not read lto-wrapper output");
454
455   add_output_files (wrapper_output);
456
457   t = pex_get_status (pex, 1, &status);
458   check (t == 1, LDPL_FATAL, "could not get lto-wrapper exit status");
459   check (WIFEXITED (status) && WEXITSTATUS (status) == 0, LDPL_FATAL,
460          "lto-wrapper failed");
461
462   pex_free (pex);
463
464   free (at_args);
465 }
466
467 /* Pass the original files back to the linker. */
468
469 static void
470 use_original_files (void)
471 {
472   unsigned i;
473   for (i = 0; i < num_claimed_files; i++)
474     {
475       struct plugin_file_info *info = &claimed_files[i];
476       add_input_file (info->name);
477     }
478 }
479
480
481 /* Called by the linker once all symbols have been read. */
482
483 static enum ld_plugin_status
484 all_symbols_read_handler (void)
485 {
486   unsigned i;
487   unsigned num_lto_args = num_claimed_files + lto_wrapper_num_args + 1;
488   char **lto_argv;
489   const char **lto_arg_ptr;
490   if (num_claimed_files == 0)
491     return LDPS_OK;
492
493   if (nop)
494     {
495       use_original_files ();
496       return LDPS_OK;
497     }
498
499   lto_argv = (char **) xcalloc (sizeof (char *), num_lto_args);
500   lto_arg_ptr = (const char **) lto_argv;
501   assert (lto_wrapper_argv);
502
503   write_resolution ();
504
505   free_1 ();
506
507   for (i = 0; i < lto_wrapper_num_args; i++)
508     *lto_arg_ptr++ = lto_wrapper_argv[i];
509
510   for (i = 0; i < num_claimed_files; i++)
511     {
512       struct plugin_file_info *info = &claimed_files[i];
513
514       *lto_arg_ptr++ = info->name;
515     }
516
517   *lto_arg_ptr++ = NULL;
518   exec_lto_wrapper (lto_argv);
519
520   free (lto_argv);
521
522   if (pass_through_items)
523     {
524       unsigned int i;
525       for (i = 0; i < num_pass_through_items; i++)
526         {
527           if (strncmp (pass_through_items[i], "-l", 2) == 0)
528             add_input_library (pass_through_items[i] + 2);
529           else
530             add_input_file (pass_through_items[i]);
531           free (pass_through_items[i]);
532           pass_through_items[i] = NULL;
533         }
534       free (pass_through_items);
535       pass_through_items = NULL;
536     }
537
538   return LDPS_OK;
539 }
540
541 /* Remove temporary files at the end of the link. */
542
543 static enum ld_plugin_status
544 cleanup_handler (void)
545 {
546   unsigned int i;
547   int t;
548
549   if (debug)
550     return LDPS_OK;
551
552   if (arguments_file_name)
553     {
554       t = unlink (arguments_file_name);
555       check (t == 0, LDPL_FATAL, "could not unlink arguments file");
556     }
557
558   for (i = 0; i < num_output_files; i++)
559     {
560       t = unlink (output_files[i]);
561       check (t == 0, LDPL_FATAL, "could not unlink output file");
562     }
563
564   free_2 ();
565   return LDPS_OK;
566 }
567
568 #define SWAP(type, a, b) \
569   do { type tmp_; tmp_ = (a); (a) = (b); (b) = tmp_; } while(0)
570
571 /* Compare two hash table entries */
572
573 static int eq_sym (const void *a, const void *b)
574 {
575   const struct ld_plugin_symbol *as = (const struct ld_plugin_symbol *)a;
576   const struct ld_plugin_symbol *bs = (const struct ld_plugin_symbol *)b;
577
578   return !strcmp (as->name, bs->name);
579 }
580
581 /* Hash a symbol */
582
583 static hashval_t hash_sym (const void *a)
584 {
585   const struct ld_plugin_symbol *as = (const struct ld_plugin_symbol *)a;
586
587   return htab_hash_string (as->name);
588 }
589
590 /* Determine how strong a symbol is */
591
592 static int symbol_strength (struct ld_plugin_symbol *s)
593 {
594   switch (s->def) 
595     { 
596     case LDPK_UNDEF:
597     case LDPK_WEAKUNDEF:
598       return 0;
599     case LDPK_WEAKDEF:
600       return 1;
601     default:
602       return 2;
603     }
604 }
605
606 /* In the ld -r case we can get dups in the LTO symbol tables, where
607    the same symbol can have different resolutions (e.g. undefined and defined).
608
609    We have to keep that in the LTO symbol tables, but the dups confuse
610    gold and then finally gcc by supplying incorrect resolutions.
611
612    Problem is that the main gold symbol table doesn't know about subids
613    and does not distingush the same symbols in different states.
614
615    So we drop duplicates from the linker visible symbol table
616    and keep them in a private table. Then later do own symbol
617    resolution for the duplicated based on the results for the
618    originals.
619
620    Then when writing out the resolution file readd the dropped symbols.
621    
622    XXX how to handle common? */
623
624 void
625 resolve_conflicts (struct plugin_symtab *t, struct plugin_symtab *conflicts)
626 {
627   htab_t symtab = htab_create (t->nsyms, hash_sym, eq_sym, NULL);
628   int i;
629   int out;
630   int outlen;
631
632   outlen = t->nsyms;
633   conflicts->syms = xmalloc (sizeof (struct ld_plugin_symbol) * outlen);
634   conflicts->aux = xmalloc (sizeof (struct sym_aux) * outlen);
635
636   /* Move all duplicate symbols into the auxillary conflicts table. */
637   out = 0;
638   for (i = 0; i < t->nsyms; i++) 
639     {
640       struct ld_plugin_symbol *s = &t->syms[i];
641       struct sym_aux *aux = &t->aux[i];
642       void **slot;
643
644       slot = htab_find_slot (symtab, s, INSERT);
645       if (*slot != NULL)
646         {
647           int cnf;
648           struct ld_plugin_symbol *orig = (struct ld_plugin_symbol *)*slot;
649           struct sym_aux *orig_aux = &t->aux[orig - t->syms];
650
651           /* Always let the linker resolve the strongest symbol */
652           if (symbol_strength (orig) < symbol_strength (s)) 
653             {
654               SWAP (struct ld_plugin_symbol, *orig, *s);
655               SWAP (uint32_t, orig_aux->slot, aux->slot);
656               SWAP (unsigned, orig_aux->id, aux->id);
657               /* Don't swap conflict chain pointer */
658             } 
659
660           /* Move current symbol into the conflicts table */
661           cnf = conflicts->nsyms++;
662           conflicts->syms[cnf] = *s;
663           conflicts->aux[cnf] = *aux;
664           aux = &conflicts->aux[cnf];
665
666           /* Update conflicts chain of the original symbol */
667           aux->next_conflict = orig_aux->next_conflict;
668           orig_aux->next_conflict = cnf;
669
670           continue;
671         }
672
673       /* Remove previous duplicates in the main table */
674       if (out < i)
675         {
676           t->syms[out] = *s;
677           t->aux[out] = *aux;
678         }
679
680       /* Put original into the hash table */
681       *slot = &t->syms[out];
682       out++;
683     }
684
685   assert (conflicts->nsyms <= outlen);
686   assert (conflicts->nsyms + out == t->nsyms);
687   
688   t->nsyms = out;
689   htab_delete (symtab);
690 }
691
692 /* Parse the plugin options. */
693
694 static void
695 process_option (const char *option)
696 {
697   if (strcmp (option, "-debug") == 0)
698     debug = 1;
699   else if (strcmp (option, "-nop") == 0)
700     nop = 1;
701   else if (!strncmp (option, "-pass-through=", strlen("-pass-through=")))
702     {
703       num_pass_through_items++;
704       pass_through_items = xrealloc (pass_through_items,
705                                      num_pass_through_items * sizeof (char *));
706       pass_through_items[num_pass_through_items - 1] =
707           xstrdup (option + strlen ("-pass-through="));
708     }
709   else
710     {
711       int size;
712       char *opt = xstrdup (option);
713       lto_wrapper_num_args += 1;
714       size = lto_wrapper_num_args * sizeof (char *);
715       lto_wrapper_argv = (char **) xrealloc (lto_wrapper_argv, size);
716       lto_wrapper_argv[lto_wrapper_num_args - 1] = opt;
717       if (strncmp (option, "-fresolution=", sizeof ("-fresolution=") - 1) == 0)
718         resolution_file = opt + sizeof ("-fresolution=") - 1;
719     }
720 }
721
722 /* Called by gold after loading the plugin. TV is the transfer vector. */
723
724 enum ld_plugin_status
725 onload (struct ld_plugin_tv *tv)
726 {
727   struct ld_plugin_tv *p;
728   enum ld_plugin_status status;
729
730   status = onload_format_checks (tv);
731   if (status != LDPS_OK)
732     return status;
733
734   p = tv;
735   while (p->tv_tag)
736     {
737       switch (p->tv_tag)
738         {
739         case LDPT_MESSAGE:
740           message = p->tv_u.tv_message;
741           break;
742         case LDPT_REGISTER_CLAIM_FILE_HOOK:
743           register_claim_file = p->tv_u.tv_register_claim_file;
744           break;
745         case LDPT_ADD_SYMBOLS:
746           add_symbols = p->tv_u.tv_add_symbols;
747           break;
748         case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
749           register_all_symbols_read = p->tv_u.tv_register_all_symbols_read;
750           break;
751         case LDPT_GET_SYMBOLS:
752           get_symbols = p->tv_u.tv_get_symbols;
753           break;
754         case LDPT_REGISTER_CLEANUP_HOOK:
755           register_cleanup = p->tv_u.tv_register_cleanup;
756           break;
757         case LDPT_ADD_INPUT_FILE:
758           add_input_file = p->tv_u.tv_add_input_file;
759           break;
760         case LDPT_ADD_INPUT_LIBRARY:
761           add_input_library = p->tv_u.tv_add_input_library;
762           break;
763         case LDPT_OPTION:
764           process_option (p->tv_u.tv_string);
765           break;
766         default:
767           break;
768         }
769       p++;
770     }
771
772   check (register_claim_file, LDPL_FATAL, "register_claim_file not found");
773   check (add_symbols, LDPL_FATAL, "add_symbols not found");
774   status = register_claim_file (claim_file_handler);
775   check (status == LDPS_OK, LDPL_FATAL,
776          "could not register the claim_file callback");
777
778   if (register_cleanup)
779     {
780       status = register_cleanup (cleanup_handler);
781       check (status == LDPS_OK, LDPL_FATAL,
782              "could not register the cleanup callback");
783     }
784
785   if (register_all_symbols_read)
786     {
787       check (get_symbols, LDPL_FATAL, "get_symbols not found");
788       status = register_all_symbols_read (all_symbols_read_handler);
789       check (status == LDPS_OK, LDPL_FATAL,
790              "could not register the all_symbols_read callback");
791     }
792
793   return LDPS_OK;
794 }