2020-12-14 22:26:11 +08:00
|
|
|
/* libdeps plugin for the GNU linker.
|
2024-01-04 19:52:08 +08:00
|
|
|
Copyright (C) 2020-2024 Free Software Foundation, Inc.
|
2020-12-14 22:26:11 +08:00
|
|
|
|
|
|
|
This file is part of the GNU Binutils.
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
|
|
|
MA 02110-1301, USA. */
|
|
|
|
|
|
|
|
#include "sysdep.h"
|
|
|
|
#include "bfd.h"
|
|
|
|
#if BFD_SUPPORTS_PLUGINS
|
|
|
|
#include "plugin-api.h"
|
|
|
|
|
|
|
|
#include <ctype.h> /* For isspace. */
|
|
|
|
|
|
|
|
extern enum ld_plugin_status onload (struct ld_plugin_tv *tv);
|
|
|
|
|
|
|
|
/* Helper for calling plugin api message function. */
|
|
|
|
#define TV_MESSAGE if (tv_message) (*tv_message)
|
|
|
|
|
|
|
|
/* Function pointers to cache hooks passed at onload time. */
|
|
|
|
static ld_plugin_register_claim_file tv_register_claim_file = 0;
|
|
|
|
static ld_plugin_register_all_symbols_read tv_register_all_symbols_read = 0;
|
|
|
|
static ld_plugin_register_cleanup tv_register_cleanup = 0;
|
|
|
|
static ld_plugin_message tv_message = 0;
|
|
|
|
static ld_plugin_add_input_library tv_add_input_library = 0;
|
|
|
|
static ld_plugin_set_extra_library_path tv_set_extra_library_path = 0;
|
|
|
|
|
|
|
|
/* Handle/record information received in a transfer vector entry. */
|
|
|
|
static enum ld_plugin_status
|
|
|
|
parse_tv_tag (struct ld_plugin_tv *tv)
|
|
|
|
{
|
|
|
|
#define SETVAR(x) x = tv->tv_u.x
|
|
|
|
switch (tv->tv_tag)
|
|
|
|
{
|
|
|
|
case LDPT_REGISTER_CLAIM_FILE_HOOK:
|
|
|
|
SETVAR(tv_register_claim_file);
|
|
|
|
break;
|
|
|
|
case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
|
|
|
|
SETVAR(tv_register_all_symbols_read);
|
|
|
|
break;
|
|
|
|
case LDPT_REGISTER_CLEANUP_HOOK:
|
|
|
|
SETVAR(tv_register_cleanup);
|
|
|
|
break;
|
|
|
|
case LDPT_MESSAGE:
|
|
|
|
SETVAR(tv_message);
|
|
|
|
break;
|
|
|
|
case LDPT_ADD_INPUT_LIBRARY:
|
|
|
|
SETVAR(tv_add_input_library);
|
|
|
|
break;
|
|
|
|
case LDPT_SET_EXTRA_LIBRARY_PATH:
|
|
|
|
SETVAR(tv_set_extra_library_path);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#undef SETVAR
|
|
|
|
return LDPS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Defs for archive parsing. */
|
|
|
|
#define ARMAGSIZE 8
|
|
|
|
typedef struct arhdr
|
|
|
|
{
|
|
|
|
char ar_name[16];
|
|
|
|
char ar_date[12];
|
|
|
|
char ar_uid[6];
|
|
|
|
char ar_gid[6];
|
|
|
|
char ar_mode[8];
|
|
|
|
char ar_size[10];
|
|
|
|
char ar_fmag[2];
|
|
|
|
} arhdr;
|
|
|
|
|
|
|
|
typedef struct linerec
|
|
|
|
{
|
|
|
|
struct linerec *next;
|
|
|
|
char line[];
|
|
|
|
} linerec;
|
|
|
|
|
|
|
|
#define LIBDEPS "__.LIBDEP/ "
|
|
|
|
|
|
|
|
static linerec *line_head, **line_tail = &line_head;
|
|
|
|
|
|
|
|
static enum ld_plugin_status
|
|
|
|
get_libdeps (int fd)
|
|
|
|
{
|
|
|
|
arhdr ah;
|
|
|
|
int len;
|
|
|
|
unsigned long mlen;
|
2022-05-03 18:40:41 +08:00
|
|
|
size_t amt;
|
2020-12-14 22:26:11 +08:00
|
|
|
linerec *lr;
|
|
|
|
enum ld_plugin_status rc = LDPS_NO_SYMS;
|
|
|
|
|
|
|
|
lseek (fd, ARMAGSIZE, SEEK_SET);
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
len = read (fd, (void *) &ah, sizeof (ah));
|
|
|
|
if (len != sizeof (ah))
|
|
|
|
break;
|
|
|
|
mlen = strtoul (ah.ar_size, NULL, 10);
|
|
|
|
if (!mlen || strncmp (ah.ar_name, LIBDEPS, sizeof (LIBDEPS)-1))
|
|
|
|
{
|
|
|
|
lseek (fd, mlen, SEEK_CUR);
|
|
|
|
continue;
|
|
|
|
}
|
2022-05-03 18:40:41 +08:00
|
|
|
amt = mlen + sizeof (linerec);
|
|
|
|
if (amt <= mlen)
|
|
|
|
return LDPS_ERR;
|
|
|
|
lr = malloc (amt);
|
2020-12-14 22:26:11 +08:00
|
|
|
if (!lr)
|
|
|
|
return LDPS_ERR;
|
|
|
|
lr->next = NULL;
|
|
|
|
len = read (fd, lr->line, mlen);
|
|
|
|
lr->line[mlen-1] = '\0';
|
|
|
|
*line_tail = lr;
|
|
|
|
line_tail = &lr->next;
|
|
|
|
rc = LDPS_OK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2024-06-24 23:00:14 +08:00
|
|
|
/* Parse arguments in-place as contiguous C-strings
|
|
|
|
and return the number of arguments. */
|
|
|
|
|
|
|
|
static int
|
|
|
|
parse_libdep (char *str)
|
2020-12-14 22:26:11 +08:00
|
|
|
{
|
2024-06-24 23:00:14 +08:00
|
|
|
char *src, *dst;
|
|
|
|
char quote;
|
|
|
|
int narg;
|
|
|
|
|
|
|
|
src = dst = str;
|
|
|
|
|
|
|
|
for (; isspace ((unsigned char) *src); ++src)
|
|
|
|
;
|
|
|
|
|
|
|
|
if (*src == '\0')
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
narg = 1;
|
|
|
|
quote = 0;
|
|
|
|
|
|
|
|
while (*src)
|
2020-12-14 22:26:11 +08:00
|
|
|
{
|
2024-06-24 23:00:14 +08:00
|
|
|
if (*src == '\'' || *src == '\"')
|
2020-12-14 22:26:11 +08:00
|
|
|
{
|
2024-06-24 23:00:14 +08:00
|
|
|
if (!quote)
|
|
|
|
quote = *src++;
|
|
|
|
else if (*src == quote)
|
2020-12-14 22:26:11 +08:00
|
|
|
{
|
2024-06-24 23:00:14 +08:00
|
|
|
++src;
|
|
|
|
quote = 0;
|
2020-12-14 22:26:11 +08:00
|
|
|
}
|
|
|
|
else
|
2024-06-24 23:00:14 +08:00
|
|
|
*dst++ = *src++;
|
2020-12-14 22:26:11 +08:00
|
|
|
}
|
2024-06-24 23:00:14 +08:00
|
|
|
else if (!quote && isspace ((unsigned char) *src))
|
2020-12-14 22:26:11 +08:00
|
|
|
{
|
2024-06-24 23:00:14 +08:00
|
|
|
++narg;
|
|
|
|
++src;
|
|
|
|
*dst++ = '\0';
|
|
|
|
for (; isspace ((unsigned char) *src); ++src);
|
2020-12-14 22:26:11 +08:00
|
|
|
}
|
2024-06-24 23:00:14 +08:00
|
|
|
else
|
|
|
|
*dst++ = *src++;
|
2020-12-14 22:26:11 +08:00
|
|
|
}
|
2024-06-24 23:00:14 +08:00
|
|
|
|
|
|
|
*dst = '\0';
|
|
|
|
|
|
|
|
if (quote)
|
|
|
|
{
|
|
|
|
TV_MESSAGE (LDPL_WARNING,
|
|
|
|
"libdep syntax error: unterminated quoted string");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return narg;
|
2020-12-14 22:26:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static char *prevfile;
|
|
|
|
|
|
|
|
/* Standard plugin API registerable hook. */
|
2024-06-24 23:00:14 +08:00
|
|
|
|
2020-12-14 22:26:11 +08:00
|
|
|
static enum ld_plugin_status
|
|
|
|
onclaim_file (const struct ld_plugin_input_file *file, int *claimed)
|
|
|
|
{
|
|
|
|
enum ld_plugin_status rv;
|
|
|
|
|
|
|
|
*claimed = 0;
|
|
|
|
|
|
|
|
/* If we've already seen this file, ignore it. */
|
|
|
|
if (prevfile && !strcmp (file->name, prevfile))
|
|
|
|
return LDPS_OK;
|
|
|
|
|
|
|
|
/* If it's not an archive member, ignore it. */
|
|
|
|
if (!file->offset)
|
|
|
|
return LDPS_OK;
|
|
|
|
|
|
|
|
if (prevfile)
|
|
|
|
free (prevfile);
|
|
|
|
|
|
|
|
prevfile = strdup (file->name);
|
|
|
|
if (!prevfile)
|
|
|
|
return LDPS_ERR;
|
|
|
|
|
|
|
|
/* This hook only gets called on actual object files.
|
2024-06-24 23:00:14 +08:00
|
|
|
We have to examine the archive ourselves, to find
|
|
|
|
our LIBDEPS member. */
|
2020-12-14 22:26:11 +08:00
|
|
|
rv = get_libdeps (file->fd);
|
|
|
|
if (rv == LDPS_ERR)
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
if (rv == LDPS_OK)
|
|
|
|
{
|
|
|
|
linerec *lr = (linerec *)line_tail;
|
|
|
|
/* Inform the user/testsuite. */
|
|
|
|
TV_MESSAGE (LDPL_INFO, "got deps for library %s: %s",
|
|
|
|
file->name, lr->line);
|
|
|
|
fflush (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return LDPS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Standard plugin API registerable hook. */
|
2024-06-24 23:00:14 +08:00
|
|
|
|
2020-12-14 22:26:11 +08:00
|
|
|
static enum ld_plugin_status
|
|
|
|
onall_symbols_read (void)
|
|
|
|
{
|
|
|
|
linerec *lr;
|
2024-06-24 23:00:14 +08:00
|
|
|
int nargs;
|
|
|
|
char const *arg;
|
2020-12-14 22:26:11 +08:00
|
|
|
enum ld_plugin_status rv = LDPS_OK;
|
|
|
|
|
|
|
|
while ((lr = line_head))
|
|
|
|
{
|
|
|
|
line_head = lr->next;
|
2024-06-24 23:00:14 +08:00
|
|
|
nargs = parse_libdep (lr->line);
|
|
|
|
arg = lr->line;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < nargs; i++, arg = strchr (arg, '\0') + 1)
|
2020-12-14 22:26:11 +08:00
|
|
|
{
|
2024-06-24 23:00:14 +08:00
|
|
|
if (arg[0] != '-')
|
2020-12-14 22:26:11 +08:00
|
|
|
{
|
2024-06-24 23:00:14 +08:00
|
|
|
TV_MESSAGE (LDPL_WARNING, "ignoring libdep argument %s", arg);
|
|
|
|
fflush (NULL);
|
|
|
|
continue;
|
2020-12-14 22:26:11 +08:00
|
|
|
}
|
2024-06-24 23:00:14 +08:00
|
|
|
if (arg[1] == 'l')
|
|
|
|
rv = tv_add_input_library (arg + 2);
|
|
|
|
else if (arg[1] == 'L')
|
|
|
|
rv = tv_set_extra_library_path (arg + 2);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TV_MESSAGE (LDPL_WARNING, "ignoring libdep argument %s", arg);
|
|
|
|
fflush (NULL);
|
|
|
|
}
|
|
|
|
if (rv != LDPS_OK)
|
|
|
|
break;
|
2020-12-14 22:26:11 +08:00
|
|
|
}
|
|
|
|
free (lr);
|
|
|
|
}
|
2024-06-24 23:00:14 +08:00
|
|
|
|
2020-12-14 22:26:11 +08:00
|
|
|
line_tail = NULL;
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Standard plugin API registerable hook. */
|
2024-06-24 23:00:14 +08:00
|
|
|
|
2020-12-14 22:26:11 +08:00
|
|
|
static enum ld_plugin_status
|
|
|
|
oncleanup (void)
|
|
|
|
{
|
|
|
|
if (prevfile)
|
|
|
|
{
|
|
|
|
free (prevfile);
|
|
|
|
prevfile = NULL;
|
|
|
|
}
|
2024-06-24 23:00:14 +08:00
|
|
|
|
2020-12-14 22:26:11 +08:00
|
|
|
if (line_head)
|
|
|
|
{
|
|
|
|
linerec *lr;
|
2024-06-24 23:00:14 +08:00
|
|
|
|
2020-12-14 22:26:11 +08:00
|
|
|
while ((lr = line_head))
|
|
|
|
{
|
|
|
|
line_head = lr->next;
|
|
|
|
free (lr);
|
|
|
|
}
|
2024-06-24 23:00:14 +08:00
|
|
|
|
2020-12-14 22:26:11 +08:00
|
|
|
line_tail = NULL;
|
|
|
|
}
|
2024-06-24 23:00:14 +08:00
|
|
|
|
2020-12-14 22:26:11 +08:00
|
|
|
return LDPS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Standard plugin API entry point. */
|
2024-06-24 23:00:14 +08:00
|
|
|
|
2020-12-14 22:26:11 +08:00
|
|
|
enum ld_plugin_status
|
|
|
|
onload (struct ld_plugin_tv *tv)
|
|
|
|
{
|
|
|
|
enum ld_plugin_status rv;
|
|
|
|
|
|
|
|
/* This plugin requires a valid tv array. */
|
|
|
|
if (!tv)
|
|
|
|
return LDPS_ERR;
|
|
|
|
|
|
|
|
/* First entry should always be LDPT_MESSAGE, letting us get
|
|
|
|
hold of it easily so we can send output straight away. */
|
|
|
|
if (tv[0].tv_tag == LDPT_MESSAGE)
|
|
|
|
tv_message = tv[0].tv_u.tv_message;
|
|
|
|
|
|
|
|
do
|
|
|
|
if ((rv = parse_tv_tag (tv)) != LDPS_OK)
|
|
|
|
return rv;
|
|
|
|
while ((tv++)->tv_tag != LDPT_NULL);
|
|
|
|
|
|
|
|
/* Register hooks. */
|
2020-12-17 05:40:42 +08:00
|
|
|
if (tv_register_claim_file
|
|
|
|
&& tv_register_all_symbols_read
|
|
|
|
&& tv_register_cleanup)
|
2020-12-14 22:26:11 +08:00
|
|
|
{
|
2020-12-17 05:40:42 +08:00
|
|
|
(*tv_register_claim_file) (onclaim_file);
|
|
|
|
(*tv_register_all_symbols_read) (onall_symbols_read);
|
|
|
|
(*tv_register_cleanup) (oncleanup);
|
2020-12-14 22:26:11 +08:00
|
|
|
}
|
2024-06-24 23:00:14 +08:00
|
|
|
|
2020-12-14 22:26:11 +08:00
|
|
|
fflush (NULL);
|
|
|
|
return LDPS_OK;
|
|
|
|
}
|
|
|
|
#endif /* BFD_SUPPORTS_PLUGINS */
|