#include "clld.h"
#include <Bstream.h>
#include <set_error.h>
#include <fstream.h>
//extern "C" {
// #include <math.h>
// #include <stdlib.h>
//}
int link_obs ( List<InternalObjectFile> & obs, ObjectFile & resobj ) {
unsigned long aktoff = 0;
ListItem<InternalObjectFile> * aktob;
for ( aktob = obs.get_head(); aktob; aktob = aktob->get_next()) {
// eventually warn about unused object-files
if (! aktob->used) {
if (verbose)
cerr << "WARNING: file '"+aktob->name+"' will not be linked\n"
" because none of its exported symbols were used\n";
} else {
if (! aktob->shared) {
// apply internal relocation where necessary
for ( ListItem<InternalReloc> * aktrel = aktob->intrelocs.get_head();
aktrel;
aktrel = aktrel->get_next()
) {
if (aktrel->obj->shared) {
ListItem<Import> * imp = aktrel->shimport;
imp->offset += aktoff;
} else {
unsigned long tmp = (unsigned long) aktrel->obj->body.peek(aktrel->offset, 5);
tmp += aktoff;
aktrel->obj->body.poke(aktrel->offset, tmp, 5);
}
}
// add the actual offset to the relocation table of this object-file
for ( ListItem<Reloc> * aktr = aktob->relocs.get_head();
aktr;
aktr = aktr->get_next()
) {
aktr->offset += aktoff;
}
if (shared) {
// add the actual offset to the import-list of the shared library
for ( ListItem<Import> * akti = aktob->shimports.get_head();
akti;
akti = akti->get_next()
) {
akti->offset += aktoff;
}
// add the actual offset to the export-list of this object-file
for ( AVLItem<Symbol> * akte = aktob->exports.get_head();
akte;
akte = aktob->exports.get_next(akte)
) {
if (! akte->numerical) {
akte->ivalue += aktoff;
}
}
}
aktoff += aktob->body.length();
}
}
}
// now collect the object-files and generate the executable binary / shared lib
for ( aktob = obs.get_head();
aktob;
aktob = aktob->get_next()
) {
if (aktob->used && !aktob->shared) {
resobj.body += aktob->body;
// add the relocation information for the actual object-file
for ( ListItem<Reloc> * aktr = aktob->relocs.rem_head();
aktr;
// see below
) {
resobj.relocs.add_tail(aktr);
aktr = aktob->relocs.rem_head();
}
if (shared) {
// add to the shared-library imports
for ( ListItem<Import> *akti = aktob->shimports.rem_head();
akti;
// see below
) {
resobj.imports.add_tail(akti);
akti = aktob->shimports.rem_head();
}
// add to the shared-library exports
for ( AVLItem<Symbol> * akts = aktob->exports.get_head();
akts;
akts = aktob->exports.get_next(akts)
) {
resobj.exports.insert(Symbol(*akts));
}
}
}
}
return 0;
}
int read_objfile( List<InternalObjectFile> & obs, const Str & name,
bool statf, bool library )
{
#ifdef OBSOLETE_LIBGXX
BIstream f;
f.open(name, ios::in | ios::binary);
#else
ifstream f(name, ios::in | ios::binary);
#endif
if (f.fail()) {
if (!library)
set_error("","unable to open object-file '"+name+"'");
return 20;
}
ListItem<InternalObjectFile> * ni = new ListItem<InternalObjectFile>;
if (ni == 0) return 20;
#ifndef OBSOLETE_LIBGXX
BIstream in(f);
in >> *ni;
#else
f >> *ni;
#endif
if (ni->fail) {
delete ni;
add_error("read_objfile() ","cannot read object file '"+name+"'");
return 20;
}
if (statf) {
// we link every object statically
ni->shared = false;
}
if (shared) { // if we build a shared library, any object file is "used"
ni->used = true;
}
// every reloc inside the single object file will need an additional
// internal relocation, later:
for (ListItem<Reloc> * rp = ni->relocs.get_head();
rp;
rp = rp->get_next()
) {
ni->intrelocs.add_tail(InternalReloc(ni, rp->offset));
}
obs.add_tail(ni);
if (verbose) {
cerr << "read '"+name+"', " << ni->relocs.length() << " relocs, "
<< ni->imports.length() << " imports, "
<< ni->exports.size() << " exports.\n";
}
return 0;
}
int next_arg(int & argc, const char ** & aktarg, Str & p) {
if (argc <= 1) {
cerr << "missing parameter for option '" << *aktarg << "'\n";
return -1;
}
aktarg++;
argc--;
p = Str(*aktarg);
return 0;
}
void print_syntax(void) {
cerr << "clld [-v] [-o <target-filename>] [-static <*.slo-file>]\n"
" [-shared] <object-file(s)>... \n";
}
extern const char * version_tag;
bool verbose = false;
bool shared = false;
int main( int argc, const char ** argv ) {
Str dst_name = "a.out";
List<Str> lib_path;
List<InternalObjectFile> obs;
{
const char ** aktarg = argv;
if (argc) {
argc--;
aktarg++;
}
while (argc) {
Str arg(*aktarg);
if (arg.chr(0) != '-') {
if (read_objfile( obs, arg, false, false )) {
disp_error();
return 20;
}
} else if (arg.chr(1) == 'L') {
lib_path.add_tail(arg.after(1));
} else if (arg.chr(1) == 'l') {
Str suffix = "/lib" + arg.after(1) + ".slo";
ListItem<Str> *lib_dir = lib_path.get_head();
while(lib_dir) {
if (!read_objfile(obs, *lib_dir+suffix,
false, true)) {
break;
}
lib_dir = lib_dir->get_next();
}
if(!lib_dir) {
set_error("library not found: ",
arg.after(1));
disp_error();
return 21;
}
} else if (arg == "-o") {
if (next_arg (argc, aktarg, dst_name)) return 20;
} else if (arg == "-h" || arg == "-?") {
print_syntax();
return 0;
} else if (arg == "-v") {
verbose = true;
} else if (arg == "-shared") {
shared = true;
} else if (arg == "-static") {
Str on;
if (next_arg(argc, aktarg, on)) return 20;
if (read_objfile( obs, on, true, false )) {
disp_error();
return 20;
}
} else {
cerr << "unknown option '" << arg << "', type clld -h for help\n";
return 20;
}
aktarg++;
argc--;
}
}
if (verbose) {
cerr << "clld " << (version_tag+1) << "\nwritten by Lutz Vieweg 1994\n";
}
if (obs.length() == 0) {
cerr << "you need to specify at least one object file to link\n";
return 20;
}
if (shared) { // test whether all object files are ordinary ones
for (ListItem<InternalObjectFile> * p = obs.get_head();
p;
p = p->get_next()
) {
if (p->shared) {
cerr << "ERROR: shared libraries have to be built from static\n"
" object-files only, cannot link '" << p->name << "'\n";
return 20;
}
}
} else {
// we need to link at least the first object file, whether it's used or not
(obs.get_head())->used = true;
}
List<CodeImport> cimports;
List<InternalObjectFileP> shlibs;
if (resolve_references(obs, cimports, shlibs)) {
cerr << "linkage failed due to unresolved references\n";
return 20;
}
ObjectFile resobj; // this will become the resulting object file,
// which parts of it are written depends on
// whether we built an executable or a shared lib.
if (link_obs(obs, resobj)) {
disp_error();
return 20;
}
if (shared) {
// if we are building a shared library, write out the whole object
// file for (static or dynamic) linking at the host computer
resobj.shared = true;
Str shl_name = dst_name+".slo";
resobj.name = dst_name+".sl";
#ifdef OBSOLETE_LIBGXX
BOstream shl_stream;
shl_stream.open ( shl_name, ios::out | ios::bin | ios::trunc );
#else
ofstream shl_stream(shl_name, ios::out | ios::binary | ios::trunc );
#endif
if (shl_stream.fail()) {
cerr << "ERROR: unable to open file '"+shl_name+"' for writing\n";
return 20;
}
#ifndef OBSOLETE_LIBGXX
BOstream shl(shl_stream);
shl << resobj;
#else
shl_stream << resobj;
#endif
if (verbose) {
cerr << "wrote '"+shl_name+"', " << resobj.relocs.length() << " relocs, "
<< resobj.imports.length() << " imports, "
<< resobj.exports.size() << " exports.\n";
}
#ifdef OBSOLETE_LIBGXX
if (shl_stream.fail()) {
#else
if (shl.fail()) {
#endif
cerr << "ERROR: write to file '"+shl_name+"' failed\n";
return 20;
}
}
Str bin_name = dst_name;
if (shared) {
bin_name += ".sl";
}
{
NibStr rdl; // build relocation and dynamic linkage data
{ // build (non-internal) Reloc-list
NibStr rlist(5 + resobj.relocs.length()*5 + 5);
// actual relocation address = 0;
rlist.poke(0, (unsigned long)0 , 5);
unsigned long offset = 5;
for ( ListItem<Reloc> * aktrel = resobj.relocs.get_head();
aktrel;
aktrel = aktrel->get_next()
) {
rlist.poke(offset, aktrel->offset, 5);
offset += 5;
}
rlist.poke (offset, (unsigned long)0, 5);
rdl += rlist;
}
if (!shared) {
// add shared library binding informations to executable
{
unsigned long o;
// add base-address storage array
NibStr ba(5 + 5*shlibs.length() + 5);
for (o = 0; o < shlibs.length()+1; o++) {
ba.poke(o*5, 0xfffff, 5);
}
ba.poke(o*5, (unsigned long)0, 5);
rdl += ba;
}
{
// add code-import-list
NibStr cilist(cimports.length()*(2+5+5) + 2);
unsigned long offset = 0;
for ( ListItem<CodeImport> * aktcimp = cimports.get_head();
aktcimp;
aktcimp = aktcimp->get_next()
) {
cilist.poke(offset, (aktcimp->targetlib*5)+1, 2);
offset += 2;
cilist.poke(offset, aktcimp->liboff, 5);
offset += 5;
cilist.poke(offset, aktcimp->offset, 5);
offset += 5;
}
cilist.poke (offset, (unsigned long)0, 2);
rdl += cilist;
}
{
// add the reference list for imports in the shared libraries
NibStr rl;
for ( ListItem<InternalObjectFileP> * aktshlib = shlibs.get_head();
aktshlib;
aktshlib = aktshlib->get_next()
) {
for ( ListItem<Import> * aktimp = aktshlib->iofp->shimports.get_head();
aktimp;
aktimp = aktimp->get_next()
) {
rl += NibStr((aktimp->targetlib*5)+1,2);
rl += NibStr(aktimp->offset,5);
}
rl += NibStr((unsigned long)0,2);
}
rdl += rl;
}
} else {
// add import list to the binary of the shared library that
// is being built
NibStr ilist(resobj.imports.length()*5);
unsigned long offset = 0;
for ( ListItem<Import> * akti = resobj.imports.get_tail();
akti;
akti = akti->get_prev()
) {
ilist.poke(offset, akti->offset, 5);
offset += 5;
}
rdl += ilist;
}
// prepend size of relocation-list & shared lib binding data,
// and add rdl-data to resobj.body
resobj.body = NibStr(rdl.length()+5, 5) + rdl + resobj.body;
}
// prepend "magic word" for the dynamic linker,
// which allows it to identify LibraryObjects created
// by this linker. The last nibble of the "magic word"
// may be incremented for later revisions of the
// relocation/shared-library header.
resobj.body = NibStr((unsigned long)0x47430, 5) + resobj.body;
// capsulate resobj.body in a LibraryData Object (DOEXT0)
resobj.body = NibStr((unsigned long)0x02b88, 5) +
NibStr(resobj.body.length()+5 , 5) +
resobj.body;
if (!shared) {
// append call of dynamic linker (global name "GCCLDD")
resobj.body += NibStr((unsigned long)0x02e48, 5) +
NibStr((unsigned long)0x06 , 2) +
NibStr((unsigned long)0x4c434347, 8) +
NibStr((unsigned long)0x4444, 4);
}
{
// prepend the names of all used shared libraries
NibStr shn;
for ( ListItem<InternalObjectFileP> * aktshlib = shlibs.get_tail();
aktshlib;
aktshlib = aktshlib->get_prev()
) {
shn += NibStr((unsigned long)0x02e48, 5) +
NibStr((unsigned long)(aktshlib->iofp->name.length()), 2);
for (unsigned long i = 0; i < aktshlib->iofp->name.length(); i++) {
shn += NibStr((unsigned long)aktshlib->iofp->name.chr(i), 2);
}
}
resobj.body = shn + resobj.body;
}
// capsulate resobj.body in DOCOL and SEMI
resobj.body = NibStr((unsigned long)0x02d9d,5) +
resobj.body +
NibStr((unsigned long)0x0312b,5);
{ // prepend the "HPHP48-E" identifyer
NibStr ident(16);
ident.poke (0, (unsigned long)0x50485048 , 8);
ident.poke (8, (unsigned long)0x452d3834 , 8);
resobj.body = ident + resobj.body;
}
// write out the binary (of the executable or shared library)
#ifdef OBSOLETE_LIBGXX
BOstream dst_stream;
dst_stream.open (bin_name, ios::out | ios::bin | ios::trunc );
#else
ofstream dst_stream(bin_name, ios::out | ios::binary | ios::trunc );
#endif
if (dst_stream.fail()) {
cerr << "ERROR: unable to open file '"+bin_name+"' for writing\n";
return 20;
}
#ifndef OBSOLETE_LIBGXX
BOstream dst(dst_stream);
dst.write( (char *)resobj.body, (resobj.body.length()+1)>>1 );
if (dst.fail()) {
#else
dst_stream.write( (char *)resobj.body, (resobj.body.length()+1)>>1 );
if (dst_stream.fail()) {
#endif
cerr << "ERROR: write to file '"+bin_name+"' failed\n";
return 20;
}
if (verbose) {
cerr << "wrote '" << bin_name << "', " << resobj.body.length() << " nibbles, "
<< shlibs.length() << " shared libraries, " << resobj.relocs.length()
<< " relocs.\n";
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1