--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/elf4rom/src/processoptions.cpp Fri Jan 15 09:07:44 2010 +0000
@@ -0,0 +1,609 @@
+/*
+* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <boost/program_options.hpp>
+namespace po = boost::program_options;
+#include <boost/regex.hpp>
+#include <boost/filesystem.hpp>
+namespace fs = boost::filesystem;
+
+#include <iostream>
+
+#include <iterator>
+#include <string>
+#include <map>
+#include <set>
+
+#include <ctype.h>
+#include <cstdlib>
+
+using namespace std;
+
+#include "defs.h"
+#include "romdetails.h"
+#include "inputfile.h"
+#include "elfromerror.h"
+
+static inline void downcase(std::string & s){
+ for (std::string::iterator i = s.begin(); i != s.end(); i++)
+ *i = tolower(*i);
+}
+
+static int required_option(const po::variables_map& vm, const char* option){
+ if (vm.count(option) == 0 || vm[option].defaulted()) {
+ cerr << "Error: option \'" << option << "\' required.\n";
+ return 1;
+ }
+ return 0;
+}
+
+static int either_or_required(const po::variables_map& vm, const char* option1, const char* option2){
+ if ((vm.count(option1) == 0 || vm[option1].defaulted()) &&
+ (vm.count(option2) == 0 || vm[option2].defaulted())) {
+ cerr << "Error: either option \'" << option1 << "\' or option \'" << option2 <<"\' required.\n";
+ return 1;
+ }
+ return 0;
+}
+
+RomDetails * ProcessOptions(int ac, char* av[]) {
+ RomDetails * details = new RomDetails;
+ int errors = 0;
+
+ try {
+
+ string phys_addr;
+ po::options_description desc(" Command Line Only");
+ desc.add_options()
+ ("help,h", "produce help message")
+ ("config-file,c", po::value<string>(), "pathname of config file "
+ "(overrides default of elf4rom.cfg and value "
+ "of ELF4ROM_CFG_FILE environment variable)")
+ ;
+
+ po::options_description config(" Command Line and Configuration File");
+ config.add_options()
+ ("board-name,b", po::value<string>(&details->iBoardName), "name of board targeted e.g. versatilepb")
+ ("debug,d", po::value< vector<string> >(&details->iTargetFiles)->multitoken()->composing(),
+ "collect ELF and DWARF data from the listed files")
+ ("drive,D", po::value<string>(&details->iDrive), "drive on which to find ELF files")
+ ("exclude,e", po::value< vector<string> >(&details->iExcludeFiles)->multitoken()->composing(),
+ "exclude collection of ELF and DWARF data from the listed files")
+ ("input,i", po::value<string>(&details->iRomFile), "pathname of ROM image")
+ ("logfile,l", po::value<string>(&details->iLogFile), "pathname of ROMBUILD log file")
+ ("no-dwarf,n", po::bool_switch(&details->iNoDwarf), "suppress generatation of DWARF in output"
+ " (prevents source level debugging but saves time and space)")
+ ("output,o", po::value<string>(&details->iElfRomFile), "pathname of output file")
+ // lexical_cast doesn't understand <LinearAddress> even though its just a typedef
+ // for unsigned int
+ ("physical-address,p", po::value<string>(&phys_addr), "physical address of ROM on device. Overrides board-name")
+ ("search,s", po::bool_switch(&details->iSearch), "search for ELF files in build directory if .sym file not "
+ "found in release directory")
+ ("strip,S", po::bool_switch(&details->iStrip), "suppress generation of symbol table and DWARF in output"
+ " (useful to produce a 'loadable' ELF image for e.g. a simulator)")
+ ("trace,t", po::bool_switch(&details->iTrace), "switch on trace")
+ ;
+
+ po::options_description cmdline_options;
+ cmdline_options.add(desc).add(config);
+
+ po::options_description config_file_options;
+ config_file_options.add(config);
+
+ po::variables_map vm;
+ //po::store(po::parse_command_line(ac, av, desc), vm);
+ po::store(po::command_line_parser(ac, av).options(cmdline_options).run(), vm);
+
+ char * cfgFile = "elf4rom.cfg";
+ if (vm.count("config-file")) {
+ char * xcfgFile = (char *)(vm["config-file"].as<string>().c_str());
+ fs::path cfgpath(xcfgFile);
+
+ if (fs::exists(cfgpath)) {
+ cfgFile = xcfgFile;
+ } else {
+ cerr << "Warning: specified config file " << xcfgFile << " not found: will not attempt to use default.\n";
+ }
+ } else {
+ char * envCfgFile = getenv("ELF4ROM_CFG_FILE");
+ if (envCfgFile != NULL)
+ cfgFile = envCfgFile;
+ }
+
+ ifstream ifs(cfgFile);
+ store(parse_config_file(ifs, config_file_options), vm);
+
+ po::notify(vm);
+
+ if (vm.count("help")) {
+ cout << "elf4rom [option]";
+ cout << cmdline_options << "\n";
+ delete details;
+ exit(EXIT_SUCCESS) ;
+ }
+
+
+ errors += required_option(vm, "input");
+ errors += required_option(vm, "logfile");
+ errors += required_option(vm, "output");
+
+ errors += either_or_required(vm, "board-name", "physical-address");
+
+ if (vm.count("physical-address")){
+ char *f;
+ const char * p = phys_addr.c_str();
+ details->iRomPhysAddr = strtoul(p ,&f , 16);
+ if (f == p){
+ cerr << "Error: invalid arg to --physical-address: " << phys_addr.c_str() << "\n";
+ exit(EXIT_FAILURE);
+ }
+ } else if (vm.count("board-name")){
+ // TODO: figure out address frmo board name
+ cerr << "Error: --board-name option not implemented yet\n";
+ exit(EXIT_FAILURE) ;
+ }
+
+ if (errors) {
+ cerr << "elf4rom [option]";
+ cerr << cmdline_options << "\n";
+ delete details;
+ exit(EXIT_FAILURE) ;
+ }
+ }
+ catch(exception& e) {
+ cerr << "error: " << e.what() << "\n";
+ exit(EXIT_FAILURE) ;
+ }
+ catch(...) {
+ cerr << "Exception of unknown type!\n";
+ exit(EXIT_FAILURE) ;
+ }
+
+ return details;
+}
+
+
+static bool VerifyLogFile(string::const_iterator & start, string::const_iterator & end, string::const_iterator & rest){
+#if 0
+// Look for somthing like the following
+ROMBUILD - Rom builder V2.08 (Build 593)
+Copyright (c) 1996-2007 Symbian Software Ltd.
+or
+ROMBUILD - Rom builder V2.08 (Build 596)
+Copyright (c) 1996-2009 Nokia Corporation.
+#endif
+
+ const char * banner = "ROMBUILD.*Rom builder.*Copyright.*";
+ boost::regex e(banner);
+ boost::match_results<string::const_iterator> what;
+ if (boost::regex_search(start, end, what, e)){
+ rest = what[0].second;
+ return true;
+ }
+ return false;
+}
+
+static void OffenceWarning (string & offender){
+ cerr << "Warning: The following section of the ROM Log appears corrupt:\n" << offender << "\n";
+}
+
+static inline unsigned int ConvertToUnsignedLong(string & s, string & offender){
+ char * endp;
+ const char * ss = s.c_str();
+ unsigned int res = strtoul(ss, &endp,16);
+ if (endp == ss)
+ OffenceWarning(offender);
+ return res;
+}
+
+static bool ProcessXIPFile(string::const_iterator & start, string::const_iterator & end,
+ string::const_iterator & rest, RomDetails * details){
+ const char * f =
+ "Processing file (\\S+)\\s*" // 1
+ "(\\[Primary\\]\\s*)?" // 2
+ "(\\[Secondary\\]\\s*)?" // 3
+ "(ELF File:\\s*\\S+\\s*)?" // 4
+ "(ELF MD5:\\s*\\S+\\s*)?" // 5
+ "Load Address:\\s+(\\S+)\\s*" // 6
+ "Size:\\s+\\S+\\s*"
+ "Uids:\\s+\\S+\\s+\\S+\\s+\\S+\\s+\\S+\\s*"
+ "Entry point:\\s+\\S+\\s*"
+ "Code start addr:\\s+(\\S+)\\s*" // 7
+ "Data start addr:\\s+(\\S+)\\s*" // 8
+ "DataBssLinearBase:\\s+(\\S+)\\s*" // 9
+ "Text size:\\s+\\S+\\s*"
+ "Code size:\\s+(\\S+)\\s*" // 10
+ "Data size:\\s+(\\S+)\\s*" // 11
+ "BssSize:\\s+(\\S+)\\s*" // 12
+ ;
+ boost::regex e(f);
+ boost::match_results<string::const_iterator> what;
+ if (boost::regex_search(start, end, what, e)){
+ string filename(what[1].first, what[1].second);
+ string offender(what[0].first, what[0].second);
+ if (filename.size() == 0)
+ OffenceWarning(offender);
+ bool primary = what[2].matched;
+ if (what[2].matched){
+ primary = true;
+ }
+
+ const int kla = 6;
+ string ls(what[kla].first, what[kla].second);
+ LinearAddr load = ConvertToUnsignedLong(ls, offender);
+
+ const int kta = kla + 1;
+ string ts(what[kta].first, what[kta].second);
+ LinearAddr text = ConvertToUnsignedLong(ts, offender);
+
+ const int kda = kta + 1;
+ string ds(what[kda].first, what[kda].second);
+ LinearAddr data = ConvertToUnsignedLong(ds, offender);
+
+ const int kva = kda + 1;
+ string vds(what[kva].first, what[kva].second);
+ VirtualAddr vdata = ConvertToUnsignedLong(vds, offender);
+
+ const int kts = kva + 1;
+ string tss(what[kts].first, what[kts].second);
+ size_t textSize = ConvertToUnsignedLong(tss, offender);
+
+ const int kds = kts + 1;
+ string dss(what[kds].first, what[kds].second);
+ size_t fileDataSize = ConvertToUnsignedLong(dss, offender);
+
+ const int kbs = kds + 1;
+ string bsss(what[kbs].first, what[kbs].second);
+ size_t bssSize = ConvertToUnsignedLong(bsss, offender);
+ VirtualAddr bss = vdata + fileDataSize;
+ size_t memDataSize = fileDataSize + bssSize;
+
+ string elffile("");
+ details->iXIPFiles.push_back(XIPFileDetails(filename,
+ elffile,
+ load,
+ text,
+ textSize,
+ vdata,
+ fileDataSize,
+ data,
+ bss,
+ memDataSize));
+ if (primary)
+ new(&details->iPrimary)XIPFileDetails(filename,
+ elffile,
+ load,
+ text,
+ textSize,
+ vdata,
+ fileDataSize,
+ data,
+ bss,
+ memDataSize);
+
+ rest = what[0].second;
+ return true;
+ }
+ rest = start;
+ return false;
+
+}
+
+static void CheckXIPFiles(RomDetails * details, std::vector<string> & list){
+ for (std::vector<string>::iterator i = list.begin(); i != list.end(); i++) {
+ bool found = false;
+ for (RomDetails::XIPFileList::iterator j = details->iXIPFiles.begin(); j != details->iXIPFiles.end(); j++){
+ fs::path e32filePath(j->iE32File);
+ String e32FileName(e32filePath.leaf());
+ if ((*i) == e32FileName){
+ found = true;
+ break;
+ }
+
+ }
+ if (!found){
+ cerr << "WARNING: " << (*i) << " not found in ROM\n";
+ }
+ }
+}
+
+static void CheckDebugXIPFiles(RomDetails * details){
+ CheckXIPFiles(details, details->iTargetFiles);
+}
+
+static void CheckExcludeXIPFile(RomDetails * details){
+ CheckXIPFiles(details, details->iExcludeFiles);
+}
+
+static void ProcessXIPFiles(string::const_iterator & start, string::const_iterator & end,
+ string::const_iterator & rest, RomDetails * details){
+ while (ProcessXIPFile(start,end,rest,details)){
+ start = rest;
+ }
+ CheckDebugXIPFiles(details);
+ CheckExcludeXIPFile(details);
+}
+
+
+static void ProcessRomDetails(string::const_iterator & start, string::const_iterator & end,
+ string::const_iterator & rest, RomDetails * details){
+ const char * align = "Linear base address:\\s*([\\S]+)$";
+ boost::regex e(align);
+ boost::match_results<string::const_iterator> what;
+ if (boost::regex_search(start, end, what, e)){
+ string offender(what[0].first, what[0].second);
+ string lbas(what[1].first, what[1].second);
+ details->iRomBaseLinearAddr = ConvertToUnsignedLong(lbas, offender);
+ } else {
+ cerr << "Error: " << details->iLogFile << " not a valid ROM log file. Could not find Linear base address." << "\n";
+ exit(EXIT_FAILURE) ;
+ }
+}
+
+static fs::path FindBuildPath(RomDetails * details){
+ const string epoc32("epoc32");
+ const string builddir("build");
+ const string epocRoot(getenv("EPOCROOT"));
+ if (details->iDrive.size() > 0) {
+ string drive(details->iDrive);
+ if (drive.size() == 1){
+ drive += ":";
+ } else if (((drive.size() == 2) && (drive[drive.size()-1] != ':')) || (drive.size() > 2)){
+ cerr << "Error: Invalid drive specification: " << drive << "\n";
+ exit(EXIT_FAILURE) ;
+ }
+ fs::path buildpath(drive);
+
+ buildpath /= epocRoot;
+ buildpath /= epoc32;
+ buildpath /= builddir;
+ return buildpath;
+ }
+ fs::path primary_path(details->iPrimary.iE32File);
+ fs::path buildpath;
+ for (fs::path::iterator i = primary_path.begin(); i != primary_path.end(); i++){
+ string item(*i);
+ downcase(item);
+ buildpath /= item;
+ if (item == epoc32) {
+ buildpath /= builddir;
+ break;
+ }
+ }
+ return buildpath;
+}
+
+class PathCache {
+public:
+ typedef std::map<string, string> PathMap;
+
+ PathCache(fs::path & apath):
+ iBuildPath(apath)
+ {
+ if (fs::exists(apath)){
+ fs::recursive_directory_iterator i(apath);
+ iCurrent = i;
+ }
+ }
+ bool FindPath(string & pattern, string & result);
+ fs::path & GetBuildPath() { return iBuildPath; }
+
+private:
+ bool GetNextPath(string & path);
+ bool Filtered(fs::path & path);
+
+private:
+
+ fs::path iBuildPath;
+ fs::recursive_directory_iterator iCurrent;
+ fs::recursive_directory_iterator iEnd;
+ PathMap iPathMap;
+ static const std::string filters;
+};
+
+
+
+bool PathCache::Filtered(fs::path & apath){
+ std::string ext(fs::extension(apath));
+ downcase(ext);
+ if (ext.size() > 0){
+ if (filters.find(ext) != std::string::npos)
+ return true;
+ }
+ return false;
+}
+
+bool PathCache::GetNextPath(string & path){
+ for (; iCurrent != iEnd; ++iCurrent ){
+ if (fs::is_directory(iCurrent->status()))
+ continue;
+ fs::path candidate(iCurrent->path());
+ if (!Filtered(candidate)) {
+ path = candidate.string();
+ ++iCurrent;
+ return true;
+ }
+ }
+ return false;
+}
+
+static void GetTarget(string & source, string & target){
+ fs::path t1(source);
+ fs::path::iterator s = t1.begin();
+ fs::path::iterator start = t1.end();
+ int n = 0;
+ for (; n < 3 && s != start; n++, start--){}
+ if (n < 3) {
+ warnx(0, "%s does not have 3 elements\n", source.c_str());
+ }
+
+ fs::path q;
+ for (; start != t1.end(); start++){
+ q /= fs::path(*start);
+ }
+ target = q.string();
+ downcase(target);
+}
+
+bool PathCache::FindPath(string & source, string & result){
+ string ss;
+ if (source.size() == 0) return false;
+ GetTarget(source, ss);
+
+ // now check the cache
+ PathMap::iterator res = iPathMap.find(ss);
+ if (res != iPathMap.end()){
+ result = res->second;
+ return true;
+ }
+
+ // otherwise iterate until we find a match
+ string candidate;
+ while (GetNextPath(candidate)){
+ downcase(candidate);
+ size_t n = candidate.rfind(ss);
+ if (n != string::npos){
+ size_t csize = candidate.size();
+ size_t sssize = ss.size();
+ if ((csize - sssize) == n){
+ // put it in cache anyway just in case its the primary
+ iPathMap[ss] = candidate;
+ result = candidate;
+ return true;
+ }
+ }
+ string x;
+ GetTarget(candidate, x);
+ iPathMap[x] = candidate;
+ }
+ return false;
+}
+
+const std::string PathCache::filters(
+ ".cpp"
+ ".h"
+ ".mk"
+ ".o"
+ ".in"
+ ".via"
+ ".mbg"
+ ".rsg"
+ ".bat"
+ ".make"
+ ".def"
+ ".armv5"
+ );
+
+static bool FindSymFile(XIPFileDetails & detail, string & rootname){
+ fs::path buildpath(rootname);
+ fs::path e32path(detail.iE32File);
+ buildpath /= e32path;
+ // check for pre-RAPTOR .sym file
+ fs::path elfpath = fs::change_extension(buildpath, ".sym");
+ if (fs::exists(elfpath)) {
+ detail.iElfFile = elfpath.string();
+ return true;
+ }
+ // check for RAPTOR .sym file
+ fs::path symPath(buildpath.string() + ".sym");
+ if (fs::exists(symPath)) {
+ detail.iElfFile = symPath.string();
+ return true;
+ }
+ return false;
+}
+
+static void FindElfFile(XIPFileDetails & detail, PathCache & cache, bool search){
+ // see if there's a .sym file
+ string root(cache.GetBuildPath().root_name());
+ if (FindSymFile(detail, root)) return;
+ if (!search) {
+ cerr << "Warning: could not find ELF file for " << detail.iE32File << ".\n";
+ return;
+ }
+ string s(detail.iE32File);
+ string res;
+ if (s.size() == 0) return;
+ if (cache.GetBuildPath().string().empty()) return;
+
+ if (cache.FindPath(s, res)){
+ detail.iElfFile = res;
+ } else
+ cerr << "Warning: could not find ELF file for " << detail.iE32File << ".\n";
+}
+
+
+static void FindElfFiles(RomDetails * details){
+ fs::path buildroot = FindBuildPath(details);
+ PathCache cache(buildroot);
+ bool search = details->iSearch;
+ FindElfFile(details->iPrimary, cache, search);
+ for(RomDetails::XIPFileList::iterator i = details->iXIPFiles.begin(); i != details->iXIPFiles.end(); i++){
+ fs::path e32filePath(i->iE32File);
+ String e32FileName(e32filePath.leaf());
+ if (details->iTargetFiles.empty()) {
+ bool find = true;
+ for (std::vector<String>::iterator ef = details->iExcludeFiles.begin(); ef != details->iExcludeFiles.end(); ef++) {
+ fs::path excludePath(*ef);
+ String excludeFile(excludePath.leaf());
+ if (e32FileName == excludeFile) {
+ find = false;
+ break;
+ }
+ }
+ if (find)
+ FindElfFile(*i, cache, search);
+ } else {
+ for (std::vector<String>::iterator tf = details->iTargetFiles.begin(); tf != details->iTargetFiles.end(); tf++) {
+ fs::path targetPath(*tf);
+ String targetFile(targetPath.leaf());
+ if (e32FileName == targetFile) {
+ FindElfFile(*i, cache, search);
+ break;
+ }
+ }
+ }
+ }
+
+}
+
+RomDetails * ProcessRomDetails(RomDetails * details){
+ InputFile in(details->iLogFile);
+ char * data = in.GetData();
+ string logfile(data);
+ string::const_iterator start = logfile.begin();
+ string::const_iterator end = logfile.end();
+ string::const_iterator rest;
+
+ if (!VerifyLogFile(start, end , rest)) {
+ cerr << "error: " << details->iLogFile << " not a valid ROM log file." << "\n";
+ exit(EXIT_FAILURE) ;
+ }
+
+ ProcessXIPFiles(start,end,rest,details);
+
+ ProcessRomDetails(rest, end, rest, details);
+
+ FindElfFiles(details);
+
+ // TODO: make sure functions that return data allocated with new []
+ // have an appropriate return value so that delete [] can be used
+ delete [] data;
+
+ return details;
+}