tools/elf4rom/src/processoptions.cpp
author Gareth Stockwell <gareth.stockwell@accenture.com>
Wed, 22 Sep 2010 15:40:40 +0100
branchgraphics-phase-3
changeset 111 345f1c88c950
parent 34 92d87f2e53c2
permissions -rwxr-xr-x
Fixes to syborg-graphicswrapper.vcproj These changes allow syborg-graphicswrapper to link against the hostthreadadapter and khronosapiwrapper libraries built by the graphics.simulator component. The .vcproj file uses relative paths, which requires that the following three packages are laid out as follows: os/ graphics adapt/ graphics.simulator qemu

/*
* 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;
}