#!/usr/bin/env pike
// -*- Pike -*-

// Partial linker for COFF files, written by Fredrik Hubinette 2000 - 2001
// 
// Things left to do:
//   Support long-format import libaries
//   Merge sections (if given -O ?)
//   Support line number information
//   Test if resulting files work with cl.exe
//   Support 64 bit architechtures
//   Rename sections
//   Support section symbols (used by pthread.lib)

//#define DEBUG

class Reloc
{
  Symbol sym;
  int loc;
  int type;
};

class Section
{
  string name;
  int|string data;
  array(Reloc) relocs;

  int virtual_size;
  int characteristics;

  /* internal stuff */
  array(Symbol) symbols_in_this_section=({});
  string file;
};

class Symbol
{
  string name;
  int value;
  Section section;
  int type;
  int cls;
  array(Symbol) aux;

  /* We might want to patch the name of the antisymbol somehow to
   * make sure symbols are never defined more than once
   * As long as the modification is zapped in the demangle process
   * it should be fine...
   */
  Symbol antisymbol()
    {
      Symbol s=Symbol();
      s->name=name+"@__pntld_internal__";
      s->value=value;
      s->section=section;
      s->type=(type & 0xf) | ( (type>>4)==2 ? 0 : (2<<4) );
      s->cls=cls;
      s->aux=aux;
      return s;
    }

  int defined()
    {
      return !!(section || value);
    }

  int shareable()
    {
      if(!defined()) return 1;
      if(sscanf(name,"__real@%*s") || name == "__NULL_IMPORT_DESCRIPTOR")
	return 1;
      if(!section && value) return 1; /* common symbols */
      return 0;
    }
};


mapping(string:Symbol) global_symbols=([]);
array(Section) global_sections=({});
array(string) global_directives=({});
array(string) libpath=({});
array(string) libraries=({});

int err;
int output_machine;


#define CHR_LINK_INFO 0x200
#define CHR_LINK_REMOVE 0x800
#define CHR_LINK_ALIGN_1 0x100000
#define CHR_LINK_MEM_DISCARDABLE 0x2000000


#define MACHINE_I386 0x14c
#define MACHINE_IA64 0x200


class Linker
{
  string stringtable="";

  mapping(string:int) num_secnames=([]);
  mapping(Symbol:int) symbol_to_number=([]);
  mapping(Section:int) section_to_number=([]);
  mapping(string:int) directive_to_number=([]);

  array get_ordered(mapping m)
    {
      array i=indices(m);
      array v=values(m);
      sort(v,i);
      return i;
    }


  mapping(string:int) stringtablecache=([]);
  int add_string(string s)
    {
      int pos;
//      werror("Adding string: %O\n",s);
      s+="\0";
      if(pos=stringtablecache[s]) return pos-1;
      pos=search(stringtable, s);
      if(pos == -1)
      {
	pos=strlen(stringtable);
	stringtable+=s;
      }
      stringtablecache[s]=pos-1;
      return pos;
    }

  int add_directive(string d)
    {
      if(directive_to_number[d])
	return directive_to_number[d]-1;
#ifdef DEBUG
      werror("Adding directive: %O\n",d);
#endif

      int num=sizeof(directive_to_number);
      directive_to_number[d]=num+1;
      return num;
    }

  string add_sym_name(string s)
    {
      if(strlen(s) <= 8)
	return s + "\0"*(8-strlen(s));
      return sprintf("%-4c%-4c",0,add_string(s));
    }

  int add_symbol(Symbol s)
    {
      if(search(s->name,"@__pntld_internal__")>=0)
	return -1;
      if(symbol_to_number[s])
      {
#ifdef DEBUG
	werror("Adding symbol [%d]: %s @ %O:%O again\n",
	       symbol_to_number[s]-1,s->name, 
	       s->section ? s->section->name : "",
	       s->section ? s->section->file : "",
	       );
#endif
	return symbol_to_number[s]-1;
      }

      int num=sizeof(symbol_to_number);
#ifdef DEBUG
      werror("Adding symbol [%d]: %s @ %O:%O\n",num,s->name,
	     s->section ? s->section->name : "external",
	     s->section ? s->section->file : "" );
#endif

      symbol_to_number[s]=num+1;
      if(s->section) add_section(s->section);
      return num;
    }

  int add_section(Section s)
    {
      if(section_to_number[s])
	return section_to_number[s]-1;

      string name=s->name;
      sscanf(name,"%s$",name);

      if(name == ".drectve")
      {
#ifdef DEBUG
	werror("Adding directives: %O\n",s->data);
#endif
	map(s->data/" ",add_directive);
	return -1;
      }

#if 0
      if(name == ".idata")
      { 
        string name= ( s->file /"/" )[-1];
        add_directive("-LIB:"+name);
        /* import DLL instead of section */
        return -1; /* undefined */
      }
#endif

      int num=sizeof(section_to_number);
#ifdef DEBUG
      werror("Adding section [%d]: %s\n",num,s->name);
#endif

      section_to_number[s]=num+1;


      if(s->relocs)
	foreach(s->relocs, Reloc r)
	  if(r->sym)
	    add_symbol(r->sym);

      foreach(s->symbols_in_this_section, Symbol sym)
	add_symbol(sym);

      return num;
    }

  int virtual_data_size(int|string s)
    {
      return intp(s) ? s : strlen(s);
    }

  int file_data_size(int|string s)
    {
      return intp(s) ? 0 : strlen(s);
    }

  string out()
    {

      if(sizeof(directive_to_number))
      {
	Section s=Section();
	s->name=".drectve";
	s->data=get_ordered(directive_to_number) * " ";
	s->characteristics = CHR_LINK_INFO | CHR_LINK_REMOVE | CHR_LINK_ALIGN_1;
	int num=sizeof(section_to_number);
	section_to_number[s]=num+1;
	add_section(s);
      }

      string symboltable="";
      string sectiondata="";
      string sectiontable="";

      int secnum_save=sizeof(section_to_number);

      /* Actually output data */
      int base=20 /*coff*/ + sizeof(section_to_number) * 40;

      foreach(get_ordered(section_to_number), Section s)
	{
#ifdef DEBUG
	  werror("Encoding section [%d]: %s\n",add_section(s),s->name);
#endif
	  sectiontable+=
	    sprintf("%s%-4c%-4c%-4c%-4c%-4c%-4c%-2c%-2c%-4c",
		    add_sym_name(s->name),
		    s->virtual_size,
		    0, /* virtual address */
		    virtual_data_size(s->data),
		    stringp(s->data) ? (base + strlen(sectiondata)) : 0,
		    s->relocs && (base + strlen(sectiondata) + file_data_size(s->data)),
		    0, /* no linenums yet */
		    s->relocs && sizeof(s->relocs),
		    0,
		    s->characteristics);

	  if(stringp(s->data))
	    sectiondata+=s->data;

	  if(s->relocs)
	    foreach(s->relocs, Reloc r)
	      sectiondata+=sprintf("%-4c%-4c%-2c",
				   r->loc,
				   r->sym && add_symbol(r->sym),
				   r->type);
	}

      foreach(get_ordered(symbol_to_number), Symbol s)
	{
	  symboltable+=sprintf("%s%-4c%-2c%-2c%c%c",
			       add_sym_name(s->name),
			       s->value,
			       s->section ? add_section(s->section) +1 : 0,
			       s->type,
			       s->cls,
			       0); /* aux symbols not supported */
	}



      if( secnum_save != sizeof(section_to_number))
      {
	werror("Sections appeared too late!\n");
	exit(1);
      }

      if( strlen(sectiontable) != sizeof(section_to_number) * 40)
      {
	werror("Complete failure %d != %d!\n",
	       strlen(sectiontable),
	       sizeof(section_to_number) * 40);
	exit(1);
      }

      string ret=
	sprintf("%-2c%-2c%-4c%-4c%-4c%-2c%-2c" /* coff */
		"%s%s%s%s" /* the rest */ , 
		
		/* coff */
		output_machine,
		sizeof(section_to_number),
		time(),
		base+strlen(sectiondata),
		sizeof(symbol_to_number),
		0, /* opt header size */
		0, /* no character */

		sectiontable,
		sectiondata,
		symboltable,
		stringtable);

      if(ret [ base + strlen(sectiondata)..
	     base+strlen(sectiondata)+strlen(symboltable) -1 ] != symboltable)
      {
	werror("Symbol table offset is wrong.\n");
	exit(1);
      }

      return ret;
    }
  
}


mapping machine_table = ([
  0:"Unknown",
  MACHINE_I386:"Intel x86",
  MACHINE_IA64:"Intel IA64",
  ]);

class Bitfield
{
  array(string) names;

  string desc(int num)
  {
    int x;
    array(string) ret=({});
    while(num && x<sizeof(names))
    {
      if(1 & num)
	ret+=({ names[x] });
      x++;
      num>>=1;
      num&=0x7fffffff;
    }
    if(!sizeof(ret) || num)
      ret+=({ sprintf("%x",num << x) });

    return ret*" | ";
  }
  void create(string ... n)
    {
      names=n;
    }
}

Bitfield character_descriptor=Bitfield(
  "RELOCS_STRIPPED",
  "EXECUTABLE_IMAGE",
  "LINE_NUMS_STRIPPED",
  "LOCAL_SYMS_STRIPPED",
  "AGGRESSIVE_WS_TRIM",
  "LARGE_ADDRESS_AWARE",
  "16BIT_MACHINE",
  "BYTES_REVERSED_LO",
  "32BIT_MACHINE",
  "DEBUG_STRIPPED",
  "REMOVABLE_RUN_FROM_SWAP",
  "FILE_SYSTEM",
  "DLL",
  "UP_SYSTE_MONLY",
  "BYTES_REVERSED_HI" );

Bitfield section_character_descriptor=Bitfield(
  "DSECT",
  "NOLOAD",
  "GROUP",
  "TYPE_NO_PAD",
  "*COPY",
  "*CODE",
  "INITED_DATA",
  "UNINITED_DATA",
  "LNK_OTHER",
  "LNK_INFO",
  "TYPE_OVER",
  "LNK_REMOVE",
  "LNK_COMDAT",
  "-",
  "-",
  "MEM_FARDATA",
  "*MEM_PURGEABLE",
  "*MEM_16BIT",
  "*MEM_LOCKED",
  "*MEM_PRELOAD",
  "ALIGN_1",
  "ALIGN_2",
  "ALIGN_4",
  "ALIGN_8",
  "LNK_NRELOC_OVFL",
  "MEM_DISCARDABLE",
  "MEM_NOT_CACHED",
  "MEM_NOT_PAGED",
  "MEM_SHARED",
  "MEM_EXECUTE",
  "MEM_READ",
  "MEM_WRITE",
);

class dumpfile
{
  string filename;
  string data;
  int base;
  int machine;

  int stringtable;
  int symboltable, numsymbols;
  int sections, numsections;

  array(Symbol) file_symbols;
  array(Section) file_sections;

  void hexdump(int pos)
    {
      string x=data[pos..pos+128];
      foreach(x/8,string line)
	{
	  write("%06x: %{%02x %} %O\n",pos,values(line),line);
	  pos+=strlen(x);
	}
      string line=x%8;
      write("%06x: %{%02x %} %s %O\n",
	    pos,
	    values(line),
	    "   "*(8-strlen(line)),
	    line);
    }

  int i4(int pos)
    {
      pos+=base;
      return data[pos] | (data[pos+1]<<8) | (data[pos+2]<<16) | (data[pos+3]<<24);
    }

  int i8(int pos)
    {
      pos+=base;
      return data[pos] | (data[pos+1]<<8) | (data[pos+2]<<16) | (data[pos+3]<<24) |
	(data[pos+4]<<32) | (data[pos+5]<<40) | (data[pos+6]<<48) | (data[pos+7]<<56);
    
    }

  int i1(int pos)
    {
      pos+=base;
      return data[pos];
    }

  int i2(int pos)
    {
      pos+=base;
      return data[pos] | (data[pos+1]<<8);
    }

  string nulltermstring(int p)
    {
      p+=base;
      int end=search(data,"\0",p);
      return data[p..end-1];
    }

  string range(int from, int len)
    {
      from+=base;
      return data[from..from+len-1];
    }

  string getCOFFstring(int pos)
    {
      if(!i4(pos)) 
      {
	return nulltermstring(stringtable + i4(pos+4));
      }
      return ( range(pos,8)/"\0" )[0];
    }

  string decodeRelocType(int t)
    {
      switch(machine)
      {
        case MACHINE_I386:
	  switch(t)
	  {
	    case 0: return "absolute";
	    case 1: return "dir16";
	    case 2: return "rel16";
	    case 6: return "dir32";
	    case 7: return "dir32nb";
	    case 9: return "seg12";
	    case 10: return "sect";
	    case 11: return "secrel";
	    case 20: return "rel32";
	  }
	  break;
        case MACHINE_IA64:
	  switch(t)
	  {
	    case 0: return "absolute";
	    case 1: return "imm14";
	    case 2: return "imm22";
	    case 3: return "imm64";
	    case 4: return "dir32";
	    case 5: return "dir64";
	    case 6: return "pcrel21b";
	    case 7: return "pcrel21m";
	    case 8: return "pcrel21f";
	    case 9: return "gprel22";
	    case 10: return "ltoff22";
	    case 11: return "sect";
	    case 12: return "secrel22";
	    case 13: return "secrel64i";
	    case 14: return "secrel32";
	    case 15: return "ltoff64";
	    case 16: return "dir32nb";
	    case 31: return "addend";
	  }
	  break;
      }
      return sprintf("<%d>",t);
    }


  array(Reloc) dumpRelocs(int pos, int num_relocs)
    {
      array(Reloc) ret=({});
      for(int e=0;e<num_relocs;e++)
      {
#ifdef DEBUG
	write("  [ %08x = symbol(%5d, %8s) = %s]\n",
	      i4(pos),
	      i4(pos+4),
	      decodeRelocType(i2(pos+8)),
	      (machine == MACHINE_IA64 && i2(pos+2)==31 ?
	       "N/A" : getsymbolname(i4(pos+4))));
#endif
	Reloc r=Reloc();
	r->loc=i4(pos);
	r->type=i2(pos+8);

	if(machine != MACHINE_IA64 || r->type != 31) {
	  r->sym=file_symbols[i4(pos+4)];
	  if (!r->sym) {
	    write("  Failed to find symbol %O for relocation.\n",
		  getsymbolname(i4(pos+4)));
	  }
	}

	pos+=10;

	ret+=({r});
      }

      return ret;
    }

  int translatevaddr(int addr)
    {
      int pos=sections;
      for(int e=0;e<numsections;e++)
      {
	int start=i4(pos+12);
	if(addr >= start && addr < start + i4(pos+8))
	{
//	  write("--start=%d-%d-sect=%d--\n",start,i4(pos+20),e);
	  return addr - start + i4(pos+20);
	}
	pos+=40;
      }
      return -1;
    }

  void dumpSECTS()
    {
      int pos=sections;
#ifdef DEBUG
      write("\n-=SECTIONS=-\n");
#endif
      for(int e=0;e<numsections;e++)
      {
	string caracter;
#ifdef DEBUG
	write("\n");
	write("Name               : %s\n",getCOFFstring(pos));
	write("Virtual Size       : %x\n",i4(pos+8));
	write("Virtual Addres     : %x\n",i4(pos+12));
	write("RAW data size      : %x\n",i4(pos+16));
	write("Ptr2RawData        : %x\n",i4(pos+20));
	write("Ptr2Relocs         : %x\n",i4(pos+24));
	write("Ptr2Linenums       : %x\n",i4(pos+28));
	write("num relocs         : %x\n",i2(pos+32));
	write("num linenums       : %x\n",i2(pos+34));
	write("characteristics    : %s\n",
	      caracter=section_character_descriptor->desc(i4(pos+36)));
#endif
	
	file_sections[e]->name=getCOFFstring(pos);
	file_sections[e]->file=filename;

	if(i4(pos+20))
	{
	  file_sections[e]->data=range(i4(pos+20),i4(pos+16));
	}else{
	  file_sections[e]->data=i4(pos+16);
	}

	file_sections[e]->virtual_size=i4(pos+8);;
	file_sections[e]->characteristics=i4(pos+36);

	if(i4(pos+24))
	  file_sections[e]->relocs=dumpRelocs(i4(pos+24), i2(pos+32));


	if(file_sections[e]->characteristics & CHR_LINK_INFO)
	{
	  foreach(file_sections[e]->data/" ", string directive)
	    if(search(global_directives, directive) == -1)
	      global_directives+=({ directive });
	  
	}
	else
	{
	  global_sections+=({ file_sections[e] });
	}
#ifdef DEBUG
	if(search(caracter,"LNK_INFO")!=-1)
	  write("%O\n",range(i4(pos+20),i4(pos+16)));
#endif
	pos+=40;
      }
    }

  string getvirtualname(int vaddr)
    {
      int x=translatevaddr(vaddr);
      if(x==-1) return sprintf("<out of bounds %x>",vaddr);
      return nulltermstring(x);
    }

  /* Symbol table stuff */
  string decodeCOFFtypeLSB(int t)
    {
      switch(t)
      {
	case 0: return "null";
	case 1: return "void";
	case 2: return "char";
	case 3: return "short";
	case 4: return "int";
	case 5: return "long";
	case 6: return "float";
	case 7: return "double";
	case 8: return "struct";
	case 9: return "union";
	case 10: return "enum";
	case 11: return "MOE";
	case 12: return "byte";
	case 13: return "word";
	case 14: return "uint";
	case 15: return "dword";
	default: return sprintf("<%x>",t);
      }
    }

  string decodeCOFFtype(int t)
    {
      switch(t >> 4)
      {
	case 0: return decodeCOFFtypeLSB(t);
	case 1: return decodeCOFFtypeLSB(t & 15) + "*";
	case 2: return decodeCOFFtypeLSB(t & 15) + "()";
	case 3: return decodeCOFFtypeLSB(t & 15) + "[]";
	default: return sprintf("<%x>",t);
      }
    }

  string decodeCOFFstorageclass(int t)
    {
      switch(t)
      {
	case -1: case 255: return "End of function";
	case 0: return "null";
	case 1: return "automatic";
	case 2: return "external";
	case 3: return "static";
	case 4: return "register";
	case 5: return "external def";
	case 6: return "label";
	case 7: return "undef label";
	case 8: return "member of struct";
	case 9: return "argument";
	case 10: return "struct tag";
	case 11: return "member of union";
	case 12: return "union tag";
	case 13: return "type def";
	case 14: return "undef static";
	case 15: return "enum tag";
	case 16: return "member of enum";
	case 17: return "register param";
	case 18: return "bit field";
	case 100: return "block";
	case 101: return "function";
	case 102: return "end of struct";
	case 103: return "file";
	case 104: return "section";
	case 105: return "weak external";
	default: return sprintf("<%x>",t);
      }
    }

  string getsymbolname(int x)
    {
      if(x >= numsymbols) return sprintf("Out of bound (%d)",x);
      return getCOFFstring(symboltable + x * 18);
    }


  Symbol export_symbol(Symbol s)
    {
      Symbol ret;
      string tmpname=s->name;
//      if(s->name == "___gmpz_clear")
//	werror("Exporting... ___gmpz_clear: %O\n",s->section);
      
      if(ret = global_symbols[tmpname])
      {
	if(ret->defined() && s->defined())
	{
	  if(!(ret->shareable() && s->shareable()))
	  {
	    if(search(ret->name,"@__pntld_internal__")==-1)
	    {
	      werror("%s: Warning: Symbol %s redefined.\n",
		     filename,
		     s->name);
	    }
	    ret=s;
	  }
	}
	
#if 0
	if(s->defined())
	{
	  if( (ret->type >> 4) != ( s->type>>4) )
	  {
	    werror("Warning: same symbol, different levels of indirection!!!!!!\n");
	    err++;
	  }
	}
#endif

	if(s->defined())
	{
//	  if(s->name == "___gmpz_clear")
//	    werror("Overriding %d\n",s==ret);

	  ret->section = s->section;
	  ret->value = s->value;
	  ret->type = s->type;
	}
      }else{
	ret=s;
      }
      
      global_symbols[tmpname]=ret;

      return ret;
    }

  void dumpSymTable()
    {
      array ret=allocate(numsymbols);
      int pos=symboltable;
      for(int e=0;e<numsymbols;e++)
      {
	int aux;
#ifdef DEBUG
#if 1
	write("[%5d] %08x %4x  %-9s %-9s %x  %s\n",
	      e,
	      i4(pos+8),
	      i2(pos+12),
	      decodeCOFFtype(i2(pos+14)),
	      decodeCOFFstorageclass(i1(pos+16)),
	      aux=i1(pos+17),
	      getCOFFstring(pos));
	      
#else
	write("[%5d] name          : %s\n",e,getCOFFstring(pos));
	write("[%5d] value         : %x\n",e,i4(pos+8));
	write("[%5d] secnum        : %d\n",e,i2(pos+12));
	write("[%5d] type          : %s\n",e,decodeCOFFtype(i2(pos+14)));
	write("[%5d] Storage class : %s\n",e,decodeCOFFstorageclass(i1(pos+16)));
	write("[%5d] Num Aux syms  : %x\n",e,aux=i1(pos+17));
#endif
#endif
	switch(i1(pos+16))
	{
	  case 103: /* file */
	    break;

	  default:

	    string name=getCOFFstring(pos);
	    int cls=i1(pos+16);
	    int sect=i2(pos+12);
	    int value=i4(pos+8);
	    int type=i2(pos+14);
	    if(sect > 32768) sect=65536-sect;
	    
	    Symbol s=Symbol();
	    s->name=name;
	    s->value=value;
//	werror("SECT: %O %O\n",sect,file_sections);
	    if(sect > 0)
	      s->section=file_sections[sect-1];
	    s->type=type;
	    s->cls=cls;
	    s->aux=0;
	    
#define COFFSYM_external 2
	    
	    switch(cls)
	    {
	      case COFFSYM_external:
		if(s->section) export_symbol(s->antisymbol());
		s=export_symbol(s);
	    }
	    file_symbols[e]=s;
	    
	}
	aux=i1(pos+17);
	
	pos+=18;
	for(int a=0;a<aux;a++)
	{
	  pos+=18;
	  e++;
	}
      }
    }

  void dumpCOFF(int pos)
    {
#ifdef DEBUG
      write("-=COFF HEADER=-\n");
#endif
      sscanf(reverse(range(pos,20)),
	     "%2c%2c%4c%4c%4c%2c%2c",
	     int characteristics,
	     int sizeofoptheader,
	     numsymbols,
	     symboltable,
	     int timestamp,
	     numsections,
	     machine);

#ifdef DEBUG
      write("Machine     : %s\n",machine_table[machine] || sprintf("%x",machine));
      write("Sections    : %x\n",numsections);
      write("Timesamp    : %s",ctime(timestamp));
      write("Symbol Table: %x\n",symboltable);
      write("Symbols     : %d\n",numsymbols);
      write("Optheadsize : %x\n",sizeofoptheader);
      write("Character   : %s\n",character_descriptor->desc(characteristics));
#endif

      if(!output_machine) output_machine = machine;

      stringtable = symboltable + numsymbols*18;
      sections=pos+20+sizeofoptheader;

      file_symbols=allocate(numsymbols);
      file_sections=allocate(numsections,Section)();

      dumpSymTable();
      dumpSECTS();
    }

  int dumpPE(int pos)
    {
#ifdef DEBUG
      write("Potential PE identifier at 0x%x ... ",pos);
#endif
      if(range(pos,4)!="PE\0\0")
      {
#ifdef DEBUG
	write("No.\n");
#endif
	return 0;
      } else {
#ifdef DEBUG
	write("Yes.\n");
#endif
      }
      dumpCOFF(pos+4);
      return 1;
    }

  void dumpArchive(int pos)
    {
      string name;
      int num;
      int longnames;
      while(pos < strlen(data))
      {
	num++;
	int size = (int) range(pos+48,10);
	name=range(pos,16);
	if(name[0]=='/')
	{
	  if(num == 3)
	  {
	    longnames=pos+60;
	  }
	  else if(longnames && sscanf(name,"/%d",int longnamenum))
	  {
	    name=nulltermstring(longnames + longnamenum);
	  }
	}else{
	  if(sscanf(reverse(name),"%*s/%s",name))
	    name=reverse(name);
	}
#ifdef DEBUG
	write("\n");
	write("# name: %s\n",name);
	write("# date: %s",ctime( (int) range(pos+16,12) ) );
	write("# uid : %s\n",range(pos+28,6));
	write("# gid : %s\n",range(pos+34,6));
	write("# mode: %s\n",range(pos+40,8));
	write("# size: %s\n",range(pos+48,10));
#endif
	if(name[0]!='/')
	{
	  dumpfile(data, pos+60, 0, name);
	}
	pos+=60;
	pos+=(size+1)&~1;
      }
    }

  void dumpImportLib()
    {
      string name= ( filename/"/" )[-1];
      sscanf(range(0,20),
	     "%-2c%-2c%-2c%-2c%-4c%-4c%-2c%-2c",
	     int sig1,
	     int sig2,
	     int version,
	     int machine,
	     int timestamp,
	     int size_of_data,
	     int ordinal,
	     int type);
#if 0
      write("Sig1      : %x\n",sig1);
      write("Sig2      : %x\n",sig2);
      write("version   : %x\n",version);
      write("Machine   : %x\n", machine);
      write("Time      : %s",ctime(timestamp));
      write("Ordinal   : %x\n",ordinal);
      write("Type:     : %x\n",type);
      write("Name      : %s\n",nulltermstring(20));

      write("Importing library: %O\n",name);
#endif

#if 0
      global_directives|=({ "-LIB:"+name });
#else
      Section s=Section();
#if 0
      s->name=".idata";
      s->file=filename;
#else
      s->name=".drectve";
//      werror("Creating Directive -LIB:%s\n",name);
      s->data = "-LIB:"+name;
      s->file="autogenerated";
#endif
      string symname=nulltermstring(20);
#ifdef DEBUG
      werror("Importing symbol: %O\n",symname);
#endif

      Symbol sym=global_symbols[symname];
      if(!sym)
	global_symbols[symname]=sym=Symbol();

      sym->name=symname;
      sym->section=s;
      sym->type=((type&3)? 0:0x20); /* correct ???? */
      sym->cls = COFFSYM_external;


      symname="__imp_"+symname;
#ifdef DEBUG
      werror("Importing symbol: %O\n",symname);
#endif

      sym=global_symbols[symname];
      if(!sym)
	global_symbols[symname]=sym=Symbol();

      sym->name=symname;
      sym->section=s;
      sym->type=0;
      sym->cls = COFFSYM_external;

#endif
    }

  void create(string d, void|int p, void|int len, void|string f)
    {
      base=p;
      data=d;
      filename=f;
      if(!len) len=strlen(d)-base;

#ifdef DEBUG
      write("Dumping: Data[%d] Base:0x%08x Len:0x%08x Filename:%O\n",
	    sizeof(d), p, len, f);
#endif /* DEBUG */

      if(range(0,8) == "!<arch>\n")
      {
#ifdef DEBUG
	write("Archive!\n");
#endif
	dumpArchive(8);
      }else{
	if(i2(0) == 0 && i2(2) == 0xffff)
	{
          dumpImportLib();
	}else{
	  dumpCOFF(0);
	}
      }
    }
}

string getfile_caseindependent(string path, string soughtfile)
{
  array files;
  files = get_dir(path);
  if(!files)
    return 0;

  foreach(files, string file)
    if(lower_case(file) == lower_case(soughtfile))
      return combine_path(path, file);

  return 0;
}

int main(int argc, array(string) argv)
{
  int export_all;
  int strip=0;
  string output="a.out";
//  werror("%O\n",argv);
  werror("Pike Win32 partial linker.\n"
	 "$Id: pntld,v 1.24 2008/07/14 14:03:26 mast Exp $\n"
	 "Written by Fredrik Hubinette 2000 - 2001\n");

  foreach(Getopt.find_all_options(argv,aggregate(
    ({"output",Getopt.HAS_ARG,({"-o"})}),
    ({"R",Getopt.HAS_ARG,({"-R"})}),
    ({"L",Getopt.HAS_ARG,({"-L"})}),
    ({"l",Getopt.HAS_ARG,({"-l"})}),
    ({"S",Getopt.NO_ARG,({"-S"})}),
    ({"a",Getopt.NO_ARG,({"-a"})}),
    ({"ignore","Getopt.HAS_ARG",({"-r","-i","-s","-g","-B","-W"})}),
    )),array opt)
    {
      switch(opt[0])
      {
	case "S": strip++; break;
	  
	case "L":
	  libpath|=({opt[1]});
	  break;

	case "R":
	  global_directives|=({ "-?rpath:"+opt[1] });
	  break;

	case "l":
	  switch(opt[1])
	  {
	    case "c": case "m": break;
	    default:
	      libraries|=({opt[1]});
	  }
	  break;

	case "a":
	  export_all++;
	  break;

	case "output":
	  output=opt[1];
	  break;
      }
    }

  if(getenv("NTLD_LIBRARY_PATH"))
    libpath|=getenv("NTLD_LIBRARY_PATH")/":";

#ifdef DEBUG
  werror("libpath: %O\n", libpath);
#endif

  if(getenv("NTLD_RUN_PATH"))
    foreach(getenv("NTLD_RUN_PATH")/":",string p)
      global_directives|=({"-?rpath:"+p});

  libpath|=({"."});

  foreach(libraries, string lib)
  {
    int found;
    foreach(libpath, string path)
    {
      string filename=getfile_caseindependent(path,lib+".lib");
      
      if(!filename)
        continue;
      
      if(string file=Stdio.read_file(filename))
      {
        dumpfile(file, 0, 0, filename);
        found=1;
        break;
      } else {
	werror("WARNING: Failed to read library file: %O (errno:%d).\n",
	       filename, errno());
      }
    }
    if(!found)
    {
      werror("Failed to find library: %s\n",lib);
      err++;
    }
  }

  argv=Getopt.get_args(argv);

  if(sizeof(argv)>1)
    output_machine = 0;

  foreach(argv[1..],string file)
  {
#if 0
    werror("file: "+file+"\n");
    werror("cwd:  "+getcwd()+"\n");
    werror("stat: %O\n",file_stat( file ) );
#endif
    string data = Stdio.read_file( file );
    dumpfile(data,0,0,file+":"+file);
  }

  if(strip)
  {
    global_sections=Array.filter(global_sections,
				 lambda(Section s)
      {
	return !(s->characteristics  & CHR_LINK_MEM_DISCARDABLE);
      });
  }

  if(err) exit(err);

  /* perform fixups */

  foreach(values(global_symbols), Symbol s)
    if(s->section)
     s->section->symbols_in_this_section+=({s});

  if(!export_all)
  {
    foreach(global_sections, Section s)
      {
	if(sscanf(s->name,".idata%*s"))
	{
	  string name= ( s->file /"/" )[-1];
	  Section s2=Section();
	  s2->name=".drectve";
	  s2->data = "-LIB:"+name;
	  foreach(s->symbols_in_this_section,  Symbol sym)
	    {
//	      werror("Fixing (%s) %s : %s\n",name,s->name,sym->name);
	      sym->section=s2;
	      sym->value=0;
	      if((sym->type >> 4) ==  2)
	      {
		if(search(sym->name,"@__pntld_internal__")==-1)
		{
		  werror("Warning: Symbol %s is double indirected!\n",sym->name);
		}
	      }
	      sym->type=2<<4;
	    }
	}
      }
  }


  if(err) exit(err);

  Linker l=Linker();

  map(global_directives, l->add_directive);

  werror("Creating %s\n",output);
  if(export_all)
  {
    map(global_sections, l->add_section);
    map(values(global_symbols), l->add_symbol);
  }else{
    foreach(global_directives, string dir)
      {
	if(2==sscanf(lower_case(dir),"%*[-/]export:%*s"))
	{
	  string sym=dir[8..];
	  int exported;
//	  werror("Exporting symbol: %O\n",sym);
	  if(global_symbols[sym])
	  {
	    l->add_symbol(global_symbols[sym]);
	    exported++;
	  }

	  if(!exported)
	  {
	    werror("Warning: Failed to find export symbol %s\n",sym);
	  }
	}
      }
  }

  if(err) exit(err);
  rm(output);
  Stdio.write_file(output, l->out());
  exit(err);
}
