source: rtems-tools/linkers/rtems-syms.cpp

Last change on this file was cfef4b1, checked in by Sebastian Huber <sebastian.huber@…>, on 02/26/24 at 09:17:08

linkers: Allow generation of symbol map file only

If a symbol map file is specified by the user and no output file, then
just generate the symbol map file. The user can then compile the file
using its own build jobs.

  • Property mode set to 100644
File size: 19.4 KB
Line 
1/*
2 * Copyright (c) 2011-2014, Chris Johns <chrisj@rtems.org>
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16/**
17 * @file
18 *
19 * @ingroup rtems_rld
20 *
21 * @brief RTEMS Symbols Main manages opions, sequence of operations and exceptions.
22 *
23 */
24
25#if HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include <fstream>
30#include <iomanip>
31#include <iostream>
32#include <regex>
33
34#include <cxxabi.h>
35#include <signal.h>
36#include <stdlib.h>
37#include <string.h>
38#include <unistd.h>
39
40#include <getopt.h>
41
42#include <rld.h>
43#include <rld-cc.h>
44#include <rld-outputter.h>
45#include <rld-process.h>
46#include <rld-symbols.h>
47#include <rld-rtems.h>
48
49#ifndef HAVE_KILL
50#define kill(p,s) raise(s)
51#endif
52
53/**
54 * Header text.
55 */
56static const char* const c_header[] =
57{
58  "/*",
59  " * RTEMS Global Symbol Table",
60  " *  Automatically generated. Do not edit..",
61  " */",
62  "",
63  "#include <stddef.h>",
64  "#include <stdint.h>",
65  "",
66  "extern void* rtems_rtl_tls_get_base (void);",
67  "",
68  "extern const unsigned char rtems__rtl_base_globals[];",
69  "extern const unsigned int rtems__rtl_base_globals_size[];",
70  "",
71  "typedef size_t (*rtems_rtl_tls_offset_func)(void);",
72  "typedef struct rtems_rtl_tls_offset {",
73  "  size_t index;",
74  "  rtems_rtl_tls_offset_func offset;",
75  "} rtems_rtl_tls_offset;",
76  "",
77  "void rtems_rtl_base_sym_global_add (const unsigned char* , unsigned int,",
78  "                                    rtems_rtl_tls_offset*, size_t );",
79  "",
80  "asm(\".pushsection \\\".rodata\\\"\");",
81  "",
82  "asm(\"  .align   4\");",
83  "asm(\"  .local   rtems__rtl_base_globals\");",
84  "asm(\"rtems__rtl_base_globals:\");",
85  "#if __mips__",
86  " asm(\"  .align 0\");",
87  "#else",
88  " asm(\"  .balign 1\");",
89  "#endif",
90  0
91};
92
93static const char* const c_sym_table_end[] =
94{
95  "asm(\"  .byte    0\");",
96  "asm(\"  .ascii   \\\"\\xde\\xad\\xbe\\xef\\\"\");",
97  "",
98  0
99};
100
101static const char* const c_tls_call_table_start[] =
102{
103  "rtems_rtl_tls_offset rtems_rtl_tls_offsets[] = {",
104  0
105};
106
107static const char* const c_tls_call_table_end[] =
108{
109  "};",
110  "#define RTEMS_RTL_TLS_OFFSETS_NUM " \
111  "(sizeof(rtems_rtl_tls_offsets) / (sizeof(rtems_rtl_tls_offsets[0])))",
112  "",
113  0
114};
115
116static const char* const c_trailer[] =
117{
118  "/*",
119  " * Symbol table size.",
120  " */",
121  "asm(\"  .align   4\");",
122  "asm(\"  .local   rtems__rtl_base_globals_size\");",
123  "asm(\"rtems__rtl_base_globals_size:\");",
124  "asm(\"  .long rtems__rtl_base_globals_size - rtems__rtl_base_globals\");",
125  "asm(\"  .popsection\");",
126  "",
127  0
128};
129
130static const char* const c_rtl_call_body_embeded[] =
131{
132  "{",
133  "  rtems_rtl_base_sym_global_add (&rtems__rtl_base_globals[0],",
134  "                                 rtems__rtl_base_globals_size[0],",
135  "                                 &rtems_rtl_tls_offsets[0],",
136  "                                 RTEMS_RTL_TLS_OFFSETS_NUM);",
137  "}",
138  0
139};
140
141static const char* const c_rtl_call_body[] =
142{
143  "{",
144  "  rtems_rtl_base_sym_global_add (&rtems__rtl_base_globals[0],",
145  "                                 rtems__rtl_base_globals_size[0],",
146  "                                 NULL,",
147  "                                 0);",
148  "}",
149  0
150};
151
152/**
153 * Paint the data to the temporary file.
154 */
155static void
156temporary_file_paint (rld::process::tempfile& t, const char* const lines[])
157{
158  for (int l = 0; lines[l]; ++l)
159    t.write_line (lines[l]);
160}
161
162/**
163 * The constructor trailer.
164 */
165static void
166c_constructor_trailer (rld::process::tempfile& c)
167{
168  c.write_line ("static void init(void) __attribute__ ((constructor));");
169  c.write_line ("static void init(void)");
170  temporary_file_paint (c, c_rtl_call_body);
171}
172
173/**
174 * The embedded trailer.
175 */
176static void
177c_embedded_trailer (rld::process::tempfile& c)
178{
179  c.write_line ("void rtems_rtl_base_global_syms_init(void);");
180  c.write_line ("void rtems_rtl_base_global_syms_init(void)");
181  temporary_file_paint (c, c_rtl_call_body_embeded);
182}
183
184/**
185 * Filter the symbols given a list of regx expressions.
186 */
187class symbol_filter
188{
189public:
190  typedef std::vector < std::string > expressions;
191
192  symbol_filter ();
193
194  void load (const std::string& file);
195  void add (const std::string& re);
196
197  void filter (const rld::symbols::symtab& symbols,
198               rld::symbols::symtab&       filtered_symbols);
199
200private:
201
202  expressions expr;
203};
204
205symbol_filter::symbol_filter ()
206{
207}
208
209void
210symbol_filter::load (const std::string& file)
211{
212  std::ifstream in (file);
213  std::string   re;
214  while (in >> re)
215    add (re);
216}
217
218void
219symbol_filter::add (const std::string& re)
220{
221  expr.push_back (re);
222}
223
224void
225symbol_filter::filter (const rld::symbols::symtab& symbols,
226                       rld::symbols::symtab&       filtered_symbols)
227{
228  if (expr.size () > 0)
229  {
230    for (auto& re : expr)
231    {
232      const std::regex sym_re(re);
233      for (const auto& sym : symbols)
234      {
235        std::smatch      m;
236        if (std::regex_match (sym.second->demangled (), m, sym_re))
237          filtered_symbols[sym.first] = sym.second;
238      }
239    }
240  }
241  else
242  {
243    for (const auto& sym : symbols)
244      filtered_symbols[sym.first] = sym.second;
245  }
246}
247
248/**
249 * Generate the symbol map object file for loading or linking into
250 * a running RTEMS machine.
251 */
252
253struct output_sym
254{
255  enum struct output_mode {
256    symbol,
257    tls_func,
258    tls_call_table
259  };
260  rld::process::tempfile& c;
261  const bool              embed;
262  const bool              weak;
263  const output_mode       mode;
264  size_t&                 index;
265
266  output_sym(rld::process::tempfile& c_,
267             bool                    embed_,
268             bool                    weak_,
269             output_mode             mode_,
270             size_t&                 index_)
271    : c (c_),
272      embed (embed_),
273      weak (weak_),
274      mode (mode_),
275      index (index_) {
276  }
277
278  void operator ()(const rld::symbols::symtab::value_type& value);
279};
280
281void
282output_sym::operator ()(const rld::symbols::symtab::value_type& value)
283{
284  const rld::symbols::symbol& sym = *(value.second);
285
286  /*
287   * Weak symbols without a value are probably unresolved externs. Ignore them.
288   */
289  if (weak && sym.value () == 0)
290    return;
291
292  switch (mode) {
293    case output_mode::symbol:
294    default:
295      /*
296       * Set TLS value to 0. It is filled in at run time by the call
297       * table.
298       */
299      c.write_line ("asm(\"  .asciz \\\"" + sym.name () + "\\\"\");");
300      {
301        std::string val;
302        if (sym.type () == STT_TLS) {
303          val = "0";
304        } else {
305          if (embed) {
306            val = sym.name ();
307          } else {
308            std::stringstream oss;
309            oss << std::hex << std::showbase << std::internal <<
310              std::setfill ('0') << std::setw (10) << sym.value ();
311            val = oss.str ();
312          }
313        }
314        c.write_line ("#if __SIZEOF_POINTER__ == 8");
315        c.write_line ("asm(\"  .quad " + val + "\");");
316        c.write_line ("#else");
317        c.write_line ("asm(\"  .long " + val + "\");");
318        c.write_line ("#endif");
319      }
320      break;
321    case output_mode::tls_func:
322      if (sym.type () == STT_TLS) {
323        c.write_line ("#define RTEMS_TLS_INDEX_" + sym.name () + " " + std::to_string(index));
324        c.write_line ("static size_t rtems_rtl_tls_" + sym.name () + "(void) {");
325        c.write_line ("  extern __thread char "  + sym.name () +  "[];");
326        c.write_line ("  size_t tls_base = (size_t) rtems_rtl_tls_get_base ();");
327        c.write_line ("  size_t tls_addr = (size_t) "  + sym.name () +  ";");
328        c.write_line ("  return tls_addr - tls_base;");
329        c.write_line ("}");
330        c.write_line ("");
331      }
332      break;
333    case output_mode::tls_call_table:
334      if (sym.type () == STT_TLS) {
335        c.write_line ("  { RTEMS_TLS_INDEX_" + sym.name () +
336                      ", rtems_rtl_tls_" + sym.name () + " },");
337      }
338      break;
339  }
340  ++index;
341}
342
343static void
344generate_c (rld::process::tempfile& c,
345            rld::symbols::symtab&   symbols,
346            bool                    embed)
347{
348  if (rld::verbose ())
349    std::cout << "symbol C file: " << c.name () << std::endl;
350
351  c.open (true);
352  temporary_file_paint (c, c_header);
353
354  /*
355   * Add the symbols. The is the globals and the weak symbols that have been
356   * linked into the base image. A weak symbols present in the base image is no
357   * longer weak and should be consider a global symbol. You cannot link a
358   * global symbol with the same in a dynamically loaded module.
359   */
360  size_t index = 0;
361  std::for_each (symbols.begin (),
362                 symbols.end (),
363                 output_sym (c, embed, false,
364                             output_sym::output_mode::symbol, index));
365
366  temporary_file_paint (c, c_sym_table_end);
367
368  if (embed) {
369    index = 0;
370    std::for_each (symbols.begin (),
371                   symbols.end (),
372                   output_sym (c, embed, false,
373                               output_sym::output_mode::tls_func, index));
374    temporary_file_paint (c, c_tls_call_table_start);
375    index = 0;
376    std::for_each (symbols.begin (),
377                   symbols.end (),
378                   output_sym (c, embed, false,
379                               output_sym::output_mode::tls_call_table, index));
380    temporary_file_paint (c, c_tls_call_table_end);
381  }
382
383  temporary_file_paint (c, c_trailer);
384
385  if (embed)
386    c_embedded_trailer (c);
387  else
388    c_constructor_trailer (c);
389}
390
391static void
392generate_symmap (rld::process::tempfile& c,
393                 const std::string&      output,
394                 rld::symbols::symtab&   symbols,
395                 bool                    embed)
396{
397  generate_c (c, symbols, embed);
398
399  if (rld::verbose ())
400    std::cout << "symbol O file: " << output << std::endl;
401
402  rld::process::arg_container args;
403
404  rld::cc::make_cc_command (args);
405  rld::cc::append_flags (rld::cc::ft_cflags, args);
406
407  args.push_back ("-O2");
408  args.push_back ("-c");
409  args.push_back ("-o");
410  args.push_back (output);
411  args.push_back (c.name ());
412
413  rld::process::tempfile out;
414  rld::process::tempfile err;
415  rld::process::status   status;
416
417  status = rld::process::execute (rld::cc::get_cc (),
418                                  args,
419                                  out.name (),
420                                  err.name ());
421
422
423  if ((status.type != rld::process::status::normal) ||
424      (status.code != 0))
425  {
426    err.output (rld::cc::get_cc (), std::cout);
427    throw rld::error ("Compiler error", "compiling wrapper");
428  }
429}
430
431/**
432 * RTEMS Symbols options.
433 */
434static struct option rld_opts[] = {
435  { "help",        no_argument,            NULL,           'h' },
436  { "version",     no_argument,            NULL,           'V' },
437  { "verbose",     no_argument,            NULL,           'v' },
438  { "warn",        no_argument,            NULL,           'w' },
439  { "keep",        no_argument,            NULL,           'k' },
440  { "embed",       no_argument,            NULL,           'e' },
441  { "symc",        required_argument,      NULL,           'S' },
442  { "output",      required_argument,      NULL,           'o' },
443  { "map",         required_argument,      NULL,           'm' },
444  { "cc",          required_argument,      NULL,           'C' },
445  { "exec-prefix", required_argument,      NULL,           'E' },
446  { "cflags",      required_argument,      NULL,           'c' },
447  { "filter",      required_argument,      NULL,           'f' },
448  { "filter-re",   required_argument,      NULL,           'F' },
449  { NULL,          0,                      NULL,            0 }
450};
451
452void
453usage (int exit_code)
454{
455  std::cout << "rtems-syms [options] kernel" << std::endl
456            << "Options and arguments:" << std::endl
457            << " -h        : help (also --help)" << std::endl
458            << " -V        : print version number and exit (also --version)" << std::endl
459            << " -v        : verbose (trace import parts), can supply multiple times" << std::endl
460            << "             to increase verbosity (also --verbose)" << std::endl
461            << " -w        : generate warnings (also --warn)" << std::endl
462            << " -k        : keep temporary files (also --keep)" << std::endl
463            << " -e        : embedded symbol table (also --embed)" << std::endl
464            << " -S file   : symbol's C file (also --symc)" << std::endl
465            << " -o file   : output object file (also --output)" << std::endl
466            << " -m file   : output a map file (also --map)" << std::endl
467            << " -C file   : target C compiler executable (also --cc)" << std::endl
468            << " -E prefix : the RTEMS tool prefix (also --exec-prefix)" << std::endl
469            << " -c cflags : C compiler flags (also --cflags)" << std::endl
470            << " -f file   : file of symbol filters (also --filter)" << std::endl
471            << " -F re     : filter regx expression (also --filter-re)" << std::endl;
472  ::exit (exit_code);
473}
474
475static void
476fatal_signal (int signum)
477{
478  signal (signum, SIG_DFL);
479
480  rld::process::temporaries_clean_up ();
481
482  /*
483   * Get the same signal again, this time not handled, so its normal effect
484   * occurs.
485   */
486  kill (getpid (), signum);
487}
488
489static void
490setup_signals (void)
491{
492  if (signal (SIGINT, SIG_IGN) != SIG_IGN)
493    signal (SIGINT, fatal_signal);
494#ifdef SIGHUP
495  if (signal (SIGHUP, SIG_IGN) != SIG_IGN)
496    signal (SIGHUP, fatal_signal);
497#endif
498  if (signal (SIGTERM, SIG_IGN) != SIG_IGN)
499    signal (SIGTERM, fatal_signal);
500#ifdef SIGPIPE
501  if (signal (SIGPIPE, SIG_IGN) != SIG_IGN)
502    signal (SIGPIPE, fatal_signal);
503#endif
504#ifdef SIGCHLD
505  signal (SIGCHLD, SIG_DFL);
506#endif
507}
508
509int
510main (int argc, char* argv[])
511{
512  int ec = 0;
513
514  setup_signals ();
515
516  try
517  {
518    rld::files::cache   kernel;
519    rld::symbols::table symbols;
520    symbol_filter       filter;
521    std::string         kernel_name;
522    std::string         output;
523    std::string         map;
524    std::string         cc;
525    std::string         symc;
526    bool                embed = false;
527
528    rld::set_cmdline (argc, argv);
529
530    while (true)
531    {
532      int opt = ::getopt_long (argc, argv, "hvVwkef:S:o:m:E:c:C:f:F:", rld_opts, NULL);
533      if (opt < 0)
534        break;
535
536      switch (opt)
537      {
538        case 'V':
539          std::cout << "rtems-syms (RTEMS Symbols) " << rld::version ()
540                    << ", RTEMS revision " << rld::rtems::version ()
541                    << std::endl;
542          ::exit (0);
543          break;
544
545        case 'v':
546          rld::verbose_inc ();
547          break;
548
549        case 'w':
550          break;
551
552        case 'k':
553          rld::process::set_keep_temporary_files ();
554          break;
555
556        case 'e':
557          embed = true;
558          break;
559
560        case 'o':
561          output = optarg;
562          break;
563
564        case 'm':
565          map = optarg;
566          break;
567
568        case 'C':
569          if (rld::cc::is_exec_prefix_set ())
570            std::cerr << "warning: exec-prefix ignored when CC provided" << std::endl;
571          rld::cc::set_cc (optarg);
572          break;
573
574        case 'E':
575          if (rld::cc::is_cc_set ())
576            std::cerr << "warning: exec-prefix ignored when CC provided" << std::endl;
577          rld::cc::set_exec_prefix (optarg);
578          break;
579
580        case 'c':
581          rld::cc::set_flags (optarg, rld::cc::ft_cflags);
582          break;
583
584        case 'S':
585          symc = optarg;
586          break;
587
588        case 'f':
589          filter.load (optarg);
590          break;
591
592        case 'F':
593          filter.add (optarg);
594          break;
595
596        case '?':
597          usage (3);
598          break;
599
600        case 'h':
601          usage (0);
602          break;
603      }
604    }
605
606    /*
607     * Set the program name.
608     */
609    rld::set_progname (argv[0]);
610
611    argc -= optind;
612    argv += optind;
613
614    if (rld::verbose ())
615      std::cout << "RTEMS Kernel Symbols " << rld::version () << std::endl;
616
617    /*
618     * If there are no object files there is nothing to link.
619     */
620    if (argc == 0)
621      throw rld::error ("no kernel file", "options");
622    if (argc != 1)
623      throw rld::error ("only one kernel file", "options");
624    if (output.empty () && symc.empty() && map.empty ())
625      throw rld::error ("no output, symbol C file, or map", "options");
626
627    kernel_name = *argv;
628
629    if (rld::verbose ())
630      std::cout << "kernel: " << kernel_name << std::endl;
631
632    /*
633     * Load the symbols from the kernel.
634     */
635    try
636    {
637      /*
638       * Load the kernel ELF file symbol table.
639       */
640      kernel.open ();
641      kernel.add (kernel_name);
642      kernel.load_symbols (symbols, true);
643
644      /*
645       * If the full path to CC is not provided and the exec-prefix is not set
646       * by the command line see if it can be detected from the object file
647       * types. This must be after we have added the object files because they
648       * are used when detecting.
649       */
650      if (!cc.empty ())
651        rld::cc::set_cc (cc);
652      if (!rld::cc::is_cc_set () && !rld::cc::is_exec_prefix_set ())
653        rld::cc::set_exec_prefix (rld::elf::machine_type ());
654
655      /*
656       * Filter the symbols.
657       */
658      rld::symbols::symtab filter_symbols;
659      filter.filter (symbols.globals (), filter_symbols);
660      filter.filter (symbols.weaks (), filter_symbols);
661      if (filter_symbols.size () == 0)
662        throw rld::error ("no filtered symbols", "filter");
663      if (rld::verbose ())
664        std::cout << "Filtered symbols: " << filter_symbols.size () << std::endl;
665
666      /*
667       * Create a map file if asked too.
668       */
669      if (!map.empty ())
670      {
671        std::ofstream mout;
672        mout.open (map.c_str());
673        if (!mout.is_open ())
674          throw rld::error ("map file open failed", "map");
675        mout << "RTEMS Kernel Symbols Map" << std::endl
676             << " kernel: " << kernel_name << std::endl
677             << std::endl;
678        rld::symbols::output (mout, filter_symbols);
679        mout.close ();
680      }
681
682      /*
683       * Create an output file if asked too.
684       */
685      if (!output.empty () || !symc.empty())
686      {
687        rld::process::tempfile c (".c");
688
689        if (!symc.empty ())
690        {
691          c.override (symc);
692          c.keep ();
693        }
694
695        /*
696         * Generate and if requested compile the symbol map.
697         */
698        if (output.empty())
699          generate_c (c, filter_symbols, embed);
700        else
701          generate_symmap (c, output, filter_symbols, embed);
702      }
703
704      kernel.close ();
705    }
706    catch (...)
707    {
708      kernel.close ();
709      throw;
710    }
711
712    kernel.close ();
713  }
714  catch (rld::error re)
715  {
716    std::cerr << "error: "
717              << re.where << ": " << re.what
718              << std::endl;
719    ec = 10;
720  }
721  catch (std::exception& e)
722  {
723    int   status;
724    char* realname;
725    realname = abi::__cxa_demangle (e.what(), 0, 0, &status);
726    std::cerr << "error: exception: " << realname << " [";
727    ::free (realname);
728    const std::type_info &ti = typeid (e);
729    realname = abi::__cxa_demangle (ti.name(), 0, 0, &status);
730    std::cerr << realname << "] " << e.what () << std::endl;
731    ::free (realname);
732    ec = 11;
733  }
734  catch (...)
735  {
736    /*
737     * Helps to know if this happens.
738     */
739    std::cout << "error: unhandled exception" << std::endl;
740    ec = 12;
741  }
742
743  return ec;
744}
Note: See TracBrowser for help on using the repository browser.