/* xa65 object file loader and linker for C++ * For loading and linking 65816 object files * Copyright (C) 1992,2005 Bisqwit (http://iki.fi/bisqwit/) * * Version 1.9.0 - Aug 18 2003, Sep 4 2003, Jan 23 2004, * Jan 31 2004 */ #define DEBUG_FIXUPS 0 #include #include #include "o65.hh" using std::set; using std::map; using std::make_pair; using std::fprintf; #ifndef stderr using std::stderr; #endif #include "relocdata.hh" // TODO: per-segment linkage instruction namespace { unsigned LoadWord(FILE *fp) { unsigned temp = fgetc(fp); if(temp == (unsigned)EOF)return temp; return temp | (fgetc(fp) << 8); } unsigned LoadDWord(FILE *fp) { unsigned temp = LoadWord(fp); if(temp == (unsigned)EOF) return temp; unsigned temp2 = LoadWord(fp); if(temp2 == (unsigned)EOF) return temp2; return temp | (temp2 << 16); } unsigned LoadSWord(FILE *fp, bool use32) { return use32 ? LoadDWord(fp) : LoadWord(fp); } } class O65::Defs { std::map symno; std::map nosym; std::set undefines; std::map defines; public: Defs(): symno(), nosym(), undefines(), defines() { } unsigned AddUndefined(const std::string& name) { unsigned a = symno.size(); undefines.insert(a); symno[name] = a; nosym[a] = name; return a; } unsigned GetSymno(const std::string& name) const { std::map::const_iterator i = symno.find(name); if(i == symno.end()) return ~0U; return i->second; } bool IsDefined(unsigned a) const { return defines.find(a) != defines.end(); } unsigned GetValue(unsigned a) const { return defines.find(a)->second; } void Define(unsigned a, unsigned value) { undefines.erase(a); defines[a] = value; } const vector GetExternList() const { vector result; result.reserve(undefines.size()); for(std::set::const_iterator i = undefines.begin(); i != undefines.end(); ++i) { const std::string& name = nosym.find(*i)->second; result.push_back(name); } return result; } void DumpUndefines() const { for(std::set::const_iterator i = undefines.begin(); i != undefines.end(); ++i) { const std::string& name = nosym.find(*i)->second; fprintf(stderr, "Symbol %s is still not defined\n", name.c_str()); } } }; class O65::Segment { public: vector space; //! where it is assumed to start unsigned base; //! absolute addresses of all publics typedef map publicmap_t; publicmap_t publics; public: typedef Relocdata RT; RT R; public: Segment(): space(), base(0), publics(), R() { } private: friend class O65; void Locate(SegmentSelection seg, unsigned diff, bool is_me); void LocateSym(unsigned symno, unsigned newaddress); void LoadRelocations(FILE* fp); }; O65::O65() : customheaders(), defs(new Defs), code(NULL), data(NULL), zero(NULL), bss(NULL), error(false) { } O65::~O65() { delete code; delete data; delete zero; delete bss; delete defs; } O65::O65(const O65& b) : customheaders(), defs(new Defs(*b.defs)), code(b.code ? new Segment(*b.code) : NULL), data(b.data ? new Segment(*b.data) : NULL), zero(b.zero ? new Segment(*b.zero) : NULL), bss(b.bss ? new Segment(*b.bss) : NULL), error(b.error) { } const O65& O65::operator= (const O65& b) { if(&b == this) return *this; delete code; code = b.code ? new Segment(*b.code) : NULL; delete data; data = b.data ? new Segment(*b.data) : NULL; delete zero; zero = b.zero ? new Segment(*b.zero) : NULL; delete bss; bss = b.bss ? new Segment(*b.bss) : NULL; delete defs; defs = new Defs(*b.defs); return *this; } void O65::Load(FILE* fp) { rewind(fp); /* FIXME: No validity checks here */ // Skip header LoadWord(fp);LoadWord(fp);LoadWord(fp); /* FIXME: No validity checks here */ // Skip mode unsigned mode = LoadWord(fp); bool use32 = mode & 0x2000; if(this->code) delete this->code; if(this->data) delete this->data; if(this->zero) delete this->zero; if(this->bss) delete this->bss; this->code = new Segment; this->data = new Segment; this->zero = new Segment; this->bss = new Segment; this->code->base = LoadSWord(fp, use32); this->code->space.resize(LoadSWord(fp, use32)); this->data->base = LoadSWord(fp, use32); this->data->space.resize(LoadSWord(fp, use32)); this->bss->base = LoadSWord(fp, use32); this->bss->space.resize(LoadSWord(fp, use32)); this->zero->base = LoadSWord(fp, use32); this->zero->space.resize(LoadSWord(fp, use32)); LoadSWord(fp, use32); // Skip stack_len //fprintf(stderr, "@%X: stack len\n", ftell(fp)); // Skip some headers for(;;) { unsigned len = fgetc(fp); if(!len || len == (unsigned)EOF)break; len &= 0xFF; unsigned char type = fgetc(fp); if(len >= 2) len -= 2; else len = 0; std::string data(len, '\0'); for(unsigned a=0; acode->space[0], this->code->space.size(), 1, fp); fread(&this->data->space[0], this->data->space.size(), 1, fp); // zero and bss Segments don't exist in o65 format. // load external symbols unsigned num_und = LoadSWord(fp, use32); //fprintf(stderr, "@%X: %u externs..\n", ftell(fp), num_und); for(unsigned a=0; aAddUndefined(varname); } //fprintf(stderr, "@%X: code relocs..\n", ftell(fp)); code->LoadRelocations(fp); //fprintf(stderr, "@%X: data relocs..\n", ftell(fp)); data->LoadRelocations(fp); // relocations don't exist for zero/bss in o65 format. unsigned num_global = LoadSWord(fp, use32); //fprintf(stderr, "@%X: %u globals\n", ftell(fp), num_global); for(unsigned a=0; apublics[name] = address; } } void O65::Locate(SegmentSelection seg, unsigned newaddress) { Segment**s = GetSegRef(seg); if(!s) return; unsigned diff = newaddress - (*s)->base; if(code) code->Locate(seg, diff, seg==CODE); if(data) data->Locate(seg, diff, seg==DATA); if(zero) zero->Locate(seg, diff, seg==ZERO); if(bss) bss->Locate(seg, diff, seg==BSS); } unsigned O65::GetBase(SegmentSelection seg) const { const Segment*const *s = GetSegRef(seg); if(!s) return 0; return (*s)->base; } void O65::LinkSym(const std::string& name, unsigned value) { unsigned symno = defs->GetSymno(name); if(symno == ~0U) { fprintf(stderr, "O65: Attempt to define unknown symbol '%s' as %X\n", name.c_str(), value); return; } if(defs->IsDefined(symno)) { unsigned oldvalue = defs->GetValue(symno); fprintf(stderr, "O65: Attempt to redefine symbol '%s' as %X, old value %X\n", name.c_str(), value, oldvalue ); } if(code) code->LocateSym(symno, value); if(data) data->LocateSym(symno, value); if(zero) zero->LocateSym(symno, value); if(bss) bss->LocateSym(symno, value); defs->Define(symno, value); } void O65::Segment::Locate(SegmentSelection seg, unsigned diff, bool is_me) { if(is_me) { /* Relocate publics */ map::iterator i; for(i = publics.begin(); i != publics.end(); ++i) { i->second += diff; } } /* Fix all references to symbols in the given seg */ for(unsigned a=0; a>8) & 255; } for(unsigned a=0; a>8)&255, addr); #endif space[addr] = (newvalue>>8) & 255; } for(unsigned a=0; a>8) & 255; space[addr+2] = (newvalue>>16) & 255; } for(unsigned a=0; a>16, addr); #endif space[addr] = (newvalue>>16) & 255; } // Update the relocs and fixups so that they are still usable // after this operation. Enables also subsequent Locate() calls. // This updates the position of each reloc & fixup. if(is_me) { for(unsigned a=0; a>8) & 255; } for(unsigned a=0; a>8)&255, symno); #endif space[addr] = (newvalue>>8) & 255; } for(unsigned a=0; a>8) & 255; space[addr+2] = (newvalue>>16) & 255; } for(unsigned a=0; a>16, symno); #endif space[addr] = (newvalue>>16) & 255; } } const vector& O65::GetSeg(SegmentSelection seg) const { if(const Segment*const *s = GetSegRef(seg)) { return (*s)->space; } return code->space; } unsigned O65::GetSegSize(SegmentSelection seg) const { if(const Segment*const *s = GetSegRef(seg)) { return (*s)->space.size(); } return 0; } unsigned O65::GetSymAddress(SegmentSelection seg, const std::string& name) const { if(const Segment*const *s = GetSegRef(seg)) { Segment::publicmap_t::const_iterator i = (*s)->publics.find(name); if(i == code->publics.end()) { fprintf(stderr, "Attempt to find symbol %s which not exists.\n", name.c_str()); return 0; } return i->second; } return 0; } bool O65::HasSym(SegmentSelection seg, const std::string& name) const { if(const Segment*const *s = GetSegRef(seg)) { const O65::Segment::publicmap_t& pubs = (*s)->publics; return pubs.find(name) != pubs.end(); } return false; } const vector O65::GetSymbolList(SegmentSelection seg) const { vector result; if(const Segment*const *s = GetSegRef(seg)) { const O65::Segment::publicmap_t& pubs = (*s)->publics; result.reserve(pubs.size()); for(O65::Segment::publicmap_t::const_iterator i = pubs.begin(); i != pubs.end(); ++i) { result.push_back(i->first); } } return result; } const vector O65::GetExternList() const { return defs->GetExternList(); } void O65::Verify() const { defs->DumpUndefines(); } O65::Segment** O65::GetSegRef(SegmentSelection seg) { Segment** result = NULL; switch(seg) { case CODE: result = &code; break; case DATA: result = &data; break; case ZERO: result = &zero; break; case BSS: result = &bss; break; default: return NULL; } if(!*result) *result = new Segment; return result; } const O65::Segment*const * O65::GetSegRef(SegmentSelection seg) const { switch(seg) { case CODE: return code ? &code : NULL; case DATA: return data ? &data : NULL; case ZERO: return zero ? &zero : NULL; case BSS: return bss ? &bss : NULL; } return NULL; } void O65::Resize(SegmentSelection seg, unsigned newsize) { if(Segment**s = GetSegRef(seg)) { (*s)->space.resize(newsize); } } void O65::Write(SegmentSelection seg, unsigned addr, unsigned char value) { if(Segment**s = GetSegRef(seg)) { (*s)->space[addr] = value; } } void O65::LoadSegFrom(SegmentSelection seg, const vector& buf) { if(Segment**s = GetSegRef(seg)) { (*s)->space = buf; } } bool O65::Error() const { return error; } void O65::SetError() { error = true; } void O65::DeclareByteRelocation(SegmentSelection seg, const std::string& name, unsigned addr) { Segment**s = GetSegRef(seg); if(!s) return; unsigned symno = defs->GetSymno(name); if(symno == ~0U) symno = defs->AddUndefined(name); else if(defs->IsDefined(symno)) { unsigned value = defs->GetValue(symno); addr -= (*s)->base; (*s)->space[addr] = value & 0xFF; return; } (*s)->R.R16lo.AddReloc(addr, symno); } void O65::DeclareWordRelocation(SegmentSelection seg, const std::string& name, unsigned addr) { Segment**s = GetSegRef(seg); if(!s) return; unsigned symno = defs->GetSymno(name); if(symno == ~0U) symno = defs->AddUndefined(name); else if(defs->IsDefined(symno)) { unsigned value = defs->GetValue(symno); addr -= (*s)->base; (*s)->space[addr ] = value & 0xFF; (*s)->space[addr + 1] = (value >> 8) & 0xFF; return; } (*s)->R.R16.AddReloc(addr, symno); } /* void O65::DeclareHiByteRelocation(SegmentSelection seg, const std::string& name, unsigned addr) { Segment**s = GetSegRef(seg); if(!s) return; unsigned symno = defs->GetSymno(name); if(symno == ~0U) symno = defs->AddUndefined(name); else if(defs->IsDefined(symno)) { unsigned value = defs->GetValue(symno); addr -= (*s)->base; (*s)->space[addr] = (value >> 8) & 0xFF; return; } (*s)->R.R16hi.AddReloc(addr, symno); } */ void O65::DeclareLongRelocation(SegmentSelection seg, const std::string& name, unsigned addr) { Segment**s = GetSegRef(seg); if(!s) return; unsigned symno = defs->GetSymno(name); if(symno == ~0U) symno = defs->AddUndefined(name); else if(defs->IsDefined(symno)) { unsigned value = defs->GetValue(symno); addr -= (*s)->base; (*s)->space[addr ] = value & 0xFF; (*s)->space[addr + 1] = (value >> 8) & 0xFF; (*s)->space[addr + 2] = (value >>16) & 0xFF; return; } (*s)->R.R24.AddReloc(addr, symno); } void O65::Segment::LoadRelocations(FILE* fp) { int addr = -1; for(;;) { int c = fgetc(fp); if(!c || c == EOF)break; if(c == 255) { addr += 254; continue; } addr += c; c = fgetc(fp); unsigned type = c & 0xE0; unsigned area = c & 0x07; switch(area) { case 0: // external { unsigned symno = LoadWord(fp); switch(type) { case 0x20: { R.R16lo.AddReloc(addr, symno); break; } case 0x40: { RT::R16hi_t::Type tmp(addr, fgetc(fp)); R.R16hi.AddReloc(tmp, symno); break; } case 0x80: { R.R16.AddReloc(addr, symno); break; } case 0xA0: { RT::R24seg_t::Type tmp(addr, LoadWord(fp)); R.R24seg.AddReloc(tmp, symno); break; } case 0xC0: { R.R24.AddReloc(addr, symno); break; } default: { fprintf(stderr, "Error: External reloc type %02X not supported yet\n", type); } } break; } case CODE: // code =2 case DATA: // data =3 case ZERO: // zero =5 case BSS: // bss =4 { SegmentSelection seg = (SegmentSelection)area; //fprintf(stderr, "Fixup into %u (%X) -- type=%02X\n", seg, addr, type); switch(type) { case 0x20: { R.R16lo.AddFixup(seg, addr); break; } case 0x40: { RT::R16hi_t::Type tmp(addr, fgetc(fp)); R.R16hi.AddFixup(seg, tmp); break; } case 0x80: { R.R16.AddFixup(seg, addr); break; } case 0xA0: { RT::R24seg_t::Type tmp(addr, LoadWord(fp)); R.R24seg.AddFixup(seg, tmp); break; } case 0xC0: { R.R24.AddFixup(seg, addr); break; } default: { fprintf(stderr, "Error: Fixup type %02X not supported yet\n", type); } } break; } default: { fprintf(stderr, "Error: Reloc area type %02X not supported yet\n", area); } } } } const vector >& O65::GetCustomHeaders() const { return customheaders; } /* Get relocation data of the given segment */ const Relocdata O65::GetRelocData(SegmentSelection seg) const { const Segment*const *s = GetSegRef(seg); if(!s) return Relocdata (); return (*s)->R; } const std::string GetSegmentName(const SegmentSelection seg) { switch(seg) { case CODE: return "code"; case DATA: return "data"; case ZERO: return "zero"; case BSS: return "bss"; } return "?"; }