1 /* test_plugin.c -- simple linker plugin test
3 Copyright (C) 2008-2023 Free Software Foundation, Inc.
4 Written by Cary Coutant <ccoutant@google.com>.
6 This file is part of gold.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
21 MA 02110-1301, USA. */
30 #include "plugin-api.h"
37 struct ld_plugin_symbol
* syms
;
38 struct claimed_file
* next
;
52 static struct claimed_file
* first_claimed_file
= NULL
;
53 static struct claimed_file
* last_claimed_file
= NULL
;
55 static ld_plugin_register_claim_file register_claim_file_hook
= NULL
;
56 static ld_plugin_register_all_symbols_read register_all_symbols_read_hook
= NULL
;
57 static ld_plugin_register_cleanup register_cleanup_hook
= NULL
;
58 static ld_plugin_add_symbols add_symbols
= NULL
;
59 static ld_plugin_get_symbols get_symbols
= NULL
;
60 static ld_plugin_get_symbols get_symbols_v2
= NULL
;
61 static ld_plugin_get_symbols get_symbols_v3
= NULL
;
62 static ld_plugin_add_input_file add_input_file
= NULL
;
63 static ld_plugin_message message
= NULL
;
64 static ld_plugin_get_input_file get_input_file
= NULL
;
65 static ld_plugin_release_input_file release_input_file
= NULL
;
66 static ld_plugin_get_input_section_count get_input_section_count
= NULL
;
67 static ld_plugin_get_input_section_type get_input_section_type
= NULL
;
68 static ld_plugin_get_input_section_name get_input_section_name
= NULL
;
69 static ld_plugin_get_input_section_contents get_input_section_contents
= NULL
;
70 static ld_plugin_update_section_order update_section_order
= NULL
;
71 static ld_plugin_allow_section_ordering allow_section_ordering
= NULL
;
72 static ld_plugin_get_wrap_symbols get_wrap_symbols
= NULL
;
76 static const char *opts
[MAXOPTS
];
79 enum ld_plugin_status
onload(struct ld_plugin_tv
*tv
);
80 enum ld_plugin_status
claim_file_hook(const struct ld_plugin_input_file
*file
,
82 enum ld_plugin_status
all_symbols_read_hook(void);
83 enum ld_plugin_status
cleanup_hook(void);
85 static void parse_readelf_line(char*, struct sym_info
*);
88 onload(struct ld_plugin_tv
*tv
)
90 struct ld_plugin_tv
*entry
;
95 for (entry
= tv
; entry
->tv_tag
!= LDPT_NULL
; ++entry
)
97 switch (entry
->tv_tag
)
99 case LDPT_API_VERSION
:
100 api_version
= entry
->tv_u
.tv_val
;
102 case LDPT_GOLD_VERSION
:
103 gold_version
= entry
->tv_u
.tv_val
;
105 case LDPT_LINKER_OUTPUT
:
109 opts
[nopts
++] = entry
->tv_u
.tv_string
;
111 case LDPT_REGISTER_CLAIM_FILE_HOOK
:
112 register_claim_file_hook
= entry
->tv_u
.tv_register_claim_file
;
114 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK
:
115 register_all_symbols_read_hook
=
116 entry
->tv_u
.tv_register_all_symbols_read
;
118 case LDPT_REGISTER_CLEANUP_HOOK
:
119 register_cleanup_hook
= entry
->tv_u
.tv_register_cleanup
;
121 case LDPT_ADD_SYMBOLS
:
122 add_symbols
= entry
->tv_u
.tv_add_symbols
;
124 case LDPT_GET_SYMBOLS
:
125 get_symbols
= entry
->tv_u
.tv_get_symbols
;
127 case LDPT_GET_SYMBOLS_V2
:
128 get_symbols_v2
= entry
->tv_u
.tv_get_symbols
;
130 case LDPT_GET_SYMBOLS_V3
:
131 get_symbols_v3
= entry
->tv_u
.tv_get_symbols
;
133 case LDPT_ADD_INPUT_FILE
:
134 add_input_file
= entry
->tv_u
.tv_add_input_file
;
137 message
= entry
->tv_u
.tv_message
;
139 case LDPT_GET_INPUT_FILE
:
140 get_input_file
= entry
->tv_u
.tv_get_input_file
;
142 case LDPT_RELEASE_INPUT_FILE
:
143 release_input_file
= entry
->tv_u
.tv_release_input_file
;
145 case LDPT_GET_INPUT_SECTION_COUNT
:
146 get_input_section_count
= *entry
->tv_u
.tv_get_input_section_count
;
148 case LDPT_GET_INPUT_SECTION_TYPE
:
149 get_input_section_type
= *entry
->tv_u
.tv_get_input_section_type
;
151 case LDPT_GET_INPUT_SECTION_NAME
:
152 get_input_section_name
= *entry
->tv_u
.tv_get_input_section_name
;
154 case LDPT_GET_INPUT_SECTION_CONTENTS
:
155 get_input_section_contents
= *entry
->tv_u
.tv_get_input_section_contents
;
157 case LDPT_UPDATE_SECTION_ORDER
:
158 update_section_order
= *entry
->tv_u
.tv_update_section_order
;
160 case LDPT_ALLOW_SECTION_ORDERING
:
161 allow_section_ordering
= *entry
->tv_u
.tv_allow_section_ordering
;
163 case LDPT_GET_WRAP_SYMBOLS
:
164 get_wrap_symbols
= *entry
->tv_u
.tv_get_wrap_symbols
;
173 fprintf(stderr
, "tv_message interface missing\n");
177 if (register_claim_file_hook
== NULL
)
179 fprintf(stderr
, "tv_register_claim_file_hook interface missing\n");
183 if (register_all_symbols_read_hook
== NULL
)
185 fprintf(stderr
, "tv_register_all_symbols_read_hook interface missing\n");
189 if (register_cleanup_hook
== NULL
)
191 fprintf(stderr
, "tv_register_cleanup_hook interface missing\n");
195 (*message
)(LDPL_INFO
, "API version: %d", api_version
);
196 (*message
)(LDPL_INFO
, "gold version: %d", gold_version
);
198 for (i
= 0; i
< nopts
; ++i
)
199 (*message
)(LDPL_INFO
, "option: %s", opts
[i
]);
201 if ((*register_claim_file_hook
)(claim_file_hook
) != LDPS_OK
)
203 (*message
)(LDPL_ERROR
, "error registering claim file hook");
207 if ((*register_all_symbols_read_hook
)(all_symbols_read_hook
) != LDPS_OK
)
209 (*message
)(LDPL_ERROR
, "error registering all symbols read hook");
213 if ((*register_cleanup_hook
)(cleanup_hook
) != LDPS_OK
)
215 (*message
)(LDPL_ERROR
, "error registering cleanup hook");
219 if (get_input_section_count
== NULL
)
221 fprintf(stderr
, "tv_get_input_section_count interface missing\n");
225 if (get_input_section_type
== NULL
)
227 fprintf(stderr
, "tv_get_input_section_type interface missing\n");
231 if (get_input_section_name
== NULL
)
233 fprintf(stderr
, "tv_get_input_section_name interface missing\n");
237 if (get_input_section_contents
== NULL
)
239 fprintf(stderr
, "tv_get_input_section_contents interface missing\n");
243 if (update_section_order
== NULL
)
245 fprintf(stderr
, "tv_update_section_order interface missing\n");
249 if (allow_section_ordering
== NULL
)
251 fprintf(stderr
, "tv_allow_section_ordering interface missing\n");
255 if (get_wrap_symbols
== NULL
)
257 fprintf(stderr
, "tv_get_wrap_symbols interface missing\n");
262 const char **wrap_symbols
;
264 if (get_wrap_symbols(&count
, &wrap_symbols
) == LDPS_OK
)
266 (*message
)(LDPL_INFO
, "Number of wrap symbols = %lu", count
);
267 for (; count
> 0; --count
)
268 (*message
)(LDPL_INFO
, "Wrap symbol %s", wrap_symbols
[count
- 1]);
272 fprintf(stderr
, "tv_get_wrap_symbols interface call failed\n");
280 enum ld_plugin_status
281 claim_file_hook (const struct ld_plugin_input_file
* file
, int* claimed
)
286 struct claimed_file
* claimed_file
;
287 struct ld_plugin_symbol
* syms
;
291 struct sym_info info
;
297 int irfile_was_opened
= 0;
300 (*message
)(LDPL_INFO
,
301 "%s: claim file hook called (offset = %ld, size = %ld)",
302 file
->name
, (long)file
->offset
, (long)file
->filesize
);
304 /* Look for matching syms file for an archive member. */
305 if (file
->offset
== 0)
306 snprintf(syms_name
, sizeof(syms_name
), "%s.syms", file
->name
);
308 snprintf(syms_name
, sizeof(syms_name
), "%s-%d.syms",
309 file
->name
, (int)file
->offset
);
310 irfile
= fopen(syms_name
, "r");
313 irfile_was_opened
= 1;
314 end_offset
= 1 << 20;
317 /* Otherwise, see if the file itself is a syms file. */
318 if (!irfile_was_opened
)
320 irfile
= fdopen(file
->fd
, "r");
321 (void)fseek(irfile
, file
->offset
, SEEK_SET
);
322 end_offset
= file
->offset
+ file
->filesize
;
325 /* Look for the beginning of output from readelf -s. */
326 len
= fread(buf
, 1, 13, irfile
);
327 if (len
< 13 || strncmp(buf
, "\nSymbol table", 13) != 0)
330 /* Skip the two header lines. */
331 (void) fgets(buf
, sizeof(buf
), irfile
);
332 (void) fgets(buf
, sizeof(buf
), irfile
);
334 if (add_symbols
== NULL
)
336 fprintf(stderr
, "tv_add_symbols interface missing\n");
340 /* Parse the output from readelf. The columns are:
341 Index Value Size Type Binding Visibility Section Name. */
342 syms
= (struct ld_plugin_symbol
*)malloc(sizeof(struct ld_plugin_symbol
) * 8);
346 while (ftell(irfile
) < end_offset
347 && fgets(buf
, sizeof(buf
), irfile
) != NULL
)
349 parse_readelf_line(buf
, &info
);
351 /* Ignore local symbols. */
352 if (strncmp(info
.bind
, "LOCAL", 5) == 0)
355 weak
= strncmp(info
.bind
, "WEAK", 4) == 0;
356 if (strncmp(info
.sect
, "UND", 3) == 0)
357 def
= weak
? LDPK_WEAKUNDEF
: LDPK_UNDEF
;
358 else if (strncmp(info
.sect
, "COM", 3) == 0)
361 def
= weak
? LDPK_WEAKDEF
: LDPK_DEF
;
363 if (strncmp(info
.vis
, "INTERNAL", 8) == 0)
365 else if (strncmp(info
.vis
, "HIDDEN", 6) == 0)
367 else if (strncmp(info
.vis
, "PROTECTED", 9) == 0)
368 vis
= LDPV_PROTECTED
;
372 /* If the symbol is listed in the options list, special-case
373 it as a comdat symbol. */
375 for (i
= 0; i
< nopts
; ++i
)
377 if (info
.name
!= NULL
&& strcmp(info
.name
, opts
[i
]) == 0)
384 if (nsyms
>= maxsyms
)
386 syms
= (struct ld_plugin_symbol
*)
387 realloc(syms
, sizeof(struct ld_plugin_symbol
) * maxsyms
* 2);
393 if (info
.name
== NULL
)
394 syms
[nsyms
].name
= NULL
;
397 len
= strlen(info
.name
);
398 syms
[nsyms
].name
= malloc(len
+ 1);
399 strncpy(syms
[nsyms
].name
, info
.name
, len
+ 1);
401 if (info
.ver
== NULL
)
402 syms
[nsyms
].version
= NULL
;
405 len
= strlen(info
.ver
);
406 syms
[nsyms
].version
= malloc(len
+ 1);
407 strncpy(syms
[nsyms
].version
, info
.ver
, len
+ 1);
409 syms
[nsyms
].def
= def
;
410 syms
[nsyms
].visibility
= vis
;
411 syms
[nsyms
].size
= info
.size
;
412 syms
[nsyms
].comdat_key
= is_comdat
? syms
[nsyms
].name
: NULL
;
413 syms
[nsyms
].resolution
= LDPR_UNKNOWN
;
417 claimed_file
= (struct claimed_file
*) malloc(sizeof(struct claimed_file
));
418 if (claimed_file
== NULL
)
421 claimed_file
->name
= file
->name
;
422 claimed_file
->handle
= file
->handle
;
423 claimed_file
->nsyms
= nsyms
;
424 claimed_file
->syms
= syms
;
425 claimed_file
->next
= NULL
;
426 if (last_claimed_file
== NULL
)
427 first_claimed_file
= claimed_file
;
429 last_claimed_file
->next
= claimed_file
;
430 last_claimed_file
= claimed_file
;
432 (*message
)(LDPL_INFO
, "%s: claiming file, adding %d symbols",
436 (*add_symbols
)(file
->handle
, nsyms
, syms
);
439 if (irfile_was_opened
)
444 enum ld_plugin_status
445 all_symbols_read_hook(void)
449 struct claimed_file
* claimed_file
;
450 struct ld_plugin_input_file file
;
453 struct sym_info info
;
457 const char* filename
;
459 (*message
)(LDPL_INFO
, "all symbols read hook called");
461 if (get_symbols_v3
== NULL
)
463 fprintf(stderr
, "tv_get_symbols (v3) interface missing\n");
467 for (claimed_file
= first_claimed_file
;
468 claimed_file
!= NULL
;
469 claimed_file
= claimed_file
->next
)
471 enum ld_plugin_status status
= (*get_symbols_v3
)(
472 claimed_file
->handle
, claimed_file
->nsyms
, claimed_file
->syms
);
473 if (status
== LDPS_NO_SYMS
)
475 (*message
)(LDPL_INFO
, "%s: no symbols", claimed_file
->name
);
479 for (i
= 0; i
< claimed_file
->nsyms
; ++i
)
481 switch (claimed_file
->syms
[i
].resolution
)
489 case LDPR_PREVAILING_DEF
:
490 res
= "PREVAILING_DEF_REG";
492 case LDPR_PREVAILING_DEF_IRONLY
:
493 res
= "PREVAILING_DEF_IRONLY";
495 case LDPR_PREVAILING_DEF_IRONLY_EXP
:
496 res
= "PREVAILING_DEF_IRONLY_EXP";
498 case LDPR_PREEMPTED_REG
:
499 res
= "PREEMPTED_REG";
501 case LDPR_PREEMPTED_IR
:
502 res
= "PREEMPTED_IR";
504 case LDPR_RESOLVED_IR
:
507 case LDPR_RESOLVED_EXEC
:
508 res
= "RESOLVED_EXEC";
510 case LDPR_RESOLVED_DYN
:
511 res
= "RESOLVED_DYN";
517 (*message
)(LDPL_INFO
, "%s: %s: %s", claimed_file
->name
,
518 claimed_file
->syms
[i
].name
, res
);
522 if (add_input_file
== NULL
)
524 fprintf(stderr
, "tv_add_input_file interface missing\n");
527 if (get_input_file
== NULL
)
529 fprintf(stderr
, "tv_get_input_file interface missing\n");
532 if (release_input_file
== NULL
)
534 fprintf(stderr
, "tv_release_input_file interface missing\n");
538 for (claimed_file
= first_claimed_file
;
539 claimed_file
!= NULL
;
540 claimed_file
= claimed_file
->next
)
542 int irfile_was_opened
= 0;
545 (*get_input_file
) (claimed_file
->handle
, &file
);
547 if (file
.offset
== 0)
548 snprintf(syms_name
, sizeof(syms_name
), "%s.syms", file
.name
);
550 snprintf(syms_name
, sizeof(syms_name
), "%s-%d.syms",
551 file
.name
, (int)file
.offset
);
552 irfile
= fopen(syms_name
, "r");
555 irfile_was_opened
= 1;
556 end_offset
= 1 << 20;
559 if (!irfile_was_opened
)
561 irfile
= fdopen(file
.fd
, "r");
562 (void)fseek(irfile
, file
.offset
, SEEK_SET
);
563 end_offset
= file
.offset
+ file
.filesize
;
566 /* Look for the beginning of output from readelf -s. */
567 len
= fread(buf
, 1, 13, irfile
);
568 if (len
< 13 || strncmp(buf
, "\nSymbol table", 13) != 0)
570 fprintf(stderr
, "%s: can't re-read original input file\n",
575 /* Skip the two header lines. */
576 (void) fgets(buf
, sizeof(buf
), irfile
);
577 (void) fgets(buf
, sizeof(buf
), irfile
);
580 while (ftell(irfile
) < end_offset
581 && fgets(buf
, sizeof(buf
), irfile
) != NULL
)
583 parse_readelf_line(buf
, &info
);
585 /* Look for file name. */
586 if (strncmp(info
.type
, "FILE", 4) == 0)
588 len
= strlen(info
.name
);
590 strncpy(p
, info
.name
, len
+ 1);
596 if (irfile_was_opened
)
599 (*release_input_file
) (claimed_file
->handle
);
601 if (filename
== NULL
)
602 filename
= claimed_file
->name
;
604 if (claimed_file
->nsyms
== 0)
607 if (strlen(filename
) >= sizeof(buf
))
609 (*message
)(LDPL_FATAL
, "%s: filename too long", filename
);
612 strcpy(buf
, filename
);
613 p
= strrchr(buf
, '.');
615 || (strcmp(p
, ".syms") != 0
616 && strcmp(p
, ".c") != 0
617 && strcmp(p
, ".cc") != 0))
619 (*message
)(LDPL_FATAL
, "%s: filename has unknown suffix",
625 (*message
)(LDPL_INFO
, "%s: adding new input file", buf
);
626 (*add_input_file
)(buf
);
632 enum ld_plugin_status
635 (*message
)(LDPL_INFO
, "cleanup hook called");
640 parse_readelf_line(char* p
, struct sym_info
* info
)
647 p
+= strcspn(p
, " ");
651 p
+= strcspn(p
, " ");
655 info
->size
= atoi(p
);
656 p
+= strcspn(p
, " ");
661 p
+= strcspn(p
, " ");
666 p
+= strcspn(p
, " ");
669 /* Visibility field. */
671 p
+= strcspn(p
, " ");
677 p
+= strcspn(p
, "]");
678 p
+= strspn(p
, "] ");
683 p
+= strcspn(p
, " ");
687 len
= strcspn(p
, "@\n");
688 if (len
> 0 && p
[len
] == '@')
690 /* Get the symbol version. */
694 vp
+= strspn(vp
, "@");
695 vlen
= strcspn(vp
, "\n");