I also think the best solution is the one which allows future
developments...these are always required :)
In our case, I think a good solution is to implement some kind of linker, as
in this model (top-down):
//a class which holds all the segments and has ownership over them
class Binary{
- flags; //processor type, architecture, ...
- segments; //collection of segments
Segment *createSegment(); //create an empty segment
//create a new segment from the relevant code and symbols from assembler
//internal symbols like Label, used only inside that code, are not
necessary to be taken
//instead, an Assembler must define special kinds of Labels, which acts
like:
//Symbols: with a bound address inside that code
//References: with an address outside the code
void createSegmentFrom(Assembler &assembler);
void erase(Segment *s); //erase a segment
void linkAll(); //link all the symbols from all segments
//considers all de symbols from s as possibly modified and relocate them
in all other segments
void link(Segment *s);
Segment *findSegmentBySymbol(char *symName); //finds the Segment
which defines symName
};
//has ownership over symbols, references, data, exeCode
class Segment{
- flags; //data/code, read_only, initialized, ...
- symbols; //collection of symbols defined in this segment
- references; //collection of extern symbols needed to relocate the code
from this segment
- data; //the actual code/data of segment
- exeCode; //the executable linked version of data, needed for code
segments used for JIT; not needed for writing files
Symbol *findSymbol(char *symName); //find the symbol with the
given name
void *getExeAddr(char *symName); //get the address in exeCode
of symName
};
class Symbol{
- flags; //if it is exported (like a function for DLL creation), ...
- name; //symbol name
- offset; //symbol offset in data
};
//has ownership over relocations
class Reference{
- flags //if it is imported (like a DLL function), ...
- name
- relocations //collection of the relocation of this reference
};
class Relocation{
- type //absolute, relative, ...
- size //1,4,8 - its size in data (maybe size can be a subtype of
type)
- offset //offset in data
};
With this kind of linker, someone can do:
X86Binary bin86;
void (*Call)();
Call compile(Function &f)
{
X86Assembler a86;
//generate code
bin86.createSegmentFrom(a86);
Segment *s=bin86.findSegmentBySymbol(
f.name);
bin86.link(s);
return (Call)s->getExeAddr(
f.name);
}
other possible usages:
//write an executable/library/object file
COFFWriter coff(bin86);
coff.write("app.exe");
//save the Binary to a cache, with all its segments, symbols, references,
etc
//and reload it later, in order to avoid recompilations
BinaryWriter bw(bin86);
bw.write("cache.bin");
...
bw.read("cache.bin");
I think on this architecture can be built later and it is sufficiently
powerful and extensible...or some variants of it.
If you handle the interface with the Assembler and linkage, I can also work
on on writers/loaders :)
Thank you,
Razvan Aciu