diff options
Diffstat (limited to 'closures/compiler/ognlm.pas')
-rw-r--r-- | closures/compiler/ognlm.pas | 1521 |
1 files changed, 1521 insertions, 0 deletions
diff --git a/closures/compiler/ognlm.pas b/closures/compiler/ognlm.pas new file mode 100644 index 0000000000..fb0f558a0c --- /dev/null +++ b/closures/compiler/ognlm.pas @@ -0,0 +1,1521 @@ +{ + 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. |