source: rtems-tools/linkers/rtems-tld.cpp @ ea29902

4.104.115
Last change on this file since ea29902 was ea29902, checked in by Chris Johns <chrisj@…>, on 08/01/14 at 06:44:32

Add initial support for the RTEM Trace Linker.

The RTEMS Trace Linker or rtems-rld creates an RTEMS executable with
trace support built in without any changes the existing code.

This commit is an initial starting point with function signatures
being read from INI files.

  • Property mode set to 100644
File size: 11.6 KB
Line 
1/*
2 * Copyright (c) 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 Trace Linker manages creating a tracable RTEMS executable.
22 *
23 */
24
25#if HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include <algorithm>
30#include <cctype>
31#include <functional>
32#include <iostream>
33#include <locale>
34#include <sstream>
35
36#include <cxxabi.h>
37#include <signal.h>
38#include <stdlib.h>
39#include <string.h>
40#include <unistd.h>
41
42#include <getopt.h>
43
44#include <rld.h>
45#include <rld-cc.h>
46#include <rld-config.h>
47#include <rld-process.h>
48
49#ifndef HAVE_KILL
50#define kill(p,s) raise(s)
51#endif
52
53namespace rld
54{
55
56  /**
57   * Trim from start.
58   */
59  inline std::string &ltrim (std::string &s)
60  {
61    s.erase (s.begin (),
62            std::find_if (s.begin (), s.end (),
63                         std::not1 (std::ptr_fun < int, int > (std::isspace))));
64    return s;
65  }
66
67  /**
68   * Trim from end.
69   */
70  inline std::string &rtrim (std::string &s)
71  {
72    s.erase (std::find_if (s.rbegin (), s.rend (),
73                           std::not1 (std::ptr_fun < int, int > (std::isspace))).base(),
74             s.end());
75    return s;
76  }
77
78  /**
79   * Trim from both ends.
80   */
81  inline std::string &trim (std::string &s)
82  {
83    return ltrim (rtrim (s));
84  }
85}
86
87/**
88 * RTEMS Trace Linker.
89 */
90namespace trace
91{
92  /**
93   * A container of arguments.
94   */
95  typedef std::vector < std::string > function_args;
96
97  /**
98   * The return value.
99   */
100  typedef std::string function_return;
101
102  /**
103   * A function's signature.
104   */
105  struct function_sig
106  {
107    std::string     name; /**< The function's name. */
108    function_args   args; /**< The function's list of arguments. */
109    function_return ret;  /**< The fuctions return value. */
110  };
111
112  /**
113   * A container of function signatures.
114   */
115  typedef std::map < std::string, function_sig > function_sigs;
116
117  /**
118   * Trace Linker.
119   */
120  class linker
121  {
122  public:
123    linker ();
124
125    /**
126     * Load the user's configuration.
127     */
128    void load_config (const std::string& path);
129
130    /**
131     * Dump the linker status.
132     */
133    void dump (std::ostream& out);
134
135  private:
136
137    rld::config::config config; /**< User configuration. */
138    function_sigs       sigs;   /**< Function signatures. */
139  };
140
141  linker::linker ()
142  {
143  }
144
145  void
146  linker::load_config (const std::string& path)
147  {
148    config.clear ();
149    config.load (path);
150
151    /*
152     * The configuration must contain a "trace" section. This is the top level
153     * configuration and must the following fields:
154     *
155     *   # < add here >
156     *
157     * The 'trace" section may optionally contain a number of 'include' records
158     * and these configuration files are included.
159     */
160
161    const rld::config::section& tsec = config.get_section ("trace");
162    bool                        have_includes = false;
163
164    try
165    {
166      const rld::config::record& irec = tsec.get_record ("include");
167
168      have_includes = true;
169
170      /*
171       * Include records are a path which we can just load.
172       *
173       * @todo Add a search path. See 'rld::files' for details. We can default
174       *       the search path to the install $prefix of this tool and we can
175       *       then provide a default set of function signatures for RTEMS
176       *       APIs.
177       */
178
179      for (rld::config::items::const_iterator ii = irec.items.begin ();
180           ii != irec.items.end ();
181           ++ii)
182      {
183        config.load ((*ii).text);
184      }
185    }
186    catch (rld::error re)
187    {
188      /*
189       * No include records, must be all inlined. If we have includes it must
190       * be another error so throw it.
191       */
192      if (have_includes)
193        throw;
194    }
195
196    /*
197     * Get the function signatures from the configuration and load them into
198     * the sig map.
199     */
200
201    const rld::config::section& fssec = config.get_section ("function-signatures");
202
203    for (rld::config::records::const_iterator ri = fssec.recs.begin ();
204         ri != fssec.recs.end ();
205         ++ri)
206    {
207      /*
208       * There can only be one function signature in the configuration.
209       */
210      if ((*ri).items.size () > 1)
211        throw rld::error ("duplicate", "function signature: " + (*ri).name);
212
213      function_sig sig;
214      sig.name = (*ri).name;
215
216      /*
217       * Function signatures are defined as the return value then the arguments
218       * delimited by a comma and white space. No checking is made of the
219       * return value or arguments.
220       */
221      rld::config::items::const_iterator ii = (*ri).items.begin ();
222      std::stringstream                  ts((*ii).text);
223      std::string                        arg;
224      while (std::getline (ts, arg, ','))
225      {
226        rld::trim (arg);
227        if (!arg.empty ())
228        {
229          if (sig.ret.empty ())
230            sig.ret = arg;
231          else
232            sig.args.push_back(arg);
233        }
234      }
235
236      if (sig.ret.empty ())
237        throw rld::error ("no return value", "function signature: " + (*ri).name);
238
239      if (sig.args.empty ())
240        throw rld::error ("no arguments", "function signature: " + (*ri).name);
241
242      sigs[sig.name] = sig;
243    }
244  }
245
246  void
247  linker::dump (std::ostream& out)
248  {
249    const rld::config::paths& cpaths = config.get_paths ();
250    out << "Configuration Files: " << cpaths.size () << std::endl;
251    for (rld::config::paths::const_iterator pi = cpaths.begin ();
252         pi != cpaths.end ();
253         ++pi)
254    {
255      out << " " << (*pi) << std::endl;
256    }
257
258    out << std::endl
259        << "Function Signatures: " << sigs.size () << std::endl;
260    for (function_sigs::const_iterator si = sigs.begin ();
261         si != sigs.end ();
262         ++si)
263    {
264      const function_sig& sig = (*si).second;
265      out << " " << sig.name << ": " << sig.ret << ' ' << sig.name << '(';
266      for (function_args::const_iterator fai = sig.args.begin ();
267           fai != sig.args.end ();
268           ++fai)
269      {
270        if (fai != sig.args.begin ())
271          out << ", ";
272        out << (*fai);
273      }
274      out << ");" << std::endl;
275    }
276  }
277}
278
279/**
280 * RTEMS Trace Linker options. This needs to be rewritten to be like cc where
281 * only a single '-' and long options is present. Anything after '--' is passed
282 * to RTEMS's real linker.
283 */
284static struct option rld_opts[] = {
285  { "help",        no_argument,            NULL,           'h' },
286  { "version",     no_argument,            NULL,           'V' },
287  { "verbose",     no_argument,            NULL,           'v' },
288  { "warn",        no_argument,            NULL,           'w' },
289  { "exec-prefix", required_argument,      NULL,           'E' },
290  { "march",       required_argument,      NULL,           'a' },
291  { "mcpu",        required_argument,      NULL,           'c' },
292  { "config",      required_argument,      NULL,           'C' },
293  { NULL,          0,                      NULL,            0 }
294};
295
296void
297usage (int exit_code)
298{
299  std::cout << "rtems-trace-ld [options] objects" << std::endl
300            << "Options and arguments:" << std::endl
301            << " -h        : help (also --help)" << std::endl
302            << " -V        : print linker version number and exit (also --version)" << std::endl
303            << " -v        : verbose (trace import parts), can supply multiple times" << std::endl
304            << "             to increase verbosity (also --verbose)" << std::endl
305            << " -w        : generate warnings (also --warn)" << std::endl
306            << " -E prefix : the RTEMS tool prefix (also --exec-prefix)" << std::endl
307            << " -a march  : machine architecture (also --march)" << std::endl
308            << " -c cpu    : machine architecture's CPU (also --mcpu)" << std::endl
309            << " -C ini    : user configuration INI file (also --config)" << std::endl;
310  ::exit (exit_code);
311}
312
313static void
314fatal_signal (int signum)
315{
316  signal (signum, SIG_DFL);
317
318  rld::process::temporaries.clean_up ();
319
320  /*
321   * Get the same signal again, this time not handled, so its normal effect
322   * occurs.
323   */
324  kill (getpid (), signum);
325}
326
327static void
328setup_signals (void)
329{
330  if (signal (SIGINT, SIG_IGN) != SIG_IGN)
331    signal (SIGINT, fatal_signal);
332#ifdef SIGHUP
333  if (signal (SIGHUP, SIG_IGN) != SIG_IGN)
334    signal (SIGHUP, fatal_signal);
335#endif
336  if (signal (SIGTERM, SIG_IGN) != SIG_IGN)
337    signal (SIGTERM, fatal_signal);
338#ifdef SIGPIPE
339  if (signal (SIGPIPE, SIG_IGN) != SIG_IGN)
340    signal (SIGPIPE, fatal_signal);
341#endif
342#ifdef SIGCHLD
343  signal (SIGCHLD, SIG_DFL);
344#endif
345}
346
347int
348main (int argc, char* argv[])
349{
350  int ec = 0;
351
352  setup_signals ();
353
354  try
355  {
356    trace::linker linker;
357    std::string   ld_cmd;
358    std::string   configuration;
359    bool          exec_prefix_set = false;
360#if HAVE_WARNINGS
361    bool          warnings = false;
362#endif
363
364    while (true)
365    {
366      int opt = ::getopt_long (argc, argv, "hvwVE:a:c:C:", rld_opts, NULL);
367      if (opt < 0)
368        break;
369
370      switch (opt)
371      {
372        case 'V':
373          std::cout << "rtems-trace-ld (RTEMS Trace Linker) " << rld::version ()
374                    << std::endl;
375          ::exit (0);
376          break;
377
378        case 'v':
379          rld::verbose_inc ();
380          break;
381
382        case 'w':
383#if HAVE_WARNINGS
384          warnings = true;
385#endif
386          break;
387
388        case 'E':
389          exec_prefix_set = true;
390          rld::cc::exec_prefix = optarg;
391          break;
392
393        case 'a':
394          rld::cc::march = optarg;
395          break;
396
397        case 'c':
398          rld::cc::mcpu = optarg;
399          break;
400
401        case 'C':
402          configuration = optarg;
403          break;
404
405        case '?':
406          usage (3);
407          break;
408
409        case 'h':
410          usage (0);
411          break;
412      }
413    }
414
415    argc -= optind;
416    argv += optind;
417
418    if (rld::verbose ())
419      std::cout << "RTEMS Trace Linker " << rld::version () << std::endl;
420
421    /*
422     * Load the remaining command line arguments into the linker command line.
423     */
424    while (argc--)
425    {
426      if (ld_cmd.length () != 0)
427        ld_cmd += " ";
428      ld_cmd += *argv++;
429    }
430
431    /*
432     * If there are no object files there is nothing to link.
433     */
434    if (ld_cmd.empty ())
435      throw rld::error ("no trace linker options", "options");
436
437    /*
438     * Perform a trace link.
439     */
440    try
441    {
442      linker.load_config (configuration);
443      linker.dump (std::cout);
444    }
445    catch (...)
446    {
447      throw;
448    }
449
450  }
451  catch (rld::error re)
452  {
453    std::cerr << "error: "
454              << re.where << ": " << re.what
455              << std::endl;
456    ec = 10;
457  }
458  catch (std::exception e)
459  {
460    int   status;
461    char* realname;
462    realname = abi::__cxa_demangle (e.what(), 0, 0, &status);
463    std::cerr << "error: exception: " << realname << " [";
464    ::free (realname);
465    const std::type_info &ti = typeid (e);
466    realname = abi::__cxa_demangle (ti.name(), 0, 0, &status);
467    std::cerr << realname << "] " << e.what () << std::endl;
468    ::free (realname);
469    ec = 11;
470  }
471  catch (...)
472  {
473    /*
474     * Helps to know if this happens.
475     */
476    std::cout << "error: unhandled exception" << std::endl;
477    ec = 12;
478  }
479
480  return ec;
481}
Note: See TracBrowser for help on using the repository browser.