libdep_plugin.c 7.5 KB

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