implementation module xcoff_linker;

import StdFile,StdArray,StdClass,StdEnum,StdInt,StdBool,StdChar;
from StdMisc import abort;
from StdList import ++;
from StdString import %;

import linker2;

swap_bytes i :== i;
//swap_bytes i = ((i>>24) bitand 0xff) bitor ((i>>8) bitand 0xff00) bitor ((i<<8) bitand 0xff0000) bitor (i<<24);

(!=) a b :== a<>b;

is_nil [] = True;
is_nil _ = False;

not_nil [] = False;
not_nil _ = True;

(CHAR) string i :== string.[i];

(BYTE) string i :== toInt (string.[i]);

(IWORD) string i = (string BYTE (i+1)<<8) bitor (string BYTE i);

(ILONG) string i = (string BYTE (i+3)<<24) bitor (string BYTE (i+2)<<16) bitor (string BYTE (i+1)<<8) bitor (string BYTE i);

read_long :: *{#Char} Int -> (!Int,!*{#Char});
read_long a=:{[i]=e0,[i1]=e1,[i2]=e2,[i3]=e3} i
	= ((toInt e0) bitor (toInt e1<<8) bitor (toInt e2<<16) bitor (toInt e3<<24),a);
{
	i1=i+1;
	i2=i+2;
	i3=i+3;
}

/*
PRINT_STRING s	| not error = s; {}{ (error,_)=ferror (fwrites s stderr); }
PRINT_INT i 	| not error = i; {}{ (error,_)=ferror (fwritec ' ' (fwritei i stderr)); }
*/

:: *Sections = Sections !*String !*String !Sections | EndSections;

read_xcoff_files :: [String] NamesTable Bool Files Int -> (![String],!Sections,!Int,![*Xcoff],!NamesTable,!Files);
read_xcoff_files file_names names_table0 one_pass_link files0 file_n
	= case file_names of {
		[]
			-> ([],EndSections,file_n,[],names_table0,files0);
		[file_name:file_names]
			| is_nil error
				-> (error2,Sections text_section data_section sections,file_n1,[xcoff_file1:xcoff_files],symbol_table2,files2);
				{
					(error2,sections,file_n1,xcoff_files,symbol_table2,files2)
						= read_xcoff_files file_names names_table1 one_pass_link files1 (inc file_n);
					xcoff_file1 = sort_modules xcoff_file0;
				}
				-> (error,EndSections,file_n,[],names_table1,files1);
			{}{
				(error,text_section,data_section,xcoff_file0,names_table1,files1)
					= read_xcoff_file file_name names_table0 one_pass_link files0 file_n;
			}
	}

import_symbols_in_xcoff_files :: [String] ![*Xcoff] !NamesTable -> ([String],![*Xcoff],!NamesTable);
import_symbols_in_xcoff_files undefined_symbols [] names_table0
	=	(undefined_symbols,[],names_table0);
import_symbols_in_xcoff_files undefined_symbols0 [xcoff0:xcoff_list0] names_table0
	=	(undefined_symbols2,[xcoff1:xcoff_list1],names_table2);
	{
		(undefined_symbols1,xcoff1,names_table1)
			= import_symbols_in_xcoff_file undefined_symbols0 xcoff0 names_table0;
		
		(undefined_symbols2,xcoff_list1,names_table2)
			= import_symbols_in_xcoff_files undefined_symbols1 xcoff_list0 names_table1;
	}

	import_symbols_in_xcoff_file :: [String] Xcoff NamesTable -> ([String],!Xcoff,!NamesTable);
	import_symbols_in_xcoff_file undefined_symbols0 xcoff0 names_table0
		=	(undefined_symbols1,{xcoff0 & symbol_table = {symbol_table & symbols=symbols1,imported_symbols=EmptySymbolIndex}},names_table1);
		{
			(undefined_symbols1,symbols1,names_table1)
				= import_symbols imported_symbols undefined_symbols0 symbols names_table0;
			{symbol_table=symbol_table=:{imported_symbols,symbols}}=xcoff0;
		}

		import_symbols :: SymbolIndexList [String] SymbolArray NamesTable -> ([String],!SymbolArray,!NamesTable);
		import_symbols EmptySymbolIndex undefined_symbols symbol_table0 names_table0
			= (undefined_symbols,symbol_table0,names_table0);
		import_symbols (SymbolIndex index symbol_index_list) undefined_symbols symbol_table0=:{[index]=symbol} names_table0
			= case (symbol) of {
				ImportLabel label_name
					->	case (names_table_element) of {
							NamesTableElement symbol_name symbol_n file_n symbol_list
								->	import_symbols symbol_index_list undefined_symbols symbol_table1 names_table1;
							{
								symbol_table1 = { symbol_table0 & [index] = ImportedLabel file_n symbol_n};
							}
							EmptyNamesTableElement
								| size label_name<6 || label_name.[0]!='_' || label_name.[1]!='_' || label_name.[2]!='i' 
													|| label_name.[3]!='m' || label_name.[4]!='p' || label_name.[5]!='_'
									->	import_symbols symbol_index_list [label_name:undefined_symbols] symbol_table0 names_table1;
									->	case names_table_element2 of {
											NamesTableElement _ symbol_n file_n _
												| file_n<0
													-> import_symbols symbol_index_list undefined_symbols symbol_table1 names_table2;
													{
														symbol_table1 = {symbol_table0 & [index] = ImportedFunctionDescriptor file_n symbol_n};
													}
												_
													-> import_symbols symbol_index_list [label_name:undefined_symbols] symbol_table0 names_table2;
										}
										{
											(names_table_element2,names_table2) = find_symbol_in_symbol_table (label_name % (6,size label_name-1)) names_table1;
										}
//								->	abort ("undefined symbol "+++label_name);

						}
				{
					(names_table_element,names_table1) = find_symbol_in_symbol_table label_name names_table0;
				}
			}

xcoff_array :: Int -> !*{#*Xcoff};
xcoff_array n = { empty_xcoff \\ i<-[0..dec n]};

create_xcoff_boolean_array :: Int Int Int Int LibraryList [*Xcoff] -> (!*{#Bool},!*{#Int},!*{#*Xcoff});
create_xcoff_boolean_array n_xcoff_files n_xcoff_symbols n_libraries n_library_symbols library_list list0
	=	(createArray (n_xcoff_symbols+n_library_symbols) False,offset_array1,xcoff_a);
	{
		(offset_array1,xcoff_a)=fill_offsets 0 0 list0 (createArray (n_xcoff_files+n_libraries) 0)
			//	(createArray n_xcoff_files empty_xcoff);
				(xcoff_array n_xcoff_files);
		
		fill_offsets :: Int Int [*Xcoff] *{#Int} *{#*Xcoff} -> (!*{#Int},!*{#*Xcoff});
		fill_offsets file_n offset [] offset_array xcoff_a
			= (fill_library_offsets library_list file_n offset offset_array,xcoff_a);
		fill_offsets file_n offset [xcoff=:{n_symbols}:xcoff_list] offset_array xcoff_a
			= fill_offsets (inc file_n) (offset+n_symbols) xcoff_list {offset_array & [file_n]=offset} {xcoff_a & [file_n]=xcoff};
		
		fill_library_offsets :: LibraryList Int Int *{#Int} -> *{#Int};
		fill_library_offsets EmptyLibraryList file_n offset offset_array
			= offset_array;
		fill_library_offsets (Library _ symbols n_symbols libraries) file_n offset offset_array
			= fill_library_offsets libraries (inc file_n) (offset+n_symbols) {offset_array & [file_n]=offset};
	}

:: OffsetArray :== {#Int};
/*
UPDATE a i v
	# (s,a) = usize a
	| i>=0 && i<s
		= { a & [i] = v};
		= abort ("UPDATE "+++toString i);

SELECT a i
	# (s,a) = usize a
	| i>=0 && i<s
		= a.[i];
		= abort ("SELECT "+++toString i);

PRINT_INT i 	| not error = i; {}{ (error,_)=ferror (fwritec ' ' (fwritei i stderr)); }

PRINT_INT2 i j 	| not error = i; {}{ (error,_)=ferror (fwritec ' ' (fwritei j (fwritec ' ' (fwritei i stderr)))); }

(PRINT_STRING);
(PRINT_STRING) s r | not error = r; {}{ (error,_)=ferror (fwrites s stderr); }

INCORRECT_INDEX a i = i<0 || i>=size a;
*/

mark_used_modules :: !Int !Int !*{#Bool} !OffsetArray !*{#*Xcoff} -> (!*{#Bool},!*{#*Xcoff});
mark_used_modules main_symbol_n main_file_n marked_bool_a0 marked_offset_a xcoff_a
	= mark_used_module main_file_n main_symbol_n marked_offset_a xcoff_a marked_bool_a0;
{
	mark_used_module :: Int Int OffsetArray *{#*Xcoff} *{#Bool} -> (!*{#Bool},!*{#*Xcoff});
	mark_used_module file_n symbol_n marked_offset_a xcoff_a marked_bool_a0
		| file_n<0
			= ({marked_bool_a0 & [marked_offset_a.[size marked_offset_a+file_n]+symbol_n]=True},xcoff_a);
		| marked_bool_a0.[bool_offset]
			= (marked_bool_a0,xcoff_a);
#			marked_bool_a1 = {marked_bool_a0 & [bool_offset]=True};
			= case symbol of {
				Module section_n _ _ _ _ n_relocations relocations
					| section_n==TEXT_SECTION
						-> mark_relocations_module 0 n_relocations relocations marked_bool_a1 xcoff_a1;
					| section_n==DATA_SECTION
						-> mark_relocations_module 0 n_relocations relocations marked_bool_a1 xcoff_a1;
					| section_n==BSS_SECTION
						-> (marked_bool_a1,xcoff_a1);
				SectionLabel section_n label_offset
					| section_n>=1
						-> mark_used_module file_n section_symbol_n marked_offset_a xcoff_a3 marked_bool_a1;
//						-> if (section_symbol_n<0)
//							(abort "")
//							(mark_used_module file_n section_symbol_n marked_offset_a xcoff_a3 marked_bool_a1);
					{
						xcoff_a3 = replace_symbol xcoff_a2 file_n symbol_n (Label section_n label_offset section_symbol_n);
						(section_symbol_n,xcoff_a2) = select_section_symbol_n xcoff_a1 file_n section_n;
					}
				ImportedLabel symbol_file_n imported_symbol_n
					->	if (symbol_file_n<0)
							(mark_used_module symbol_file_n imported_symbol_n marked_offset_a xcoff_a1 marked_bool_a1)
							(mark_used_module symbol_file_n imported_symbol_n marked_offset_a xcoff_a2 marked_bool_a1);
						{
							xcoff_a2=replace_imported_label_symbol xcoff_a1 file_n symbol_n symbol_file_n imported_symbol_n;
						}
				ImportedLabelPlusOffset symbol_file_n imported_symbol_n _
					-> mark_used_module symbol_file_n imported_symbol_n marked_offset_a xcoff_a1 marked_bool_a1;
				ImportedFunctionDescriptor symbol_file_n imported_symbol_n
					->	mark_used_module symbol_file_n imported_symbol_n marked_offset_a xcoff_a1 marked_bool_a1;
				_
					-> abort ("file "+++toString file_n+++" symbol "+++toString symbol_n);
			}
		{}{
			bool_offset=marked_offset_a.[file_n]+symbol_n;
						
			(symbol,xcoff_a1) = select_symbol xcoff_a file_n symbol_n;

			select_section_symbol_n :: *{#*Xcoff} Int Int -> (!Int,!*{#*Xcoff});
			select_section_symbol_n xcoff_a0 file_n section_n = (symbol_section_n,{xcoff_a1 & [file_n] = file1});
			{	
				(file0,xcoff_a1) = replace xcoff_a0 file_n empty_xcoff;
				(symbol_section_n,file1) = select_symbol_section_n1 file0 section_n;
			}
			
			select_symbol :: *{#*Xcoff} Int Int -> (!Symbol,!*{#*Xcoff});
			select_symbol xcoff_a0 file_n symbol_n = (symbol,{xcoff_a1 & [file_n] = file1});
			{	
				(file0,xcoff_a1) = replace xcoff_a0 file_n empty_xcoff;
				(symbol,file1) = select_symbol1 file0 symbol_n;
			}

			replace_symbol :: *{#*Xcoff} Int Int Symbol -> *{#*Xcoff};
			replace_symbol xcoff_a0 file_n symbol_n symbol = {xcoff_a1 & [file_n] = file1};
			{	
				(file0,xcoff_a1) = replace xcoff_a0 file_n empty_xcoff;
				file1 = replace_symbol1 file0 symbol;
			
				replace_symbol1 :: *SXcoff Symbol -> *SXcoff;
				replace_symbol1 file=:{symbol_table} symbol = {file & symbol_table=replace_symbol2 symbol_table symbol};
				{
					replace_symbol2 :: *SSymbolTable Symbol -> !*SSymbolTable;
					replace_symbol2 symbol_table=:{symbols} symbol = {symbol_table & symbols={symbols & [symbol_n]=symbol}};
				};
			}
		
			replace_imported_label_symbol :: *{#*Xcoff} Int Int Int Int -> *{#*Xcoff};
			replace_imported_label_symbol xcoff_a0 file_n symbol_n symbol_file_n imported_symbol_n
				= case label_symbol of {
					SectionLabel section_n v_label_offset
						-> case module_symbol of {
							Module _ v_module_offset _ _ _ _ _
								-> // E (fwritec '#' (fwrites (toString v_module_offset) (fwritec '-' (fwrites (toString v_label_offset) stderr))))
									(replace_symbol {xcoff_a1 & [symbol_file_n] = file3} file_n symbol_n
									(ImportedLabelPlusOffset symbol_file_n section_symbol_n (v_label_offset-v_module_offset)));
							_
								-> {xcoff_a1 & [symbol_file_n] = file3};
						   }
						{
							(module_symbol,file3) = select_symbol1 file2 section_symbol_n;
							(section_symbol_n,file2) = select_symbol_section_n1 file1 section_n;
						}						
					Label _ v_label_offset module_n
						-> case module_symbol of {
							Module _ v_module_offset _ _ _ _ _
								-> 	(replace_symbol {xcoff_a1 & [symbol_file_n] = file2} file_n symbol_n
									(ImportedLabelPlusOffset symbol_file_n module_n (v_label_offset-v_module_offset)));
							_
								-> {xcoff_a1 & [symbol_file_n] = file2};
						   }
						{
							(module_symbol,file2) = select_symbol1 file1 module_n;
						}						
					_
						-> {xcoff_a1 & [symbol_file_n] = file1};
				  }
			{	
				(file0,xcoff_a1) = replace xcoff_a0 symbol_file_n empty_xcoff;
				(label_symbol,file1) = select_symbol1 file0 imported_symbol_n;	
			}

			select_symbol_section_n1 :: *SXcoff Int -> (!Int,!*SXcoff);
			select_symbol_section_n1 file=:{symbol_table} section_n = (section_symbol_n,{file & symbol_table=symbol_table1});
			{
				(section_symbol_n,symbol_table1)=select_symbol_section_n2 symbol_table section_n;
				
				select_symbol_section_n2 :: *SSymbolTable Int -> (!Int,!*SSymbolTable);
				select_symbol_section_n2 symbol_table=:{section_symbol_ns} section_n
					= (section_symbol_n,{symbol_table & section_symbol_ns=section_symbol_ns1});
				{
					(section_symbol_n,section_symbol_ns1) = uselect section_symbol_ns section_n;
				}
			};

			select_symbol1 :: *SXcoff Int -> (!Symbol,!*SXcoff);
			select_symbol1 file=:{symbol_table} symbol_n = (symbol,{file & symbol_table=symbol_table1});
			{
				(symbol,symbol_table1)=select_symbol2 symbol_table symbol_n;
				
				select_symbol2 :: *SSymbolTable Int -> (!Symbol,!*SSymbolTable);
				select_symbol2 symbol_table=:{symbols} symbol_n = (symbol,{symbol_table & symbols=symbols1});
				{
					(symbol,symbols1) = uselect symbols symbol_n;
				}
			};

			mark_relocations_module relocation_n n_relocations relocation_string marked_bool_a0 xcoff_a
				| relocation_n>=n_relocations
					= (marked_bool_a0,xcoff_a);
					= mark_relocations_module (inc relocation_n) n_relocations relocation_string marked_bool_a1 xcoff_a1;
					{
						(marked_bool_a1,xcoff_a1) = mark_used_module file_n relocation_symbol_n marked_offset_a xcoff_a marked_bool_a0;
						relocation_symbol_n=relocation_string ILONG (relocation_n*SIZE_OF_RELOCATION+4);
					}
		}
}

:: *ModuleOffsets :== *{#Int};

compute_module_offsets :: Int Int [SXcoff] {#Bool} -> (!Int,!ModuleOffsets);
compute_module_offsets n_symbols text_offset0 xcoff_list marked_bool_a
	= compute_files_module_offsets xcoff_list text_offset0 0 (createArray n_symbols 0);
	{
		compute_files_module_offsets :: [SXcoff] Int Int ModuleOffsets -> (!Int,!ModuleOffsets);
		compute_files_module_offsets [] text_offset0 file_symbol_index module_offsets0
			= (text_offset0,module_offsets0);
		compute_files_module_offsets [xcoff=:{n_symbols}:xcoff_list] text_offset0 file_symbol_index module_offsets0
			= compute_files_module_offsets xcoff_list text_offset1 (file_symbol_index+n_symbols) module_offsets1;
			{
				(text_offset1,module_offsets1)
					= compute_section_module_offsets file_symbol_index marked_bool_a symbol_table.text_symbols symbols text_offset0 module_offsets0;
					
				symbol_table=xcoff.symbol_table;
				symbols=symbol_table.symbols;
			}
	}

compute_bss_module_offsets :: [SXcoff] Int Int {#Bool} ModuleOffsets -> (!Int,!ModuleOffsets);
compute_bss_module_offsets [] bss_offset0 file_symbol_index marked_bool_a module_offsets0
	= (bss_offset0,module_offsets0);
compute_bss_module_offsets [xcoff=:{n_symbols}:xcoff_list] bss_offset0 file_symbol_index marked_bool_a module_offsets0
	= compute_bss_module_offsets xcoff_list bss_offset1 (file_symbol_index+n_symbols) marked_bool_a module_offsets1;
	{
		(bss_offset1,module_offsets1)
			= compute_section_module_offsets file_symbol_index marked_bool_a symbol_table.bss_symbols symbol_table.symbols bss_offset0 module_offsets0;
		symbol_table=xcoff.symbol_table;
	}

compute_data_module_offsets :: [SXcoff] Int Int ModuleOffsets -> (!Int,!ModuleOffsets);
compute_data_module_offsets [] data_offset0 file_symbol_index module_offsets0
	= (data_offset0,module_offsets0);
compute_data_module_offsets [xcoff=:{n_symbols}:xcoff_list] data_offset0 file_symbol_index module_offsets0
	= compute_data_module_offsets xcoff_list data_offset1 (file_symbol_index+n_symbols) module_offsets1;
	{
		(data_offset1,module_offsets1)
			= compute_data_section_module_offsets file_symbol_index symbol_table.data_symbols symbol_table.symbols data_offset0 module_offsets0;
		symbol_table=xcoff.symbol_table;
	}

	compute_section_module_offsets :: Int {#Bool} SymbolIndexList SSymbolArray Int ModuleOffsets -> (!Int,!ModuleOffsets);
	compute_section_module_offsets file_symbol_index marked_bool_a EmptySymbolIndex symbol_array offset0 module_offsets0
		= (offset0,module_offsets0);
	compute_section_module_offsets file_symbol_index marked_bool_a (SymbolIndex module_n symbol_list) symbol_array=:{[module_n]=module_symbol} offset0 module_offsets0
		| not marked_bool_a.[file_symbol_index+module_n]
			= compute_section_module_offsets file_symbol_index marked_bool_a symbol_list symbol_array offset0 module_offsets0;
			= compute_section_module_offsets file_symbol_index marked_bool_a symbol_list symbol_array offset1 module_offsets1;
			{
				(offset1,module_offsets1)=compute_module_offset module_symbol module_n offset0 file_symbol_index module_offsets0;
			}

	compute_data_section_module_offsets :: Int SymbolIndexList SSymbolArray Int ModuleOffsets -> (!Int,!ModuleOffsets);
	compute_data_section_module_offsets file_symbol_index EmptySymbolIndex symbol_array offset0 module_offsets0
		= (offset0,module_offsets0);
	compute_data_section_module_offsets file_symbol_index (SymbolIndex module_n symbol_list) symbol_array=:{[module_n]=module_symbol} offset0 module_offsets0
		= compute_data_section_module_offsets file_symbol_index symbol_list symbol_array offset1 module_offsets1;
		{
			(offset1,module_offsets1)=compute_module_offset module_symbol module_n offset0 file_symbol_index module_offsets0;	
		}

		compute_module_offset :: Symbol Int Int Int ModuleOffsets -> (!Int,!ModuleOffsets);
		compute_module_offset (Module _ _ length _ _ _ _) module_n offset0 file_symbol_index module_offsets0
			= (aligned_offset0+length,{module_offsets0 & [file_symbol_index+module_n] = aligned_offset0});
			{
				aligned_offset0=(offset0+alignment_mask) bitand (bitnot alignment_mask);
				alignment_mask=dec (1<<alignment);
				alignment = 2;
			}

// E :: !a !.b -> .b;
// E a b = b;

split_data_symbol_lists_of_files2 :: !{#Int} !{#Bool} ![*SXcoff] -> [*SXcoff];
split_data_symbol_lists_of_files2 offset_a marked_bool_a xcoff_list
	=	split_data_symbol_lists_of_files2 0 xcoff_list;
	{
		split_data_symbol_lists_of_files2 :: Int [*SXcoff] -> [*SXcoff];
		split_data_symbol_lists_of_files2 file_symbol_index []
			= [];
		split_data_symbol_lists_of_files2 file_symbol_index [xcoff=:{n_symbols,symbol_table}:xcoff_list0] 
			= [ {xcoff & symbol_table={symbol_table & data_symbols=data_symbols1,symbols=symbol_table1 }} : xcoff_list1];
		{
			(data_symbols1,symbol_table1)	= split_data_symbol_list2 symbol_table.data_symbols symbol_table.symbols;
			xcoff_list1						= split_data_symbol_lists_of_files2 (file_symbol_index+n_symbols) xcoff_list0;
//			symbol_table2 = remove_unmarked_symbols 0 n_symbols file_symbol_index marked_bool_a symbol_table1;

			split_data_symbol_list2 :: SymbolIndexList *SSymbolArray -> (!SymbolIndexList,!*SSymbolArray);
			split_data_symbol_list2 EmptySymbolIndex symbol_array0
				= (EmptySymbolIndex,symbol_array0);
			split_data_symbol_list2 (SymbolIndex module_n symbol_list) symbol_array0=:{[module_n]=module_symbol}
				| not marked_bool_a.[file_symbol_index+module_n]
					= split_data_symbol_list2 symbol_list symbol_array0;
					= case module_symbol of {
						Module DATA_SECTION _ _ _ _ _ _
							-> (SymbolIndex module_n data_symbols,symbol_array1); {
								(data_symbols,symbol_array1) = split_data_symbol_list2 symbol_list symbol_array0;
							};
					}
		
			remove_unmarked_symbols :: Int Int Int {#Bool} *SSymbolArray -> *SSymbolArray;
			remove_unmarked_symbols index n_symbols first_symbol_index marked_bool_a symbols0
				| index==n_symbols
					= symbols0;
				| marked_bool_a.[first_symbol_index+index]
					= remove_unmarked_symbols (inc index) n_symbols first_symbol_index marked_bool_a symbols0;
					= remove_unmarked_symbols (inc index) n_symbols first_symbol_index marked_bool_a { symbols0 & [index]=EmptySymbol };
		}
	}

compute_imported_library_symbol_offsets :: LibraryList Int Int Int Int {#Bool} *{#Int} -> (!LibraryList,!Int,!Int,!*{#Int});
compute_imported_library_symbol_offsets EmptyLibraryList text_offset0 thunk_data_offset0 n_libraries symbol_n marked_bool_a module_offset_a0
	=	(EmptyLibraryList,text_offset0,thunk_data_offset0,module_offset_a0);
compute_imported_library_symbol_offsets (Library library_name library_symbols n_symbols library_list0) text_offset0 thunk_data_offset0 n_libraries symbol_n marked_bool_a module_offset_a0
	=	(Library library_name imported_symbols n_imported_symbols library_list1,text_offset2,thunk_data_offset2,module_offset_a2);
	{
		(library_list1,text_offset2,thunk_data_offset2,module_offset_a2)
			= compute_imported_library_symbol_offsets library_list0 text_offset1 thunk_data_offset1 (inc n_libraries) (symbol_n+n_symbols) marked_bool_a module_offset_a1;
		(imported_symbols,text_offset1,thunk_data_offset1,module_offset_a1)
			= compute_library_symbol_offsets library_symbols symbol_n text_offset0 thunk_data_offset0 marked_bool_a module_offset_a0;
		n_imported_symbols = (text_offset1-text_offset0) / 6;
		
		compute_library_symbol_offsets :: LibrarySymbolsList Int Int Int {#Bool} *{#Int} -> (!LibrarySymbolsList,!Int,!Int,!*{#Int});
		compute_library_symbol_offsets EmptyLibrarySymbolsList symbol_n text_offset0 thunk_data_offset0 marked_bool_a module_offset_a0
			= (EmptyLibrarySymbolsList,text_offset0,thunk_data_offset0+4,module_offset_a0);
		compute_library_symbol_offsets (LibrarySymbol symbol_name symbol_list) symbol_n text_offset0 thunk_data_offset0 marked_bool_a module_offset_a0
			| marked_bool_a.[symbol_n]
				= (LibrarySymbol symbol_name imported_symbols,text_offset1,thunk_data_offset1,module_offset_a1);
				{
					(imported_symbols,text_offset1,thunk_data_offset1,module_offset_a1)
						= compute_library_symbol_offsets symbol_list (symbol_n+2) (text_offset0+6) (thunk_data_offset0+4) marked_bool_a 
							{module_offset_a0 & [symbol_n]=text_offset0,[symbol_n+1]=thunk_data_offset0};
				}
				= compute_library_symbol_offsets symbol_list (symbol_n+2) text_offset0 thunk_data_offset0 marked_bool_a module_offset_a0;
	}

(FWI) infixl;
(FWI) f i = fwritei (swap_bytes i) f;

(FWS) infixl;
(FWS) f s :== fwrites s f;

(FWC) infixl;
(FWC) f c :== fwritec c f;

(FWZ) infixl;
(FWZ) f i :== write_zero_bytes_to_file i f;

write_code_to_pe_files :: [SXcoff] Int Int {#Bool} {#Int} {#Int} XcoffArray Bool Sections !*File *Files -> (![[*String]],!*File,!*Files);
write_code_to_pe_files [] offset0 first_symbol_n marked_bool_a module_offset_a marked_offset_a xcoff_a one_pass_link sections pe_file files
	= ([],pe_file,files);
write_code_to_pe_files [xcoff=:{n_symbols,file_name}:xcoff_list] first_symbol_n offset0 marked_bool_a module_offset_a marked_offset_a xcoff_a one_pass_link (Sections text_section data_section sections) pe_file files
#	(ok,xcoff_file,files)	= fopen file_name FReadData files;
	| not ok
		= abort ("Cannot read file: "+++file_name);
#
	(offset1,xcoff_file,pe_file) = write_code_to_pe_file xcoff first_symbol_n offset0 marked_bool_a module_offset_a marked_offset_a xcoff_a xcoff_file pe_file;
	(file_data,xcoff_file) = read_data_from_object_file xcoff xcoff_file;
	with {
		read_data_from_object_file {symbol_table={data_symbols,symbols}} xcoff_file
			= read_data_from_object_file data_symbols symbols xcoff_file;
		{
			read_data_from_object_file EmptySymbolIndex symbol_table xcoff_file
				= ([],xcoff_file);
			read_data_from_object_file (SymbolIndex module_n symbol_list) symbol_table=:{[module_n] = symbol} xcoff_file
				| marked_bool_a.[first_symbol_n+module_n]
					# (data_a,xcoff_file) = case symbol of {
						(Module DATA_SECTION virtual_module_offset length virtual_address file_offset n_relocations relocations)
						#
							(ok,xcoff_file)			= fseek xcoff_file file_offset FSeekSet;
							| not ok
								-> abort ("Read error");
						#
							(data_a,xcoff_file)	= freads xcoff_file length;
							| size data_a==length
								-> (data_a,xcoff_file);
						};
					  (data_strings,xcoff_file)=read_data_from_object_file symbol_list symbol_table xcoff_file;
					= ([data_a:data_strings],xcoff_file);
					= read_data_from_object_file symbol_list symbol_table xcoff_file;
		}
	}
	
	(ok,files) 				= fclose xcoff_file files;
	| not ok
		= abort ("Error while reading file: "+++file_name);

#	(data_strings,pe_file,files)	= write_code_to_pe_files xcoff_list (first_symbol_n+n_symbols) offset1 marked_bool_a module_offset_a marked_offset_a xcoff_a one_pass_link sections pe_file files;
	=	([file_data : data_strings],pe_file,files);

write_code_to_pe_file :: SXcoff Int Int {#Bool} {#Int} {#Int} XcoffArray *File *File -> (!Int,!*File,!*File);
write_code_to_pe_file {symbol_table={text_symbols,symbols}}
		first_symbol_n offset0 marked_bool_a module_offset_a marked_offset_a0 xcoff_a xcoff_file pe_file
	=	write_text_to_pe_file text_symbols offset0 symbols xcoff_file pe_file;
	{
		write_text_to_pe_file :: SymbolIndexList Int SSymbolArray *File *File -> (!Int,!*File,!*File);
		write_text_to_pe_file EmptySymbolIndex offset0 symbol_table xcoff_file pe_file
			= (offset0,xcoff_file,pe_file);
		write_text_to_pe_file (SymbolIndex module_n symbol_list) offset0 symbol_table=:{[module_n] = symbol} xcoff_file pe_file
			| marked_bool_a.[first_symbol_n+module_n]
				#(offset1,xcoff_file,pe_file) = write_text_module_to_pe_file symbol offset0 module_offset_a xcoff_file pe_file;
				= write_text_to_pe_file symbol_list offset1 symbol_table xcoff_file pe_file;
				= write_text_to_pe_file symbol_list offset0 symbol_table xcoff_file pe_file;
			{}{
				write_text_module_to_pe_file :: Symbol Int {#Int} *File *File -> (!Int,!*File,!*File);
				write_text_module_to_pe_file (Module TEXT_SECTION virtual_module_offset length virtual_address file_offset n_relocations relocations)
						offset0 module_offset_a=:{[o_i]=real_module_offset} xcoff_file pe_file
				#
					(ok,xcoff_file)			= fseek xcoff_file file_offset FSeekSet;
					| not ok
						= abort ("Read error");
				#
					(text_a0,xcoff_file)	= freads xcoff_file length;
					| size text_a0==length
						= (aligned_offset0+length,xcoff_file,fwrites text_a1 (write_nop_bytes_to_file (aligned_offset0-offset0) pe_file));
						{						
							aligned_offset0=(offset0+alignment_mask) bitand (bitnot alignment_mask);
							alignment_mask=dec (1<<alignment);
							alignment=2;

							text_a1 = relocate_text 0 symbols text_a0;

							relocate_text relocation_n symbol_a text_a0
								| relocation_n==n_relocations
									= text_a0;
									= relocate_text (inc relocation_n) symbol_a text1;
									{
										text1 = case relocation_type of {
											REL_REL32
												-> relocate_branch symbol_a.[relocation_symbol_n] relocation_offset (relocation_offset-virtual_address) virtual_module_offset
													real_module_offset module_offset_a first_symbol_n relocation_symbol_n symbol_a marked_offset_a0 xcoff_a text_a0;
											REL_DIR32
												-> relocate_long_pos symbol_a.[relocation_symbol_n] (relocation_offset-virtual_address) module_offset_a 
													first_symbol_n relocation_symbol_n symbol_a marked_offset_a0 xcoff_a text_a0;
											REL_DUMMY
												-> relocate_branch symbol_a.[relocation_symbol_n] relocation_offset (relocation_offset-virtual_address) virtual_module_offset
													real_module_offset module_offset_a first_symbol_n relocation_symbol_n symbol_a marked_offset_a0 xcoff_a text_a0;
										}
									}
								{							
									relocation_type=relocations IWORD (relocation_index+8);
									relocation_symbol_n=relocations ILONG (relocation_index+4);
									relocation_offset=relocations ILONG relocation_index;

									relocation_index=relocation_n * SIZE_OF_RELOCATION;
								}
						}
				{
					o_i=first_symbol_n+module_n;
				}
			}
	}

write_imported_library_functions_code :: LibraryList Int *File -> *File;
write_imported_library_functions_code EmptyLibraryList thunk_data_offset0 pe_file0
	= pe_file0;
write_imported_library_functions_code (Library _ imported_symbols _ library_list) thunk_data_offset0 pe_file0
	=	write_imported_library_functions_code library_list thunk_data_offset1 pe_file1;
	{
		(thunk_data_offset1,pe_file1) = write_library_functions_code imported_symbols thunk_data_offset0 pe_file0;
		
		write_library_functions_code :: LibrarySymbolsList Int *File -> (!Int,!*File);
		write_library_functions_code EmptyLibrarySymbolsList thunk_data_offset0 pe_file0
			= (thunk_data_offset0+4,pe_file0);
		write_library_functions_code (LibrarySymbol symbol_name symbol_list) thunk_data_offset0 pe_file0
			= write_library_functions_code symbol_list (thunk_data_offset0+4) pe_file1;
			{
				pe_file1 = pe_file0 FWC '\377' FWC '\045' FWI thunk_data_offset0;
			}
	}

relocate_data :: Int Int Int Int Int String Int {#Int} {#Int} {!Symbol} XcoffArray *{#Char}-> *{#Char};
relocate_data relocation_n n_relocations virtual_module_offset virtual_section_offset real_module_offset text_relocations 
		first_symbol_n module_offset_a marked_offset_a0 symbol_a xcoff_a data0
	| relocation_n==n_relocations
		= data0;
		= relocate_data (inc relocation_n) n_relocations virtual_module_offset virtual_section_offset real_module_offset text_relocations
						first_symbol_n module_offset_a marked_offset_a0 symbol_a xcoff_a data1;
	{
		data1 = relocate_symbol relocation_type module_offset_a symbol_a marked_offset_a0 xcoff_a data0;
		
		relocate_symbol :: Int {#Int} {!Symbol} {#Int} XcoffArray *{#Char} -> *{#Char};
		relocate_symbol REL_DIR32 module_offset_a symbol_a marked_offset_a0 xcoff_a data0
			= relocate_long_pos symbol_a.[relocation_symbol_n] (relocation_offset-virtual_section_offset) module_offset_a 
								first_symbol_n relocation_symbol_n symbol_a marked_offset_a0 xcoff_a data0;

		relocation_type=text_relocations IWORD (relocation_index+8);
		relocation_symbol_n=text_relocations ILONG (relocation_index+4);
		relocation_offset=text_relocations ILONG relocation_index;

		relocation_index=relocation_n * SIZE_OF_RELOCATION;
	}

relocate_long_pos :: Symbol Int {#Int} Int Int {!Symbol} {#Int} XcoffArray *{#Char} -> *{#Char};
relocate_long_pos (Module section_n virtual_label_offset _ _ _ _ _) index module_offset_a first_symbol_n symbol_n symbol_a marked_offset_a0 xcoff_a data0
	= add_to_long_at_offset real_label_offset index data0;
	{
		real_label_offset=module_offset_a.[first_symbol_n+symbol_n];
	}
relocate_long_pos (Label _ offset module_n) index module_offset_a first_symbol_n symbol_n symbol_a marked_offset_a0 xcoff_a data0
	= case (symbol_a.[module_n]) of {
		Module _ virtual_label_offset _ _ _ _ _
			-> add_to_long_at_offset (real_label_offset+offset-virtual_label_offset) index data0;
			{
				real_label_offset=module_offset_a.[first_symbol_n+ /*symbol_n*/ module_n];
			}
	  }
relocate_long_pos (ImportedLabel file_n symbol_n) index module_offset_a first_symbol_n _ symbol_a marked_offset_a0 xcoff_a data0
	| file_n<0
		=	add_to_long_at_offset real_label_offset index data0;
		{
			real_label_offset = module_offset_a.[marked_offset_a0.[file_n + size marked_offset_a0]+symbol_n];
		}
		=	case (symbol_a.[symbol_n]) of {
				Module section_n _ _ _ _ _ _
					-> add_to_long_at_offset real_label_offset index data0;
					{
						real_label_offset = module_offset_a.[first_symbol_n+symbol_n];
					}
			}
			{
				first_symbol_n = marked_offset_a0.[file_n];
				symbol_a=xcoff_a.[file_n].symbol_table.symbols;		
			}
relocate_long_pos (ImportedLabelPlusOffset file_n symbol_n label_offset) index module_offset_a first_symbol_n _ symbol_a marked_offset_a0 xcoff_a data0
		=	case (symbol_a.[symbol_n]) of {
				Module section_n _ _ _ _ _ _
					-> add_to_long_at_offset (real_label_offset+label_offset) index data0;
					{
						real_label_offset = module_offset_a.[first_symbol_n+symbol_n];
					}
			}
		{
			first_symbol_n = marked_offset_a0.[file_n];
			symbol_a=xcoff_a.[file_n].symbol_table.symbols;		
		}
relocate_long_pos (ImportedFunctionDescriptor file_n symbol_n) index module_offset_a first_symbol_n _ symbol_a marked_offset_a0 xcoff_a data0
	| file_n<0
		=	add_to_long_at_offset real_label_offset index data0;
		{
			real_label_offset = module_offset_a.[marked_offset_a0.[file_n + size marked_offset_a0]+symbol_n+1];
		}

relocate_branch :: Symbol Int Int Int Int {#Int} Int Int {!Symbol} {#Int} XcoffArray *{#Char} -> *{#Char};
relocate_branch (Module TEXT_SECTION virtual_label_offset _ _ _ _ _) relocation_offset index virtual_module_offset
		real_module_offset module_offset_a first_symbol_n symbol_n symbol_a marked_offset_a0 xcoff_a text0
	= add_to_long_at_offset (-4+(virtual_module_offset-relocation_offset)+(real_label_offset-real_module_offset)) index text0;
	{
		real_label_offset = module_offset_a.[first_symbol_n+symbol_n];
	}
relocate_branch (Label _ /*TEXT_SECTION*/ offset module_n) relocation_offset index virtual_module_offset 
		real_module_offset module_offset_a first_symbol_n symbol_n symbol_a marked_offset_a0 xcoff_a text0
	= case symbol_a.[module_n] of {
		Module TEXT_SECTION virtual_label_offset _ _ _ _ _
			-> add_to_long_at_offset (-4+(virtual_label_offset-relocation_offset)+(real_label_offset-real_module_offset)) index text0;
		{
			real_label_offset = module_offset_a.[first_symbol_n+ /*symbol_n*/ module_n]+offset;
		}
	}
relocate_branch (ImportedLabel file_n symbol_n) relocation_offset index virtual_module_offset
		real_module_offset module_offset_a first_symbol_n _ symbol_a marked_offset_a0 xcoff_a text0
	| file_n<0
		= add_to_long_at_offset (-4+(virtual_module_offset-relocation_offset)+(real_label_offset-real_module_offset)) index text0;
		{
			real_label_offset = module_offset_a.[marked_offset_a0.[file_n + size marked_offset_a0]+symbol_n];
		}
		= case (symbol_a.[symbol_n]) of {
			Module TEXT_SECTION _ _ _ _ _ _
				-> add_to_long_at_offset (-4+(virtual_module_offset-relocation_offset)+(real_label_offset-real_module_offset)) index text0;
				{
					real_label_offset = module_offset_a.[first_symbol_n+symbol_n];
				}

//			_ -> abort ("relocate_branch "+++file_name+++" "+++toString file_n+++" "+++toString symbol_n);

			}
		{
			first_symbol_n = marked_offset_a0.[file_n];
			symbol_a=xcoff_a.[file_n].symbol_table.symbols;
		}
relocate_branch (ImportedLabelPlusOffset file_n symbol_n label_offset) relocation_offset index virtual_module_offset
		real_module_offset module_offset_a first_symbol_n _ symbol_a marked_offset_a0 xcoff_a text0
	=	case (symbol_a.[symbol_n]) of {
			Module TEXT_SECTION _ _ _ _ _ _
				->	add_to_long_at_offset (-4+(virtual_module_offset-relocation_offset)+(real_label_offset-real_module_offset)+label_offset) index text0;
				{
					real_label_offset = module_offset_a.[first_symbol_n+symbol_n];
				}	
		}
	{
		first_symbol_n = marked_offset_a0.[file_n];
		symbol_a=xcoff_a.[file_n].symbol_table.symbols;
	}

add_to_long_at_offset :: Int Int *{#Char} -> *{#Char};
add_to_long_at_offset w index array=:{[index]=v0,[index1]=v1,[index2]=v2,[index3]=v3}
	= {array & [index]=toChar new_v,[index1]=toChar (new_v>>8),[index2]=toChar (new_v>>16),[index3]=toChar (new_v>>24)};
	{		
		new_v=v+w;
		v = (toInt v0)+(toInt v1<<8)+(toInt v2<<16)+(toInt v3<<24);

		index1 = index+1;
		index2 = index+2;
		index3 = index+3;
	}

write_data_to_pe_files :: [[*{#Char}]] [SXcoff] Int Int {#Bool} {#Int} {#Int} XcoffArray *File -> *File;
write_data_to_pe_files [] [] first_symbol_n offset0 marked_bool_a module_offset_a marked_offset_a xcoff_a pe_file0
	= pe_file0;
write_data_to_pe_files [data_section_strings:data_section_list] [xcoff=:{n_symbols,symbol_table={data_symbols,symbols}}:xcoff_list] first_symbol_n offset0 marked_bool_a module_offset_a marked_offset_a xcoff_a pe_file0
	= write_data_to_pe_files data_section_list xcoff_list (first_symbol_n+n_symbols) offset1 marked_bool_a module_offset_a marked_offset_a xcoff_a pe_file1; {
		(offset1,pe_file1)
			= write_toc_or_data_to_pe_file data_symbols first_symbol_n offset0 symbols marked_bool_a
					module_offset_a marked_offset_a xcoff_a data_section_strings pe_file0;
	}

	write_toc_or_data_to_pe_file :: SymbolIndexList Int Int SSymbolArray {#Bool} {#Int} {#Int} XcoffArray [*{#Char}] *File -> (!Int,!*File);
	write_toc_or_data_to_pe_file EmptySymbolIndex first_symbol_n offset0 symbol_table marked_bool_a module_offset_a marked_offset_a0 xcoff_a data_section_strings pe_file0
		= (offset0,pe_file0);
	write_toc_or_data_to_pe_file (SymbolIndex module_n symbol_list) first_symbol_n offset0 symbol_table=:{[module_n] = symbol} marked_bool_a module_offset_a marked_offset_a0 xcoff_a data_section_strings pe_file0
		| marked_bool_a.[first_symbol_n+module_n]
			= case data_section_strings of {
				[data_a0:data_section_strings]
					-> write_toc_or_data_to_pe_file symbol_list first_symbol_n offset1 symbol_table marked_bool_a module_offset_a marked_offset_a0 xcoff_a data_section_strings pe_file1;
					{
						(offset1,pe_file1) = write_data_module_to_pe_file symbol offset0 module_offset_a marked_offset_a0 xcoff_a data_a0 pe_file0;
					}
				};
			= write_toc_or_data_to_pe_file symbol_list first_symbol_n offset0 symbol_table marked_bool_a module_offset_a marked_offset_a0 xcoff_a data_section_strings pe_file0;
		{}{
			write_data_module_to_pe_file :: Symbol Int {#Int} {#Int} XcoffArray *{#Char} *File -> (!Int,!*File);
			write_data_module_to_pe_file (Module section_n virtual_module_offset length virtual_address _ n_relocations relocations)
					offset0 module_offset_a=:{[o_i]=real_module_offset} marked_offset_a0 xcoff_a data_a0 pe_file0
				| section_n==DATA_SECTION
					= (aligned_offset0+length,fwrites data_a1 (write_zero_bytes_to_file (aligned_offset0-offset0) pe_file0));
					{
						aligned_offset0=(offset0+alignment_mask) bitand (bitnot alignment_mask);
						alignment_mask=dec (1<<alignment);					
						alignment=2;
					
						data_a1 = relocate_data 0 n_relocations virtual_module_offset virtual_address real_module_offset relocations
												first_symbol_n module_offset_a marked_offset_a0 symbol_table xcoff_a data_a0;
					}
				{
					o_i=first_symbol_n+module_n;
				}
		}

(THEN) infixl;
(THEN) a f :== f a;

write_idata library_list n_libraries n_imported_symbols idata_vaddr xcoff_file0
	=	xcoff_file0
			THEN write_import_descriptors library_list (idata_vaddr+descriptor_block_size+thunk_data_size) (idata_vaddr+descriptor_block_size)
			THEN write_thunk_data library_list (idata_vaddr+descriptor_block_size+thunk_data_size+file_name_block_size)
			THEN write_library_file_names library_list
			THEN write_imported_symbols library_list;
	{
		descriptor_block_size = 20*(n_libraries+1);
		thunk_data_size = (n_imported_symbols+n_libraries)<<2;
		file_name_block_size = compute_file_names_size library_list 0;
		
			compute_file_names_size EmptyLibraryList s = s;
			compute_file_names_size (Library file_name _ _ libraries) s
				= compute_file_names_size libraries ((s+size file_name+2) bitand (-2));

		write_import_descriptors EmptyLibraryList library_name_offset thunk_data_offset pe_file0
			= pe_file0 FWI 0 FWI 0 FWI 0 FWI 0 FWI 0;
		write_import_descriptors (Library file_name _ n_symbols libraries) library_name_offset thunk_data_offset pe_file0
			= write_import_descriptors libraries (library_name_offset+((2+size file_name) bitand (-2)))
				(thunk_data_offset+(n_symbols+1)<<2) (pe_file0 FWI 0 FWI 0 FWI 0 FWI library_name_offset FWI thunk_data_offset);
	
		write_library_file_names EmptyLibraryList pe_file0
			= pe_file0;
		write_library_file_names (Library file_name _ _ libraries) pe_file0
			= write_library_file_names libraries pe_file2;
			{
				pe_file2
					| size file_name bitand 1==0
						= pe_file1 FWC '\0';
						= pe_file1;
				pe_file1 = pe_file0 FWS file_name FWC '\0';
			}

		write_thunk_data EmptyLibraryList symbols_offset0 coff_file0
			= coff_file0;
		write_thunk_data (Library _ imported_symbols _ libraries) symbols_offset0 coff_file0
			= write_thunk_data libraries symbols_offset1 (coff_file1 FWI 0);
			{
				(symbols_offset1,coff_file1)=write_library_thunk_data imported_symbols symbols_offset0 coff_file0;
				
				write_library_thunk_data EmptyLibrarySymbolsList symbols_offset0 coff_file0
				 	= (symbols_offset0,coff_file0);
 				write_library_thunk_data (LibrarySymbol symbol_name symbols) symbols_offset0 coff_file0
 					= write_library_thunk_data symbols symbols_offset1 (coff_file0 FWI symbols_offset0);
 					{
 						symbols_offset1 = (symbols_offset0+size symbol_name+4) bitand (-2)
 					}
			}

		write_imported_symbols EmptyLibraryList coff_file0
			= coff_file0;
		write_imported_symbols (Library _ imported_symbols _ libraries) coff_file0
			= write_imported_symbols libraries (write_library_symbols imported_symbols coff_file0);
			{
				write_library_symbols EmptyLibrarySymbolsList coff_file0
					= coff_file0;
				write_library_symbols (LibrarySymbol symbol_name symbols) coff_file0
					= write_library_symbols symbols coff_file2;
					{
						coff_file2
							| size symbol_name bitand 1==0
								= coff_file1 FWC '\0';
								= coff_file1;
						coff_file1 = coff_file0
							FWC '\0' FWC '\0' FWS (remove_at_size symbol_name) FWC '\0';
					}
			}

		remove_at_size s = remove_at_size_i (size s-1);
		{
			remove_at_size_i -1
				= s;
			remove_at_size_i i
				| s.[i]<>'@'
					= remove_at_size_i (i-1);
					= s % (0,i-1) +++t;
					{
						t :: {#Char};
						t = createArray (size s-i) '\0';
					}
		}
	}

compute_idate_strings_size :: LibraryList Int Int Int {#Bool} -> (!Int,!Int);
compute_idate_strings_size EmptyLibraryList idata_string_size0 n_imported_symbols0 symbol_n marked_bool_a
	=	(idata_string_size0,n_imported_symbols0);
compute_idate_strings_size (Library file_name imported_symbols _ libraries) idata_string_size0 n_imported_symbols0 symbol_n0 marked_bool_a
	=	compute_idate_strings_size libraries idata_string_size2 n_imported_symbols1 symbol_n1 marked_bool_a;
	{
		idata_string_size1 = (idata_string_size0+size file_name+2) bitand (-2);

		(idata_string_size2,n_imported_symbols1,symbol_n1)
			= idata_strings_size_of_symbol_names imported_symbols idata_string_size1 n_imported_symbols0 symbol_n0 marked_bool_a;
		
		idata_strings_size_of_symbol_names :: LibrarySymbolsList Int Int Int {#Bool} -> (!Int,!Int,!Int);
		idata_strings_size_of_symbol_names EmptyLibrarySymbolsList idata_string_size0 n_imported_symbols0 symbol_n marked_bool_a
			= (idata_string_size0,n_imported_symbols0,symbol_n);
		idata_strings_size_of_symbol_names (LibrarySymbol symbol_name imported_symbols) idata_string_size0 n_imported_symbols0 symbol_n marked_bool_a
			| not marked_bool_a.[symbol_n]
				= idata_strings_size_of_symbol_names imported_symbols idata_string_size0 n_imported_symbols0 (symbol_n+2) marked_bool_a;
				= idata_strings_size_of_symbol_names imported_symbols ((idata_string_size0+size symbol_name+4) bitand (-2)) (inc n_imported_symbols0) (symbol_n+2) marked_bool_a;
	}

write_zero_longs_to_file n pe_file0
	| n==0
		= pe_file0;
		= write_zero_longs_to_file (dec n) (fwritei 0 pe_file0);

write_zero_bytes_to_file n pe_file0
	| n==0
		= pe_file0;
		= write_zero_bytes_to_file (dec n) (fwritec '\0' pe_file0);

write_nop_bytes_to_file n pe_file0
	| n==0
		= pe_file0;
		= write_nop_bytes_to_file (dec n) (fwritec (toChar 0x90) pe_file0);

find_main_symbol :: NamesTable -> (!Bool,!Int,!Int);
find_main_symbol names_table0
	= case (main_names_table_element) of {
		(NamesTableElement _ symbol_n file_n _)
			-> (True,symbol_n,file_n);
		_
			-> (False,0,0);
	}
	{
		(main_names_table_element,_)=find_symbol_in_symbol_table "_mainCRTStartup" names_table0;
	}

/*
calculate_names_table_size :: Int Int Int SNamesTable -> (!Int,!Int);
calculate_names_table_size index n_strings strings_size names_table
	| index==4096
		= (n_strings,strings_size);
calculate_names_table_size index n_strings strings_size names_table=:{[index]=names}
	= calculate_names_table_size (inc index) n_strings1 strings_size1 names_table;
	{
		(n_strings1,strings_size1) = calculate_names_list_size names n_strings strings_size;
		
		calculate_names_list_size :: NamesTableElement Int Int -> (!Int,!Int);
		calculate_names_list_size EmptyNamesTableElement n_strings strings_size
			= (n_strings,strings_size);
		calculate_names_list_size (NamesTableElement name _ _ names) n_strings strings_size
			= calculate_names_list_size names (inc n_strings) (strings_size+(size name));
	}
*/

link_xcoff_files :: ![String] ![String] !String !Int !Bool !Files -> (!Bool,![String],!Files);
link_xcoff_files file_names library_file_names application_file_name stack_size open_console_window files

	/*
			#! (ok,f,files) = fopen "object_files" FWriteText files;
			#! f = write_file_names file_names f;
				with {
					write_file_names [] f
						= f;
					write_file_names [file_name:file_names] f
						# f=fwrites file_name f;
						# f=fwritec '\n' f;
						= write_file_names file_names f;
				}
			#! (ok,files) = fclose f files;
	*/

#!
	one_pass_link = True;
	(read_xcoff_files_errors,sections,n_xcoff_files,xcoff_list0,names_table0,files1)
		= read_xcoff_files file_names create_names_table one_pass_link files 0;
	| not_nil read_xcoff_files_errors
		= (False,read_xcoff_files_errors,files1);
#!
	n_libraries = length library_file_names;
	(read_library_errors,library_list0,n_library_symbols,files2,names_table1)
		= read_library_files library_file_names (~n_libraries) 0 files1 names_table0;
	| not_nil read_library_errors
		= (False,read_library_errors,files2);
#!
	(undefined_symbols0,xcoff_list1,names_table2)=import_symbols_in_xcoff_files [] xcoff_list0 names_table1;
	| not_nil undefined_symbols0
		= (False,["Undefined symbols:" : undefined_symbols0],files2);
#!
		(main_symbol_error,main_symbol_n,main_file_n) = find_main_symbol names_table2;
	| not  main_symbol_error
		= (False,["Symbol \"main\" not defined"],files2);
#!
	(ok,files5)
		= write_xcoff_file application_file_name n_xcoff_files xcoff_list1 n_libraries n_library_symbols library_list0 main_symbol_n main_file_n one_pass_link sections stack_size open_console_window files2;
	| not ok
		= (False,["Link error: Cannot write the application file '"+++toString application_file_name+++"'"],files5);
	= (True,[],files5);
/*
	= (True,[
			"\nNumber of symbols: ",toString n_xcoff_symbols,
			"\nData relocations: ",toString n_data_relocations,
			"\nText relocations: ",toString n_text_relocations,
			"\nNumber of strings: ",toString n_strings,
			"\nSize of strings: ",toString strings_size
			],files5);
*/

xcoff_list_to_array n_xcoff_files xcoff_list
	= fill_array 0 xcoff_list (createArray n_xcoff_files empty_xcoff);
{		
	fill_array file_n [] xcoff_a
		= xcoff_a;
	fill_array file_n [xcoff:xcoff_list] xcoff_a
		= fill_array (inc file_n) xcoff_list {xcoff_a & [file_n]=xcoff};
}

xcoff_array_to_list :: Int *{#*SXcoff} -> [*SXcoff];
xcoff_array_to_list i a0
	| i >= size a0
		= [];
		= let! {a_i} in [a_i : xcoff_array_to_list (inc i) a2];
		{
			(a_i,a2)=replace a0 i empty_xcoff;
		}
/*
print :: !String !a -> Bool;
print s a
	# (error,_)=ferror (fwrites s stderr);
	| not error
		= True;
*/

write_xcoff_file :: .{#Char} Int *[*SXcoff] Int Int .LibraryList Int Int Bool *Sections Int Bool *Files -> (!Bool,*Files);
write_xcoff_file application_file_name n_xcoff_files xcoff_list1 n_libraries n_library_symbols library_list0 
		main_symbol_n main_file_n one_pass_link sections stack_size open_console_window files2
#
	(n_xcoff_symbols,xcoff_list2) = n_symbols_of_xcoff_list 0 xcoff_list1;

	(marked_bool_a0,offset_a,xcoff_a0)
		= create_xcoff_boolean_array n_xcoff_files n_xcoff_symbols n_libraries n_library_symbols library_list0 xcoff_list2;

	(marked_bool_a1,xcoff_a1) = mark_used_modules main_symbol_n main_file_n marked_bool_a0 offset_a xcoff_a0;

	xcoff_list3 = xcoff_array_to_list 0 xcoff_a1;

	xcoff_list4 = split_data_symbol_lists_of_files2 offset_a marked_bool_a1 xcoff_list3;
	
	(idata_strings_size,n_imported_symbols)
		= compute_idate_strings_size library_list0 0 0 n_xcoff_symbols marked_bool_a1 ;

	text_vaddr = 0x401000;

	(text_end_vaddr0,module_offset_a0)
		= compute_module_offsets (n_xcoff_symbols+n_library_symbols) text_vaddr xcoff_list4 marked_bool_a1;

	text_end_vaddr = text_end_vaddr0+6*n_imported_symbols;

	data_vaddr = (text_end_vaddr+4095) bitand (-4096);

	(data_end_vaddr,module_offset_a1)
		= compute_data_module_offsets xcoff_list4 data_vaddr 0 module_offset_a0;
	
	pe_data_section_size0 = data_end_vaddr-data_vaddr;

	bss_vaddr = (data_end_vaddr+4095) bitand (-4096);

	pe_text_section_size1=text_end_vaddr-text_vaddr;
		
	xcoff_a2 = xcoff_list_to_array n_xcoff_files xcoff_list4;	

	(bss_end_vaddr,module_offset_a2)
		= compute_bss_module_offsets xcoff_list4 bss_vaddr 0 marked_bool_a1 module_offset_a1;

	bss_section_size = bss_end_vaddr-bss_vaddr;

	text_section_size_4096=(pe_text_section_size1+4095) bitand (-4096);
	data_section_size_4096=(pe_data_section_size0+4095) bitand (-4096);
	bss_section_size_4096=(bss_section_size+4095) bitand (-4096);

	idata_vaddr=0x1000+text_section_size_4096+data_section_size_4096+bss_section_size_4096;

	thunk_data_offset = 0x400000+idata_vaddr+20*(n_libraries+1);

	(library_list1,_/*text_end_vaddr*/,_,module_offset_a3)
		= compute_imported_library_symbol_offsets library_list0 text_end_vaddr0 thunk_data_offset (~n_libraries) n_xcoff_symbols marked_bool_a1 module_offset_a2;
	
	idata_section_size = (n_libraries+1)*20 + (n_imported_symbols+n_libraries)<<2 + idata_strings_size;

	main_offset=module_offset_a3.[offset_a.[main_file_n]+main_symbol_n];

	(create_ok,pe_file,files3)
		= create_coff_file application_file_name pe_text_section_size1 pe_data_section_size0 bss_section_size
							idata_section_size main_offset stack_size open_console_window files2;
	| not create_ok
		= (False,files3);
		# (data_sections0,pe_file,files4)
			= write_code_to_pe_files xcoff_list4 0 0 marked_bool_a1 module_offset_a3 offset_a xcoff_a2 one_pass_link sections pe_file files3;

		  pe_text_section_size_512=(pe_text_section_size1+511) bitand (-512);

		  pe_file = pe_file
			THEN write_imported_library_functions_code library_list1 thunk_data_offset
			THEN write_zero_bytes_to_file (pe_text_section_size_512-pe_text_section_size1);

		  pe_data_section_size_512=(pe_data_section_size0+511) bitand (-512);
		  idata_section_size_512=(idata_section_size+511) bitand (-512);

		  pe_file = pe_file
			THEN write_data_to_pe_files data_sections0 xcoff_list4 0 0 marked_bool_a1 module_offset_a3 offset_a xcoff_a2
			THEN write_zero_bytes_to_file (pe_data_section_size_512-pe_data_section_size0)
			THEN write_idata library_list1 n_libraries n_imported_symbols idata_vaddr
			THEN write_zero_bytes_to_file (idata_section_size_512-idata_section_size);

		  (ok,files5)=fclose pe_file files4;
		= (ok,files5);
