1/* libdeps plugin for the GNU linker. 2 Copyright (C) 2020-2022 Free Software Foundation, Inc. 3 4 This file is part of the GNU Binutils. 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, 19 MA 02110-1301, USA. */ 20 21#include "sysdep.h" 22#include "bfd.h" 23#if BFD_SUPPORTS_PLUGINS 24#include "plugin-api.h" 25 26#include <ctype.h> /* For isspace. */ 27 28extern enum ld_plugin_status onload (struct ld_plugin_tv *tv); 29 30/* Helper for calling plugin api message function. */ 31#define TV_MESSAGE if (tv_message) (*tv_message) 32 33/* Function pointers to cache hooks passed at onload time. */ 34static ld_plugin_register_claim_file tv_register_claim_file = 0; 35static ld_plugin_register_all_symbols_read tv_register_all_symbols_read = 0; 36static ld_plugin_register_cleanup tv_register_cleanup = 0; 37static ld_plugin_message tv_message = 0; 38static ld_plugin_add_input_library tv_add_input_library = 0; 39static ld_plugin_set_extra_library_path tv_set_extra_library_path = 0; 40 41/* Handle/record information received in a transfer vector entry. */ 42static enum ld_plugin_status 43parse_tv_tag (struct ld_plugin_tv *tv) 44{ 45#define SETVAR(x) x = tv->tv_u.x 46 switch (tv->tv_tag) 47 { 48 case LDPT_REGISTER_CLAIM_FILE_HOOK: 49 SETVAR(tv_register_claim_file); 50 break; 51 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK: 52 SETVAR(tv_register_all_symbols_read); 53 break; 54 case LDPT_REGISTER_CLEANUP_HOOK: 55 SETVAR(tv_register_cleanup); 56 break; 57 case LDPT_MESSAGE: 58 SETVAR(tv_message); 59 break; 60 case LDPT_ADD_INPUT_LIBRARY: 61 SETVAR(tv_add_input_library); 62 break; 63 case LDPT_SET_EXTRA_LIBRARY_PATH: 64 SETVAR(tv_set_extra_library_path); 65 break; 66 default: 67 break; 68 } 69#undef SETVAR 70 return LDPS_OK; 71} 72 73/* Defs for archive parsing. */ 74#define ARMAGSIZE 8 75typedef struct arhdr 76{ 77 char ar_name[16]; 78 char ar_date[12]; 79 char ar_uid[6]; 80 char ar_gid[6]; 81 char ar_mode[8]; 82 char ar_size[10]; 83 char ar_fmag[2]; 84} arhdr; 85 86typedef struct linerec 87{ 88 struct linerec *next; 89 char line[]; 90} linerec; 91 92#define LIBDEPS "__.LIBDEP/ " 93 94static linerec *line_head, **line_tail = &line_head; 95 96static enum ld_plugin_status 97get_libdeps (int fd) 98{ 99 arhdr ah; 100 int len; 101 unsigned long mlen; 102 size_t amt; 103 linerec *lr; 104 enum ld_plugin_status rc = LDPS_NO_SYMS; 105 106 lseek (fd, ARMAGSIZE, SEEK_SET); 107 for (;;) 108 { 109 len = read (fd, (void *) &ah, sizeof (ah)); 110 if (len != sizeof (ah)) 111 break; 112 mlen = strtoul (ah.ar_size, NULL, 10); 113 if (!mlen || strncmp (ah.ar_name, LIBDEPS, sizeof (LIBDEPS)-1)) 114 { 115 lseek (fd, mlen, SEEK_CUR); 116 continue; 117 } 118 amt = mlen + sizeof (linerec); 119 if (amt <= mlen) 120 return LDPS_ERR; 121 lr = malloc (amt); 122 if (!lr) 123 return LDPS_ERR; 124 lr->next = NULL; 125 len = read (fd, lr->line, mlen); 126 lr->line[mlen-1] = '\0'; 127 *line_tail = lr; 128 line_tail = &lr->next; 129 rc = LDPS_OK; 130 break; 131 } 132 return rc; 133} 134 135/* Turn a string into an argvec. */ 136static char ** 137str2vec (char *in) 138{ 139 char **res; 140 char *s, *first, *end; 141 char *sq, *dq; 142 int i; 143 144 end = in + strlen (in); 145 s = in; 146 while (isspace ((unsigned char) *s)) s++; 147 first = s; 148 149 i = 1; 150 while ((s = strchr (s, ' '))) 151 { 152 s++; 153 i++; 154 } 155 res = (char **)malloc ((i+1) * sizeof (char *)); 156 if (!res) 157 return res; 158 159 i = 0; 160 sq = NULL; 161 dq = NULL; 162 res[0] = first; 163 for (s = first; *s; s++) 164 { 165 if (*s == '\\') 166 { 167 memmove (s, s+1, end-s-1); 168 end--; 169 } 170 if (isspace ((unsigned char) *s)) 171 { 172 if (sq || dq) 173 continue; 174 *s++ = '\0'; 175 while (isspace ((unsigned char) *s)) s++; 176 if (*s) 177 res[++i] = s; 178 } 179 if (*s == '\'' && !dq) 180 { 181 if (sq) 182 { 183 memmove (sq, sq+1, s-sq-1); 184 memmove (s-2, s+1, end-s-1); 185 end -= 2; 186 s--; 187 sq = NULL; 188 } 189 else 190 { 191 sq = s; 192 } 193 } 194 if (*s == '"' && !sq) 195 { 196 if (dq) 197 { 198 memmove (dq, dq+1, s-dq-1); 199 memmove (s-2, s+1, end-s-1); 200 end -= 2; 201 s--; 202 dq = NULL; 203 } 204 else 205 { 206 dq = s; 207 } 208 } 209 } 210 res[++i] = NULL; 211 return res; 212} 213 214static char *prevfile; 215 216/* Standard plugin API registerable hook. */ 217static enum ld_plugin_status 218onclaim_file (const struct ld_plugin_input_file *file, int *claimed) 219{ 220 enum ld_plugin_status rv; 221 222 *claimed = 0; 223 224 /* If we've already seen this file, ignore it. */ 225 if (prevfile && !strcmp (file->name, prevfile)) 226 return LDPS_OK; 227 228 /* If it's not an archive member, ignore it. */ 229 if (!file->offset) 230 return LDPS_OK; 231 232 if (prevfile) 233 free (prevfile); 234 235 prevfile = strdup (file->name); 236 if (!prevfile) 237 return LDPS_ERR; 238 239 /* This hook only gets called on actual object files. 240 * We have to examine the archive ourselves, to find 241 * our LIBDEPS member. */ 242 rv = get_libdeps (file->fd); 243 if (rv == LDPS_ERR) 244 return rv; 245 246 if (rv == LDPS_OK) 247 { 248 linerec *lr = (linerec *)line_tail; 249 /* Inform the user/testsuite. */ 250 TV_MESSAGE (LDPL_INFO, "got deps for library %s: %s", 251 file->name, lr->line); 252 fflush (NULL); 253 } 254 255 return LDPS_OK; 256} 257 258/* Standard plugin API registerable hook. */ 259static enum ld_plugin_status 260onall_symbols_read (void) 261{ 262 linerec *lr; 263 char **vec; 264 enum ld_plugin_status rv = LDPS_OK; 265 266 while ((lr = line_head)) 267 { 268 line_head = lr->next; 269 vec = str2vec (lr->line); 270 if (vec) 271 { 272 int i; 273 for (i = 0; vec[i]; i++) 274 { 275 if (vec[i][0] != '-') 276 { 277 TV_MESSAGE (LDPL_WARNING, "ignoring libdep argument %s", 278 vec[i]); 279 fflush (NULL); 280 continue; 281 } 282 if (vec[i][1] == 'l') 283 rv = tv_add_input_library (vec[i]+2); 284 else if (vec[i][1] == 'L') 285 rv = tv_set_extra_library_path (vec[i]+2); 286 else 287 { 288 TV_MESSAGE (LDPL_WARNING, "ignoring libdep argument %s", 289 vec[i]); 290 fflush (NULL); 291 } 292 if (rv != LDPS_OK) 293 break; 294 } 295 free (vec); 296 } 297 free (lr); 298 } 299 line_tail = NULL; 300 return rv; 301} 302 303/* Standard plugin API registerable hook. */ 304static enum ld_plugin_status 305oncleanup (void) 306{ 307 if (prevfile) 308 { 309 free (prevfile); 310 prevfile = NULL; 311 } 312 if (line_head) 313 { 314 linerec *lr; 315 while ((lr = line_head)) 316 { 317 line_head = lr->next; 318 free (lr); 319 } 320 line_tail = NULL; 321 } 322 return LDPS_OK; 323} 324 325/* Standard plugin API entry point. */ 326enum ld_plugin_status 327onload (struct ld_plugin_tv *tv) 328{ 329 enum ld_plugin_status rv; 330 331 /* This plugin requires a valid tv array. */ 332 if (!tv) 333 return LDPS_ERR; 334 335 /* First entry should always be LDPT_MESSAGE, letting us get 336 hold of it easily so we can send output straight away. */ 337 if (tv[0].tv_tag == LDPT_MESSAGE) 338 tv_message = tv[0].tv_u.tv_message; 339 340 do 341 if ((rv = parse_tv_tag (tv)) != LDPS_OK) 342 return rv; 343 while ((tv++)->tv_tag != LDPT_NULL); 344 345 /* Register hooks. */ 346 if (tv_register_claim_file 347 && tv_register_all_symbols_read 348 && tv_register_cleanup) 349 { 350 (*tv_register_claim_file) (onclaim_file); 351 (*tv_register_all_symbols_read) (onall_symbols_read); 352 (*tv_register_cleanup) (oncleanup); 353 } 354 fflush (NULL); 355 return LDPS_OK; 356} 357#endif /* BFD_SUPPORTS_PLUGINS */ 358