fpc/compiler/ognlm.pas
sergei d7c5daeccd * Don't calculate data position of each object section. Instead, write out a copy of exesection's memory image, i.e. every objsection is placed at the same offset from its owning exesection both on disk and in memory. This approach is simpler and works regardless of alignment requirements, while existing one could work only if SectionDataAlign<=SectionMemAlign.
* Netware target may be broken by this change; at least it deserves a good cleanup because it contains a lot of code trying to solve similar task: to write exesections without alignment and deal with objsections that are nevertheless being aligned by TExeOutput.

git-svn-id: trunk@23291 -
2013-01-02 10:10:23 +00:00

1510 lines
59 KiB
ObjectPascal

{
Copyright (c) 1998-2006 by Peter Vreman
Copyright (c) 2011 by Armin Diehl
Contains the binary netware nlm executable writer
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
****************************************************************************
}
unit ognlm;
{$i fpcdefs.inc}
interface
uses
{ common }
cclasses,globtype,
{ target }
systems,
aasmbase,assemble,link,
{ output }
ogbase,
owbase,
ogcoff;
{*****************************************************************************
NLM File structures and constants
*****************************************************************************}
{
LString0 -> 1 byte Length, Text, #0
LString -> 1 byte length, Text
Basic NLM File Structure:
FixedHeader
nlm32_i386_external_fixed_header 130 bytes
VarHdr1
NLM Description: LString0 2+n bytes
Stacksize 4 bytes
reserved = 0 4 bytes
' LONG' 5 bytes
NLM screen name: LString0 2+n bytes
NLM thread name: LString0 2+n bytes
Optional Headers beginning with stamp (without '')
'VeRsIoN#': nlm32_i386_external_version_header 32 bytes
'CoPyRiGhT=': LString0 2+n bytes
'MeSsAgEs': nlm32_i386_external_extended_header 124 bytes
'CuStHeAd': nlm32_i386_external_custom_header
'CyGnUsEx': nlm32_i386_external_cygnus_ext_header 16 bytes
.text
.data
.relocs=
addr(32),addr(32),...
addr and $80000000 > 0 -> FixupToSeg=.text else .data
addr and $40000000 > 0 -> FixupInSeg=.text else .data
.importedSymbols
name LString 1+n bytes
number of references r 4 bytes
addresses r*4 bytes
.exportedSymbols
name LString 1+n bytes
addr 4 bytes
addr and $80000000 > 0 -> .text else .data
...
.modules
.nlmdebugrecs
type (0=.data,1=.code,2,..=????) 1 byte
addr 4 bytes
name LString 1+n bytes
...
}
const NLM_MAX_DESCRIPTION_LENGTH = 127;
NLM_MAX_SCREEN_NAME_LENGTH = 71;
NLM_MAX_THREAD_NAME_LENGTH = 71; // some netware docs limit this to 12 ?
NLM_OLD_THREAD_NAME_LENGTH = 5;
NLM_HEADER_VERSION = 4;
NLM_DEFAULT_STACKSIZE = (32 * 1024);
NLM_VERSION_STAMP = 'VeRsIoN#';
NLM_COPYRIGHT_STAMP = 'CoPyRiGhT=';
NLM_CYGNUS_STAMP = 'CyGnUsEx';
NLM_MESSAGES_STAMP = 'MeSsAgEs';
NLM_CUSTOM_STAMP = 'CuStHeAd';
NLM_SIGNATURE = 'NetWare Loadable Module'#$1A;
NLM_FLAGS_REENTRANT = 1;
NLM_FLAGS_MULTILOAD = 2;
NLM_FLAGS_SYNCHRONIZE = 4;
NLM_FLAGS_PSEUDOPREEMPTION = 8;
NLM_FLAGS_OSDOMAIN = $10;
NLM_FLAGS_AUTOUNLOAD = $40;
type
uint32 = longword;
nlm32_i386_external_fixed_header = packed record
signature : array[0..23] of char;
version : uint32;
(* The name of the module, which must be a DOS name (1-8 characters followed
by a period and a 1-3 character extension). The first byte is the byte
length of the name and the last byte is a null terminator byte. This
field is fixed length, and any unused bytes should be null bytes. The
value is set by the OUTPUT keyword to NLMLINK. *)
moduleName : string[13]; //array[0..13] of byte;
codeImageOffset : uint32; // The byte offset of the code image from the start of the file.
codeImageSize : uint32; // The size of the code image, in bytes.
dataImageOffset : uint32; // The byte offset of the data image from the start of the file.
dataImageSize : uint32; // The size of the data image, in bytes.
uninitializedDataSize : uint32; // The size of the uninitialized data region that the loader has to be
// allocated at load time. Uninitialized data follows the initialized
// data in the NLM address space.
customDataOffset : uint32; // The byte offset of the custom data from the start of the file. The
// custom data is set by the CUSTOM keyword to NLMLINK. It is possible
// for this to be EOF if there is no custom data.
customDataSize : uint32; // The size of the custom data, in bytes.
moduleDependencyOffset : uint32; // The byte offset of the module dependencies from the start of the file.
// The module dependencies are determined by the MODULE keyword in NLMLINK.
numberOfModuleDependencies : uint32; // he number of module dependencies at the moduleDependencyOffset.
relocationFixupOffset : uint32; // The byte offset of the relocation fixup data from the start of the file
numberOfRelocationFixups : uint32;
externalReferencesOffset : uint32;
numberOfExternalReferences : uint32;
publicsOffset : uint32;
numberOfPublics : uint32;
debugInfoOffset : uint32; // The byte offset of the internal debug info from the start of the file.
// It is possible for this to be EOF if there is no debug info.
numberOfDebugRecords : uint32;
codeStartOffset : uint32;
exitProcedureOffset : uint32;
checkUnloadProcedureOffset : uint32;
moduleType : uint32;
flags : uint32;
end;
{ The version header is one of the optional auxiliary headers and
follows the fixed length and variable length NLM headers. }
{ The header is recognized by "VeRsIoN#" in the stamp field. }
nlm32_i386_external_version_header = packed record
stamp : array[0..7] of char; // VeRsIoN#
majorVersion,
minorVersion,
revision,
year,
month,
day : uint32;
end;
{ The header is recognized by "MeSsAgEs" in the stamp field. }
nlm32_i386_external_extended_header = packed record
stamp : array[0..7] of char; // MeSsAgEs
languageID : uint32;
messageFileOffset : uint32;
messageFileLength : uint32;
messageCount : uint32;
helpFileOffset : uint32;
helpFileLength : uint32;
RPCDataOffset : uint32;
RPCDataLength : uint32;
sharedCodeOffset : uint32;
sharedCodeLength : uint32;
sharedDataOffset : uint32;
sharedDataLength : uint32;
sharedRelocationFixupOffset : uint32;
sharedRelocationFixupCount : uint32;
sharedExternalReferenceOffset: uint32;
sharedExternalReferenceCount : uint32;
sharedPublicsOffset : uint32;
sharedPublicsCount : uint32;
sharedDebugRecordOffset : uint32;
sharedDebugRecordCount : uint32;
SharedInitializationOffset : uint32;
SharedExitProcedureOffset : uint32;
productID : longint;
reserved0 : longint;
reserved1 : longint;
reserved2 : longint;
reserved3 : longint;
reserved4 : longint;
reserved5 : longint;
end;
nlm32_i386_external_custom_header = packed record
stamp : array[0..7] of char; // CuStHeAd
hdrLength : uint32;
dataOffset : uint32;
dataLength : uint32;
//dataStamp : array[0..7] of char;
//hdr : uint32;
end;
{ The internal Cygnus header is written out externally as a custom
header. We don't try to replicate that structure here. }
{ The header is recognized by "CyGnUsEx" in the stamp field. }
{ File location of debugging information. }
{ Length of debugging information. }
nlm32_i386_external_cygnus_ext_header = packed record
stamp : array[0..7] of char; // CyGnUsEx
offset : uint32;
length : uint32;
end;
//------------------
TNLMExeSection = class(TExeSection)
public
constructor createnw(AList:TFPHashObjectList;const n:string);
end;
TsecType = (Section_text,Section_data,Section_other);
TNLMexeoutput = class(texeoutput)
private
FRelocsGenerated,FImportsGenerated : boolean;
FNumRelocs : longword;
FNumExternals : longword;
FNumModules : longword;
FNumDebugSymbols : longword;
fSizeWoDebugSyms : longword;
FnumExports : longword;
NlmSymbols : TDynamicArray;
ExeSecsListSize : longint;
nlmImpNames, // name of import. module name as import
nlmImports : TFPHashObjectList; // name of import, list of relocs as object
headerAlignBytes : longint;
FexportFunctionOffsets:TFPList; // offsets in .exports for function addresses, an offset of $80000000 is needed
nlmHeader : nlm32_i386_external_fixed_header;
nlmVersionHeader : nlm32_i386_external_version_header;
nlmExtHeader : nlm32_i386_external_extended_header;
nlmCustHeader : nlm32_i386_external_custom_header;
//nlmHelpFileName : TCmdStr;
//nlmMessagesFileName: TCmdStr;
//nlmXdcFileName : TCmdStr;
nlmCopyright : string;
nlmThreadname : string;
nlmScreenname : string;
nlmDescription : string;
function totalheadersize:longword;
procedure createNlm_symbol(const name:shortstring;value:longword;secType:TSecType);
procedure globalsyms_create_symbol(p:TObject;arg:pointer);
procedure ExeSectionList_write_header(p:TObject;arg:pointer);
procedure ExeSectionList_calc_size(p:TObject;arg:pointer);
procedure ExeSectionList_write_data(p:TObject;arg:pointer);
procedure GenerateImports;
procedure GenerateExports;
procedure GenerateRelocs;
procedure ExeSectionList_pass2_header(p:TObject;arg:pointer);
protected
function writedata:boolean;override;
public
constructor create; override;
destructor destroy; override;
procedure MemPos_Header;override;
procedure DataPos_Header;override;
procedure fillNlmVersionHeader;
procedure GenerateLibraryImports(ImportLibraryList:TFPHashObjectList);override;
procedure MemPos_Start;override;
procedure MemPos_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;
nlmImports := TFPHashObjectList.create(true);
nlmImpNames := TFPHashObjectList.create(false);
NlmSymbols := TDynamicArray.create(4096);
FexportFunctionOffsets := TFPList.Create;
end;
destructor TNLMexeoutput.destroy;
begin
nlmImports.Free;
nlmImpNames.Free;
nlmSymbols.Free;
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;
dpos,pad: aword;
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 DataPos<FWriter.Size then
InternalError(2012103001);
//if Align(FWriter.Size,SectionDataAlign)-FWriter.Size>0 then
// writeln (name,' align ',Align(FWriter.Size,SectionDataAlign)-FWriter.Size,' SectionDataAlign:',SectionDataAlign);
FWriter.Writezeros(DataPos-FWriter.Size);
for i:=0 to ObjSectionList.Count-1 do
begin
objsec:=TObjSection(ObjSectionList[i]);
if oso_data in objsec.secoptions then
begin
{ objsection must be within SecAlign bytes from the previous one }
dpos:=objsec.MemPos-MemPos+DataPos;
pad:=dpos-FWriter.Size;
if (dpos<FWriter.Size) or
(pad>=max(objsec.SecAlign,1)) then
internalerror(200602251);
if assigned(exemap) then
if objsec.data.size > 0 then
exemap.Add(' 0x'+hexstr(dpos,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
for j := 1 to pad do
FWriter.write(b,1);
end else
FWriter.writezeros(pad);
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 (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 FRelocsGenerated then
exit;
exesec:=FindExeSection('.reloc');
if exesec=nil then
exit;
objsec:=internalObjData.createsection('.reloc',0,[oso_data,oso_load,oso_keep]);
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.MemPos_Start;
var
exesec : TExeSection;
begin
exesec:=FindExeSection('.reloc');
if exesec=nil then
InternalError(2012072602);
exesec.SecOptions:=exesec.SecOptions-[oso_disabled];
inherited;
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.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 : '';
dollarsign: '$';
);
initialization
{$ifdef i386}
RegisterAssembler(as_i386_nlmcoff_info,TNLMCoffAssembler);
{$endif i386}
end.