fpc/compiler/ognlm.pas
nickysn 2af5c9d508 * changed type used for section alignment from byte/shortint to longint, so the
internal object writer and the internal linker support object files with
  sections with alignment >=256

git-svn-id: trunk@37518 -
2017-10-25 15:47:29 +00:00

1511 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(info: pasminfo; 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:longint;Aoptions:TObjSectionOptions);override;
end;
implementation
uses
{$ifdef win32}
Windows,
{$endif win32}
SysUtils,
cutils,verbose,globals,
fmodule,aasmdata,
ogmap,export,owar
;
{****************************************************************************
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.Disabled:=false;
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:longint;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(info: pasminfo; smart:boolean);
begin
inherited;
CObjOutput:=TNLMCoffObjOutput;
CInternalAr:=tarobjectwriter;
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.