瀏覽代碼

internal linker support for creating novell netware loadable modules

git-svn-id: trunk@17192 -
armin 14 年之前
父節點
當前提交
7eace122dc
共有 8 個文件被更改,包括 1886 次插入8 次删除
  1. 1 0
      .gitattributes
  2. 1 1
      compiler/aasmbase.pas
  3. 3 0
      compiler/link.pas
  4. 7 1
      compiler/ogbase.pas
  5. 1484 0
      compiler/ognlm.pas
  6. 1 0
      compiler/systems.pas
  7. 5 5
      compiler/systems/i_nwm.pas
  8. 384 1
      compiler/systems/t_nwm.pas

+ 1 - 0
.gitattributes

@@ -336,6 +336,7 @@ compiler/ogelf.pas svneol=native#text/plain
 compiler/oglx.pas svneol=native#text/plain
 compiler/ogmacho.pas svneol=native#text/plain
 compiler/ogmap.pas svneol=native#text/plain
+compiler/ognlm.pas svneol=native#text/plain
 compiler/optbase.pas svneol=native#text/plain
 compiler/optcse.pas svneol=native#text/plain
 compiler/optdead.pas svneol=native#text/plain

+ 1 - 1
compiler/aasmbase.pas

@@ -40,7 +40,7 @@ interface
        TAsmsymbind=(
          AB_NONE,AB_EXTERNAL,AB_COMMON,AB_LOCAL,AB_GLOBAL,AB_WEAK_EXTERNAL,
          { global in the current program/library, but not visible outside it }
-         AB_PRIVATE_EXTERN,AB_LAZY);
+         AB_PRIVATE_EXTERN,AB_LAZY,AB_IMPORT);
 
        TAsmsymtype=(
          AT_NONE,AT_FUNCTION,AT_DATA,AT_SECTION,AT_LABEL,

+ 3 - 0
compiler/link.pas

@@ -1051,6 +1051,9 @@ Implementation
         if ErrorCount>0 then
           goto myexit;
 
+        { parse linker options specific for output format }
+        exeoutput.ParseScript (linkscript);
+
         { Create .exe sections and add .o sections }
         ParseScript_Order;
         exeoutput.RemoveUnreferencedSections;

+ 7 - 1
compiler/ogbase.pas

@@ -482,6 +482,7 @@ interface
         procedure GenerateLibraryImports(ImportLibraryList:TFPHashObjectList);virtual;
         procedure GenerateDebugLink(const dbgname:string;dbgcrc:cardinal);
         function WriteExeFile(const fn:string):boolean;
+        procedure ParseScript (linkscript:TCmdStrList); virtual;
         property Writer:TObjectWriter read FWriter;
         property ExeSectionList:TFPHashObjectList read FExeSectionList;
         property ObjDataList:TFPObjectList read FObjDataList;
@@ -544,7 +545,7 @@ implementation
 
     procedure TObjSymbol.SetAddress(apass:byte;aobjsec:TObjSection;abind:TAsmsymbind;atyp:Tasmsymtype);
       begin
-        if not(abind in [AB_GLOBAL,AB_LOCAL,AB_COMMON]) then
+        if not(abind in [AB_GLOBAL,AB_LOCAL,AB_COMMON,AB_IMPORT]) then
           internalerror(200603016);
         if not assigned(aobjsec) then
           internalerror(200603017);
@@ -1549,6 +1550,11 @@ implementation
       end;
 
 
+    procedure TExeOutput.ParseScript (linkscript:TCmdStrList);
+      begin
+      end;
+
+
     function  TExeOutput.FindExeSection(const aname:string):TExeSection;
       begin
         result:=TExeSection(ExeSectionList.Find(aname));

+ 1484 - 0
compiler/ognlm.pas

@@ -0,0 +1,1484 @@
+{
+    Copyright (c) 1998-2006 by Peter Vreman
+    Copyright (c) 2011 by Armin Diehl
+
+    Contains the binary netware nlm executable writer
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ****************************************************************************
+}
+unit ognlm;
+
+{$i fpcdefs.inc}
+
+interface
+
+    uses
+       { common }
+       cclasses,globtype,
+       { target }
+       systems,
+       aasmbase,assemble,link,
+       { output }
+       ogbase,
+       owbase,
+       ogcoff;
+
+{*****************************************************************************
+                    NLM File structures and constants
+*****************************************************************************}
+
+{
+
+LString0 -> 1 byte Length, Text, #0
+LString  -> 1 byte length, Text
+
+Basic NLM File Structure:
+
+FixedHeader
+  nlm32_i386_external_fixed_header     130 bytes
+
+VarHdr1
+  NLM Description: LString0              2+n bytes
+  Stacksize                              4 bytes
+  reserved = 0                           4 bytes
+  ' LONG'                                5 bytes
+  NLM screen name: LString0              2+n bytes
+  NLM thread name: LString0              2+n bytes
+
+Optional Headers beginning with stamp (without '')
+'VeRsIoN#':   nlm32_i386_external_version_header     32 bytes
+'CoPyRiGhT=': LString0                              2+n bytes
+'MeSsAgEs':   nlm32_i386_external_extended_header   124 bytes
+'CuStHeAd':   nlm32_i386_external_custom_header
+'CyGnUsEx':   nlm32_i386_external_cygnus_ext_header 16 bytes
+
+.text
+.data
+.relocs=
+  addr(32),addr(32),...
+  addr and $80000000 > 0 -> FixupToSeg=.text else .data
+  addr and $40000000 > 0 -> FixupInSeg=.text else .data
+.importedSymbols
+  name   LString                                   1+n bytes
+  number of references r                             4 bytes
+  addresses                                        r*4 bytes
+.exportedSymbols
+  name   LString                                   1+n bytes
+  addr                                               4 bytes
+    addr and $80000000 > 0 -> .text else .data
+  ...
+.modules
+
+.nlmdebugrecs
+  type (0=.data,1=.code,2,..=????)                   1 byte
+  addr                                               4 bytes
+  name LString                                     1+n bytes
+  ...
+
+}
+
+
+const NLM_MAX_DESCRIPTION_LENGTH = 127;
+      NLM_MAX_SCREEN_NAME_LENGTH = 71;
+      NLM_MAX_THREAD_NAME_LENGTH = 71;   // some netware docs limit this to 12 ?
+      NLM_OLD_THREAD_NAME_LENGTH = 5;
+      NLM_HEADER_VERSION         = 4;
+      NLM_DEFAULT_STACKSIZE      = (32 * 1024);
+      NLM_VERSION_STAMP          = 'VeRsIoN#';
+      NLM_COPYRIGHT_STAMP        = 'CoPyRiGhT=';
+      NLM_CYGNUS_STAMP           = 'CyGnUsEx';
+      NLM_MESSAGES_STAMP         = 'MeSsAgEs';
+      NLM_CUSTOM_STAMP           = 'CuStHeAd';
+      NLM_SIGNATURE              = 'NetWare Loadable Module'#$1A;
+      NLM_FLAGS_REENTRANT        = 1;
+      NLM_FLAGS_MULTILOAD        = 2;
+      NLM_FLAGS_SYNCHRONIZE      = 4;
+      NLM_FLAGS_PSEUDOPREEMPTION = 8;
+      NLM_FLAGS_OSDOMAIN         = $10;
+      NLM_FLAGS_AUTOUNLOAD       = $40;
+
+
+  type
+    uint32 = longword;
+
+    nlm32_i386_external_fixed_header = packed record
+        signature                  : array[0..23] of char;
+        version                    : uint32;
+        (* The name of the module, which must be a DOS name (1-8 characters followed
+           by a period and a 1-3 character extension).  The first byte is the byte
+           length of the name and the last byte is a null terminator byte.  This
+           field is fixed length, and any unused bytes should be null bytes.  The
+           value is set by the OUTPUT keyword to NLMLINK. *)
+        moduleName                 : string[13]; //array[0..13] of byte;
+        codeImageOffset            : uint32;     // The byte offset of the code image from the start of the file.
+        codeImageSize              : uint32;     // The size of the code image, in bytes.
+        dataImageOffset            : uint32;     // The byte offset of the data image from the start of the file.
+        dataImageSize              : uint32;     // The size of the data image, in bytes.
+        uninitializedDataSize      : uint32;     // The size of the uninitialized data region that the loader has to be
+                                                 // allocated at load time.  Uninitialized data follows the initialized
+                                                 // data in the NLM address space.
+        customDataOffset           : uint32;     // The byte offset of the custom data from the start of the file.  The
+                                                 // custom data is set by the CUSTOM keyword to NLMLINK.  It is possible
+                                                 // for this to be EOF if there is no custom data.
+        customDataSize             : uint32;     // The size of the custom data, in bytes.
+        moduleDependencyOffset     : uint32;     // The byte offset of the module dependencies from the start of the file.
+                                                 // The module dependencies are determined by the MODULE keyword in NLMLINK.
+        numberOfModuleDependencies : uint32;     // he number of module dependencies at the moduleDependencyOffset.
+        relocationFixupOffset      : uint32;     // The byte offset of the relocation fixup data from the start of the file
+        numberOfRelocationFixups   : uint32;
+        externalReferencesOffset   : uint32;
+        numberOfExternalReferences : uint32;
+        publicsOffset              : uint32;
+        numberOfPublics            : uint32;
+        debugInfoOffset            : uint32;     // The byte offset of the internal debug info from the start of the file.
+                                                 // It is possible for this to be EOF if there is no debug info.
+        numberOfDebugRecords       : uint32;
+        codeStartOffset            : uint32;
+        exitProcedureOffset        : uint32;
+        checkUnloadProcedureOffset : uint32;
+        moduleType                 : uint32;
+        flags                      : uint32;
+      end;
+
+
+  { The version header is one of the optional auxiliary headers and
+     follows the fixed length and variable length NLM headers.  }
+  { The header is recognized by "VeRsIoN#" in the stamp field.  }
+
+     nlm32_i386_external_version_header = packed record
+          stamp           : array[0..7] of char;  // VeRsIoN#
+          majorVersion,
+          minorVersion,
+          revision,
+          year,
+          month,
+          day             : uint32;
+       end;
+  { The header is recognized by "MeSsAgEs" in the stamp field.  }
+
+     nlm32_i386_external_extended_header = packed record
+          stamp                        : array[0..7] of char;  // MeSsAgEs
+          languageID                   : uint32;
+          messageFileOffset            : uint32;
+          messageFileLength            : uint32;
+          messageCount                 : uint32;
+          helpFileOffset               : uint32;
+          helpFileLength               : uint32;
+          RPCDataOffset                : uint32;
+          RPCDataLength                : uint32;
+          sharedCodeOffset             : uint32;
+          sharedCodeLength             : uint32;
+          sharedDataOffset             : uint32;
+          sharedDataLength             : uint32;
+          sharedRelocationFixupOffset  : uint32;
+          sharedRelocationFixupCount   : uint32;
+          sharedExternalReferenceOffset: uint32;
+          sharedExternalReferenceCount : uint32;
+          sharedPublicsOffset          : uint32;
+          sharedPublicsCount           : uint32;
+          sharedDebugRecordOffset      : uint32;
+          sharedDebugRecordCount       : uint32;
+          SharedInitializationOffset   : uint32;
+          SharedExitProcedureOffset    : uint32;
+          productID : longint;
+          reserved0 : longint;
+          reserved1 : longint;
+          reserved2 : longint;
+          reserved3 : longint;
+          reserved4 : longint;
+          reserved5 : longint;
+       end;
+
+     nlm32_i386_external_custom_header = packed record
+          stamp       : array[0..7] of char;  // CuStHeAd
+          hdrLength   : uint32;
+          dataOffset  : uint32;
+          dataLength  : uint32;
+          //dataStamp   : array[0..7] of char;
+          //hdr         : uint32;
+       end;
+  { The internal Cygnus header is written out externally as a custom
+     header.  We don't try to replicate that structure here.   }
+  { The header is recognized by "CyGnUsEx" in the stamp field.  }
+  { File location of debugging information.   }
+  { Length of debugging information.   }
+
+     nlm32_i386_external_cygnus_ext_header = packed record
+          stamp       : array[0..7] of char;  // CyGnUsEx
+          offset      : uint32;
+          length      : uint32;
+       end;
+
+
+//------------------
+
+
+       TNLMExeSection = class(TExeSection)
+       public
+         constructor createnw(AList:TFPHashObjectList;const n:string);
+       end;
+
+       TsecType = (Section_text,Section_data,Section_other);
+
+       TNLMexeoutput = class(texeoutput)
+       private
+         FRelocsGenerated,FImportsGenerated : boolean;
+         FNumRelocs         : longword;
+         FNumExternals      : longword;
+         FNumModules        : longword;
+         FNumDebugSymbols   : longword;
+         fSizeWoDebugSyms   : longword;
+         FnumExports        : longword;
+         NlmSymbols         : TDynamicArray;
+         ExeSecsListSize    : longint;
+         nlmImpNames,                            // name of import. module name as import
+         nlmImports         : TFPHashObjectList; // name of import, list of relocs as object
+         headerAlignBytes   : longint;
+         FexportFunctionOffsets:TFPList;    // offsets in .exports for function addresses, an offset of $80000000 is needed
+
+         nlmHeader          : nlm32_i386_external_fixed_header;
+         nlmVersionHeader   : nlm32_i386_external_version_header;
+         nlmExtHeader       : nlm32_i386_external_extended_header;
+         nlmCustHeader      : nlm32_i386_external_custom_header;
+         nlmHelpFileName    : TCmdStr;
+         nlmMessagesFileName: TCmdStr;
+         nlmXdcFileName     : TCmdStr;
+         nlmCopyright       : string;
+         nlmThreadname      : string;
+         nlmScreenname      : string;
+         nlmDescription     : string;
+
+         function  totalheadersize:longword;
+         procedure createNlm_symbol(const name:shortstring;value:longword;secType:TSecType);
+         procedure globalsyms_create_symbol(p:TObject;arg:pointer);
+         procedure ExeSectionList_write_header(p:TObject;arg:pointer);
+         procedure ExeSectionList_calc_size(p:TObject;arg:pointer);
+         procedure ExeSectionList_write_data(p:TObject;arg:pointer);
+         procedure GenerateImports;
+         procedure GenerateExports;
+         procedure GenerateRelocs;
+         procedure ExeSectionList_pass2_header(p:TObject;arg:pointer);
+       protected
+         function writedata:boolean;override;
+       public
+         constructor create; override;
+         destructor destroy; override;
+         procedure MemPos_Header;override;
+         procedure DataPos_Header;override;
+         procedure fillNlmVersionHeader;
+         procedure GenerateLibraryImports(ImportLibraryList:TFPHashObjectList);override;
+         procedure Order_End;override;
+         procedure MemPos_ExeSection(const aname:string);override;
+         procedure DataPos_ExeSection(const aname:string);override;
+         procedure NLMwriteString (const s : string; terminateWithZero : boolean);
+         procedure objNLMwriteString (const s : string; terminateWithZero : boolean);
+         procedure ParseScript (linkscript:TCmdStrList); override;
+       end;
+
+
+
+    type
+
+      TNLMCoffObjInput = class(TCoffObjInput)
+         constructor create;override;
+       end;
+
+       TNLMCoffassembler = class(tinternalassembler)
+         constructor create(smart:boolean);override;
+       end;
+
+      TNLMCoffObjData = class(TCoffObjData)
+         constructor create(const n:string);override;
+       end;
+
+      TNLMCoffObjOutput = class(TCoffObjOutput)
+         constructor create(AWriter:TObjectWriter);override;
+       end;
+
+      TNLMCoffObjSection = class(TCoffObjSection)
+         constructor create(AList:TFPHashObjectList;const Aname:string;Aalign:shortint;Aoptions:TObjSectionOptions);override;
+       end;
+
+implementation
+
+    uses
+{$ifdef win32}
+       Windows,
+{$endif win32}
+       SysUtils,
+       cutils,verbose,globals,
+       fmodule,aasmdata,
+       ogmap,export
+       ;
+
+
+{****************************************************************************
+                                 Helpers
+****************************************************************************}
+type
+  TStringObj = class (TObject)
+     fValue : string;
+     constructor create (value:string);
+     property value : string read fValue write fValue;
+  end;
+
+  constructor TStringObj.create(value:string);
+  begin
+    inherited create;
+    fValue := value;
+  end;
+
+
+
+function SectionType (aName : string) : TSecType;
+var s : string;
+begin
+  s := copy(aName,1,5);
+  if s = '.text' then result := Section_text else
+    if (s = '.data') or (copy(s,1,4)='.bss') then result := Section_data else
+      result := Section_other;
+end;
+
+{****************************************************************************
+                              TNLMexesection
+****************************************************************************}
+
+
+    constructor TNLMExeSection.createnw(AList:TFPHashObjectList;const n:string);
+      begin
+        inherited create(AList,n);
+      end;
+
+
+{****************************************************************************
+                              TNLMexeoutput
+****************************************************************************}
+
+    constructor TNLMexeoutput.create;
+      begin
+        inherited create;
+        CExeSection:=TNLMExeSection;
+        CObjData:=TNLMCoffObjData;
+        MaxMemPos:=$7FFFFFFF;
+        SectionMemAlign:=$0;
+        SectionDataAlign:=0;
+        RelocSection := true;  // always needed for NLM's
+        nlmImports := TFPHashObjectList.create(true);
+        nlmImpNames := TFPHashObjectList.create(false);
+        NlmSymbols := TDynamicArray.create(4096);
+        FexportFunctionOffsets := TFPList.Create;
+      end;
+
+    destructor TNLMexeoutput.destroy;
+      begin
+        if assigned(nlmImports) then
+          nlmImports.Free;
+        if assigned(nlmImpNames) then
+          nlmImpNames.Free;
+        if assigned(nlmSymbols) then
+          nlmSymbols.Free;
+        if assigned(FexportFunctionOffsets) then
+          FexportFunctionOffsets.Free;
+        inherited destroy;
+      end;
+
+    procedure TNLMexeoutput.createNlm_symbol(const name:shortstring;value:longword;secType:TSecType);
+      var
+        b:byte;
+      begin
+        //Comment (V_Debug,'TNLMexeoutput.write_symbol '+name);
+        {  type (0=.data,1=.code,2,..=????)                   1 byte
+           addr                                               4 bytes
+           name LString                                     1+n bytes }
+        case secType of
+          Section_Text : b := 1;
+          Section_Data : b := 0
+        else
+          exit;
+        end;
+        nlmSymbols.write(b,sizeof(b));
+        assert (sizeof(value)<>4);
+        nlmSymbols.write(value,sizeof(value));
+        nlmSymbols.write(name[0],length(name)+1);
+        inc(FNumDebugSymbols);
+      end;
+
+
+    procedure TNLMexeoutput.globalsyms_create_symbol(p:TObject;arg:pointer);
+      var
+        value  : longword;
+        exesec : TExeSection;
+        i : integer;
+        secType : TsecType;
+      begin
+        if not assigned(texesymbol(p).objsymbol) then
+          internalerror(200603053);
+        with texesymbol(p).objsymbol do
+          begin
+            exesec:=TExeSection(objsection.exesection);
+            { There is no exesection defined for special internal symbols
+              like __image_base__ }
+            if assigned(exesec) then
+              begin
+                //secval:=exesec.secsymidx;
+                value:=address-exesec.mempos;
+              end
+            else
+              begin
+                value:=address;
+              end;
+            { reloctype address to the section in the executable }
+            secType := SectionType(objsection.Name);
+            if (secType = Section_Text) or (secType = Section_Data) then
+              begin
+                i := nlmImports.FindIndexOf(texesymbol(p).name);
+                if i < 0 then
+                  createNlm_symbol(name,value,secType);
+              end;
+          end;
+      end;
+
+
+
+(*
+function SecOpts(SecOptions:TObjSectionOptions):string;
+    begin
+      result := '[';
+      if oso_Data in SecOptions then result := result + 'oso_Data ';
+       { Is loaded into memory }
+      if oso_load in SecOptions then result := result + 'oso_load ';
+       { Not loaded into memory }
+      if oso_noload in SecOptions then result := result + 'oso_noload ';
+       { Read only }
+      if oso_readonly in SecOptions then result := result + 'oso_readonly ';
+       { Read/Write }
+      if oso_write in SecOptions then result := result + 'oso_write ';
+       { Contains executable instructions }
+      if oso_executable in SecOptions then result := result + 'oso_executable ';
+       { Never discard section }
+      if oso_keep in SecOptions then result := result + 'oso_keep ';
+       { Special common symbols }
+      if oso_common in SecOptions then result := result + 'oso_common ';
+       { Contains debug info and can be stripped }
+      if oso_debug in SecOptions then result := result + 'oso_debug ';
+       { Contains only strings }
+      if oso_strings in SecOptions then result := result + 'oso_strings ';
+      result := result + ']';
+    end;
+*)
+
+    procedure TNLMexeoutput.ExeSectionList_calc_size(p:TObject;arg:pointer);
+      var
+        objsec : TObjSection;
+        i    : longint;
+      begin
+        with texesection(p) do
+          begin
+            { don't write normal section if writing only debug info }
+            if (ExeWriteMode=ewm_dbgonly) and
+               not(oso_debug in SecOptions) then
+              exit;
+
+            if oso_data in secoptions then
+              begin
+                inc (fSizeWoDebugSyms,(Align(fSizeWoDebugSyms,SectionDataAlign)-fSizeWoDebugSyms));
+                for i:=0 to ObjSectionList.Count-1 do
+                  begin
+                    objsec:=TObjSection(ObjSectionList[i]);
+                    if oso_data in objsec.secoptions then
+                      begin
+                        inc(fSizeWoDebugSyms,objsec.size);
+                        inc(fSizeWoDebugSyms,objsec.dataalignbytes);
+                      end;
+                  end;
+              end;
+          end;
+      end;
+
+
+
+    procedure TNLMexeoutput.ExeSectionList_write_Data(p:TObject;arg:pointer);
+      var
+        objsec : TObjSection;
+        i      : longint;
+      begin
+
+        with texesection(p) do
+          begin
+            { don't write normal section if writing only debug info }
+            if (ExeWriteMode=ewm_dbgonly) and
+               not(oso_debug in SecOptions) then
+              exit;
+
+            if oso_data in secoptions then
+              begin
+                //if Align(FWriter.Size,SectionDataAlign)-FWriter.Size>0 then
+                //  writeln (name,' align ',Align(FWriter.Size,SectionDataAlign)-FWriter.Size,' SectionDataAlign:',SectionDataAlign);
+                FWriter.Writezeros(Align(FWriter.Size,SectionDataAlign)-FWriter.Size);
+                for i:=0 to ObjSectionList.Count-1 do
+                  begin
+                    objsec:=TObjSection(ObjSectionList[i]);
+                    if oso_data in objsec.secoptions then
+                      begin
+                        if assigned(exemap) then
+                          exemap.Add(' nlm file offset $'+hexstr(objsec.DataPos,8)+': '+objsec.name);
+                        //writeln ('   ',objsec.name,'  size:',objsec.size,'  relocs:',objsec.ObjRelocations.count,'  DataPos:',objsec.DataPos,' MemPos:',objsec.MemPos);
+                        {for j := 0 to objsec.ObjRelocations.count-1 do
+                          begin
+                            objreloc := TObjRelocation(objsec.ObjRelocations[j]);
+                            with objreloc do
+                            begin
+                              write('        reloc DataOffset: ',DataOffset,'  OrgSize:',OrgSize,' typ:',typ);
+                              if assigned(symbol) then
+                                write(' Name: '#39,symbol.Name,#39' bind:',symbol.bind,' address:',symbol.address,' Size:',symbol.size);
+                              writeln;
+                            end;
+                          end;}
+                        if not assigned(objsec.data) then
+                          internalerror(200603042);
+                        FWriter.writezeros(objsec.dataalignbytes);
+                        //if objsec.dataalignbytes>0 then
+                        //  writeln ('  ',name,'  alignbytes: ',objsec.dataalignbytes);
+                        if objsec.DataPos<>FWriter.Size then
+                            internalerror(200602251);
+                        FWriter.writearray(objsec.data);
+                      end else
+                      begin
+                        if assigned(exemap) then //TExeMap
+                          exemap.Add('                  skipping: '+objsec.name);
+                      end;
+                  end;
+              end;
+          end;
+      end;
+
+
+    function TNLMexeoutput.totalheadersize:longword;
+      var
+        varHdrSize,
+        optHdrSize,
+        hdrSize: longword;
+      begin
+        optHdrSize := 0;
+        inc(optHdrSize,2+length(nlmDescription));
+        inc(optHdrSize,8); // Stacksize+reserved
+        inc(optHdrSize,NLM_OLD_THREAD_NAME_LENGTH);
+        inc(optHdrSize,2+length(nlmScreenname));
+        inc(optHdrSize,2+length(nlmThreadname));
+
+        varHdrSize := 0;
+        if nwcopyright <> '' then
+          inc(varHdrSize,sizeof(NLM_COPYRIGHT_STAMP)+2+length(nlmCopyright));
+        hdrSize := sizeof(nlm32_i386_external_fixed_header)+
+                   sizeof(nlm32_i386_external_extended_header)+
+                   sizeof(nlm32_i386_external_custom_header)+
+                   sizeof(nlm32_i386_external_version_header)+     // always
+                   sizeof(nlm32_i386_external_cygnus_ext_header)+  // CyGnUsEx
+                   varHdrSize+optHdrSize+
+                   8;  // empty stamp
+        result := hdrSize;
+      end;
+
+
+    procedure TNLMexeoutput.MemPos_Header;
+      begin
+        { calculate start positions after the headers }
+        currmempos:=0;
+      end;
+
+
+  procedure TNLMexeoutput.ExeSectionList_write_header(p:TObject;arg:pointer);
+  var
+    nam : string;
+    u32,al : longword;
+    alignAmount:longint;
+      begin
+        with tExeSection(p) do
+          begin
+            //comment (v_debug,'ExeSectionList_write_header: '+name);
+            nam := name;
+            alignAmount := 4 - ((length (nam) + 1) MOD 4);
+            FWriter.write(nam[1],length(nam));
+            FWriter.WriteZeros(1+alignAmount);
+            al := 0;
+            // for .stab we have to ignore leading zeros due to alignment in file
+            if nam='.stab' then
+              if assigned(ObjSectionList[0]) then
+                al := TObjSection(ObjSectionList[0]).dataalignbytes;
+            u32 := dataPos+al; FWriter.write(u32,sizeof(u32));
+            u32 := size-al; FWriter.write(u32,sizeof(u32));
+          end;
+      end;
+
+
+
+    procedure TNLMexeoutput.ExeSectionList_pass2_header(p:TObject;arg:pointer);
+    var len,alignAmount:longint;
+      begin
+        {list of sections, extension of binutils,CuStHeAd points to this list
+          The format of the section information is:
+           null terminated section name
+           zeroes to adjust to 4 byte boundary
+           4 byte section data file pointer
+           4 byte section size }
+
+        with TExeSection(p) do
+          begin
+            alignAmount := 4 - ((length (Name) + 1) MOD 4);
+            len := length(name) + 1 + alignAmount + 8;
+            if ObjSectionList.Count>0 then
+              inc(len,TObjSection(ObjSectionList[0]).dataalignbytes);
+            inc(plongint(arg)^,len);
+          end;
+      end;
+
+    procedure TNLMexeoutput.DataPos_Header;
+      begin
+        ExeSecsListSize:=0;
+        ExeSectionList.ForEachCall(@ExeSectionList_pass2_header,@ExeSecsListSize);
+
+        headerAlignBytes := align(totalheadersize+ExeSecsListSize,16)-(totalheadersize+ExeSecsListSize);  // align as in TObjData.sectiontype2align
+        currdatapos:=totalheadersize+ExeSecsListSize+headerAlignBytes;
+      end;
+
+
+    procedure TNLMexeoutput.fillNlmVersionHeader;
+    var
+        hour,min,sec,hsec,Year,Month,Day : word;
+    begin
+      DecodeTime(Time,hour,min,sec,hsec);
+      DecodeDate(Date,year,month,day);
+      nlmVersionHeader.stamp := NLM_VERSION_STAMP;
+      if nlmVersionHeader.year = 0 then
+        begin
+          nlmVersionHeader.year := Year;
+          nlmVersionHeader.month := Month;
+          nlmVersionHeader.day := Day;
+        end;
+    end;
+
+
+
+    function TNLMexeoutput.writedata:boolean;
+      var
+        dummyLong       : array[0..4] of char;
+        textExeSec,
+        dataExeSec,
+        bssExeSec,
+        relocsExeSec,
+        exportsExeSec,
+        importsExeSec,
+        xdcExeSec,
+        messagesExeSec,
+        helpExeSec,
+        customExeSec    : TExeSection;
+        hassymbols      : boolean;
+        nlmCygnusHeader : nlm32_i386_external_cygnus_ext_header;
+        ModuleName      : string;
+        exesym          : TExeSymbol;
+        expOffset       : PtrUInt;
+        expAddr         : longword;
+        i               : integer;
+
+      begin
+        result:=false;
+        textExeSec:=FindExeSection('.text');
+        dataExeSec:=FindExeSection('.data');
+        bssExeSec:=FindExeSection('.bss');
+        relocsExeSec:=FindExeSection('.reloc');
+        importsExeSec:=FindExeSection('.imports');
+        exportsExeSec:=FindExeSection('.exports');
+        xdcExeSec:=FindExeSection('.xdc');
+        messagesExeSec:=FindExeSection('.messages');
+        helpExeSec:=FindExeSection('.help');
+        customExeSec:=FindExeSection('.custom');
+
+        // exported function need the upper bit in the address
+        // to be set (=CODE), do this here to avoid another
+        // reloc type. The ExportFunctionOffsets list was
+        // filled in GenerateExports
+        if FexportFunctionOffsets.Count>0 then
+          begin
+            if not assigned(exportsExeSec) then
+              internalerror(201103201);   // we have to have a .export section
+            if not assigned(exportsExeSec.ObjSectionList[0]) then
+              internalerror(201103202);   // nothing in the .exports section but we have data in FexportFunctionOffsets
+            for i := 0 to FexportFunctionOffsets.Count-1 do
+              begin
+                expOffset := PtrUint(FexportFunctionOffsets[i]);
+                if TObjSection(exportsExeSec.ObjSectionList[0]).Data.size < expOffset+3 then
+                  internalerror(201103203);  // offset in FexportFunctionOffsets out of range
+                with TObjSection(exportsExeSec.ObjSectionList[0]) do
+                begin  // set the upper bit of address to indicate .text
+                  Data.seek(expOffset);
+                  Data.read(expAddr,4);
+                  Data.seek(expOffset);
+                  expAddr := expAddr or $80000000;
+                  Data.write(expAddr,4);
+                end;
+              end;
+           end;
+
+        if not assigned(TextExeSec) or
+           not assigned(RelocsExeSec) or
+           not assigned(DataExeSec) then
+          internalerror(200602231);   // we have to have .data, .text and .reloc
+        { do we need to write symbols? }
+        hassymbols:=(ExeWriteMode=ewm_dbgonly) or
+                    (
+                     (ExeWriteMode=ewm_exefull) and
+                     not(cs_link_strip in current_settings.globalswitches)
+                    );
+
+        { Initial header, will be updated later }
+        nlmHeader.signature := NLM_SIGNATURE;
+        nlmHeader.version := NLM_HEADER_VERSION;
+        moduleName := upperCase(current_module.exefilename^);
+        nlmHeader.moduleName := moduleName;
+        nlmHeader.codeImageOffset := TextExeSec.DataPos+TObjSection(TextExeSec.ObjSectionList[0]).dataalignbytes; // ??? may be that align has to be moved to fixups/imports
+        nlmHeader.codeImageSize := TextExeSec.Size;
+        nlmHeader.dataImageOffset := DataExeSec.DataPos;
+        nlmHeader.dataImageSize := DataExeSec.Size;
+        if assigned(BSSExeSec) then
+          nlmHeader.uninitializedDataSize:=BSSExeSec.Size;
+        if assigned(customExeSec) then
+          begin
+            nlmHeader.customDataOffset := customExeSec.DataPos;
+            nlmHeader.customDataSize := customExeSec.Size;
+          end;
+        if FNumModules > 0 then
+          begin
+            nlmHeader.moduleDependencyOffset := FindExeSection('.modules').DataPos+4;  // 4 bytes dummy
+            nlmHeader.numberOfModuleDependencies := FNumModules;
+          end;
+        nlmHeader.relocationFixupOffset := relocsExeSec.DataPos;
+        nlmHeader.numberOfRelocationFixups := FNumRelocs;
+        nlmHeader.externalReferencesOffset := importsExeSec.DataPos+4;  // 4 bytes dummy
+        nlmHeader.numberOfExternalReferences := FNumExternals;
+        if assigned(exportsExeSec) then
+          if exportsExeSec.Size>0 then
+          begin
+            nlmHeader.publicsOffset := exportsExeSec.dataPos;
+            nlmHeader.numberOfPublics := FnumExports;
+          end;
+        nlmHeader.codeStartOffset := EntrySym.Address;
+
+        {exit function}
+        exesym:=texesymbol(ExeSymbolList.Find('_Stop'));
+        if assigned(exesym) then
+          nlmHeader.exitProcedureOffset := exesym.ObjSymbol.address;
+
+        {check exit function}
+        exesym:=texesymbol(ExeSymbolList.Find('FPC_NW_CHECKFUNCTION'));
+        if assigned(exesym) then
+          nlmHeader.checkUnloadProcedureOffset := exesym.ObjSymbol.address;
+
+        // calc file pos after all exesections
+        fSizeWoDebugSyms := totalheadersize + ExeSecsListSize + headerAlignBytes;
+        ExeSectionList.ForEachCall(@ExeSectionList_calc_size,nil);
+
+        nlmExtHeader.stamp := NLM_MESSAGES_STAMP;
+        //extHeader.languageID    // TODO: where to get this from ?
+        if assigned(messagesExeSec) then
+          begin
+           nlmExtHeader.messageFileOffset := messagesExeSec.DataPos;
+           nlmExtHeader.messageFileLength := messagesExeSec.Size;
+          end;
+        //nlmExtHeader.messageCount  // TODO: how is messageCount set ?
+        if assigned(helpExeSec) then
+          begin
+           nlmExtHeader.helpFileOffset := helpExeSec.DataPos;
+           nlmExtHeader.helpFileLength := helpExeSec.Size;
+          end;
+        //nlmExtHeader.productID     // TODO: were does this came from ?
+        if assigned(xdcExeSec) then
+          begin
+            nlmExtHeader.RPCDataOffset  := xdcExeSec.DataPos;
+            nlmExtHeader.RPCDataLength  := xdcExeSec.Size;
+          end;
+
+        if hassymbols then
+        begin
+          nlmHeader.debugInfoOffset := fSizeWoDebugSyms;
+          ExeSymbolList.ForEachCall(@globalsyms_create_symbol,nil);
+          nlmHeader.numberOfDebugRecords := FNumDebugSymbols;
+        end;
+
+        fillNlmVersionHeader;
+        FWriter.write(nlmHeader,sizeof(nlmHeader));
+
+        { variable header }
+        NLMWriteString(nlmDescription,true);
+        if stacksize < NLM_DEFAULT_STACKSIZE then stacksize := NLM_DEFAULT_STACKSIZE;
+        FWriter.Write(stacksize,4);
+        FWriter.writezeros(4);
+        dummyLong := ' LONG';
+        FWriter.Write(dummyLong,sizeof(dummyLong));  // old thread name
+        NLMWriteString(nlmScreenname,true);
+        NLMWriteString(nlmThreadname,true);
+
+        {version}
+        FWriter.Write(nlmVersionHeader,sizeof(nlmVersionHeader));
+        {copyright}
+        if nlmCopyright <> '' then
+        begin
+          FWriter.write(NLM_COPYRIGHT_STAMP,sizeof(NLM_COPYRIGHT_STAMP));
+          NLMWriteString(nlmCopyright,true);
+        end;
+        {messages}
+        FWriter.write(nlmExtHeader,sizeof(nlmExtHeader));
+
+        {custhead}
+        nlmCustHeader.stamp := NLM_CUSTOM_STAMP;
+        nlmCustHeader.dataLength := ExeSecsListSize;
+        nlmCustHeader.dataOffset := totalheadersize;
+        nlmCustHeader.hdrLength := $10;               // why 16 ?, this is what binutils write
+        FWriter.write(nlmCustHeader,sizeof(nlmCustHeader));
+
+        {CyGnUsEx}
+        // bfd has a strange way to read the sections:
+        // the section directory is written under CuStHeAd
+        // when bfd finds the neader "CyGnUsEx", it uses the
+        // offset and size from CuStHeAd to read the section table
+
+        nlmCygnusHeader.stamp  := NLM_CYGNUS_STAMP;  // CyGnUsEx
+        // ld writes some unknown values here, bfd irgnores the values at all
+        // lets write the offset and length of the segment table
+        nlmCygnusHeader.offset := nlmCustHeader.dataLength;
+        nlmCygnusHeader.length := nlmCustHeader.dataOffset;
+        FWriter.write(nlmCygnusHeader,sizeof(nlmCygnusHeader));
+        FWriter.WriteZeros(8);   // empty stamp + align next to 16 bytes
+
+        if FWriter.Size<>totalheadersize then
+          internalerror(201103061);               // headersize <> header written
+
+        { Section headers, CuStHeAd points to this section, not needed by
+          netware. Can be used to find the section in the nlm file, binutils
+          will use this section }
+        ExeSectionList.ForEachCall(@ExeSectionList_write_header,nil);
+        FWriter.WriteZeros(headerAlignBytes);
+        if FWriter.Size<>totalheadersize+ExeSecsListSize+headerAlignBytes then
+          internalerror(201103062);
+        { Section data }
+        ExeSectionList.ForEachCall(@ExeSectionList_write_data,nil);
+
+        if hassymbols then
+          FWriter.writearray(NlmSymbols);  // specific symbols for the internal netware debugger
+
+        result:=true;
+      end;
+
+
+
+    procedure TNLMexeoutput.GenerateLibraryImports(ImportLibraryList:TFPHashObjectList);
+      var
+        idata5objsection : TObjSection;
+        basedllname : string;
+
+        function AddImport(const afuncname,amangledname:string; isvar:boolean):TObjSymbol;
+        var
+          secname:string;
+        begin
+          //Comment (V_Debug,'TNLMexeoutput.GenerateLibraryImports.AddImport '+afuncName);
+          result:=nil;
+          if assigned(exemap) then
+            exemap.Add(' Importing Function '+afuncname);
+
+          if not isvar then
+            with internalobjdata do
+            begin
+              secname:=basedllname+'_i_'+amangledname;
+              idata5objsection:=createsection(sec_idata5, secname);
+              internalobjdata.SetSection(idata5objsection);
+              result:=internalobjdata.SymbolDefine('_'+amangledname,AB_IMPORT,AT_FUNCTION);
+            end;
+        end;
+
+      var
+        i,j           : longint;
+        ImportLibrary : TImportLibrary;
+        ImportSymbol  : TImportSymbol;
+        exesym        : TExeSymbol;
+        importAddressList : TFPObjectList;
+      begin
+        if ImportLibraryList.Count > 0 then
+          begin
+            {objsec:=}internalObjData.createsection('.imports',0,[oso_data,oso_keep]);
+            i := 0;
+            internalobjdata.writebytes(i,4);  // dummy to avoid deletion
+            {objsec:=}internalObjData.createsection('.modules',0,[oso_data,oso_keep]);
+            internalobjdata.writebytes(i,4);  // dummy to avoid deletion
+          end;
+        for i:=0 to ImportLibraryList.Count-1 do
+          begin
+            ImportLibrary:=TImportLibrary(ImportLibraryList[i]);
+            idata5objsection:=nil;
+            for j:=0 to ImportLibrary.ImportSymbolList.Count-1 do
+              begin
+                ImportSymbol:=TImportSymbol(ImportLibrary.ImportSymbolList[j]);
+                exesym:=TExeSymbol(ExeSymbolList.Find(ImportSymbol.MangledName));
+                if assigned(exesym) and
+                   (exesym.State<>symstate_defined) then
+                  begin
+                    basedllname:=ExtractFileName(ImportLibrary.Name);
+                    exesym.objsymbol:=AddImport(ImportSymbol.Name,ImportSymbol.MangledName,ImportSymbol.IsVar);
+                    exesym.State:=symstate_defined;
+                    importAddressList := TFPObjectList.create(false);
+                    nlmImports.Add(ImportSymbol.Name,importAddressList);
+                    if pos('.',basedllname) = 0 then
+                      basedllname := basedllname + '.nlm';
+                    nlmImpNames.Add(ImportSymbol.Name,TStringObj.create(lower(basedllname)));
+                  end;
+              end;
+          end;
+
+        PackUnresolvedExeSymbols('after DLL imports');
+        GenerateExports;
+      end;
+
+
+
+    procedure TNLMexeoutput.GenerateImports;
+    var
+        exesec,
+        impexesec  : TExeSection;
+        objsec     : TObjSection;
+        objreloc   : TObjRelocation;
+        i,j,k      : integer;
+        importAddressList : TFPObjectList;
+        name,mName : string;
+        b          : byte;
+        modules    : string;
+        modName    : TStringObj;
+    begin
+      if FImportsGenerated then exit;
+      FImportsGenerated := true;
+      impexesec:=FindExeSection('.imports');
+      if impexesec=nil then exit;
+
+      for i:=0 to ExeSectionList.Count-1 do
+        begin
+          exesec:=TExeSection(ExeSectionList[i]);
+          for j:=0 to exesec.ObjSectionList.count-1 do
+            begin
+              objsec:=TObjSection(exesec.ObjSectionList[j]);
+              if j=0 then
+                begin
+                  exesec.DataPos:=objSec.DataPos;
+                  exesec.MemPos:=objSec.MemPos;
+                end;
+              if (copy(objsec.name,1,5) <> '.text') and (copy(objsec.name,1,4) <> '.bss') and (copy(objsec.name,1,5) <> '.data') then
+                  continue;
+              for k:=0 to objsec.ObjRelocations.Count-1 do
+                begin
+                  objreloc := TObjRelocation(objsec.ObjRelocations[k]);
+                  if assigned(objreloc.symbol) then
+                    begin
+                      //writeln (objreloc.symbol.name,' ',objreloc.symbol.bind);
+                      if objreloc.symbol.bind = AB_IMPORT then
+                        begin
+                          importAddressList := TFPObjectList(nlmImports.Find(objreloc.symbol.name));
+                          if assigned(importAddressList) then
+                            begin
+                              objreloc.objsection := objsec;   // points to idata5
+                              importAddressList.Add(objreloc);
+                            end else
+                            begin
+                              comment(v_error,objreloc.symbol.name+' is external but not defined in nlm imports');
+                            end;
+                        end;
+                    end
+                end;
+            end;
+        end;
+
+      modules := '';
+      for i := 0 to nlmImports.count-1 do
+        begin
+          importAddressList := TFPObjectList(nlmImports.Items[i]);
+          if importAddressList.Count > 0 then
+            begin
+              name := nlmImports.NameOfIndex(i);
+
+              // find the module to be imported and add it to the list
+              // of modules to be auto loaded
+              modName := TStringObj(nlmImpNames.Find(name));
+              if assigned(modName) then
+                begin
+                  mName := modName.Value;
+                  if mName <> '' then
+                    if copy(mName,1,1) <> '!' then  // special, with ! only the imp will be included but no module is autoloaded, needed i.e. for netware.imp
+                      begin
+                        if pos(mName+';',modules) < 1 then
+                          begin
+                            modules := modules + mName + ';';
+                            inc(FNumModules);
+                          end;
+                      end;
+                end;
+              internalobjdata.SetSection(TObjSection(impexesec.ObjSectionList[0]));
+              objNLMwriteString (name,false);           // name of symbol
+              k := importAddressList.Count;
+              internalobjdata.writebytes(k,sizeof(k));    // number of references
+              inc(FNumExternals);
+              for j := 0 to importAddressList.Count-1 do
+                begin
+                  objreloc := TObjRelocation(importAddressList[j]);
+                  objsec := objreloc.objsection;
+                  if oso_executable in objreloc.objsection.SecOptions then
+                    begin
+                      if objreloc.typ <> RELOC_RELATIVE then comment(v_error,'reference to external symbols must be RELOC_RELATIVE');
+                      // TODO: how to check if size is 4 ????
+
+                      k := objsec.MemPos + objreloc.DataOffset;
+                      k := k or $40000000;
+                      // TODO: data|code
+                      internalobjdata.writebytes(k,sizeof(k));    // address
+
+                      // the netware loader requires an offset at the import address
+                      // for call = E8 this is -4
+                      // TODO: how can we check the needed offset ??
+                      if objreloc.DataOffset > 0 then
+                        begin
+                          objsec.Data.seek(objreloc.DataOffset-1);
+                          objsec.data.read(b,1);
+                          if b <> $E8 then
+                            comment(v_error,'no rcall (E8) before imported symbol target address');
+                          k := -4;
+                          objsec.Data.write(k,sizeof(k));
+                        end else
+                        begin
+                          objsec.Data.seek(objreloc.DataOffset);
+                          k := 0;
+                          objsec.Data.write(k,sizeof(k));
+                        end;
+                        objreloc.typ := RELOC_NONE;  // to avoid that TCoffObjSection.fixuprelocs changes the address again
+                    end else
+                      comment(v_error,'Importing of symbols only supported for .text');
+                end;
+            end;
+        end;
+
+      exesec := FindExeSection('.modules');
+      if not assigned(exesec) then internalerror(201103272);  // exe section .modules does not exist ???
+      internalobjdata.SetSection(TObjSection(exesec.ObjSectionList[0]));
+      for i := 1 to FNumModules do
+        begin
+          name := GetToken(modules,';');
+          objNLMwriteString (name,false);
+        end;
+    end;
+
+
+    procedure TNLMexeoutput.GenerateExports;
+    var
+        hp  : texported_item;  { for exports }
+        len : byte;
+        addr: longword;
+        exesym : texesymbol;
+    begin
+      internalObjData.createsection('.exports',0,[oso_data,oso_keep]);
+      {name   LString                                   1+n bytes
+      addr                                               4 bytes
+      addr and $80000000 > 0 -> .text else .data}
+      hp:=texported_item(current_module._exports.first);
+      if assigned(hp) then
+        if assigned(exemap) then
+           exemap.Add('');
+      while assigned(hp) do
+        begin
+          { Export the Symbol }
+          if assigned(exemap) then
+            exemap.Add(' Exporting Function '+hp.sym.prettyname+' as '+hp.name^);
+          len := length(hp.name^);
+          internalobjdata.writebytes(len,1);
+          internalobjdata.writebytes(hp.name^[1],len);
+          exesym:=texesymbol(ExeSymbolList.Find(hp.sym.prettyname));
+          if not assigned(exesym) then
+          begin
+            comment(v_error,'exported symbol '+hp.sym.prettyname+' not found');
+            exit;
+          end;
+          // for exported functions we have to set the upper bit
+          // this will be done in .writedata
+          if not hp.is_var then
+            FexportFunctionOffsets.Add(pointer(PtrUInt(internalobjdata.CurrObjSec.Size)));
+          internalobjdata.writereloc(0,4,exesym.ObjSymbol,RELOC_ABSOLUTE32);
+
+          addr := 0;
+          internalobjdata.writebytes(addr,4);
+          inc(FnumExports);
+          hp:=texported_item(hp.next);
+        end;
+    end;
+
+    procedure TNLMexeoutput.GenerateRelocs;
+
+      var
+        exesec : TExeSection;
+        objsec : TObjSection;
+        objreloc : TObjRelocation;
+        i,j,k : longint;
+        offset : longword;
+        inSec,toSec : TsecType;
+        targetSectionName : string;
+
+      begin
+        if not RelocSection or FRelocsGenerated then
+          exit;
+        exesec:=FindExeSection('.reloc');
+        if exesec=nil then
+          exit;
+        objsec:=internalObjData.createsection('.reloc',0,exesec.SecOptions+[oso_data]);
+        exesec.AddObjSection(objsec);
+        for i:=0 to ExeSectionList.Count-1 do
+          begin
+            exesec:=TExeSection(ExeSectionList[i]);
+            for j:=0 to exesec.ObjSectionList.count-1 do
+              begin
+                objsec:=TObjSection(exesec.ObjSectionList[j]);
+                //writeln ('Relocs for ',exesec.name,' - ',objsec.name);
+                { create relocs only for sections which are loaded in memory }
+                if not (oso_load in objsec.SecOptions) then
+                  continue;
+                { create relocs only for .text and .data }
+                inSec := SectionType (objsec.name);
+                if (inSec <> Section_Text) and (inSec <> Section_Data) then
+                  continue;
+
+                for k:=0 to objsec.ObjRelocations.Count-1 do
+                  begin
+                    objreloc:=TObjRelocation(objsec.ObjRelocations[k]);
+                    if objreloc.typ <> RELOC_ABSOLUTE then
+                      continue;
+                    offset:=objsec.MemPos+objreloc.dataoffset;
+                    targetSectionName := '';
+                    if objreloc.symbol <> nil then
+                    begin
+                      //writeln ('  MemPos',objsec.MemPos,' dataOfs:',objreloc.dataoffset,' ',objsec.name,'   objreloc.symbol: ',objreloc.symbol.name,'  objreloc.symbol.objsection.name: ',objreloc.symbol.objsection.name,' ',objreloc.symbol.Typ,' ',objreloc.symbol.bind,' ',objreloc.Typ);
+                      targetSectionName := copy(objreloc.symbol.objsection.name,1,5);
+                    end else
+                      internalerror(2011030603);
+
+                    toSec := SectionType(targetSectionName);
+
+                    if (toSec = Section_Text) or (toSec = Section_Data) then
+                    begin
+                      if (inSec = Section_text) then offset := offset or $40000000;
+                      if (toSec = Section_text) then offset := offset or $80000000;
+                      internalObjData.writebytes(offset,4);
+                      inc(FNumRelocs);
+                    end;
+                  end;
+              end;
+          end;
+        FRelocsGenerated:=true;
+      end;
+
+
+    procedure TNLMexeoutput.Order_End;
+      var
+        exesec : TExeSection;
+      begin
+        inherited;
+        exesec:=FindExeSection('.reloc');
+        if exesec=nil then
+          exit;
+        exesec.SecOptions:=exesec.SecOptions + [oso_Data,oso_keep,oso_load];
+      end;
+
+
+      procedure TNLMexeoutput.MemPos_ExeSection(const aname:string);
+        begin
+          if aname='.reloc' then
+            GenerateRelocs;
+          if aname='.imports' then
+            GenerateImports;
+          if aname='.data' then
+            currMemPos := 0;  // both, data and code in the nlm have a start offset of 0
+          inherited;
+        end;
+
+
+      procedure TNLMexeoutput.DataPos_ExeSection(const aname:string);
+        begin
+          inherited;
+        end;
+
+
+      procedure TNLMexeoutput.NLMwriteString (const s : string; terminateWithZero : boolean);
+      var len : byte;
+        begin
+          if length(s) > 254 then len := 254 else len := length(s);
+          FWriter.Write(len,1);
+          if len > 0 then
+            FWriter.write(s[1],len);
+          if terminateWithZero then
+            FWriter.writeZeros(1);
+        end;
+
+
+      procedure TNLMexeoutput.objNLMwriteString (const s : string; terminateWithZero : boolean);
+      var len : byte;
+        begin
+          if length(s) > 254 then len := 254 else len := length(s);
+          Internalobjdata.writebytes(len,1);
+          if len > 0 then
+            Internalobjdata.writebytes(s[1],len);
+          if terminateWithZero then
+          begin
+            len := 0;
+            Internalobjdata.writebytes(s[1],len);
+          end;
+        end;
+
+      { parse netware specific linker options }
+      procedure TNLMexeoutput.ParseScript (linkscript:TCmdStrList);
+      var
+        hp : TCmdStrListItem;
+        opt,keyword,s : string;
+        i : integer;
+
+          function toInteger(s:string; min,max:integer; var res:integer):boolean;
+          var
+            code:word;
+          begin
+            result := false;
+            val (s,res,code);
+            if code<>0 then exit;
+            if (res < min) or (res > max) then exit;
+            result := true;
+          end;
+
+
+          procedure loadFile (const secName, fileName, Desc : string);
+          var
+            fileBuf : array [0..4095] of char;
+            bytesRead : longint;
+            fileH : THandle;
+            fn : TCmdStr;
+
+            begin
+              fn := fileName;
+              if not fileExists(fn) then
+               if not unitsearchpath.FindFile(fileName,true,fn) then
+                 begin
+                   comment(v_error,'can not find '+desc+' file '+fileName);
+                   exit;
+                 end;
+               fileH := fileOpen (fn,fmOpenRead);
+               if fileH = -1 then
+                 begin
+                   comment(v_error,'can not open '+desc+' file '+fn);
+                   exit;
+                  end;
+               { load file into section  }
+               internalObjData.createsection(secName,0,[oso_data,oso_keep]);
+               repeat
+                 bytesRead := fileRead(fileH,fileBuf,sizeof(fileBuf));
+                 if bytesRead > 0 then
+                   internalobjdata.writebytes(fileBuf,bytesRead);
+               until bytesRead < sizeof(fileBuf);
+               fileClose(fileH);
+            end;
+
+        begin
+          hp:=TCmdStrListItem(linkscript.first);
+          while assigned(hp) do
+            begin
+              opt:=hp.str;
+              if (opt='') or (opt[1]='#') then
+                continue;
+              keyword:=Upper(GetToken(opt,' '));
+              if keyword = 'AUTOUNLOAD' then
+                begin
+                  nlmHeader.flags:=nlmHeader.flags or NLM_FLAGS_AUTOUNLOAD;
+                end else
+              if keyword = 'COPYRIGHT' then
+                begin
+                  nlmCopyright := GetToken(opt,' ');
+                end else
+              if keyword = 'CUSTOM' then
+                begin
+                  loadFile ('.custom',GetToken(opt,' '),'custom data');
+                end;
+              if keyword = 'DATE' then  // month day 4-digit-year
+              begin
+                if not toInteger(GetToken(opt,' '),1,12,i) then comment(v_error,'DATE: invalid month')
+                  else nlmVersionHeader.month := i;
+                if not toInteger(GetToken(opt,' '),1,31,i) then comment(v_error,'DATE: invalid day')
+                  else nlmVersionHeader.day := i;
+                if not toInteger(GetToken(opt,' '),1900,3000,i) then comment(v_error,'DATE: invalid year')
+                  else nlmVersionHeader.year := i;
+              end else
+              if keyword = 'DEBUG' then
+              begin
+                // ignore
+              end else
+              if keyword = 'DESCRIPTION' then
+                begin
+                  nlmDescription := GetToken(opt,' ');
+                  if length (nlmDescription) > NLM_MAX_DESCRIPTION_LENGTH then
+                    nlmDescription := copy (nlmDescription,1,NLM_MAX_DESCRIPTION_LENGTH);
+                end else
+              if keyword = 'FLAG' then
+                begin
+                  s := upper(GetToken(opt,' '));
+                  if (not toInteger(GetToken(opt,' '),1,$FFFFFFF,i)) or ((s <> 'ON') and (S <> 'OFF')) then comment(v_error,'FLAG: invalid') else
+                    if (s='ON') then
+                      nlmHeader.flags:=nlmHeader.flags or i else
+                    nlmHeader.flags:=nlmHeader.flags and ($FFFFFFF-i);
+                end else
+              if keyword = 'HELP' then
+                begin
+                  loadFile ('.help',GetToken(opt,' '),'help');
+                end else
+              if keyword = 'MESSAGES' then
+                begin
+                  loadFile ('.messages',GetToken(opt,' '),'message');
+                end else
+              if keyword = 'MULTIPLE' then
+                begin
+                  nlmHeader.flags:=nlmHeader.flags or NLM_FLAGS_MULTILOAD;
+                end else
+              if keyword = 'OS_DOMAIN' then
+                begin
+                  nlmHeader.flags:=nlmHeader.flags or NLM_FLAGS_OSDOMAIN;
+                end else
+              if keyword = 'PSEUDOPREEMPTION' then
+                begin
+                  nlmHeader.flags:=nlmHeader.flags or NLM_FLAGS_PSEUDOPREEMPTION;
+                end else
+              if keyword = 'REENTRANT' then
+                begin
+                  nlmHeader.flags:=nlmHeader.flags or NLM_FLAGS_REENTRANT;
+                end else
+              if keyword = 'SCREENNAME' then
+                begin
+                  nlmScreenname := GetToken(opt,' ');
+                  if length(nlmScreenname) > NLM_MAX_SCREEN_NAME_LENGTH then
+                   nlmScreenName := copy (nlmScreenName,1,NLM_MAX_SCREEN_NAME_LENGTH);
+                end else
+              if (keyword = 'STACK') or (keyword = 'STACKSIZE') then
+                begin
+                   if (not toInteger(GetToken(opt,' '),1,$FFFFFFF,i)) then comment(v_error,'invalid stacksize') else
+                     stacksize := i;
+                end else
+              if keyword = 'SYNCHRONIZE' then
+                begin
+                  nlmHeader.flags:=nlmHeader.flags or NLM_FLAGS_SYNCHRONIZE;
+                end else
+              if keyword = 'THREADNAME' then
+                begin
+                  nlmThreadname := GetToken(opt,' ');
+                  if length(nlmThreadname) > NLM_MAX_THREAD_NAME_LENGTH then
+                    nlmThreadname := copy (nlmThreadname,1,NLM_MAX_THREAD_NAME_LENGTH);
+                end else
+              if keyword = 'TYPE' then
+                begin
+                   if (not toInteger(GetToken(opt,' '),1,16,i)) then comment(v_error,'invalid TYPE') else
+                     nlmHeader.moduleType := i;  // TODO: set executable extension (.DSK, .LAN, ...)
+                end else
+              if keyword = 'VERSION' then
+                begin
+                   if (not toInteger(GetToken(opt,' '),0,$FFFFFFF,i)) then comment(v_error,'invalid major version') else
+                     nlmVersionHeader.majorVersion := i;
+                   if (not toInteger(GetToken(opt,' '),0,99,i)) then comment(v_error,'invalid minor version') else
+                     nlmVersionHeader.minorVersion := i;
+                   if (not toInteger(GetToken(opt,' '),0,$FFFFFFF,i)) then comment(v_error,'invalid minor version') else
+                     if i > 26 then
+                       nlmVersionHeader.revision := 0 else
+                       nlmVersionHeader.revision := i;
+                end else
+              if keyword = 'XDCDATA' then
+                begin
+                  loadFile ('.xdc',GetToken(opt,' '),'xdc');
+                end;
+                { TODO: check for unknown options. This means all handled option
+                  (also in link.pas) have to be flagged if processed }
+              hp:=TCmdStrListItem(hp.next);
+            end;
+        end;
+
+{****************************************************************************
+                                TNLMCoffObjData
+****************************************************************************}
+
+    constructor TNLMCoffObjData.create(const n:string);
+      begin
+        inherited createcoff(n,true,TNLMCoffObjSection);
+      end;
+
+
+{****************************************************************************
+                               TNLMoffObjSection
+****************************************************************************}
+
+    constructor TNLMCoffObjSection.create(AList:TFPHashObjectList;const aname:string;aalign:shortint;aoptions:TObjSectionOptions);
+      begin
+        inherited create(alist,aname,aalign,aoptions);
+      end;
+
+
+    constructor TNLMCoffObjOutput.create(AWriter:TObjectWriter);
+      begin
+        // ??????
+        // if win32=false, .stabs and .stabstr will be written without oso_debug
+        // Without oso_debug the sections will be removed by the linker
+        inherited createcoff(AWriter,{win32}true);
+        cobjdata:=TNLMCoffObjData;
+      end;
+
+{****************************************************************************
+                                 TDJCoffAssembler
+****************************************************************************}
+
+    constructor TNLMCoffAssembler.Create(smart:boolean);
+      begin
+        inherited Create(smart);
+        CObjOutput:=TNLMCoffObjOutput;
+      end;
+
+    constructor TNLMCoffObjInput.create;
+      begin
+        inherited createcoff(true);
+        cobjdata:=TNLMCoffObjData;
+      end;
+
+{*****************************************************************************
+                                  Initialize
+*****************************************************************************}
+const
+    as_i386_nlmcoff_info : tasminfo =
+          (
+            id     : as_i386_nlmcoff;
+            idtxt  : 'NLMCOFF';
+            asmbin : '';
+            asmcmd : '';
+            supported_targets : [system_i386_Netware,system_i386_netwlibc];
+            flags : [af_outputbinary,af_smartlink_sections];
+            labelprefix : '.L';
+            comment : '';
+          );
+
+
+
+initialization
+{$ifdef i386}
+  RegisterAssembler(as_i386_nlmcoff_info,TNLMCoffAssembler);
+{$endif i386}
+end.

+ 1 - 0
compiler/systems.pas

@@ -185,6 +185,7 @@ interface
              ,as_ggas                  { gnu assembler called "gas" instead of "as" }
              ,as_i386_nasmhaiku
              ,as_powerpc_vasm
+             ,as_i386_nlmcoff
        );
 
        tar = (ar_none

+ 5 - 5
compiler/systems/i_nwm.pas

@@ -34,7 +34,7 @@ unit i_nwm;
             system       : system_i386_netware;
             name         : 'Netware for i386(clib)';
             shortname    : 'Netware';
-            flags        : [tf_smartlink_library];
+            flags        : [tf_smartlink_library,tf_smartlink_sections,tf_dwarf_only_local_labels];
             cpu          : cpu_i386;
             unit_env     : 'NETWAREUNITS';
             extradefines : 'NETWARE_CLIB';
@@ -50,18 +50,18 @@ unit i_nwm;
             resobjext    : '.or';
             sharedlibext : '.nlm';
             staticlibext : '.a';
-            staticlibprefix : '';
+            staticlibprefix : 'libp';
             sharedlibprefix : '';
             sharedClibext : '.nlm';
             staticClibext : '.a';
-            staticClibprefix : '';
+            staticClibprefix : 'lib';
             sharedClibprefix : '';
-            importlibprefix : 'imp';
+            importlibprefix : 'libimp';
             importlibext : '.a';
             Cprefix      : '';
             newline      : #13#10;
             dirsep       : '/';
-            assem        : as_i386_elf32;
+            assem        : as_i386_nlmcoff; // as_i386_elf32;
             assemextern  : as_gas;
             link         : nil;
             linkextern   : nil;

+ 384 - 1
compiler/systems/t_nwm.pas

@@ -97,7 +97,7 @@ implementation
     verbose,systems,globtype,globals,
     symconst,script,
     fmodule,aasmbase,aasmtai,aasmdata,aasmcpu,cpubase,symsym,symdef,
-    import,export,link,i_nwm,ogbase
+    import,export,link,i_nwm,ogbase, ogcoff, ognlm
     {$ifdef netware} ,dos {$endif}
     ;
 
@@ -123,6 +123,15 @@ implementation
       function  MakeExecutable:boolean;override;
     end;
 
+    TInternalLinkerNetware = class(TInternalLinker)
+        prelude : string;
+        constructor create;override;
+        procedure DefaultLinkScript;override;
+        procedure InitSysInitUnitName;override;
+        procedure ConcatEntryName; virtual;
+        Function  MakeSharedLibrary:boolean;override;
+      end;
+
 Const tmpLinkFileName = 'link~tmp._o_';
       minStackSize = 32768;
 
@@ -549,6 +558,379 @@ begin
 end;
 
 
+{****************************************************************************
+                            TInternalLinkerNetware
+****************************************************************************}
+
+    constructor TInternalLinkerNetware.Create;
+      begin
+        inherited Create;
+        CExeoutput:=TNLMexeoutput;
+        CObjInput:=TNLMCoffObjInput;
+      end;
+
+
+    procedure TInternalLinkerNetware.DefaultLinkScript;
+      var
+        s,s2 : TCmdStr;
+        secname,
+        secnames : string;
+        hasCopyright,
+        hasScreenname,
+        hasThreadname,
+        hasVersion,
+        hasDescription,
+        hasStacksize: boolean;
+        t : text;
+
+
+
+        procedure addLinkerOption(s : string);
+        var op : string;
+        begin
+          if s = '' then exit;
+          if s[1]  = '#' then exit;
+          LinkScript.Concat(s);
+          op := upper(GetToken(s,' '));
+          {check for options via -k that can also be specified vie
+           compiler directives in source, -k options will override
+           options in source}
+          if op = 'COPYRIGHT' then hasCopyright := true else
+          if op = 'SCREENNAME' then hasScreenname := true else
+          if op = 'THREADNAME' then hasThreadname := true else
+          if op = 'VERSION' then hasVersion := true else
+          if op = 'DESCRIPTION' then hasDescription := true else
+          if (op = 'STACK') or (op = 'STACKSIZE') then hasStacksize := true;
+        end;
+
+        { add linker scropt specified by -k@FileName }
+        procedure addLinkerOptionsFile (fileName : string);
+        var
+          t : text;
+          option : string;
+          fn : TCmdStr;
+        begin
+          fn := fileName;
+          if not sysutils.fileExists(fn) then
+            if not includesearchpath.FindFile(fileName,true,fn) then
+            begin
+              comment(v_error,'linker options file "'+fileName+'" not found');
+              exit;
+            end;
+          assign(t,fn); reset(t);
+          while not eof(t) do
+            begin
+              readln(t,option);
+              addLinkerOption(option);
+            end;
+          close(t);
+        end;
+
+        { add  linker options specified by command line parameter -k }
+        procedure addLinkerOptions;
+        var
+          s,option : string;
+          p : integer;
+        begin
+          s := ParaLinkOptions;
+          option := GetToken(s,';');
+          while option <> '' do
+          begin
+            if copy(option,1,1)='@' then
+            begin
+              delete(option,1,1);
+              addLinkerOptionsFile(option);
+            end else
+              addLinkerOption(option);
+            option := GetToken(s,';');
+          end;
+        end;
+
+        { default: nwpre but can be specified via linker options
+          bacuse this has to be the first object, we have to scan
+          linker options before adding other options }
+
+        function findPreludeInFile (fileName : string):string;
+        var
+          t : text;
+          option,s : string;
+          fn : TCmdStr;
+        begin
+          result := '';
+          fn := fileName;
+          if not sysutils.fileExists(fn) then
+            if not includesearchpath.FindFile(fileName,true,fn) then
+            begin
+              comment(v_error,'linker options file "'+fileName+'" not found');
+              exit;
+            end;
+          assign(t,fn); reset(t);
+          while not eof(t) do
+            begin
+              readln(t,option);
+              option := upper(GetToken(s,' '));
+              if option='PRELUDE' then
+                begin
+                  result := getToken(s,' ');
+                  close(t);
+                  exit;
+                end;
+            end;
+          close(t);
+        end;
+
+        function findPrelude : string;
+        var
+          s,option,keyword : string;
+          p : integer;
+        begin
+          s := ParaLinkOptions;
+          option := GetToken(s,';');
+          while option <> '' do
+          begin
+            if copy(option,1,1)='@' then
+            begin
+              delete(option,1,1);
+              result := findPreludeInFile(option);
+              if result <> '' then exit;
+            end else
+            begin
+              keyword := GetToken(option,' ');
+              if keyword = 'PRELUDE' then
+                begin
+                  result := GetToken(option,' ');
+                  exit;
+                end;
+            end;
+            option := GetToken(s,';');
+          end;
+          result := 'nwpre';
+        end;
+
+      begin
+        with LinkScript do
+          begin
+            prelude := findPrelude;  // needs to be first object, can be specified by -k"PRELUDE ObjFileName"
+            if prelude = '' then internalerror(201103271);
+            if pos ('.',prelude) = 0 then prelude := prelude + '.o';
+            s2 := FindObjectFile(prelude,'',false);
+            Comment (V_Debug,'adding init Object File '+s2);
+            Concat('READOBJECT '+MaybeQuoted(s2));
+            while not ObjectFiles.Empty do
+              begin
+                s:=ObjectFiles.GetFirst;
+                if s<>'' then
+                begin
+                  Concat('READOBJECT '+MaybeQuoted(s));
+                  Comment (V_Debug,'adding Object File '+s);
+                end;
+              end;
+            while not StaticLibFiles.Empty do
+              begin
+                s:=StaticLibFiles.GetFirst;
+                if s<>'' then
+                begin
+                  Comment (V_Debug,'adding StaticLibFile '+s);
+                  Concat('READSTATICLIBRARY '+MaybeQuoted(s));
+                end;
+              end;
+           { While not SharedLibFiles.Empty do
+              begin
+                S:=SharedLibFiles.GetFirst;
+                if FindLibraryFile(s,target_info.staticClibprefix,target_info.importlibext,s2) then
+                begin
+                  Comment (V_Debug,'adding LibraryFile '+s);
+                  Concat('READSTATICLIBRARY '+MaybeQuoted(s2));
+                end else
+                  Comment(V_Error,'Import library not found for '+S);
+              end;}
+            if IsSharedLibrary then
+              Concat('ISSHAREDLIBRARY');
+            ConcatEntryName;
+            Concat('IMAGEBASE $' + hexStr(0, SizeOf(imagebase)*2));
+            Concat('HEADER');
+            Concat('EXESECTION .text');
+            Concat('  SYMBOL __text_start__');
+            Concat('  OBJSECTION .text*');
+            Concat('  SYMBOL ___CTOR_LIST__');
+            Concat('  SYMBOL __CTOR_LIST__');
+            Concat('  LONG -1');
+            Concat('  OBJSECTION .ctor*');
+            Concat('  LONG 0');
+            Concat('  SYMBOL ___DTOR_LIST__');
+            Concat('  SYMBOL __DTOR_LIST__');
+            Concat('  LONG -1');
+            Concat('  OBJSECTION .dtor*');
+            Concat('  LONG 0');
+            Concat('  SYMBOL etext');
+            Concat('ENDEXESECTION');
+            Concat('EXESECTION .data');
+            Concat('  SYMBOL __data_start__');
+            Concat('  OBJSECTION .data*');
+            Concat('  OBJSECTION .fpc*');
+            Concat('  SYMBOL edata');
+            Concat('  SYMBOL __data_end__');
+            Concat('ENDEXESECTION');
+
+            Concat('EXESECTION .bss');
+            Concat('  SYMBOL __bss_start__');
+            Concat('  OBJSECTION .bss*');
+            Concat('  SYMBOL __bss_end__');
+            Concat('ENDEXESECTION');
+
+            Concat('EXESECTION .imports');
+            Concat('  SYMBOL __imports_start__');
+            Concat('  OBJSECTION .imports*');
+            Concat('  SYMBOL __imports_end__');
+            Concat('ENDEXESECTION');
+
+            Concat('EXESECTION .modules');
+            Concat('  SYMBOL __modules_start__');
+            Concat('  OBJSECTION .modules*');
+            Concat('  SYMBOL __modules_end__');
+            Concat('ENDEXESECTION');
+
+            Concat('EXESECTION .exports');
+            Concat('  SYMBOL __exports_start__');
+            Concat('  OBJSECTION .exports*');
+            Concat('  SYMBOL __exports_end__');
+            Concat('ENDEXESECTION');
+
+            Concat('EXESECTION .reloc');
+            Concat('  SYMBOL __reloc_start__');
+            Concat('  OBJSECTION .reloc*');
+            Concat('  SYMBOL __reloc_end__');
+            Concat('ENDEXESECTION');
+
+            Concat('EXESECTION .xdc');
+            Concat('  OBJSECTION .xdc*');
+            Concat('ENDEXESECTION');
+
+            Concat('EXESECTION .custom');
+            Concat('  OBJSECTION .custom*');
+            Concat('ENDEXESECTION');
+
+            Concat('EXESECTION .messages');
+            Concat('  OBJSECTION .messages*');
+            Concat('ENDEXESECTION');
+
+            Concat('EXESECTION .help');
+            Concat('  OBJSECTION .help*');
+            Concat('ENDEXESECTION');
+
+            Concat('EXESECTION .rdata');
+            Concat('  SYMBOL ___RUNTIME_PSEUDO_RELOC_LIST__');
+            Concat('  SYMBOL __RUNTIME_PSEUDO_RELOC_LIST__');
+            Concat('  OBJSECTION .rdata_runtime_pseudo_reloc');
+            Concat('  SYMBOL ___RUNTIME_PSEUDO_RELOC_LIST_END__');
+            Concat('  SYMBOL __RUNTIME_PSEUDO_RELOC_LIST_END__');
+            Concat('  OBJSECTION .rdata*');
+            Concat('  OBJSECTION .rodata*');
+            Concat('ENDEXESECTION');
+            Concat('EXESECTION .pdata');
+            Concat('  OBJSECTION .pdata');
+            Concat('ENDEXESECTION');
+            secnames:='.edata,.rsrc,.gnu_debuglink,'+
+                      '.debug_aranges,.debug_pubnames,.debug_info,.debug_abbrev,.debug_line,.debug_frame,.debug_str,.debug_loc,'+
+                      '.debug_macinfo,.debug_weaknames,.debug_funcnames,.debug_typenames,.debug_varnames,.debug_ranges';
+            repeat
+              secname:=gettoken(secnames,',');
+              if secname='' then
+                break;
+              Concat('EXESECTION '+secname);
+              Concat('  OBJSECTION '+secname+'*');
+              Concat('ENDEXESECTION');
+            until false;
+            { Can't use the generic rules, because that will add also .stabstr to .stab }
+            Concat('EXESECTION .stab');
+            Concat('  OBJSECTION .stab');
+            Concat('ENDEXESECTION');
+            Concat('EXESECTION .stabstr');
+            Concat('  OBJSECTION .stabstr');
+            Concat('ENDEXESECTION');
+            Concat('STABS');
+            Concat('SYMBOLS');
+            Concat('');
+
+            hasCopyright := false;
+            hasScreenname := false;
+            hasThreadname := false;
+            hasVersion := false;
+            hasDescription := false;
+            hasStacksize := false;
+            addLinkerOptions;
+            if not hasCopyright then
+              if nwcopyright <> '' then
+                Concat('COPYRIGHT "'+nwCopyright+'"');
+            if not hasScreenname then
+              if nwscreenname <> '' then
+                Concat('SCREENNAME "'+nwscreenname+'"');
+            if not hasThreadname then
+              if nwthreadname <> '' then
+                Concat('THREADNAME "'+nwthreadname+'"');
+            if not hasVersion then
+              Concat('VERSION '+tostr(dllmajor)+' '+tostr(dllminor)+' '+tostr(dllrevision));
+            if not hasDescription then
+              if description <> '' then
+                Concat ('DESCRIPTION "'+description+'"');
+            if not hasStacksize then
+              if MaxStackSizeSetExplicity then
+              begin
+                if stacksize < minStackSize then stacksize := minStackSize;
+                Concat ('STACKSIZE '+tostr(stacksize));
+              end else
+                Concat ('STACKSIZE '+tostr(minStackSize));
+          end;
+
+        // add symbols needed by nwpre. We have not loaded the ppu,
+        // therefore we do not know the externals so read it from nwpre.imp
+        s := ChangeFileExt(prelude,'.imp');  // nwpre.imp
+        if not librarysearchpath.FindFile(s,true,s2) then
+          begin
+            comment(v_error,s+' not found');
+            exit;
+          end;
+        assign(t,s2); reset(t);
+        while not eof(t) do
+          begin
+            readln(t,s);
+            s := trimspace(s);
+            if (length(s) > 0) then
+              if copy(s,1,1) <> '#' then
+                AddImportSymbol('!clib',s,0,false);
+          end;
+        close(t);
+      end;
+
+
+    procedure TInternalLinkerNetware.InitSysInitUnitName;
+      begin
+        //if target_info.system=system_i386_netware then
+        //  GlobalInitSysInitUnitName(self);
+      end;
+
+    procedure TInternalLinkerNetware.ConcatEntryName;
+      begin
+        with LinkScript do
+          begin
+            if IsSharedLibrary then
+              begin
+                Concat('ISSHAREDLIBRARY');
+                Concat('ENTRYNAME _Prelude')
+              end
+            else
+              begin
+                Concat('ENTRYNAME _Prelude')
+              end;
+          end;
+      end;
+
+
+    Function  TInternalLinkerNetware.MakeSharedLibrary:boolean;
+    begin
+      Comment(V_Error,'Make shared library not supported for netware');
+    end;
+
 {*****************************************************************************
                                      Initialize
 *****************************************************************************}
@@ -556,6 +938,7 @@ end;
 
 initialization
   RegisterExternalLinker(system_i386_netware_info,TLinkerNetware);
+  RegisterInternalLinker(system_i386_netware_info,TInternalLinkerNetware);
   RegisterImport(system_i386_netware,TImportLibNetware);
   RegisterExport(system_i386_netware,TExportLibNetware);
   RegisterTarget(system_i386_netware_info);