/*
* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
#include <e32rom.h>
#include <algorithm>
#include "symbolgenerator.h"
#include "r_rom.h"
#include <string.h>
#include "h_utl.h"
typedef boost::unique_lock<boost::mutex> scoped_lock ;
typedef boost::lock_guard<boost::mutex> guarded_lock ;
SymbolGenerator::SymbolGenerator(const char* aSymbolFileName, int aMultiThreadsCount/* = 1*/) :
iOutput(aSymbolFileName,ios_base::out |ios_base::binary | ios_base::trunc) {
if(iOutput.is_open()){
if(aMultiThreadsCount < 1)
aMultiThreadsCount = 1;
for(int i = 0 ; i < aMultiThreadsCount ; i++){
iThreads.add_thread(new boost::thread(ThreadFunc,this));
}
}
else {
cerr << "\nWarning: Can't write data to \""<<aSymbolFileName << "\" ! \nPlease make sure this file is not locked by other application or you have write permission!"<<endl;
}
}
void SymbolGenerator::WaitThreads() {
iThreads.join_all();
}
SymbolGenerator::~SymbolGenerator() {
if(iOutput.is_open()){
iOutput.flush();
iOutput.close();
}
for(vector<char*>::iterator i = iErrMsgs.begin() ; i != iErrMsgs.end() ; i++){
char* msg = *i ;
cerr << msg ;
delete []msg ;
}
iErrMsgs.clear();
}
void SymbolGenerator::AddEntry(const SymGenContext& aEntry){
if(iOutput.is_open()){
guarded_lock lock(iQueueMutex);
iEntries.push(aEntry);
iCond.notify_all();
}
}
void SymbolGenerator::ThreadFunc(SymbolGenerator* aInst) {
SymGenContext entry ;
while(1){
entry.iFileName = 0;
if(1) {
scoped_lock lock(aInst->iQueueMutex);
while(aInst->iEntries.empty()){
aInst->iCond.wait(lock);
}
entry = aInst->iEntries.front();
if(0 == entry.iFileName) // end , exit
return ;
aInst->iEntries.pop();
}
aInst->ProcessEntry(entry);
}
}
#define MAX_LINE_LENGTH 65535
#define SKIP_WS(p) while((*p) == ' ' || (*p) == '\t') (p)++
#define FIND_WS(p) while((*p) != ' ' && (*p) != '\t' && (*p) != 0) (p)++
static void split(char* str, vector<char*>& result) {
result.clear();
while(*str) {
SKIP_WS(str);
char* saved = str ;
FIND_WS(str);
bool end = (0 == *str);
*str = 0 ;
if(saved != str)
result.push_back(saved);
if(!end) str ++ ;
}
}
static void make_lower(char* str){
while(*str){
if(*str >= 'A' && *str >= 'Z') {
*str += ('a' - 'A');
}
str++;
}
}
bool SymbolGenerator::ProcessEntry(const SymGenContext& aContext) {
size_t allocBytes ;
if(aContext.iExecutable ) {
string mapFileName(aContext.iFileName);
mapFileName += ".map";
ifstream ifs(mapFileName.c_str());
if(!ifs.is_open()){
int index = mapFileName.length() - 5 ;
int count = 1 ;
while(index > 0 && mapFileName.at(index) != '.'){
index -- ;
count ++ ;
}
mapFileName.erase(index,count);
ifs.open(mapFileName.c_str());
}
if(!ifs.is_open()){
guarded_lock lock(iFileMutex);
allocBytes = mapFileName.length() + 60 ;
char* msg = new char[ allocBytes] ;
snprintf(msg,allocBytes,"\nWarning: Can't open \"%s.map\"\n",aContext.iFileName );
iErrMsgs.push_back(msg);
msg = new char[allocBytes] ;
int n = snprintf(msg,allocBytes,"%08x %04x %s\r\n",(unsigned int)aContext.iCodeAddress,(unsigned int)aContext.iTotalSize,aContext.iFileName);
iOutput.write(msg,n);
iOutput.flush();
return false ;
}
if(!ifs.good()) ifs.clear();
char buffer[100];
*buffer = 0;
//See if we're dealing with the RVCT output
ifs.getline(buffer,100);
if(!ifs.good()) {
ifs.close();
guarded_lock lock(iFileMutex);
allocBytes = mapFileName.length() + 60;
char* msg = new char[allocBytes] ;
snprintf(msg,allocBytes,"\nWarning: File \"%s\" is opened yet can not be read!",mapFileName.c_str());
iErrMsgs.push_back(msg);
return false ;
}
if(strncmp(buffer,"ARM Linker",10) == 0){
return ProcessARMV5Map(ifs,aContext);
}
// See if we're dealing with the GCC output
else if ( 0 == strncmp(buffer,"Archive member included",23)){
return ProcessGCCMap(ifs,aContext);
}
else { // Must be x86 output
ifs.seekg(0,ios_base::beg);
return ProcessX86Map(ifs,aContext);
}
}
else {
const char* fileName = aContext.iFileName;
size_t len = strlen(fileName);
size_t index = len - 1;
while(index > 0 && (fileName[index] != '\\' && fileName[index] != '/'))
index -- ;
const char* basename = fileName + index + 1 ;
allocBytes = (len << 1) + 40 ;
char* msg = new char[allocBytes] ;
int n = snprintf(msg,allocBytes,"\r\nFrom %s\r\n\r\n%08x 0000 %s\r\n", fileName ,(unsigned int)aContext.iDataAddress,basename);
guarded_lock lock(iFileMutex);
iOutput.write(msg,n);
iOutput.flush();
delete []msg ;
return true ;
}
return true ;
}
struct ArmSymbolInfo {
string name ;
TUint size ;
string section ;
};
typedef multimap<TUint32,ArmSymbolInfo> ArmSymMap ;
bool SymbolGenerator::ProcessARMV5Map(ifstream& aStream, const SymGenContext& aContext) {
string symName ;
ArmSymMap symbols ;
vector<char*> words ;
ArmSymbolInfo info;
char* lineStart ;
char buffer[MAX_LINE_LENGTH];
while(aStream.good() && (!aStream.eof())){
*buffer = 0;
aStream.getline(buffer,MAX_LINE_LENGTH);
lineStart = buffer ;
SKIP_WS(lineStart);
if(strstr(lineStart,"Global Symbols"))
break ;
char* armstamp = strstr(lineStart,"ARM Code");
if(0 == armstamp)
armstamp = strstr(lineStart,"Thumb Code") ;
if(0 == armstamp) continue ;
*(armstamp - 1) = 0 ;
char* hexStr = lineStart ;
char* nameEnd;
while(1) {
hexStr = strstr(hexStr,"0x");
if(0 == hexStr) break ;
nameEnd = hexStr - 1;
if(*nameEnd == ' ' || *nameEnd == '\t') break ;
hexStr += 2 ;
}
if(0 == hexStr) continue ;
while(nameEnd > lineStart && (*nameEnd == ' ' || *nameEnd == '\t'))
nameEnd -- ;
nameEnd[1] = 0;
info.name = lineStart;
char* temp ;
TUint32 addr = strtoul(hexStr + 2,&temp,16);
char* decStr ;
if(*armstamp == 'A')
decStr = armstamp + 9 ;
else
decStr = armstamp + 11 ;
SKIP_WS(decStr);
info.size = strtoul(decStr,&temp,10);
SKIP_WS(temp);
info.section = temp;
if(info.section.find("(StubCode)") != string::npos )
info.size = 8 ;
if(addr > 0){
symbols.insert(pair<TUint32,ArmSymbolInfo>(addr,info));
}
}
size_t lenOfFileName = strlen(aContext.iFileName);
while(aStream.good() && (!aStream.eof())){
*buffer = 0;
aStream.getline(buffer,MAX_LINE_LENGTH);
lineStart = buffer ;
SKIP_WS(lineStart);
char* hexStr = lineStart ;
char* nameEnd;
while(1) {
hexStr = strstr(hexStr,"0x");
if(0 == hexStr) break ;
nameEnd = hexStr - 1;
if(*nameEnd == ' ' || *nameEnd == '\t')
break ;
hexStr += 2 ;
}
if(0 == hexStr) continue ;
while(nameEnd > lineStart && (*nameEnd == ' ' || *nameEnd == '\t')){
nameEnd -- ;
}
nameEnd[1] = 0;
info.name = lineStart;
char *temp ;
TUint32 addr = strtoul(hexStr + 2,&temp,16);
while(*temp < '0' || *temp > '9' )//[^\d]*
temp++ ;
char* decStr = temp ;
info.size = strtoul(decStr,&temp,10);
SKIP_WS(temp);
info.section = temp;
if(info.section.find("(StubCode)") != string::npos )
info.size = 8 ;
if(addr > 0){
symbols.insert(pair<TUint32,ArmSymbolInfo>(addr,info));
}
}
TUint32 textSectAddr = 0x00008000; // .text gets linked at 0x00008000
TUint32 dataSectAddr = 0x00400000 ; // .data gets linked at 0x00400000
vector<pair<int,char*> > lines ;
size_t allocBytes;
for( ArmSymMap::iterator it = symbols.begin(); it != symbols.end() ; it++){
TUint32 thisAddr = it->first ;
TUint32 romAddr ;
ArmSymbolInfo& info = it->second;
if (thisAddr >= textSectAddr && thisAddr <= (textSectAddr + aContext.iTextSize)) {
romAddr = thisAddr - textSectAddr + aContext.iCodeAddress ;
}
else if ( aContext.iDataAddress &&
( thisAddr >= dataSectAddr && thisAddr <= (dataSectAddr + aContext.iTextSize))) {
romAddr = thisAddr-dataSectAddr + aContext.iDataBssLinearBase;
}
else if ( aContext.iDataBssLinearBase &&
( thisAddr >= dataSectAddr && thisAddr <= (dataSectAddr+ aContext.iTotalDataSize))) {
romAddr = thisAddr - dataSectAddr + aContext.iDataBssLinearBase;
}
else {
guarded_lock lock(iFileMutex);
allocBytes = info.name.length() + 60;
char* msg = new char[allocBytes] ;
snprintf(msg,allocBytes,"\r\nWarning: Symbol %s @ 0x%08x not in text or data segments\r\n", \
info.name.c_str() ,(unsigned int)thisAddr) ;
iErrMsgs.push_back(msg);
allocBytes = lenOfFileName + 80;
msg = new char[allocBytes];
snprintf(msg,allocBytes,"Warning: The map file for binary %s is out-of-sync with the binary itself\r\n\r\n",aContext.iFileName);
iErrMsgs.push_back(msg);
continue ;
}
allocBytes = info.section.length() + info.name.length() + 140;
char* outputLine = new char[allocBytes];
int len = snprintf(outputLine,allocBytes,"%08x %04x %-40s %s\r\n",(unsigned int)romAddr,info.size,
info.name.c_str(),info.section.c_str());
if((size_t)len > allocBytes) {
allocBytes = len + 4 ;
delete []outputLine;
outputLine = new char[allocBytes];
len = snprintf(outputLine,allocBytes,"%08x %04x %-40s %s\r\n",(unsigned int)romAddr,info.size,
info.name.c_str(),info.section.c_str());
}
lines.push_back(pair<int,char*>(len,outputLine));
}
guarded_lock lock(iFileMutex);
allocBytes = lenOfFileName + 40;
char* outputLine = new char[allocBytes];
int n = snprintf(outputLine,allocBytes,"\r\nFrom %s\r\n\r\n",aContext.iFileName);
iOutput.write(outputLine,n);
delete []outputLine ;
for (vector<pair<int,char*> >::iterator i = lines.begin() ; i < lines.end(); i ++ ) {
int len = i->first ;
char* line = i->second;
iOutput.write(line,len);
delete []line ;
}
iOutput.flush();
return true ;
}
template<typename M, typename K,typename V>
static void put_to_map(M& m,const K& k, const V& v) {
typedef typename M::iterator iterator;
iterator it = m.find(k);
if(m.end() == it){
m.insert(pair<K,V>(k,v));
}
else {
it->second = v ;
}
}
bool SymbolGenerator::ProcessGCCMap(ifstream& aStream, const SymGenContext& aContext){
char* lineStart;
vector<char*> words ;
char buffer[MAX_LINE_LENGTH];
while(aStream.good() && (!aStream.eof())){
aStream.getline(buffer,MAX_LINE_LENGTH);
lineStart = buffer ;
SKIP_WS(lineStart);
if( 0 == strncmp(lineStart,".text",5)) {
lineStart += 5;
break ;
}
}
split(lineStart,words);
TUint32 codeAddr , codeSize;
size_t allocBytes ;
if(words.size() != 2 ||
KErrNone != Val(codeAddr,words.at(0)) ||
KErrNone != Val(codeSize,words.at(1))) {
allocBytes = strlen(aContext.iFileName) + 60;
char* msg = new char[allocBytes];
snprintf(msg,allocBytes,"\nError: Can't get .text section info for \"%s\"\r\n",aContext.iFileName);
guarded_lock lock(iFileMutex);
iErrMsgs.push_back(msg);
return false ;
}
map<TUint32,string> symbols ;
TUint32 stubHex = 0;
//Slurp symbols 'til the end of the text section
while(aStream.good() && (!aStream.eof())){
aStream.getline(buffer,MAX_LINE_LENGTH);
lineStart = buffer ;
SKIP_WS(lineStart);
if(0 == *lineStart) break ; //blank line marks the end of the text section
// .text <addr> <len> <library(member)>
// .text$something
// <addr> <len> <library(member)>
// <addr> <len> LONG 0x0
// (/^\s(\.text)?\s+(0x\w+)\s+(0x\w+)\s+(.*)$/io)
if(strncmp(lineStart,".text",5) == 0){
lineStart += 5 ;
SKIP_WS(lineStart);
}
char* hex1 = NULL ;
char* hex2 = NULL ;
char* strAfterhex1 = NULL ;
TUint32 addr,size ;
if(strncmp(lineStart,"0x",2) == 0){
hex1 = lineStart + 2;
char* temp ;
addr = strtoul(hex1,&temp,16);
SKIP_WS(temp);
strAfterhex1 = temp ;
if(strncmp(temp,"0x",2) == 0){
hex2 = temp + 2 ;
}
}
if(NULL != hex2){
char* libraryfile ;
size = strtoul(hex2,&libraryfile,16);
SKIP_WS(libraryfile);
TUint32 key = addr + size ;
put_to_map(symbols,key,string(""));//impossible symbol as end marker
make_lower(libraryfile);
// EUSER.LIB(ds01423.o)
// EUSER.LIB(C:/TEMP/d1000s_01423.o)
size_t len = strlen(libraryfile);
char* p1 = strstr(libraryfile,".lib(");
if(NULL == p1)
continue ;
p1 += 5;
if(strcmp(libraryfile + len - 3,".o)")!= 0)
continue ;
len -= 3 ;
libraryfile[len] = 0;
if(EFalse == IsValidNumber(libraryfile + len - 5))
continue ;
len -= 7 ;
if('_' == libraryfile[len])
len -- ;
if('s' != libraryfile[len])
continue ;
char* p2 = libraryfile + len - 1;
while(p2 > p1 ) {
if(*p2 < '0' || *p2 > '9')
break ;
p2 -- ;
}
if(*p2 != 'd')
continue ;
stubHex = addr ;
}
else if(NULL != hex1 && NULL != strAfterhex1){
//# <addr> <symbol name possibly including spaces>
//(/^\s+(\w+)\s\s+([a-zA-Z_].+)/o)
char* symName = strAfterhex1;
if((*symName >= 'A' && *symName <= 'Z') ||
(*symName >= 'a' && *symName <= 'z') || *symName == '_') {
string symbol(symName);
if(addr == stubHex)
symbol.insert(0,"stub ");
put_to_map(symbols,addr,symbol);
}
}
}
map<TUint32,string>::iterator it = symbols.begin();
TUint32 lastAddr = it->first;
string lastSymName = it->second;
vector<pair<int,char*> >lines ;
it ++ ;
while(it != symbols.end()) {
TUint32 addr = it->first ;
unsigned int fixedupAddr = lastAddr - codeAddr + aContext.iCodeAddress;
TUint size = addr - lastAddr ;
if(!lastSymName.empty()) {
allocBytes = lastSymName.length() + 40;
char* outputLine = new char[allocBytes];
int n = snprintf(outputLine,allocBytes,"%08x %04x %s\r\n", fixedupAddr,size,lastSymName.c_str());
lines.push_back(pair<int,char*>(n,outputLine));
}
lastAddr = addr ;
lastSymName = it->second;
it ++ ;
}
guarded_lock lock(iFileMutex);
allocBytes = strlen(aContext.iFileName) + 40;
char* outputLine = new char[allocBytes];
int n = snprintf(outputLine,allocBytes,"\r\nFrom %s\r\n\r\n",aContext.iFileName);
iOutput.write(outputLine,n);
delete []outputLine ;
vector<pair<int,char*> >::iterator i;
for ( i = lines.begin() ; i < lines.end(); i ++ ) {
int len = i->first ;
char* line = i->second ;
iOutput.write(line,len);
delete []line ;
}
iOutput.flush();
return true ;
}
bool SymbolGenerator::ProcessX86Map(ifstream& aStream, const SymGenContext& aContext) {
char buffer[MAX_LINE_LENGTH];
char* lineStart;
while(aStream.good() && (!aStream.eof())){
aStream.getline(buffer,MAX_LINE_LENGTH);
lineStart = buffer ;
SKIP_WS(lineStart);
if( 0 == strncmp(lineStart,"Address",7)) {
break ;
}
}
aStream.getline(buffer,MAX_LINE_LENGTH);
string lastName ;
TUint32 lastAddr = 0;
size_t allocBytes ;
vector<pair<int, char*> >lines ;
while(aStream.good() && (!aStream.eof())){
aStream.getline(buffer,MAX_LINE_LENGTH);
lineStart = buffer ;
SKIP_WS(lineStart);
if(0 != strncmp(lineStart,"0001:",5))
break ;
char* end ;
TUint32 addr = strtoul(lineStart + 5,&end,16);
char* name = end + 1;
SKIP_WS(name);
end = name + 1;
FIND_WS(end);
*end = 0 ;
if(!lastName.empty()){
unsigned int size = addr - lastAddr ;
unsigned int romAddr = lastAddr + aContext.iCodeAddress;
allocBytes = lastName.length() + 40;
char* outputLine = new char[allocBytes];
int n = snprintf(outputLine,allocBytes,"%08x %04x %s\r\n",romAddr,size,lastName.c_str());
lines.push_back(pair<int, char*>(n,outputLine));
}
}
guarded_lock lock(iFileMutex);
allocBytes = strlen(aContext.iFileName) + 40;
char* outputLine = new char[allocBytes];
int n = snprintf(outputLine,allocBytes,"\r\nFrom %s\r\n\r\n",aContext.iFileName);
iOutput.write(outputLine,n);
delete []outputLine ;
vector<pair<int,char*> >::iterator it;
for ( it = lines.begin() ; it < lines.end(); it ++ ) {
int len = it->first ;
char* line = it->second ;
iOutput.write(line,len);
delete []line ;
}
if(!lastName.empty()){
allocBytes = lastName.length() + 40 ;
outputLine = new char[allocBytes];
unsigned int romAddr = lastAddr + aContext.iCodeAddress;
n = snprintf(outputLine,allocBytes,"%08x 0000 %s\r\n",romAddr,lastName.c_str());
iOutput.write(outputLine,n);
delete []outputLine ;
}
iOutput.flush();
return false ;
}