|
@@ -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.
|