URL
https://opencores.org/ocsvn/lxp32/lxp32/trunk
Subversion Repositories lxp32
Compare Revisions
- This comparison shows the changes necessary to convert path
/lxp32/trunk/tools/src/lxp32asm
- from Rev 2 to Rev 6
- ↔ Reverse comparison
Rev 2 → Rev 6
/assembler.cpp
30,6 → 30,7
_state=Initial; |
_currentFileName=filename; |
processFileRecursive(filename); |
|
// Examine symbol table |
for(auto const &sym: _obj.symbols()) { |
if(sym.second.type==LinkableObject::Unknown&&!sym.second.refs.empty()) { |
40,6 → 41,8
throw std::runtime_error(msg.str()); |
} |
} |
|
for(auto const &sym: _exportedSymbols) _obj.exportSymbol(sym); |
} |
|
void Assembler::processFileRecursive(const std::string &filename) { |
63,16 → 66,14
_line++; |
} |
|
if(_state!=Initial) throw std::runtime_error("Unexpected end of file"); |
|
_line=savedLine; |
_state=savedState; |
_currentFileName=savedFileName; |
|
for(auto const &label: _currentLabels) { |
_obj.addLocalSymbol(label, |
static_cast<LinkableObject::Word>(_obj.codeSize())); |
} |
|
_currentLabels.clear(); |
if(!_currentLabels.empty()) |
throw std::runtime_error("Symbol definition must be followed by an instruction or data definition statement"); |
} |
|
void Assembler::addIncludeSearchDir(const std::string &dir) { |
127,7 → 128,7
else throw std::runtime_error(std::string("Unexpected character: \"")+ch+"\""); |
break; |
case Word: |
if(std::isalnum(ch)||ch=='_'||ch=='@') word+=ch; |
if(std::isalnum(ch)||ch=='_'||ch=='@'||ch=='+'||ch=='-') word+=ch; |
else { |
i--; |
_state=Initial; |
206,7 → 207,12
// Perform macro substitution |
for(auto &token: list) { |
auto it=_macros.find(token); |
if(it==_macros.end()) newlist.push_back(std::move(token)); |
// Note: we don't expand a macro identifier in the #define statement |
// since that would lead to counter-intuitive results |
if(it==_macros.end()|| |
(newlist.size()==1&&newlist[0]=="#define")|| |
(newlist.size()==3&&newlist[1]==":"&&newlist[2]=="#define")) |
newlist.push_back(std::move(token)); |
else for(auto const &replace: it->second) newlist.push_back(replace); |
} |
list=std::move(newlist); |
233,7 → 239,7
else rva=elaborateInstruction(list); |
|
for(auto const &label: _currentLabels) { |
_obj.addLocalSymbol(label,rva); |
_obj.addSymbol(label,rva); |
} |
_currentLabels.clear(); |
} |
243,15 → 249,24
assert(!list.empty()); |
|
if(list[0]=="#define") { |
if(list.size()<3) throw std::runtime_error("Wrong number of tokens in the directive"); |
if(!validateIdentifier(list[1])) throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\""); |
if(list.size()<3) |
throw std::runtime_error("Wrong number of tokens in the directive"); |
if(_macros.find(list[1])!=_macros.end()) |
throw std::runtime_error("Macro \""+list[1]+"\" has been already defined"); |
if(!validateIdentifier(list[1])) |
throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\""); |
_macros.emplace(list[1],TokenList(list.begin()+2,list.end())); |
} |
else if(list[0]=="#extern") { |
else if(list[0]=="#export") { |
if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive"); |
if(!validateIdentifier(list[1])) throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\""); |
_obj.addExternalSymbol(list[1]); |
_exportedSymbols.push_back(list[1]); |
} |
else if(list[0]=="#import") { |
if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive"); |
if(!validateIdentifier(list[1])) throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\""); |
_obj.addImportedSymbol(list[1]); |
} |
else if(list[0]=="#include") { |
if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive"); |
auto filename=Utils::dequoteString(list[1]); |
285,6 → 300,8
if(list.size()>2) throw std::runtime_error("Unexpected token: \""+list[2]+"\""); |
std::size_t align=4; |
if(list.size()>1) align=static_cast<std::size_t>(numericLiteral(list[1])); |
if(!Utils::isPowerOf2(align)) throw std::runtime_error("Alignment must be a power of 2"); |
if(align<4) throw std::runtime_error("Alignment must be at least 4"); |
rva=_obj.addPadding(align); |
} |
else if(list[0]==".reserve") { |
351,6 → 368,7
else if(list[0]=="jmp") encodeJmp(list); |
else if(list[0]=="iret") encodeIret(list); |
else if(list[0]=="lc") encodeLc(list); |
else if(list[0]=="lcs") encodeLcs(list); |
else if(list[0]=="lsb") encodeLsb(list); |
else if(list[0]=="lub") encodeLub(list); |
else if(list[0]=="lw") encodeLw(list); |
358,6 → 376,7
else if(list[0]=="modu") encodeModu(list); |
else if(list[0]=="mov") encodeMov(list); |
else if(list[0]=="mul") encodeMul(list); |
else if(list[0]=="neg") encodeNeg(list); |
else if(list[0]=="nop") encodeNop(list); |
else if(list[0]=="not") encodeNot(list); |
else if(list[0]=="or") encodeOr(list); |
456,7 → 475,7
arglist.push_back(std::move(a)); |
} |
else if(list[i].size()==3&&list[i].substr(0,2)=="iv"&& |
std::isdigit(list[i][2])) // interrupt vector |
list[i][2]>='0'&&list[i][2]<='7') // interrupt vector |
{ |
a.type=Operand::Register; |
a.reg=240+(list[i][2]-'0'); |
598,11 → 617,6
void Assembler::encodeDivs(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("divs instruction requires 3 operands"); |
if(args[2].type==Operand::NumericLiteral&&args[2].i==0) { |
std::cerr<<currentFileName()<<":"<<line()<<": "; |
std::cerr<<"Warning: Division by zero"<<std::endl; |
} |
|
LinkableObject::Word w=0x54000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
613,11 → 627,6
void Assembler::encodeDivu(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("divu instruction requires 3 operands"); |
if(args[2].type==Operand::NumericLiteral&&args[2].i==0) { |
std::cerr<<currentFileName()<<":"<<line()<<": "; |
std::cerr<<"Warning: Division by zero"<<std::endl; |
} |
|
LinkableObject::Word w=0x50000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
656,8 → 665,13
_obj.addWord(w); |
|
if(args[1].type==Operand::Identifier) { |
auto symRva=_obj.addWord(static_cast<LinkableObject::Word>(args[1].i)); |
_obj.addReference(args[1].str,currentFileName(),line(),symRva); |
LinkableObject::Reference ref; |
ref.source=currentFileName(); |
ref.line=line(); |
ref.rva=_obj.addWord(0); |
ref.offset=args[1].i; |
ref.type=LinkableObject::Regular; |
_obj.addReference(args[1].str,ref); |
} |
else if(args[1].type==Operand::NumericLiteral) { |
_obj.addWord(static_cast<LinkableObject::Word>(args[1].i)); |
665,6 → 679,33
else throw std::runtime_error("\""+args[1].str+"\": bad argument"); |
} |
|
void Assembler::encodeLcs(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("lcs instruction requires 2 operands"); |
|
LinkableObject::Word w=0xA0000000; |
encodeDstOperand(w,args[0]); |
|
if(args[1].type==Operand::NumericLiteral) { |
if((args[1].i<-1048576||args[1].i>1048575)&&(args[1].i<0xFFF00000||args[1].i>0xFFFFFFFF)) |
throw std::runtime_error("\""+args[1].str+"\": out of range"); |
auto c=static_cast<LinkableObject::Word>(args[1].i)&0x1FFFFF; |
w|=(c&0xFFFF); |
w|=((c<<8)&0x1F000000); |
_obj.addWord(w); |
} |
else if(args[1].type==Operand::Identifier) { |
LinkableObject::Reference ref; |
ref.source=currentFileName(); |
ref.line=line(); |
ref.rva=_obj.addWord(w); |
ref.offset=args[1].i; |
ref.type=LinkableObject::Short; |
_obj.addReference(args[1].str,ref); |
} |
else throw std::runtime_error("\""+args[1].str+"\": bad argument"); |
} |
|
void Assembler::encodeLsb(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("lsb instruction requires 2 operands"); |
735,6 → 776,16
_obj.addWord(w); |
} |
|
void Assembler::encodeNeg(const TokenList &list) { |
// Note: "neg" is not a real instruction, but an alias for "sub dst, 0, src" |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("neg instruction requires 2 operands"); |
LinkableObject::Word w=0x44000000; |
encodeDstOperand(w,args[0]); |
encodeRd2Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeNop(const TokenList &list) { |
auto args=getOperands(list); |
if(!args.empty()) throw std::runtime_error("nop instruction doesn't take operands"); |
/assembler.h
41,6 → 41,7
std::vector<std::string> _currentLabels; |
std::string _currentFileName; |
std::vector<std::string> _includeSearchDirs; |
std::vector<std::string> _exportedSymbols; |
public: |
void processFile(const std::string &filename); |
|
80,6 → 81,7
void encodeJmp(const TokenList &list); |
void encodeIret(const TokenList &list); |
void encodeLc(const TokenList &list); |
void encodeLcs(const TokenList &list); |
void encodeLsb(const TokenList &list); |
void encodeLub(const TokenList &list); |
void encodeLw(const TokenList &list); |
87,6 → 89,7
void encodeModu(const TokenList &list); |
void encodeMov(const TokenList &list); |
void encodeMul(const TokenList &list); |
void encodeNeg(const TokenList &list); |
void encodeNop(const TokenList &list); |
void encodeNot(const TokenList &list); |
void encodeOr(const TokenList &list); |
/linkableobject.cpp
96,7 → 96,7
_code[rva++]=static_cast<Byte>(value>>24); |
} |
|
void LinkableObject::addLocalSymbol(const std::string &name,Word rva) { |
void LinkableObject::addSymbol(const std::string &name,Word rva) { |
auto &data=symbol(name); |
if(data.type!=Unknown) throw std::runtime_error("Symbol \""+name+"\" is already defined"); |
data.type=Local; |
103,15 → 103,23
data.rva=rva; |
} |
|
void LinkableObject::addExternalSymbol(const std::string &name) { |
void LinkableObject::addImportedSymbol(const std::string &name) { |
auto &data=symbol(name); |
if(data.type!=Unknown) throw std::runtime_error("Symbol \""+name+"\" is already defined"); |
data.type=External; |
data.type=Imported; |
} |
|
void LinkableObject::addReference(const std::string &symbolName,const std::string &source,int line,Word rva) { |
void LinkableObject::exportSymbol(const std::string &name) { |
auto it=_symbols.find(name); |
if(it==_symbols.end()||it->second.type==Unknown) throw std::runtime_error("Undefined symbol \""+name+"\""); |
if(it->second.type==Imported) throw std::runtime_error("Symbol \""+name+"\" can't be both imported and exported at the same time"); |
if(it->second.type==Exported) throw std::runtime_error("Symbol \""+name+"\" has been already exported"); |
it->second.type=Exported; |
} |
|
void LinkableObject::addReference(const std::string &symbolName,const Reference &ref) { |
auto &data=symbol(symbolName); |
data.refs.push_back({source,line,rva}); |
data.refs.push_back(ref); |
} |
|
LinkableObject::SymbolData &LinkableObject::symbol(const std::string &name) { |
120,7 → 128,7
|
const LinkableObject::SymbolData &LinkableObject::symbol(const std::string &name) const { |
auto const it=_symbols.find(name); |
if(it==_symbols.end()) throw std::runtime_error("Undefined symbol"); |
if(it==_symbols.end()) throw std::runtime_error("Undefined symbol \""+name+"\""); |
return it->second; |
} |
|
151,10 → 159,18
out<<std::endl; |
out<<"Start Symbol"<<std::endl; |
out<<"\tName "<<Utils::urlEncode(sym.first)<<std::endl; |
if(sym.second.type==Local) out<<"\tRVA 0x"<<Utils::hex(sym.second.rva)<<std::endl; |
else out<<"\tExternal"<<std::endl; |
if(sym.second.type==Local) out<<"\tType Local"<<std::endl; |
else if(sym.second.type==Exported) out<<"\tType Exported"<<std::endl; |
else out<<"\tType Imported"<<std::endl; |
if(sym.second.type!=Imported) out<<"\tRVA 0x"<<Utils::hex(sym.second.rva)<<std::endl; |
for(auto const &ref: sym.second.refs) { |
out<<"\tRef "<<Utils::urlEncode(ref.source)<<" "<<ref.line<<" 0x"<<Utils::hex(ref.rva)<<std::endl; |
out<<"\tRef "; |
out<<Utils::urlEncode(ref.source)<<" "; |
out<<ref.line<<" "; |
out<<"0x"<<Utils::hex(ref.rva)<<" "; |
out<<ref.offset<<" "; |
if(ref.type==Regular) out<<"Regular"<<std::endl; |
else if(ref.type==Short) out<<"Short"<<std::endl; |
} |
out<<"End Symbol"<<std::endl; |
} |
231,10 → 247,15
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
name=Utils::urlDecode(tokens[1]); |
} |
else if(tokens[0]=="External") data.type=External; |
else if(tokens[0]=="Type") { |
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
if(tokens[1]=="Local") data.type=Local; |
else if(tokens[1]=="Exported") data.type=Exported; |
else if(tokens[1]=="Imported") data.type=Imported; |
else throw std::runtime_error("Bad symbol type"); |
} |
else if(tokens[0]=="RVA") { |
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
data.type=Local; |
data.rva=std::strtoul(tokens[1].c_str(),NULL,0); |
} |
else if(tokens[0]=="Ref") { |
243,6 → 264,10
ref.source=Utils::urlDecode(tokens[1]); |
ref.line=std::strtoul(tokens[2].c_str(),NULL,0); |
ref.rva=std::strtoul(tokens[3].c_str(),NULL,0); |
ref.offset=std::strtoll(tokens[4].c_str(),NULL,0); |
if(tokens[5]=="Regular") ref.type=Regular; |
else if(tokens[5]=="Short") ref.type=Short; |
else throw std::runtime_error("Invalid reference type: \""+tokens[5]+"\""); |
data.refs.push_back(std::move(ref)); |
} |
} |
/linkableobject.h
20,12 → 20,17
public: |
typedef unsigned char Byte; |
typedef std::uint32_t Word; |
typedef std::int_least64_t Integer; |
|
enum SymbolType {Unknown,Local,External}; |
enum SymbolType {Unknown,Local,Exported,Imported}; |
enum RefType {Regular,Short}; |
|
struct Reference { |
std::string source; |
int line; |
Word rva; |
Integer offset; |
RefType type; |
}; |
struct SymbolData { |
SymbolType type=Unknown; |
32,6 → 37,7
Word rva; |
std::vector<Reference> refs; |
}; |
|
typedef std::map<std::string,SymbolData> SymbolTable; |
|
private: |
61,9 → 67,10
Word getWord(Word rva) const; |
void replaceWord(Word rva,Word value); |
|
void addLocalSymbol(const std::string &name,Word rva); |
void addExternalSymbol(const std::string &name); |
void addReference(const std::string &symbolName,const std::string &source,int line,Word rva); |
void addSymbol(const std::string &name,Word rva); |
void addImportedSymbol(const std::string &name); |
void exportSymbol(const std::string &name); |
void addReference(const std::string &symbolName,const Reference &ref); |
|
SymbolData &symbol(const std::string &name); |
const SymbolData &symbol(const std::string &name) const; |
/linker.cpp
9,6 → 9,7
#include "linker.h" |
|
#include "linkableobject.h" |
#include "utils.h" |
|
#include <iostream> |
#include <fstream> |
16,6 → 17,7
#include <map> |
#include <stdexcept> |
#include <cassert> |
#include <algorithm> |
|
void Linker::addObject(LinkableObject &obj) { |
_objects.push_back(&obj); |
29,14 → 31,8
|
// Determine entry point |
if(_objects.size()==1) _entryObject=_objects[0]; |
else { |
auto const it=_globalSymbolTable.find("entry"); |
if(it==_globalSymbolTable.end()) |
throw std::runtime_error("Entry point not defined: cannot find \"entry\" symbol"); |
if(it->second.rva!=0) |
throw std::runtime_error(it->second.obj->name()+": Entry point must refer to the start of the object"); |
_entryObject=it->second.obj; |
} |
else if(_entryObject==nullptr) |
throw std::runtime_error("Entry point not defined: cannot find \"entry\" or \"Entry\" symbol"); |
|
// Assign virtual addresses |
placeObjects(); |
46,6 → 42,7
|
// Write binary data |
writeObjects(writer); |
_bytesWritten=writer.size(); |
} |
|
void Linker::setBase(LinkableObject::Word base) { |
60,6 → 57,41
_imageSize=size; |
} |
|
void Linker::generateMap(std::ostream &s) { |
// Calculate the maximum length of a symbol name |
std::size_t len=0; |
for(auto const &obj: _objects) { |
for(auto const &sym: obj->symbols()) { |
if(sym.second.type!=LinkableObject::Imported) |
len=std::max(len,sym.first.size()); |
} |
} |
len=std::max(len+3,std::size_t(8)); // width of the first column |
|
s<<"Image base address: "<<Utils::hex(_base)<<std::endl; |
s<<"Object alignment: "<<_align<<std::endl; |
s<<"Image size: "<<(_bytesWritten/4)<<" words"<<std::endl; |
s<<"Number of objects: "<<_objects.size()<<std::endl; |
s<<std::endl; |
|
for(auto const &obj: _objects) { |
s<<"Object \""<<obj->name()<<"\" at address "<<Utils::hex(obj->virtualAddress())<<std::endl; |
s<<std::endl; |
std::multimap<LinkableObject::Word,std::pair<std::string,LinkableObject::SymbolData> > sorted; |
for(auto const &sym: obj->symbols()) sorted.emplace(sym.second.rva,sym); |
for(auto const &sym: sorted) { |
if(sym.second.second.type==LinkableObject::Imported) continue; |
s<<sym.second.first; |
s<<std::string(len-sym.second.first.size(),' '); |
s<<Utils::hex(obj->virtualAddress()+sym.second.second.rva); |
if(sym.second.second.type==LinkableObject::Local) s<<" Local"; |
else s<<" Exported"; |
s<<std::endl; |
} |
s<<std::endl; |
} |
} |
|
/* |
* Private members |
*/ |
67,14 → 99,31
void Linker::buildSymbolTable() { |
_globalSymbolTable.clear(); |
|
// Build a table of exported symbols from all modules |
for(auto const &obj: _objects) { |
auto const &table=obj->symbols(); |
for(auto const &item: table) { |
if((item.first=="entry"||item.first=="Entry")&&item.second.type!=LinkableObject::Imported) { |
if(_entryObject) { |
std::ostringstream msg; |
msg<<obj->name()<<": Duplicate definition of the entry symbol "; |
msg<<"(previously defined in "<<_entryObject->name()<<")"; |
throw std::runtime_error(msg.str()); |
} |
if(item.second.rva!=0) { |
std::ostringstream msg; |
msg<<obj->name()<<": "; |
msg<<"Entry point must refer to the start of the object"; |
throw std::runtime_error(msg.str()); |
} |
_entryObject=obj; |
} |
if(item.second.type==LinkableObject::Local) continue; |
// Insert item to the global symbol table if it doesn't exist yet |
auto it=_globalSymbolTable.emplace(item.first,GlobalSymbolData()).first; |
|
// If the symbol is local, check that it has not been already defined in another object |
if(item.second.type==LinkableObject::Local) { |
// Check that the symbol has not been already defined in another object |
if(item.second.type==LinkableObject::Exported) { |
if(it->second.obj) { |
std::ostringstream msg; |
msg<<obj->name()<<": Duplicate definition of \""<<item.first; |
84,12 → 133,28
it->second.obj=obj; |
it->second.rva=item.second.rva; |
} |
|
// Merge reference tables |
for(auto const &ref: item.second.refs) it->second.refs.emplace(obj,ref.rva); |
|
if(!item.second.refs.empty()) it->second.refs.insert(obj); |
} |
} |
|
// Check that local symbols don't shadow the public ones |
for(auto const &obj: _objects) { |
auto const &table=obj->symbols(); |
for(auto const &item: table) { |
if(item.second.type!=LinkableObject::Local) continue; |
auto it=_globalSymbolTable.find(item.first); |
if(it==_globalSymbolTable.end()) continue; |
if(!it->second.obj) continue; |
if(item.first==it->first) { |
std::ostringstream msg; |
msg<<obj->name()<<": Local symbol \""<<item.first<<"\" shadows the public one "; |
msg<<"(defined in "<<it->second.obj->name()<<")"; |
throw std::runtime_error(msg.str()); |
} |
} |
} |
|
// Check that no undefined symbols remain |
for(auto const &item: _globalSymbolTable) { |
if(item.second.obj==nullptr&&!item.second.refs.empty()) { |
96,7 → 161,7
std::ostringstream msg; |
msg<<"Undefined symbol: \""<<item.first<<"\""; |
auto const it=item.second.refs.begin(); |
msg<<" (referenced from "<<it->first->name()<<")"; |
msg<<" (referenced from "<<(*it)->name()<<")"; |
throw std::runtime_error(msg.str()); |
} |
} |
109,12 → 174,31
if(_objects.size()>1) { |
for(auto it=_objects.begin();it!=_objects.end();++it) { |
if(*it==_entryObject) { |
std::swap(*it,_objects[0]); |
_objects.erase(it); |
break; |
} |
} |
_objects.insert(_objects.begin(),_entryObject); |
} |
|
// Remove unreferenced objects |
if(_objects.size()>1) { |
std::set<const LinkableObject*> used; |
markAsUsed(_objects[0],used); |
for(auto it=_objects.begin();it!=_objects.end();) { |
if(used.find(*it)==used.end()) { |
std::cerr<<"Linker warning: skipping an unreferenced object \""; |
std::cerr<<(*it)->name()<<"\""<<std::endl; |
for(auto sym=_globalSymbolTable.begin();sym!=_globalSymbolTable.end();) { |
if(sym->second.obj==*it) sym=_globalSymbolTable.erase(sym); |
else ++sym; |
} |
it=_objects.erase(it); |
} |
else ++it; |
} |
} |
|
// Set base addresses |
for(auto it=_objects.begin();it!=_objects.end();++it) { |
(*it)->setVirtualAddress(currentBase); |
126,14 → 210,33
|
void Linker::relocateObject(LinkableObject *obj) { |
for(auto const &sym: obj->symbols()) { |
auto it=_globalSymbolTable.find(sym.first); |
assert(it!=_globalSymbolTable.end()); |
if(it->second.refs.empty()) continue; |
assert(it->second.obj); |
auto addr=it->second.obj->virtualAddress()+it->second.rva; |
LinkableObject::Word addr; |
if(sym.second.refs.empty()) continue; |
|
if(sym.second.type==LinkableObject::Local) addr=obj->virtualAddress()+sym.second.rva; |
else { |
auto it=_globalSymbolTable.find(sym.first); |
assert(it!=_globalSymbolTable.end()); |
assert(it->second.obj); |
addr=it->second.obj->virtualAddress()+it->second.rva; |
} |
|
for(auto const &ref: sym.second.refs) { |
auto offset=obj->getWord(ref.rva); |
obj->replaceWord(ref.rva,addr+offset); |
if(ref.type==LinkableObject::Regular) obj->replaceWord(ref.rva,addr+ref.offset); |
else { |
auto target=static_cast<LinkableObject::Word>(addr+ref.offset); |
if(target>0xFFFFF&&target<0xFFF00000) { |
std::ostringstream msg; |
msg<<"Address 0x"<<Utils::hex(target)<<" is out of the range for a short reference"; |
msg<<" (referenced from "<<ref.source<<":"<<ref.line<<")"; |
throw std::runtime_error(msg.str()); |
} |
target&=0x1FFFFF; |
auto w=obj->getWord(ref.rva); |
w|=(target&0xFFFF); |
w|=((target<<8)&0x1F000000); |
obj->replaceWord(ref.rva,w); |
} |
} |
} |
} |
157,3 → 260,13
else if(currentSize<_imageSize) writer.pad(_imageSize-currentSize); |
} |
} |
|
void Linker::markAsUsed(const LinkableObject *obj,std::set<const LinkableObject*> &used) { |
if(used.find(obj)!=used.end()) return; // already processed |
used.insert(obj); |
for(auto const &sym: _globalSymbolTable) { |
for(auto const &ref: sym.second.refs) { |
if(ref==obj) markAsUsed(sym.second.obj,used); |
} |
} |
} |
/linker.h
17,16 → 17,17
#include <map> |
#include <vector> |
#include <string> |
#include <set> |
|
class Linker { |
struct GlobalSymbolData { |
LinkableObject *obj=nullptr; |
LinkableObject::Word rva=0; |
std::multimap<const LinkableObject*,LinkableObject::Word> refs; |
std::set<const LinkableObject*> refs; |
}; |
|
std::vector<LinkableObject*> _objects; |
LinkableObject *_entryObject; |
LinkableObject *_entryObject=nullptr; |
std::map<std::string,GlobalSymbolData> _globalSymbolTable; |
|
// Various output options |
33,6 → 34,7
LinkableObject::Word _base=0; |
std::size_t _align=4; |
std::size_t _imageSize=0; |
std::size_t _bytesWritten=0; |
public: |
void addObject(LinkableObject &obj); |
void link(OutputWriter &writer); |
39,11 → 41,13
void setBase(LinkableObject::Word base); |
void setAlignment(std::size_t align); |
void setImageSize(std::size_t size); |
void generateMap(std::ostream &s); |
private: |
void buildSymbolTable(); |
void placeObjects(); |
void relocateObject(LinkableObject *obj); |
void writeObjects(OutputWriter &writer); |
void markAsUsed(const LinkableObject *obj,std::set<const LinkableObject*> &used); |
}; |
|
#endif |
/main.cpp
8,6 → 8,7
|
#include "assembler.h" |
#include "linker.h" |
#include "utils.h" |
|
#include <iostream> |
#include <fstream> |
25,6 → 26,7
|
bool compileOnly=false; |
std::string outputFileName; |
std::string mapFileName; |
std::vector<std::string> includeSearchDirs; |
LinkableObject::Word base=0; |
std::size_t align=4; |
38,7 → 40,7
os<<" "<<program<<" [ option(s) | input file(s) ]"<<std::endl<<std::endl; |
|
os<<"Options:"<<std::endl; |
os<<" -a <align> Section alignment (default: 4)"<<std::endl; |
os<<" -a <align> Object alignment (default: 4)"<<std::endl; |
os<<" -b <addr> Base address (default: 0)"<<std::endl; |
os<<" -c Compile only (don't link)"<<std::endl; |
os<<" -f <fmt> Output file format (see below)"<<std::endl; |
45,12 → 47,14
os<<" -h, --help Display a short help message"<<std::endl; |
os<<" -i <dir> Add directory to the list of directories used to search"<<std::endl; |
os<<" for included files (multiple directories can be specified)"<<std::endl; |
os<<" -m <file> Generate map file"<<std::endl; |
os<<" -o <file> Output file name"<<std::endl; |
os<<" -s <size> Output image size"<<std::endl; |
os<<" -- Do not interpret subsequent arguments as options"<<std::endl; |
os<<std::endl; |
os<<"Section alignment and image size must be multiples of 4."<<std::endl; |
os<<"Base address must be a multiple of section alignment."<<std::endl; |
os<<"Object alignment must be a power of two and can't be less than 4."<<std::endl; |
os<<"Base address must be a multiple of object alignment."<<std::endl; |
os<<"Image size must be a multiple of 4."<<std::endl; |
os<<std::endl; |
|
os<<"Output file formats:"<<std::endl; |
67,6 → 71,8
|
std::ifstream in(filename,std::ios_base::in); |
if(!in) return false; |
if(in.tellg()==static_cast<std::ifstream::pos_type>(-1)) |
return false; // the stream is not seekable |
|
std::vector<char> buf(idSize); |
in.read(buf.data(),idSize); |
84,7 → 90,7
bool noMoreOptions=false; |
|
std::cout<<"LXP32 Platform Assembler and Linker"<<std::endl; |
std::cout<<"Copyright (c) 2016 by Alex I. Kuznetsov"<<std::endl; |
std::cout<<"Copyright (c) 2016-2019 by Alex I. Kuznetsov"<<std::endl; |
|
if(argc<=1) { |
displayUsage(std::cout,argv[0]); |
101,11 → 107,12
} |
try { |
options.align=std::stoul(argv[i],nullptr,0); |
if(options.align%4!=0||options.align==0) throw std::exception(); |
if(!Utils::isPowerOf2(options.align)) throw std::exception(); |
if(options.align<4) throw std::exception(); |
alignmentSpecified=true; |
} |
catch(std::exception &) { |
throw std::runtime_error("Invalid section alignment"); |
throw std::runtime_error("Invalid object alignment"); |
} |
} |
else if(!strcmp(argv[i],"-b")) { |
115,7 → 122,6
} |
try { |
options.base=std::stoul(argv[i],nullptr,0); |
//if(options.base%4!=0) throw std::exception(); |
baseSpecified=true; |
} |
catch(std::exception &) { |
148,6 → 154,13
} |
options.includeSearchDirs.push_back(argv[i]); |
} |
else if(!strcmp(argv[i],"-m")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
options.mapFileName=argv[i]; |
} |
else if(!strcmp(argv[i],"-o")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
172,11 → 185,11
} |
|
if(options.base%options.align!=0) |
throw std::runtime_error("Base address must be a multiple of section alignment"); |
throw std::runtime_error("Base address must be a multiple of object alignment"); |
|
if(options.compileOnly) { |
if(alignmentSpecified) |
std::cerr<<"Warning: Section alignment is ignored in compile-only mode"<<std::endl; |
std::cerr<<"Warning: Object alignment is ignored in compile-only mode"<<std::endl; |
if(baseSpecified) |
std::cerr<<"Warning: Base address is ignored in compile-only mode"<<std::endl; |
if(formatSpecified) |
183,6 → 196,8
std::cerr<<"Warning: Output format is ignored in compile-only mode"<<std::endl; |
if(options.imageSize>0) |
std::cerr<<"Warning: Image size is ignored in compile-only mode"<<std::endl; |
if(!options.mapFileName.empty()) |
std::cerr<<"Warning: Map file is not generated in compile-only mode"<<std::endl; |
} |
|
if(inputFiles.empty()) |
278,6 → 293,14
std::cerr<<"Linker error: "<<ex.what()<<std::endl; |
return EXIT_FAILURE; |
} |
|
std::cout<<writer->size()/4<<" words written"<<std::endl; |
|
if(!options.mapFileName.empty()) { |
std::ofstream out(options.mapFileName); |
if(!out) throw std::runtime_error("Cannot open file \""+options.mapFileName+"\" for writing"); |
linker.generateMap(out); |
} |
} |
catch(std::exception &ex) { |
std::cerr<<"Error: "<<ex.what()<<std::endl; |
/outputwriter.cpp
22,6 → 22,11
* OutputWriter members |
*/ |
|
void OutputWriter::write(const char *data,std::size_t n) { |
writeData(data,n); |
_size+=n; |
} |
|
void OutputWriter::pad(std::size_t size) { |
static char zeros[256]; // static objects are zero-initialized |
while(size>0) { |
31,6 → 36,10
} |
} |
|
std::size_t OutputWriter::size() const { |
return _size; |
} |
|
/* |
* BinaryOutputWriter members |
*/ |
42,7 → 51,7
if(!_os) throw std::runtime_error("Cannot open \""+filename+"\" for writing"); |
} |
|
void BinaryOutputWriter::write(const char *data,std::size_t n) { |
void BinaryOutputWriter::writeData(const char *data,std::size_t n) { |
_os.write(data,n); |
} |
|
70,7 → 79,7
} |
} |
|
void TextOutputWriter::write(const char *data,std::size_t n) { |
void TextOutputWriter::writeData(const char *data,std::size_t n) { |
while(n>0) { |
assert(_buf.size()<4); |
auto count=std::min(4-_buf.size(),n); |
/outputwriter.h
19,11 → 19,15
*/ |
|
class OutputWriter { |
std::size_t _size=0; |
public: |
virtual ~OutputWriter() {} |
virtual void write(const char *data,std::size_t n)=0; |
virtual void write(const char *data,std::size_t n); |
virtual void abort() {} |
void pad(std::size_t size); |
std::size_t size() const; |
protected: |
virtual void writeData(const char *data,std::size_t n)=0; |
}; |
|
/* |
35,8 → 39,9
std::ofstream _os; |
public: |
BinaryOutputWriter(const std::string &filename); |
virtual void write(const char *data,std::size_t n) override; |
virtual void abort() override; |
protected: |
virtual void writeData(const char *data,std::size_t n) override; |
}; |
|
/* |
54,8 → 59,9
public: |
TextOutputWriter(const std::string &filename,Format f); |
~TextOutputWriter(); |
virtual void write(const char *data,std::size_t n) override; |
virtual void abort() override; |
protected: |
virtual void writeData(const char *data,std::size_t n) override; |
}; |
|
#endif |
/utils.h
52,6 → 52,11
|
bool ishexdigit(char ch); |
bool isoctdigit(char ch); |
|
template <typename T> bool isPowerOf2(const T &x) { |
static_assert(std::is_integral<T>::value,"Argument must be of integral type"); |
return (x!=0)&&((x&(x-1))==0); |
} |
} |
|
#endif |