diff options
Diffstat (limited to 'linkers/rtems-tld.cpp')
-rw-r--r-- | linkers/rtems-tld.cpp | 940 |
1 files changed, 940 insertions, 0 deletions
diff --git a/linkers/rtems-tld.cpp b/linkers/rtems-tld.cpp new file mode 100644 index 0000000..d206da7 --- /dev/null +++ b/linkers/rtems-tld.cpp @@ -0,0 +1,940 @@ +/* + * Copyright (c) 2014, Chris Johns <chrisj@rtems.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/** + * @file + * + * @ingroup rtems_rld + * + * @brief RTEMS Trace Linker manages creating a tracable RTEMS executable. + * + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <algorithm> +#include <cctype> +#include <functional> +#include <iostream> +#include <locale> +#include <sstream> + +#include <cxxabi.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <getopt.h> + +#include <rld.h> +#include <rld-cc.h> +#include <rld-config.h> +#include <rld-process.h> + +#ifndef HAVE_KILL +#define kill(p,s) raise(s) +#endif + +namespace rld +{ + /** + * RTEMS Trace Linker. + */ + namespace trace + { + /** + * A container of arguments. + */ + typedef std::vector < std::string > function_args; + + /** + * The return value. + */ + typedef std::string function_return; + + /** + * A function's signature. + */ + struct signature + { + std::string name; /**< The function's name. */ + function_args args; /**< The function's list of arguments. */ + function_return ret; /**< The fuctions return value. */ + + /** + * The default constructor. + */ + signature (); + + /** + * Construct the signature loading it from the configuration. + */ + signature (const rld::config::record& record); + + /** + * Return the function's declaration. + */ + const std::string decl () const; + }; + + /** + * A container of signatures. + */ + typedef std::map < std::string, signature > signatures; + + /** + * A function is list of function signatures headers and defines that allow + * a function to be wrapped. + */ + struct function + { + std::string name; /**< The name of this wrapper. */ + rld::strings headers; /**< Include statements. */ + rld::strings defines; /**< Define statements. */ + signatures signatures_; /**< Signatures in this function. */ + + /** + * Load the function. + */ + function (rld::config::config& config, + const std::string& name); + + /** + * Dump the function. + */ + void dump (std::ostream& out) const; + }; + + /** + * A container of functions. + */ + typedef std::vector < function > functions; + + /** + * A generator and that contains the functions used to trace arguments and + * return values. It also provides the implementation of those functions. + */ + struct generator + { + std::string name; /**< The name of this wrapper. */ + rld::strings headers; /**< Include statements. */ + rld::strings defines; /**< Define statements. */ + std::string map_sym_prefix; /**< Mapping symbol prefix. */ + std::string arg_trace; /**< Code template to trace an argument. */ + std::string ret_trace; /**< Code template to trace the return value. */ + rld::strings code; /**< Code block inserted before the trace code. */ + + /** + * Default constructor. + */ + generator (); + + /** + * Load the generator. + */ + generator (rld::config::config& config, + const std::string& name); + + /** + * Dump the generator. + */ + void dump (std::ostream& out) const; + }; + + /** + * Tracer. + */ + class tracer + { + public: + tracer (); + + /** + * Load the user's configuration. + */ + void load (rld::config::config& config, + const std::string& section); + + /** + * The the functions for the trace. + */ + void load_functions (rld::config::config& config, + const rld::config::section& section); + + /** + * The the traces for the tracer. + */ + void load_traces (rld::config::config& config, + const rld::config::section& section); + + /** + * Generate the wrapper object file. + */ + void generate (); + + /** + * Generate the trace functions. + */ + void generate_traces (rld::process::tempfile& c); + + /** + * Dump the wrapper. + */ + void dump (std::ostream& out) const; + + private: + + std::string name; /**< The name of the trace. */ + std::string bsp; /**< The BSP we are linking to. */ + rld::strings traces; /**< The functions to trace. */ + functions functions_; /**< The functions that can be traced. */ + generator generator_; /**< The tracer's generator. */ + }; + + /** + * Trace Linker. + */ + class linker + { + public: + linker (); + + /** + * Load the user's configuration. + */ + void load_config (const std::string& path, + const std::string& trace); + + /** + * Generate the C file. + */ + void generate_wrapper (); + + /** + * Dump the linker. + */ + void dump (std::ostream& out) const; + + private: + + rld::config::config config; /**< User configuration. */ + tracer tracer_; /**< The tracer */ + }; + + /** + * Recursive parser for strings. + */ + void + parse (rld::config::config& config, + const rld::config::section& section, + const std::string& sec_name, + const std::string& rec_name, + rld::strings& items, + bool split = true, + int depth = 0) + { + if (depth > 32) + throw rld::error ("too deep", "parsing: " + sec_name + '/' + rec_name); + + rld::config::parse_items (section, rec_name, items, false, false, split); + + rld::strings sl; + + rld::config::parse_items (section, sec_name, sl); + + for (rld::strings::iterator sli = sl.begin (); + sli != sl.end (); + ++sli) + { + const rld::config::section& sec = config.get_section (*sli); + parse (config, sec, sec_name, rec_name, items, split, depth + 1); + } + + /* + * Make the items unique. + */ + rld::strings::iterator ii; + ii = std::unique (items.begin (), items.end ()); + items.resize (std::distance (items.begin (), ii)); + } + + signature::signature () + { + } + + signature::signature (const rld::config::record& record) + { + /* + * There can only be one function signature in the configuration. + */ + if (!record.single ()) + throw rld::error ("duplicate", "signature: " + record.name); + + name = record.name; + + /* + * Signatures are defined as the return value then the arguments + * delimited by a comma and white space. No checking is made of the + * return value or arguments. + */ + rld::strings si; + rld::config::parse_items (record, si); + + if (si.size () == 0) + throw rld::error ("no return value", "signature: " + record.name); + if (si.size () == 1) + throw rld::error ("no arguments", "signature: " + record.name); + + ret = si[0]; + args.resize (si.size () - 1); + std::copy (si.begin () + 1, si.end (), args.begin ()); + } + + const std::string + signature::decl () const + { + std::string ds = ret + ' ' + name + '('; + int arg = 0; + for (function_args::const_iterator ai = args.begin (); + ai != args.end (); + ++ai) + { + if (ai != args.begin ()) + ds += ", "; + ds += (*ai) + " a" + rld::to_string (++arg); + } + ds += ')'; + return ds; + } + + function::function (rld::config::config& config, + const std::string& name) + : name (name) + { + /* + * A function section optionally contain one or more records of: + * + * # headers A list of sections containing headers or header records. + * # header A list of include string that are single or double quoted. + * # defines A list of sections containing defines or define record. + * # defines A list of define string that are single or double quoted. + * # signatures A list of section names of signatures. + * # includes A list of files to include. + * + * @note The quoting and list spliting is a little weak because a delimiter + * in a quote should not be seen as a delimiter. + */ + const rld::config::section& section = config.get_section (name); + + config.includes (section); + + parse (config, section, "headers", "header", headers); + parse (config, section, "defines", "define", defines); + + rld::strings sig_list; + section.get_record_items ("signatures", sig_list); + + for (rld::strings::const_iterator sli = sig_list.begin (); + sli != sig_list.end (); + ++sli) + { + const rld::config::section& sig_sec = config.get_section (*sli); + for (rld::config::records::const_iterator si = sig_sec.recs.begin (); + si != sig_sec.recs.end (); + ++si) + { + signature sig (*si); + signatures_[sig.name] = sig; + } + } + } + + void + function::dump (std::ostream& out) const + { + out << " Function: " << name << std::endl + << " Headers: " << headers.size () << std::endl; + for (rld::strings::const_iterator hi = headers.begin (); + hi != headers.end (); + ++hi) + { + out << " " << (*hi) << std::endl; + } + out << " Defines: " << defines.size () << std::endl; + for (rld::strings::const_iterator di = defines.begin (); + di != defines.end (); + ++di) + { + out << " " << (*di) << std::endl; + } + out << " Signatures: " << signatures_.size () << std::endl; + for (signatures::const_iterator si = signatures_.begin (); + si != signatures_.end (); + ++si) + { + const signature& sig = (*si).second; + out << " " << sig.name << ": " << sig.decl () << ';' << std::endl; + } + } + + generator::generator () + { + } + + generator::generator (rld::config::config& config, + const std::string& name) + : name (name) + { + /* + * A generator section optionally contain one or more records of: + * + * # headers A list of sections containing headers or header records. + * # header A list of include string that are single or double quoted. + * # defines A list of sections containing defines or define record. + * # defines A list of define string that are single or double quoted. + * # code-blocks A list of section names of code blocks. + * # includes A list of files to include. + * + * @note The quoting and list spliting is a little weak because a delimiter + * in a quote should not be seen as a delimiter. + */ + const rld::config::section& section = config.get_section (name); + + config.includes (section); + + parse (config, section, "headers", "header", headers); + parse (config, section, "defines", "define", defines); + parse (config, section, "code-blocks", "code", code, false); + + map_sym_prefix = section.get_record_item ("map-sym-prefix"); + arg_trace = rld::dequote (section.get_record_item ("arg-trace")); + ret_trace = rld::dequote (section.get_record_item ("ret-trace")); + } + + void + generator::dump (std::ostream& out) const + { + out << " Generator: " << name << std::endl + << " Headers: " << headers.size () << std::endl; + for (rld::strings::const_iterator hi = headers.begin (); + hi != headers.end (); + ++hi) + { + out << " " << (*hi) << std::endl; + } + out << " Defines: " << defines.size () << std::endl; + for (rld::strings::const_iterator di = defines.begin (); + di != defines.end (); + ++di) + { + out << " " << (*di) << std::endl; + } + out << " Mapping Symbol Prefix: " << map_sym_prefix << std::endl + << " Arg Trace Code: " << arg_trace << std::endl + << " Return Trace Code: " << ret_trace << std::endl + << " Code blocks: " << std::endl; + for (rld::strings::const_iterator ci = code.begin (); + ci != code.end (); + ++ci) + { + out << " > " + << rld::find_replace (*ci, "\n", "\n | ") << std::endl; + } + } + + tracer::tracer () + { + } + + void + tracer::load (rld::config::config& config, + const std::string& tname) + { + /* + * The configuration must contain a "section" section. This is the top level + * configuration and may contain: + * + * # name The name of trace being linked. + * # bsp The architecture/bsp name of the BSP. + * # options A list of options as per the long command line args. + * # traces The list of sections containing function lists to trace. + * # functions The list of sections containing function details. + * # include The list of files to include. + * + * The following records are required: + * + * # name + * # bsp + * # trace + * # functions + */ + const rld::config::section& section = config.get_section (tname); + + config.includes (section); + + name = section.get_record_item ("name"); + bsp = section.get_record_item ("bsp"); + + load_functions (config, section); + load_traces (config, section); + } + + void + tracer::load_functions (rld::config::config& config, + const rld::config::section& section) + { + rld::strings fl; + rld::config::parse_items (section, "functions", fl, true); + for (rld::strings::const_iterator fli = fl.begin (); + fli != fl.end (); + ++fli) + { + functions_.push_back (function (config, *fli)); + } + } + + void + tracer::load_traces (rld::config::config& config, + const rld::config::section& section) + { + parse (config, section, "traces", "trace", traces); + + rld::strings gens; + std::string gen; + + parse (config, section, "traces", "generator", gens); + + if (gens.size () > 1) + throw rld::error ("duplicate generators", "tracer: " + section.name); + + if (gens.size () == 0) + { + gen = + config.get_section ("default-generator").get_record_item ("generator"); + } + else + { + gen = gens[0]; + } + + generator_ = generator (config, gen); + } + + void + tracer::generate () + { + rld::process::tempfile c (".c"); + + c.open (true); + + if (rld::verbose ()) + std::cout << "wrapper C file: " << c.name () << std::endl; + + try + { + c.write_line ("/*"); + c.write_line (" * RTEMS Trace Linker Wrapper"); + c.write_line (" * Automatically generated."); + c.write_line (" */"); + + c.write_line (""); + c.write_line ("/*"); + c.write_line (" * Generator: " + generator_.name); + c.write_line (" */"); + c.write_lines (generator_.defines); + c.write_lines (generator_.headers); + c.write_line (""); + c.write_lines (generator_.code); + + generate_traces (c); + } + catch (...) + { + c.close (); + throw; + } + + c.close (); + } + + void + tracer::generate_traces (rld::process::tempfile& c) + { + for (functions::const_iterator fi = functions_.begin (); + fi != functions_.end (); + ++fi) + { + const function& funcs = *fi; + + for (rld::strings::const_iterator ti = traces.begin (); + ti != traces.end (); + ++ti) + { + const std::string& trace = *ti; + signatures::const_iterator si = funcs.signatures_.find (trace); + + if (si != funcs.signatures_.end ()) + { + c.write_line (""); + c.write_line ("/*"); + c.write_line (" * Function: " + funcs.name); + c.write_line (" */"); + c.write_lines (funcs.defines); + c.write_lines (funcs.headers); + break; + } + } + } + + c.write_line (""); + c.write_line ("/*"); + c.write_line (" * Wrappers."); + c.write_line (" */"); + + for (rld::strings::const_iterator ti = traces.begin (); + ti != traces.end (); + ++ti) + { + const std::string& trace = *ti; + bool found = false; + + for (functions::const_iterator fi = functions_.begin (); + fi != functions_.end (); + ++fi) + { + const function& funcs = *fi; + signatures::const_iterator si = funcs.signatures_.find (trace); + + if (si != funcs.signatures_.end ()) + { + found = true; + + const signature& sig = (*si).second; + + c.write_line(""); + c.write_line(sig.decl ()); + c.write_line("{"); + + std::string l; + + /* + * @todo Need to define as part of the function signature if ret + * processing is required. + */ + if (sig.ret != "void") + { + c.write_line(" " + sig.ret + " ret;"); + l = " ret ="; + } + + l += " " + generator_.map_sym_prefix + sig.name + '('; + for (size_t a = 0; a < sig.args.size (); ++a) + { + if (a) + l += ", "; + l += "a" + rld::to_string ((int) (a + 1)); + } + l += ");"; + c.write_line(l); + + if (sig.ret != "void") + { + c.write_line(" return ret;"); + } + + c.write_line("}"); + } + } + + if (!found) + throw rld::error ("not found", "trace function: " + trace); + } + } + + void + tracer::dump (std::ostream& out) const + { + out << " Tracer: " << name << std::endl + << " BSP: " << bsp << std::endl + << " Traces: " << traces.size () << std::endl; + for (rld::strings::const_iterator ti = traces.begin (); + ti != traces.end (); + ++ti) + { + out << " " << (*ti) << std::endl; + } + out << " Functions: " << functions_.size () << std::endl; + for (functions::const_iterator fi = functions_.begin (); + fi != functions_.end (); + ++fi) + { + (*fi).dump (out); + } + out << " Generator: " << std::endl; + generator_.dump (out); + } + + linker::linker () + { + } + + void + linker::load_config (const std::string& path, + const std::string& trace) + { + config.clear (); + config.load (path); + tracer_.load (config, trace); + } + + void + linker::generate_wrapper () + { + tracer_.generate (); + } + + void + linker::dump (std::ostream& out) const + { + const rld::config::paths& cpaths = config.get_paths (); + out << " Configuration Files: " << cpaths.size () << std::endl; + for (rld::config::paths::const_iterator pi = cpaths.begin (); + pi != cpaths.end (); + ++pi) + { + out << " " << (*pi) << std::endl; + } + + tracer_.dump (out); + } + } +} + +/** + * RTEMS Trace Linker options. This needs to be rewritten to be like cc where + * only a single '-' and long options is present. Anything after '--' is passed + * to RTEMS's real linker. + */ +static struct option rld_opts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "verbose", no_argument, NULL, 'v' }, + { "warn", no_argument, NULL, 'w' }, + { "keep", no_argument, NULL, 'k' }, + { "exec-prefix", required_argument, NULL, 'E' }, + { "march", required_argument, NULL, 'a' }, + { "mcpu", required_argument, NULL, 'c' }, + { "config", required_argument, NULL, 'C' }, + { NULL, 0, NULL, 0 } +}; + +void +usage (int exit_code) +{ + std::cout << "rtems-trace-ld [options] objects" << std::endl + << "Options and arguments:" << std::endl + << " -h : help (also --help)" << std::endl + << " -V : print linker version number and exit (also --version)" << std::endl + << " -v : verbose (trace import parts), can supply multiple times" << std::endl + << " to increase verbosity (also --verbose)" << std::endl + << " -w : generate warnings (also --warn)" << std::endl + << " -k : keep temporary files (also --keep)" << std::endl + << " -E prefix : the RTEMS tool prefix (also --exec-prefix)" << std::endl + << " -a march : machine architecture (also --march)" << std::endl + << " -c cpu : machine architecture's CPU (also --mcpu)" << std::endl + << " -C ini : user configuration INI file (also --config)" << std::endl; + ::exit (exit_code); +} + +static void +fatal_signal (int signum) +{ + signal (signum, SIG_DFL); + + rld::process::temporaries_clean_up (); + + /* + * Get the same signal again, this time not handled, so its normal effect + * occurs. + */ + kill (getpid (), signum); +} + +static void +setup_signals (void) +{ + if (signal (SIGINT, SIG_IGN) != SIG_IGN) + signal (SIGINT, fatal_signal); +#ifdef SIGHUP + if (signal (SIGHUP, SIG_IGN) != SIG_IGN) + signal (SIGHUP, fatal_signal); +#endif + if (signal (SIGTERM, SIG_IGN) != SIG_IGN) + signal (SIGTERM, fatal_signal); +#ifdef SIGPIPE + if (signal (SIGPIPE, SIG_IGN) != SIG_IGN) + signal (SIGPIPE, fatal_signal); +#endif +#ifdef SIGCHLD + signal (SIGCHLD, SIG_DFL); +#endif +} + +int +main (int argc, char* argv[]) +{ + int ec = 0; + + setup_signals (); + + try + { + rld::trace::linker linker; + std::string ld_cmd; + std::string configuration; + std::string trace = "tracer"; + bool exec_prefix_set = false; +#if HAVE_WARNINGS + bool warnings = false; +#endif + + while (true) + { + int opt = ::getopt_long (argc, argv, "hvwkVE:a:c:C:", rld_opts, NULL); + if (opt < 0) + break; + + switch (opt) + { + case 'V': + std::cout << "rtems-trace-ld (RTEMS Trace Linker) " << rld::version () + << std::endl; + ::exit (0); + break; + + case 'v': + rld::verbose_inc (); + break; + + case 'w': +#if HAVE_WARNINGS + warnings = true; +#endif + break; + + case 'k': + rld::process::set_keep_temporary_files (); + break; + + case 'E': + exec_prefix_set = true; + rld::cc::exec_prefix = optarg; + break; + + case 'a': + rld::cc::march = optarg; + break; + + case 'c': + rld::cc::mcpu = optarg; + break; + + case 'C': + configuration = optarg; + break; + + case '?': + usage (3); + break; + + case 'h': + usage (0); + break; + } + } + + argc -= optind; + argv += optind; + + if (rld::verbose ()) + std::cout << "RTEMS Trace Linker " << rld::version () << std::endl; + + /* + * Load the remaining command line arguments into the linker command line. + */ + while (argc--) + { + if (ld_cmd.length () != 0) + ld_cmd += " "; + ld_cmd += *argv++; + } + + /* + * If there are no object files there is nothing to link. + */ + if (ld_cmd.empty ()) + throw rld::error ("no trace linker options", "options"); + + /* + * Perform a trace link. + */ + try + { + linker.load_config (configuration, trace); + linker.generate_wrapper (); + + if (rld::verbose ()) + linker.dump (std::cout); + } + catch (...) + { + throw; + } + + } + catch (rld::error re) + { + std::cerr << "error: " + << re.where << ": " << re.what + << std::endl; + ec = 10; + } + catch (std::exception e) + { + int status; + char* realname; + realname = abi::__cxa_demangle (e.what(), 0, 0, &status); + std::cerr << "error: exception: " << realname << " ["; + ::free (realname); + const std::type_info &ti = typeid (e); + realname = abi::__cxa_demangle (ti.name(), 0, 0, &status); + std::cerr << realname << "] " << e.what () << std::endl; + ::free (realname); + ec = 11; + } + catch (...) + { + /* + * Helps to know if this happens. + */ + std::cout << "error: unhandled exception" << std::endl; + ec = 12; + } + + return ec; +} |