forked from Imagelibrary/rtems
1260 lines
34 KiB
C
1260 lines
34 KiB
C
/* SPDX-License-Identifier: BSD-2-Clause */
|
|
|
|
/**
|
|
* @file
|
|
*
|
|
* @ingroup rtems_rtld
|
|
*
|
|
* @brief RTEMS Run-Time Link Editor Shell Commands
|
|
*
|
|
* A simple RTL command to aid using the RTL.
|
|
*/
|
|
|
|
/*
|
|
* COPYRIGHT (c) 2012, 2018 Chris Johns <chrisj@rtems.org>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <inttypes.h>
|
|
#include <rtems/inttypes.h>
|
|
|
|
#include <sys/stat.h>
|
|
#include <regex.h>
|
|
#include <string.h>
|
|
|
|
#include <dlfcn.h>
|
|
|
|
#include <rtems/printer.h>
|
|
#include <rtems/rtl/rtl.h>
|
|
#include <rtems/rtl/rtl-archive.h>
|
|
#include <rtems/rtl/rtl-shell.h>
|
|
#include <rtems/rtl/rtl-trace.h>
|
|
#include "rtl-chain-iterator.h"
|
|
|
|
/**
|
|
* The type of the shell handlers we have.
|
|
*/
|
|
typedef int (*rtems_rtl_shell_handler) (const rtems_printer* printer, int argc, char *argv[]);
|
|
|
|
/**
|
|
* Table of handlers we parse to invoke the command.
|
|
*/
|
|
typedef struct
|
|
{
|
|
const char* name; /**< The sub-command's name. */
|
|
rtems_rtl_shell_handler handler; /**< The sub-command's handler. */
|
|
const char* help; /**< The sub-command's help. */
|
|
} rtems_rtl_shell_cmd;
|
|
|
|
/**
|
|
* Object summary data.
|
|
*/
|
|
typedef struct
|
|
{
|
|
int count; /**< The number of object files. */
|
|
size_t exec; /**< The amount of executable memory allocated. */
|
|
size_t symbols; /**< The amount of symbol memory allocated. */
|
|
} rtems_rtl_obj_summary;
|
|
|
|
/**
|
|
* Object summary iterator.
|
|
*/
|
|
static bool
|
|
rtems_rtl_obj_summary_iterator (rtems_chain_node* node, void* data)
|
|
{
|
|
rtems_rtl_obj_summary* summary = data;
|
|
rtems_rtl_obj* obj = (rtems_rtl_obj*) node;
|
|
++summary->count;
|
|
summary->exec += obj->exec_size;
|
|
summary->symbols += obj->global_size;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Count the number of symbols.
|
|
*/
|
|
static int
|
|
rtems_rtl_count_symbols (rtems_rtl_data* rtl)
|
|
{
|
|
int count;
|
|
int bucket;
|
|
for (count = 0, bucket = 0; bucket < rtl->globals.nbuckets; ++bucket)
|
|
count += rtems_rtl_chain_count (&rtl->globals.buckets[bucket]);
|
|
return count;
|
|
}
|
|
|
|
static int
|
|
rtems_rtl_shell_status (const rtems_printer* printer,
|
|
int argc,
|
|
char* argv[])
|
|
{
|
|
rtems_rtl_obj_summary summary;
|
|
size_t total_memory;
|
|
rtems_rtl_data* rtl;
|
|
|
|
rtl = rtems_rtl_lock ();
|
|
if (rtl == NULL)
|
|
{
|
|
rtems_printf (printer, "error: cannot lock the linker\n");
|
|
return 1;
|
|
}
|
|
|
|
summary.count = 0;
|
|
summary.exec = 0;
|
|
summary.symbols = 0;
|
|
rtems_rtl_chain_iterate (&rtl->objects,
|
|
rtems_rtl_obj_summary_iterator,
|
|
&summary);
|
|
/*
|
|
* Currently does not include the name strings in the obj struct.
|
|
*/
|
|
total_memory =
|
|
sizeof (*rtl) + (summary.count * sizeof (rtems_rtl_obj)) +
|
|
summary.exec + summary.symbols;
|
|
|
|
rtems_printf (printer, "Runtime Linker Status:\n");
|
|
rtems_printf (printer, " paths: %s\n", rtl->paths);
|
|
rtems_printf (printer, " objects: %d\n", summary.count);
|
|
rtems_printf (printer, " total memory: %zi\n", total_memory);
|
|
rtems_printf (printer, " exec memory: %zi\n", summary.exec);
|
|
rtems_printf (printer, " sym memory: %zi\n", summary.symbols);
|
|
rtems_printf (printer, " symbols: %d\n", rtems_rtl_count_symbols (rtl));
|
|
|
|
rtems_rtl_unlock ();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Object print data.
|
|
*/
|
|
typedef struct
|
|
{
|
|
const rtems_printer* printer; /**< The RTEMS printer. */
|
|
rtems_rtl_data* rtl; /**< The RTL data. */
|
|
int indent; /**< Spaces to indent. */
|
|
bool oname; /**< Print object names. */
|
|
bool names; /**< Print details of all names. */
|
|
bool stats; /**< Print stats. */
|
|
bool memory_map; /**< Print the memory map. */
|
|
bool symbols; /**< Print the global symbols. */
|
|
bool dependencies; /**< Print any dependencies. */
|
|
bool trampolines; /**< Print trampoline stats. */
|
|
bool base; /**< Include the base object file. */
|
|
const char* re_name; /**< Name regx to filter on. */
|
|
const char* re_symbol; /**< Symbol regx to filter on. */
|
|
} rtems_rtl_obj_print;
|
|
|
|
/**
|
|
* Parse an argument.
|
|
*/
|
|
static bool
|
|
rtems_rtl_parse_opt (const char opt, int argc, char *argv[])
|
|
{
|
|
size_t arg;
|
|
for (arg = 1; arg < argc; ++arg)
|
|
{
|
|
if (argv[arg][0] == '-')
|
|
{
|
|
size_t len = strlen (argv[arg]);
|
|
size_t i;
|
|
for (i = 1; i < len; ++i)
|
|
if (argv[arg][i] == opt)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
rtems_rtl_check_opts (const rtems_printer* printer,
|
|
const char* opts,
|
|
int argc,
|
|
char* argv[])
|
|
{
|
|
size_t olen = strlen (opts);
|
|
size_t arg;
|
|
for (arg = 1; arg < argc; ++arg)
|
|
{
|
|
if (argv[arg][0] == '-')
|
|
{
|
|
size_t len = strlen (argv[arg]);
|
|
size_t i;
|
|
for (i = 1; i < len; ++i)
|
|
{
|
|
bool found = false;
|
|
size_t o;
|
|
for (o = 0; o < olen; ++o)
|
|
{
|
|
if (argv[arg][i] == opts[o])
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
{
|
|
rtems_printf (printer, "error: invalid option: %c (%s)\n",
|
|
argv[arg][i], argv[arg]);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static ssize_t
|
|
rtems_rtl_parse_arg_index (const char opt,
|
|
const char* skip_opts,
|
|
int argc,
|
|
char* argv[])
|
|
{
|
|
ssize_t arg;
|
|
for (arg = 1; arg < argc; ++arg)
|
|
{
|
|
if (argv[arg][0] == '-')
|
|
{
|
|
/*
|
|
* We can check the next char because there has to be a valid char or a
|
|
* nul.
|
|
*/
|
|
if (argv[arg][1] != '\0')
|
|
{
|
|
size_t len = skip_opts != NULL ? strlen (skip_opts) : 0;
|
|
size_t i;
|
|
for (i = 0; i < len; ++i)
|
|
{
|
|
if (skip_opts[i] == argv[arg][1])
|
|
{
|
|
++arg;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (opt == ' ')
|
|
return arg;
|
|
}
|
|
/*
|
|
* Is this an option and does it match what we are looking for?
|
|
*/
|
|
if (arg < argc && argv[arg][0] == '-' && argv[arg][1] == opt)
|
|
return arg + 1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static const char*
|
|
rtems_rtl_parse_arg (const char opt,
|
|
const char* skip_opts,
|
|
int argc,
|
|
char* argv[])
|
|
{
|
|
ssize_t arg = rtems_rtl_parse_arg_index (opt, skip_opts, argc, argv);
|
|
if (arg < 0)
|
|
return NULL;
|
|
return argv[arg];
|
|
}
|
|
|
|
/**
|
|
* Regx matching.
|
|
*/
|
|
static bool
|
|
rtems_rtl_regx_compile (const rtems_printer* printer,
|
|
const char* label,
|
|
regex_t* rege,
|
|
const char* expression)
|
|
{
|
|
int r = regcomp (rege, expression, REG_EXTENDED | REG_NOSUB);
|
|
if (r != 0)
|
|
{
|
|
char rerror[128];
|
|
regerror (r, rege, rerror, sizeof(rerror));
|
|
rtems_printf (printer, "error: %s: %s\n", label, rerror);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static int
|
|
rtems_rtl_regx_match (const rtems_printer* printer,
|
|
const char* label,
|
|
regex_t* rege,
|
|
const char* string)
|
|
{
|
|
int r = regexec (rege, string, 0, NULL, 0);
|
|
if (r != 0 && r != REG_NOMATCH)
|
|
{
|
|
char rerror[128];
|
|
regerror (r, rege, rerror, sizeof(rerror));
|
|
rtems_printf (printer, "error: %s: %s\n", label, rerror);
|
|
regfree (rege);
|
|
return -1;
|
|
}
|
|
return r == 0 ? 1 : 0;
|
|
}
|
|
|
|
/**
|
|
* Print the obj name.
|
|
*/
|
|
static void
|
|
rtems_rtl_print_obj_name (const rtems_rtl_obj_print* print, rtems_rtl_obj* obj)
|
|
{
|
|
rtems_printf (print->printer, "%-*c", print->indent, ' ');
|
|
if (rtems_rtl_obj_aname (obj) != NULL)
|
|
rtems_printf (print->printer, "%s:", rtems_rtl_obj_aname (obj));
|
|
rtems_printf (print->printer, "%s\n", rtems_rtl_obj_oname (obj));
|
|
}
|
|
|
|
/**
|
|
* Print symbols.
|
|
*/
|
|
static bool
|
|
rtems_rtl_print_symbols (rtems_rtl_obj_print* print,
|
|
rtems_rtl_obj* obj,
|
|
int indent,
|
|
bool show_name)
|
|
{
|
|
regex_t rege;
|
|
int max_len = 0;
|
|
int s;
|
|
|
|
if (print->re_symbol != NULL &&
|
|
!rtems_rtl_regx_compile (print->printer,
|
|
"symbol filter",
|
|
®e, print->re_symbol))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (s = 0; s < obj->global_syms; ++s)
|
|
{
|
|
const char* sym = obj->global_table[s].name;
|
|
int len;
|
|
|
|
if (print->re_symbol != NULL)
|
|
{
|
|
int r = rtems_rtl_regx_match (print->printer, "symbol match", ®e, sym);
|
|
if (r < 0)
|
|
return false;
|
|
if (!r)
|
|
continue;
|
|
}
|
|
|
|
len = strlen (obj->global_table[s].name);
|
|
if (len > max_len)
|
|
max_len = len;
|
|
}
|
|
|
|
for (s = 0; s < obj->global_syms; ++s)
|
|
{
|
|
const char* sym = obj->global_table[s].name;
|
|
if (print->re_symbol != NULL)
|
|
{
|
|
int r = rtems_rtl_regx_match (print->printer, "symbol match", ®e, sym);
|
|
if (r < 0)
|
|
return false;
|
|
if (r == 0)
|
|
continue;
|
|
}
|
|
if (show_name)
|
|
{
|
|
show_name = false;
|
|
rtems_rtl_print_obj_name (print, obj);
|
|
}
|
|
rtems_printf (print->printer, "%-*c%-*s = %p\n", indent + 2, ' ',
|
|
max_len, sym, obj->global_table[s].value);
|
|
}
|
|
|
|
regfree (®e);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Dependencies printer.
|
|
*/
|
|
typedef struct
|
|
{
|
|
const rtems_rtl_obj_print* print; /**< The print data. */
|
|
bool first; /**< Is this the first line printed. */
|
|
bool show_name; /**< Show the object name. */
|
|
int indent; /**< The indent. */
|
|
} rtems_rtl_dep_data;
|
|
|
|
static bool
|
|
rtems_rtl_dependencies (rtems_rtl_obj* obj, rtems_rtl_obj* dependent, void* data)
|
|
{
|
|
rtems_rtl_dep_data* dd = (rtems_rtl_dep_data*) data;
|
|
if (dd->first)
|
|
{
|
|
dd->first = false;
|
|
if (dd->show_name)
|
|
{
|
|
dd->show_name = false;
|
|
rtems_rtl_print_obj_name (dd->print, obj);
|
|
}
|
|
rtems_printf (dd->print->printer, "%-*cdependencies : ", dd->indent, ' ');
|
|
dd->indent += strlen ("dependencies :");
|
|
}
|
|
else
|
|
{
|
|
rtems_printf (dd->print->printer, "\n%-*c: ", (int) dd->indent, ' ');
|
|
}
|
|
rtems_printf (dd->print->printer, "%s", dependent->oname);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Object printer.
|
|
*/
|
|
static bool
|
|
rtems_rtl_obj_printer (rtems_rtl_obj_print* print, rtems_rtl_obj* obj)
|
|
{
|
|
char flags_str[33];
|
|
int indent = print->indent + 1;
|
|
bool show_name = true;
|
|
|
|
/*
|
|
* Skip the base module unless asked to show it.
|
|
*/
|
|
if (!print->base && (obj == print->rtl->base))
|
|
return true;
|
|
|
|
if (print->re_name != NULL)
|
|
{
|
|
regex_t rege;
|
|
int r = 0;
|
|
|
|
if (!rtems_rtl_regx_compile (print->printer,
|
|
"name filter",
|
|
®e, print->re_name))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (rtems_rtl_obj_aname (obj) != NULL)
|
|
{
|
|
r = rtems_rtl_regx_match (print->printer,
|
|
"aname match",
|
|
®e,
|
|
rtems_rtl_obj_aname (obj));
|
|
if (r < 0)
|
|
return false;
|
|
}
|
|
|
|
if (r == 0)
|
|
{
|
|
r = rtems_rtl_regx_match (print->printer,
|
|
"oname match",
|
|
®e,
|
|
rtems_rtl_obj_oname (obj));
|
|
if (r < 0)
|
|
return false;
|
|
}
|
|
|
|
regfree (®e);
|
|
|
|
if (r == 0)
|
|
return true;
|
|
}
|
|
|
|
if (print->names || print->memory_map || print->stats ||
|
|
(!print->names && !print->memory_map && !print->stats &&
|
|
!print->symbols && !print->dependencies))
|
|
{
|
|
show_name = false;
|
|
rtems_rtl_print_obj_name (print, obj);
|
|
}
|
|
|
|
if (print->names)
|
|
{
|
|
rtems_printf (print->printer,
|
|
"%-*cfile name : %s\n",
|
|
indent, ' ', rtems_rtl_obj_fname (obj));
|
|
rtems_printf (print->printer,
|
|
"%-*carchive name : %s\n",
|
|
indent, ' ', rtems_rtl_obj_aname (obj));
|
|
strcpy (flags_str, "--");
|
|
if (obj->flags & RTEMS_RTL_OBJ_LOCKED)
|
|
flags_str[0] = 'L';
|
|
if (obj->flags & RTEMS_RTL_OBJ_UNRESOLVED)
|
|
flags_str[1] = 'U';
|
|
rtems_printf (print->printer,
|
|
"%-*cflags : %s\n", indent, ' ', flags_str);
|
|
rtems_printf (print->printer,
|
|
"%-*cfile offset : %" PRIdoff_t "\n", indent, ' ', obj->ooffset);
|
|
rtems_printf (print->printer,
|
|
"%-*cfile size : %zi\n", indent, ' ', obj->fsize);
|
|
}
|
|
if (print->memory_map)
|
|
{
|
|
rtems_printf (print->printer,
|
|
"%-*cexec size : %zi\n", indent, ' ', obj->exec_size);
|
|
rtems_printf (print->printer,
|
|
"%-*ctext base : %p (%zi)\n", indent, ' ',
|
|
obj->text_base, obj->text_size);
|
|
rtems_printf (print->printer,
|
|
"%-*cconst base : %p (%zi)\n", indent, ' ',
|
|
obj->const_base, obj->const_size);
|
|
rtems_printf (print->printer,
|
|
"%-*cdata base : %p (%zi)\n", indent, ' ',
|
|
obj->data_base, obj->data_size);
|
|
rtems_printf (print->printer,
|
|
"%-*cbss base : %p (%zi)\n", indent, ' ',
|
|
obj->bss_base, obj->bss_size);
|
|
}
|
|
if (print->stats)
|
|
{
|
|
rtems_printf (print->printer, "%-*cunresolved : %zu\n", indent, ' ', obj->unresolved);
|
|
rtems_printf (print->printer, "%-*cusers : %zu\n", indent, ' ', obj->users);
|
|
rtems_printf (print->printer, "%-*creferences : %zu\n", indent, ' ', obj->refs);
|
|
rtems_printf (print->printer, "%-*ctrampolines : %zu\n", indent, ' ',
|
|
rtems_rtl_obj_trampolines (obj));
|
|
rtems_printf (print->printer, "%-*csymbols : %zi\n", indent, ' ', obj->global_syms);
|
|
rtems_printf (print->printer, "%-*csymbol memory : %zi\n", indent, ' ', obj->global_size);
|
|
}
|
|
if (print->symbols)
|
|
{
|
|
if (!rtems_rtl_print_symbols (print, obj, indent, show_name))
|
|
return false;
|
|
}
|
|
if (print->dependencies)
|
|
{
|
|
rtems_rtl_dep_data dd = {
|
|
.print = print,
|
|
.first = true,
|
|
.show_name = show_name,
|
|
.indent = indent
|
|
};
|
|
rtems_rtl_obj_iterate_dependents (obj, rtems_rtl_dependencies, &dd);
|
|
if (!dd.first)
|
|
rtems_printf (print->printer, "\n");
|
|
}
|
|
if (print->trampolines)
|
|
{
|
|
if (obj->tramp_size == 0)
|
|
{
|
|
rtems_printf (print->printer, "%-*ctrampolines: not supported\n", indent, ' ');
|
|
}
|
|
else
|
|
{
|
|
size_t slots = rtems_rtl_obj_trampoline_slots (obj);
|
|
size_t used = rtems_rtl_obj_trampolines (obj);
|
|
rtems_printf (print->printer, "%-*ctrampolines:\n", indent, ' ');
|
|
rtems_printf (print->printer, "%-*cslots : %zu\n", indent + 4, ' ',
|
|
slots);
|
|
rtems_printf (print->printer, "%-*csize : %zu\n", indent + 4, ' ',
|
|
obj->tramp_size);
|
|
rtems_printf (print->printer, "%-*cslot size : %zu\n", indent + 4, ' ',
|
|
obj->tramp_size);
|
|
rtems_printf (print->printer, "%-*cused : %zu\n", indent + 4, ' ',
|
|
used);
|
|
rtems_printf (print->printer, "%-*crelocs : %zu\n", indent + 4, ' ',
|
|
obj->tramp_relocs);
|
|
rtems_printf (print->printer, "%-*cunresolved: %zu\n", indent + 4, ' ',
|
|
slots - obj->tramp_relocs);
|
|
rtems_printf (print->printer, "%-*cyield : %zu%%\n", indent + 4, ' ',
|
|
slots ? (used * 100) / slots : 0);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Object unresolved symbols printer.
|
|
*/
|
|
static bool
|
|
rtems_rtl_unresolved_printer (rtems_rtl_unresolv_rec* rec,
|
|
void* data)
|
|
{
|
|
rtems_rtl_obj_print* print = (rtems_rtl_obj_print*) data;
|
|
if (rec->type == rtems_rtl_unresolved_symbol)
|
|
rtems_printf (print->printer,
|
|
"%-*c%s\n", print->indent + 2, ' ', rec->rec.name.name);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Object print iterator.
|
|
*/
|
|
static bool
|
|
rtems_rtl_obj_print_iterator (rtems_chain_node* node, void* data)
|
|
{
|
|
rtems_rtl_obj_print* print = data;
|
|
rtems_rtl_obj* obj = (rtems_rtl_obj*) node;
|
|
return rtems_rtl_obj_printer (print, obj);
|
|
}
|
|
|
|
int
|
|
rtems_rtl_shell_list (const rtems_printer* printer, int argc, char* argv[])
|
|
{
|
|
rtems_rtl_obj_print print = { 0 };
|
|
if (!rtems_rtl_check_opts (printer, "anlmsdbt", argc, argv))
|
|
return 1;
|
|
print.printer = printer;
|
|
print.indent = 1;
|
|
print.oname = true;
|
|
if (rtems_rtl_parse_opt ('a', argc, argv))
|
|
{
|
|
print.names = true;
|
|
print.stats = true;
|
|
print.memory_map = true;
|
|
print.symbols = true;
|
|
print.dependencies = true;
|
|
print.trampolines = true;
|
|
}
|
|
else
|
|
{
|
|
print.names = rtems_rtl_parse_opt ('n', argc, argv);
|
|
print.stats = rtems_rtl_parse_opt ('l', argc, argv);;
|
|
print.memory_map = rtems_rtl_parse_opt ('m', argc, argv);;
|
|
print.symbols = rtems_rtl_parse_opt ('s', argc, argv);
|
|
print.dependencies = rtems_rtl_parse_opt ('d', argc, argv);;
|
|
print.trampolines = rtems_rtl_parse_opt ('t', argc, argv);;
|
|
print.base = rtems_rtl_parse_opt ('b', argc, argv);;
|
|
print.re_name = rtems_rtl_parse_arg (' ', NULL, argc, argv);
|
|
}
|
|
print.re_symbol = NULL;
|
|
print.rtl = rtems_rtl_lock ();
|
|
if (print.rtl == NULL)
|
|
{
|
|
rtems_printf (print.printer, "error: cannot lock the linker\n");
|
|
return 1;
|
|
}
|
|
rtems_rtl_chain_iterate (&print.rtl->objects,
|
|
rtems_rtl_obj_print_iterator,
|
|
&print);
|
|
rtems_rtl_unlock ();
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rtems_rtl_shell_sym (const rtems_printer* printer, int argc, char* argv[])
|
|
{
|
|
rtems_rtl_obj_print print = { 0 };
|
|
if (!rtems_rtl_check_opts (printer, "buo", argc, argv))
|
|
return 1;
|
|
print.printer = printer;
|
|
print.indent = 1;
|
|
print.oname = true;
|
|
print.names = false;
|
|
print.stats = false;
|
|
print.memory_map = false;
|
|
print.symbols = !rtems_rtl_parse_opt ('u', argc, argv);;
|
|
print.dependencies = false;
|
|
print.base = rtems_rtl_parse_opt ('b', argc, argv);
|
|
print.re_name = rtems_rtl_parse_arg ('o', NULL, argc, argv);;
|
|
print.re_symbol = rtems_rtl_parse_arg (' ', "ou", argc, argv);
|
|
print.rtl = rtems_rtl_lock ();
|
|
if (print.rtl == NULL)
|
|
{
|
|
rtems_printf (print.printer, "error: cannot lock the linker\n");
|
|
return 1;
|
|
}
|
|
if (print.symbols)
|
|
{
|
|
rtems_rtl_chain_iterate (&print.rtl->objects,
|
|
rtems_rtl_obj_print_iterator,
|
|
&print);
|
|
}
|
|
if (rtems_rtl_parse_opt ('u', argc, argv))
|
|
{
|
|
rtems_printf (printer, "Unresolved:\n");
|
|
rtems_rtl_unresolved_iterate (rtems_rtl_unresolved_printer, &print);
|
|
}
|
|
rtems_rtl_unlock ();
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rtems_rtl_shell_object (const rtems_printer* printer, int argc, char* argv[])
|
|
{
|
|
size_t arg;
|
|
|
|
--argc;
|
|
++argv;
|
|
|
|
for (arg = 0; arg < argc; ++arg)
|
|
{
|
|
if (argv[arg][0] == '-')
|
|
{
|
|
switch (argv[arg][1])
|
|
{
|
|
case 'h':
|
|
case '?':
|
|
rtems_printf (printer, "obj commands:\n");
|
|
rtems_printf (printer, " load <file>\n");
|
|
rtems_printf (printer, " unload <file>\n");
|
|
break;
|
|
default:
|
|
rtems_printf (printer, "error: invalid option: %s\n", argv[arg]);
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (arg >= argc)
|
|
{
|
|
rtems_printf (printer, "error: no obj command\n");
|
|
return 1;
|
|
}
|
|
|
|
if (strcmp (argv[arg], "load") == 0)
|
|
{
|
|
void* handle;
|
|
int unresolved;
|
|
|
|
++arg;
|
|
if (arg >= argc)
|
|
{
|
|
rtems_printf (printer, "error: no object file to load\n");
|
|
return 1;
|
|
}
|
|
|
|
handle = dlopen (argv[arg], RTLD_NOW | RTLD_GLOBAL);
|
|
if (handle == NULL)
|
|
{
|
|
rtems_printf (printer, "error: load: %s: %s\n", argv[arg], dlerror ());
|
|
return 1;
|
|
}
|
|
|
|
if (dlinfo (RTLD_SELF, RTLD_DI_UNRESOLVED, &unresolved) < 0)
|
|
{
|
|
rtems_printf (printer, "error: %s: %s\n", argv[arg], dlerror ());
|
|
return 1;
|
|
}
|
|
|
|
if (unresolved != 0)
|
|
{
|
|
rtems_printf (printer, "warning: unresolved symbols present\n");
|
|
return 1;
|
|
}
|
|
}
|
|
else if (strcmp (argv[arg], "unload") == 0)
|
|
{
|
|
rtems_rtl_data* rtl;
|
|
rtems_rtl_obj* obj;
|
|
|
|
++arg;
|
|
if (arg >= argc)
|
|
{
|
|
rtems_printf (printer, "error: no object file to load\n");
|
|
return 1;
|
|
}
|
|
|
|
rtl = rtems_rtl_lock ();
|
|
if (rtl == NULL)
|
|
{
|
|
rtems_printf (printer, "error: cannot lock RTL\n");
|
|
return 1;
|
|
}
|
|
|
|
obj = rtems_rtl_find_obj (argv[arg]);
|
|
if (obj == NULL)
|
|
{
|
|
rtems_rtl_unlock ();
|
|
rtems_printf (printer, "error: unload: %s: %s\n", argv[arg], dlerror ());
|
|
return 1;
|
|
}
|
|
|
|
if (!rtems_rtl_unload (obj))
|
|
{
|
|
rtems_rtl_unlock ();
|
|
rtems_printf (printer, "error: unload: %s: %s\n", argv[arg], dlerror ());
|
|
return 1;
|
|
}
|
|
|
|
rtems_rtl_unlock ();
|
|
}
|
|
else
|
|
{
|
|
rtems_printf (printer, "error: unknown obj command: %s\n", argv[arg]);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rtems_rtl_shell_archive (const rtems_printer* printer, int argc, char* argv[])
|
|
{
|
|
rtems_rtl_data* rtl;
|
|
rtems_chain_node* node;
|
|
const char* re_name;
|
|
bool details;
|
|
bool symbols;
|
|
bool duplicates;
|
|
regex_t rege;
|
|
|
|
if (!rtems_rtl_check_opts (printer, "dsl", argc, argv))
|
|
return 1;
|
|
|
|
details = rtems_rtl_parse_opt ('l', argc, argv);
|
|
symbols = rtems_rtl_parse_opt ('s', argc, argv);
|
|
duplicates = rtems_rtl_parse_opt ('d', argc, argv);
|
|
|
|
re_name = rtems_rtl_parse_arg (' ', NULL, argc, argv);
|
|
|
|
if (re_name != NULL)
|
|
{
|
|
if (!rtems_rtl_regx_compile (printer,
|
|
"name filter",
|
|
®e,
|
|
re_name))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
rtl = rtems_rtl_lock ();
|
|
if (rtl == NULL)
|
|
{
|
|
rtems_printf (printer, "error: cannot lock the linker\n");
|
|
return 1;
|
|
}
|
|
|
|
node = rtems_chain_first (&rtl->archives.archives);
|
|
|
|
while (!rtems_chain_is_tail (&rtl->archives.archives, node))
|
|
{
|
|
#define SYM_DUPLICATE (((size_t) 1) << ((8 * sizeof (size_t)) - 1))
|
|
|
|
rtems_rtl_archive* archive = (rtems_rtl_archive*) node;
|
|
|
|
if (re_name != NULL)
|
|
{
|
|
int r = rtems_rtl_regx_match (printer,
|
|
"name match",
|
|
®e,
|
|
archive->name);
|
|
if (r < 0)
|
|
{
|
|
rtems_rtl_unlock ();
|
|
return false;
|
|
}
|
|
|
|
if (r == 0)
|
|
{
|
|
node = rtems_chain_next (node);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
rtems_printf (printer, "%s%c\n",
|
|
archive->name,
|
|
details | symbols | duplicates ? ':' : ' ');
|
|
|
|
if (details)
|
|
{
|
|
rtems_printf (printer, " size : %zu\n", archive->size);
|
|
rtems_printf (printer, " symbols : %zu\n", archive->symbols.entries);
|
|
rtems_printf (printer, " refs : %zu\n", archive->refs);
|
|
rtems_printf (printer, " flags : %" PRIx32 "\n", archive->flags);
|
|
}
|
|
|
|
if (symbols)
|
|
{
|
|
const char* symbol = archive->symbols.names;
|
|
int indent = 0;
|
|
size_t s;
|
|
|
|
rtems_printf (printer, " symbols :");
|
|
|
|
for (s = 0; s < archive->symbols.entries; ++s)
|
|
{
|
|
if (archive->symbols.symbols != NULL)
|
|
symbol = archive->symbols.symbols[s].label;
|
|
|
|
rtems_printf (printer, "%-*c%s\n", indent, ' ', symbol);
|
|
indent = 12;
|
|
|
|
if (archive->symbols.symbols == NULL)
|
|
symbol += strlen (symbol) + 1;
|
|
}
|
|
|
|
if (indent == 0)
|
|
rtems_printf (printer, "\n");
|
|
}
|
|
|
|
if (duplicates)
|
|
{
|
|
rtems_chain_node* match_node;
|
|
int indent = 0;
|
|
bool show_dups = true;
|
|
|
|
match_node = rtems_chain_first (&rtl->archives.archives);
|
|
|
|
while (!rtems_chain_is_tail (&rtl->archives.archives, match_node))
|
|
{
|
|
rtems_rtl_archive* match_archive = (rtems_rtl_archive*) match_node;
|
|
const char* symbol = archive->symbols.names;
|
|
size_t s;
|
|
|
|
for (s = 0; s < archive->symbols.entries; ++s)
|
|
{
|
|
if (archive->symbols.symbols == NULL ||
|
|
(archive->symbols.symbols[s].entry & SYM_DUPLICATE) == 0)
|
|
{
|
|
const char* match_symbol = match_archive->symbols.names;
|
|
size_t ms;
|
|
|
|
if (archive->symbols.symbols != NULL)
|
|
symbol = archive->symbols.symbols[s].label;
|
|
|
|
for (ms = 0; ms < match_archive->symbols.entries; ++ms)
|
|
{
|
|
if (match_archive->symbols.symbols != NULL)
|
|
match_symbol = match_archive->symbols.symbols[ms].label;
|
|
|
|
if (symbol != match_symbol && strcmp (symbol, match_symbol) == 0)
|
|
{
|
|
if (show_dups)
|
|
{
|
|
show_dups = false;
|
|
rtems_printf (printer, " dups :");
|
|
}
|
|
rtems_printf (printer, "%-*c%s (%s)\n",
|
|
indent, ' ', symbol, archive->name);
|
|
indent = 12;
|
|
|
|
if (match_archive->symbols.symbols != NULL)
|
|
match_archive->symbols.symbols[ms].entry |= SYM_DUPLICATE;
|
|
}
|
|
|
|
if (match_archive->symbols.symbols == NULL)
|
|
match_symbol += strlen (match_symbol) + 1;
|
|
}
|
|
}
|
|
|
|
if (archive->symbols.symbols == NULL)
|
|
symbol += strlen (symbol) + 1;
|
|
}
|
|
|
|
match_node = rtems_chain_next (match_node);
|
|
}
|
|
|
|
if (indent == 0)
|
|
rtems_printf (printer, "\n");
|
|
}
|
|
|
|
node = rtems_chain_next (node);
|
|
}
|
|
|
|
regfree (®e);
|
|
|
|
node = rtems_chain_first (&rtl->archives.archives);
|
|
|
|
while (!rtems_chain_is_tail (&rtl->archives.archives, node))
|
|
{
|
|
rtems_rtl_archive* archive = (rtems_rtl_archive*) node;
|
|
if (archive->symbols.symbols != NULL)
|
|
{
|
|
size_t s;
|
|
for (s = 0; s < archive->symbols.entries; ++s)
|
|
archive->symbols.symbols[s].entry &= ~SYM_DUPLICATE;
|
|
}
|
|
node = rtems_chain_next (node);
|
|
}
|
|
|
|
rtems_rtl_unlock ();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rtems_rtl_shell_call (const rtems_printer* printer, int argc, char* argv[])
|
|
{
|
|
#define CALL_ARG_COUNT (4)
|
|
|
|
typedef void (*csig_none)(void);
|
|
typedef void (*csig_argv)(int argc, const char* argv[]);
|
|
typedef void (*csig_s)(const char* str);
|
|
typedef void (*csig_u)(unsigned int i1, unsigned int i2, unsigned int i3, unsigned int i4);
|
|
typedef void (*csig_i)(int i1, int i2, int i3, int i4);
|
|
|
|
union {
|
|
char s[64 + 1];
|
|
unsigned int u[CALL_ARG_COUNT];
|
|
int i[CALL_ARG_COUNT];
|
|
} values = { 0 };
|
|
bool keep_locked = false;
|
|
bool args_s = false;
|
|
bool args_i = false;
|
|
bool args_u = false;
|
|
ssize_t label;
|
|
rtems_rtl_data* rtl;
|
|
rtems_rtl_obj_sym* sym;
|
|
rtems_rtl_obj* obj;
|
|
|
|
|
|
if (!rtems_rtl_check_opts (printer, "lsui", argc, argv))
|
|
return 1;
|
|
|
|
keep_locked = rtems_rtl_parse_opt ('l', argc, argv);
|
|
args_s = rtems_rtl_parse_opt ('s', argc, argv);
|
|
args_u = rtems_rtl_parse_opt ('u', argc, argv);
|
|
args_i = rtems_rtl_parse_opt ('i', argc, argv);
|
|
|
|
if (args_s || args_u || args_i)
|
|
{
|
|
int c = 0;
|
|
c += args_s ? 1 : 0;
|
|
c += args_u ? 1 : 0;
|
|
c += args_i ? 1 : 0;
|
|
if (c > 1)
|
|
{
|
|
rtems_printf (printer,
|
|
"error: too many options, only one -sul at a time\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
label = rtems_rtl_parse_arg_index (' ', NULL, argc, argv);
|
|
if (label < 0)
|
|
{
|
|
rtems_printf (printer, "error: no symbol found on command line\n");
|
|
return 1;
|
|
}
|
|
|
|
if ((label + 1) < argc)
|
|
{
|
|
if (args_s)
|
|
{
|
|
size_t arg;
|
|
for (arg = label + 1; arg < argc; ++arg)
|
|
{
|
|
size_t o = strlen (values.s);
|
|
if (strlen (argv[arg]) + 1 >= (sizeof (values.s) - o))
|
|
{
|
|
rtems_printf (printer, "error: string args too big\n");
|
|
return 1;
|
|
}
|
|
if (o > 0)
|
|
values.s[o++] = ' ';
|
|
strcat (values.s, argv[arg]);
|
|
}
|
|
}
|
|
else if (args_u || args_i)
|
|
{
|
|
size_t arg;
|
|
size_t i;
|
|
if (argc > (label + 1 + CALL_ARG_COUNT))
|
|
{
|
|
rtems_printf (printer, "error: too many args\n");
|
|
return 1;
|
|
}
|
|
for (i = 0, arg = label + 1; arg < argc; ++arg)
|
|
{
|
|
if (args_u)
|
|
values.u[i] = strtoul (argv[arg], 0, 0);
|
|
else
|
|
values.i[i] = strtol (argv[arg], 0, 0);
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
|
|
rtl = rtems_rtl_lock ();
|
|
if (rtl == NULL)
|
|
{
|
|
rtems_printf (printer, "error: cannot lock the linker\n");
|
|
return 1;
|
|
}
|
|
|
|
sym = rtems_rtl_symbol_global_find (argv[label]);
|
|
if (sym == NULL)
|
|
{
|
|
rtems_rtl_unlock ();
|
|
rtems_printf (printer, "error: symbol not found: %s\n", argv[label]);
|
|
return 1;
|
|
}
|
|
|
|
obj = rtems_rtl_find_obj_with_symbol (sym);
|
|
if (obj == NULL)
|
|
{
|
|
rtems_rtl_unlock ();
|
|
rtems_printf (printer, "error: symbol obj not found: %s\n", argv[label]);
|
|
return 1;
|
|
}
|
|
|
|
if (!rtems_rtl_obj_text_inside (obj, (const void*) sym->value))
|
|
{
|
|
rtems_rtl_unlock ();
|
|
rtems_printf (printer, "error: symbol not in obj text: %s\n", argv[label]);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Lock the object file while it is being called.
|
|
*/
|
|
rtems_rtl_obj_inc_reference (obj);
|
|
|
|
rtems_rtl_unlock ();
|
|
|
|
if (args_s)
|
|
{
|
|
csig_s call = (csig_s) sym->value;
|
|
call (values.s);
|
|
}
|
|
else if (args_u)
|
|
{
|
|
csig_u call = (csig_u) sym->value;
|
|
call (values.u[0], values.u[1], values.u[2], values.u[3]);
|
|
}
|
|
else if (args_i)
|
|
{
|
|
csig_i call = (csig_i) sym->value;
|
|
call (values.i[0], values.i[1], values.i[2], values.i[3]);
|
|
}
|
|
else
|
|
{
|
|
int cargc = argc - (label + 1);
|
|
if (cargc == 0)
|
|
{
|
|
csig_none call = (csig_none) sym->value;
|
|
call ();
|
|
}
|
|
else
|
|
{
|
|
csig_argv call = (csig_argv) sym->value;
|
|
const char* cargv = argv[label + 1];
|
|
call (cargc, &cargv);
|
|
}
|
|
}
|
|
|
|
if (!keep_locked)
|
|
{
|
|
rtl = rtems_rtl_lock ();
|
|
if (rtl == NULL)
|
|
{
|
|
rtems_printf (printer, "error: cannot lock the linker\n");
|
|
return 1;
|
|
}
|
|
|
|
obj = rtems_rtl_find_obj_with_symbol (sym);
|
|
if (obj == NULL)
|
|
{
|
|
rtems_rtl_unlock ();
|
|
rtems_printf (printer, "error: symbol obj not found: %s\n", argv[label]);
|
|
return 1;
|
|
}
|
|
|
|
rtems_rtl_obj_dec_reference (obj);
|
|
|
|
rtems_rtl_unlock ();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
rtems_rtl_shell_usage (const rtems_printer* printer, const char* arg)
|
|
{
|
|
rtems_printf (printer, "%s: Runtime Linker\n", arg);
|
|
rtems_printf (printer, " %s [-hl] <command>\n", arg);
|
|
rtems_printf (printer, " where:\n");
|
|
rtems_printf (printer, " command: A n RTL command. See -l for a list plus help.\n");
|
|
rtems_printf (printer, " -h: This help\n");
|
|
rtems_printf (printer, " -l: The command list.\n");
|
|
}
|
|
|
|
int
|
|
rtems_rtl_shell_command (int argc, char* argv[])
|
|
{
|
|
const rtems_rtl_shell_cmd table[] =
|
|
{
|
|
{ "status", rtems_rtl_shell_status,
|
|
"Display the status of the RTL" },
|
|
{ "list", rtems_rtl_shell_list,
|
|
"\tList the object files currently loaded" },
|
|
{ "sym", rtems_rtl_shell_sym,
|
|
"\tDisplay the symbols, sym [<name>], sym -o <obj> [<name>]" },
|
|
{ "obj", rtems_rtl_shell_object,
|
|
"\tDisplay the object details, obj <name>" },
|
|
{ "call", rtems_rtl_shell_call,
|
|
"\tCall a symbol" },
|
|
{ "ar", rtems_rtl_shell_archive,
|
|
"\tDisplay the archive details, ar [-ls] <name>" },
|
|
{ "trace", rtems_rtl_trace_shell_command,
|
|
"\tControl the RTL trace flags, trace [-h]" }
|
|
};
|
|
|
|
rtems_printer printer;
|
|
int arg;
|
|
int t;
|
|
|
|
rtems_print_printer_printf (&printer);
|
|
|
|
for (arg = 1; arg < argc; arg++)
|
|
{
|
|
if (argv[arg][0] != '-')
|
|
break;
|
|
|
|
switch (argv[arg][1])
|
|
{
|
|
case 'h':
|
|
rtems_rtl_shell_usage (&printer, argv[0]);
|
|
return 0;
|
|
case 'l':
|
|
rtems_printf (&printer, "%s: commands are:\n", argv[0]);
|
|
for (t = 0;
|
|
t < (sizeof (table) / sizeof (const rtems_rtl_shell_cmd));
|
|
++t)
|
|
rtems_printf (&printer, " %s\t%s\n", table[t].name, table[t].help);
|
|
return 0;
|
|
default:
|
|
rtems_printf (&printer, "error: unknown option: %s\n", argv[arg]);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if ((argc - arg) < 1)
|
|
rtems_printf (&printer, "error: you need to provide a command, try %s -h\n",
|
|
argv[0]);
|
|
else
|
|
{
|
|
for (t = 0;
|
|
t < (sizeof (table) / sizeof (const rtems_rtl_shell_cmd));
|
|
++t)
|
|
{
|
|
if (strncmp (argv[arg], table[t].name, strlen (argv[arg])) == 0)
|
|
return table[t].handler (&printer, argc - 1, argv + 1);
|
|
}
|
|
rtems_printf (&printer, "error: command not found: %s (try -h)\n", argv[arg]);
|
|
}
|
|
|
|
return 1;
|
|
}
|