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 | |
---|
53 | namespace rld |
---|
54 | { |
---|
55 | |
---|
56 | /** |
---|
57 | * Trim from start. |
---|
58 | */ |
---|
59 | inline std::string <rim (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 | */ |
---|
90 | namespace 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 | */ |
---|
284 | static 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 | |
---|
296 | void |
---|
297 | usage (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 | |
---|
313 | static void |
---|
314 | fatal_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 | |
---|
327 | static void |
---|
328 | setup_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 | |
---|
347 | int |
---|
348 | main (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 | } |
---|