mirror of
				https://gitlab.com/freepascal.org/fpc/source.git
				synced 2025-11-04 15:39:24 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1522 lines
		
	
	
		
			60 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			1522 lines
		
	
	
		
			60 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
{
 | 
						|
    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;
 | 
						|
 | 
						|
    var
 | 
						|
      {for symbols defined in linker script. To generate a fixup we
 | 
						|
       need to know the segment (.text,.bss or .code) of the symbol
 | 
						|
       Pointer in list is used as TsecType
 | 
						|
       Filled by TInternalLinkerNetware.DefaultLinkScript }
 | 
						|
      nlmSpecialSymbols_Segments : TFPHashList;
 | 
						|
 | 
						|
    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;
 | 
						|
    seg: ptruint;
 | 
						|
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
 | 
						|
      if s[1] <> '.' then
 | 
						|
        begin
 | 
						|
          seg := ptruint(nlmSpecialSymbols_Segments.Find(aName));
 | 
						|
          if seg <> 0 then
 | 
						|
            result := TSecType(seg)
 | 
						|
          else
 | 
						|
            result := Section_other;
 | 
						|
        end 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,j    : longint;
 | 
						|
        b      : byte;
 | 
						|
      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
 | 
						|
                          if objsec.data.size > 0 then
 | 
						|
                            exemap.Add('  0x'+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);
 | 
						|
                        if copy (objsec.Name,1,5) = '.text' then
 | 
						|
                          begin        // write NOP's instead of zero's for .text, makes disassemble possible
 | 
						|
                            b := $90;  // NOP
 | 
						|
                            if objsec.DataAlignBytes > 0 then
 | 
						|
                              for j := 1 to objsec.DataAlignBytes do
 | 
						|
                                FWriter.write(b,1);
 | 
						|
                          end else
 | 
						|
                            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 }
 | 
						|
        if assigned(exemap) then
 | 
						|
          begin
 | 
						|
            exemap.Add('');
 | 
						|
            exemap.Add('NLM file offsets:');
 | 
						|
          end;
 | 
						|
        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 if we support importing data symbols
 | 
						|
                      //       i do not know if this is possible with netware
 | 
						|
                      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,' ',objrel
 | 
						|
                      // oc.symbol.bind,' ',objreloc.Typ);
 | 
						|
                      if objreloc.symbol.objsection.name[1] <> '.' then
 | 
						|
                        targetSectionName := objreloc.symbol.name                       // specials like __bss_start__
 | 
						|
                      else                                                              // dont use objsection.name because it begins with *
 | 
						|
                        targetSectionName := copy(objreloc.symbol.objsection.name,1,5); // all others begin with .segment, we only have to check for .text, .data or .bss
 | 
						|
                    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 = THandle(-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.
 |