implementation module RelocSection;

import StdArray, StdEnum;

from ExtInt import roundup_to_multiple;
from ExtFile import FWW,FWI,write_zero_bytes_to_file;
import Sections, /*PlatformLinkOptions,*/ xcoff, State, ExtString;

:: *RelocBlock = {
	page_rva					:: !Int,
	n_relocations				:: !Int,
	relocs						:: *[Int]
};

EmptyRelocBlock :: *RelocBlock;
EmptyRelocBlock
	= { RelocBlock |
		page_rva				= 0,
		n_relocations			= 0,
		relocs					= []
	};

:: *RelocBlocks :== !*{#RelocBlock};

/*
	#! (relocs_l,relocs_section_header,state,section_headers)
		= compute_relocs_section start_rva base_va section_header_kinds n_imported_symbols section_headers state;
*/

//import DebugUtilities;

compute_relocs_section :: !Int !Int !Int !Int !SectionHeader !*{!SectionHeader} !State -> !(!SectionHeader,!*[RelocBlock],!*{!SectionHeader},!*State);
compute_relocs_section i_reloc_section_header start_rva base_va n_imported_symbols reloc_section_header section_header_a state
	// assume order: .text, .data and .bss
	// .bss section
	# (ok,_,bss_section,section_header_a)
		= get_section_index /*RelocSectionHeader*/ 0 BssSectionHeader section_header_a;
	| not ok
		= abort "compute_relocs_section: internal error";
	
	
	#! {section_rva=bss_section_rva}
		= sh_get_pd_section_header /*reloc_section_header*/bss_section ;
		
//	#! bss_section_rva
//		= pd_get_section_rva reloc_section_header;
	#! s_virtual_bss_data 
		= sh_get_s_virtual_data /*reloc_section_header*/ bss_section;
	#! n_pages
		= (bss_section_rva + (roundup_to_multiple s_virtual_bss_data page_size)) / page_size;

	// compute fixup blocks
	#! relocs_a
		= { {EmptyRelocBlock & page_rva = page_size * i} \\ i <- [0..dec n_pages] };			
	#! (relocs_a,state,section_header_a)
		= count_relocations_per_xcoffs 0 0 relocs_a base_va section_header_a state n_imported_symbols ;
	#! (s_relocs_section,relocs_l)
		= compute_relocs_size 0 n_pages relocs_a [] 0;
		
	// update section header
	#! pd_section_header = {
			section_name				= ".reloc"
		,	section_rva					= start_rva
		,	section_flags				= IMAGE_SCN_CNT_INITIALIZED_DATA bitor
										  IMAGE_SCN_MEM_DISCARDABLE bitor
										  IMAGE_SCN_MEM_READ
		};
	#! reloc_section_header = reloc_section_header
		DSH sh_set_pd_section_header pd_section_header
		DSH sh_set_virtual_data s_relocs_section;
	= (reloc_section_header,relocs_l,section_header_a,state);
where {
	compute_relocs_size page_n n_pages relocs_a relocs_l relocs_section_i 
		| page_n == n_pages
			= (relocs_section_i,relocs_l);

			#! (page_n_reloc_block=:{n_relocations},relocs_a)
				= replace relocs_a page_n EmptyRelocBlock;
			| n_relocations == 0
				= compute_relocs_size (inc page_n) n_pages relocs_a relocs_l relocs_section_i;
				
		//		#! relocs_section_i
		//			= roundup_to_multiple relocs_section_i 4;
				#! s_fixup_block
					= s_fixup_header + n_relocations * s_fixup_entry;
			
				= compute_relocs_size (inc page_n) n_pages relocs_a [page_n_reloc_block:relocs_l] (relocs_section_i + s_fixup_block);	
} // compute_relocs_section	


//count_relocations_per_xcoffs :: !Int Int {*RelocBlock} Int !*{!SectionHeader} !*State .b -> *({*RelocBlock},*State,!*{!SectionHeader});
count_relocations_per_xcoffs file_n first_symbol_n relocs_a base_va section_headers state=:{n_xcoff_files} n_imported_symbols
	| file_n == n_xcoff_files
		#! (relocs_a,state,section_headers)
			= count_relocations_in_jump_table relocs_a state section_headers n_imported_symbols; //section_header_kinds;
		= (relocs_a,state,section_headers); 
		
		#! (n_symbols,state)
			= select_n_symbols file_n state;
		#! (relocs_a,state)
			= count_relocations_per_xcoff relocs_a state;	
		= count_relocations_per_xcoffs (inc file_n) (first_symbol_n + n_symbols) relocs_a base_va section_headers state n_imported_symbols ;
where {
	count_relocations_per_xcoff relocs_a state
		// .text
		#! (text_symbols,state)
			= selacc_text_symbols file_n state;
		#! (relocs_a,state)
			= count_relocations_per_section_kind text_symbols relocs_a state;

		// .data
		#! (data_symbols,state)
			= selacc_data_symbols file_n state;
		#! (relocs_a,state)
			= count_relocations_per_section_kind data_symbols relocs_a state;
			
		// .bss 
		#! (bss_symbols,state)
			= selacc_bss_symbols file_n state;
		#! (relocs_a,state)
			= count_relocations_per_section_kind bss_symbols relocs_a state;
		= (relocs_a,state);
	where {
		count_relocations_per_section_kind EmptySymbolIndex relocs_a state
			= (relocs_a,state);
			
		count_relocations_per_section_kind (SymbolIndex module_n sils) relocs_a state
			#! (module_symbol,state)
				= sel_symbol file_n module_n state;
			#! (relocs_a,state)
				= count_relocations_per_module module_symbol relocs_a state;
			= count_relocations_per_section_kind sils relocs_a state;
		where {
			count_relocations_per_module (Module _ _ virtual_address _ n_relocations relocations) relocs_a state
				#! (marked_module, state)
					= selacc_marked_bool_a (first_symbol_n + module_n) state;
				| not marked_module
					// not marked_module implies its symbols not marked
					= (relocs_a,state);
					
					// marked module, the symbols it contains are then also marked
					#! (module_va,state)
						= selacc_module_offset_a (first_symbol_n + module_n) state;
					= count_relocations 0 n_relocations relocations (module_va - base_va) relocs_a state;
			
			
			where {
			//	count_relocations relocation_n n_relocations relocations module_rva relocs_a state
				//	= id (count_relocations2 relocation_n n_relocations relocations module_rva relocs_a state);
					
				count_relocations relocation_n n_relocations relocations module_rva relocs_a state
					| relocation_n == n_relocations
						= (relocs_a,state);
						
						// conditions: symbol is marked && is absolute reference
						// {relative,absolute}  {marked,unmarked} {definition,reference} symbol;
						| relocation_type == REL_REL32
							= count_relocations (inc relocation_n) n_relocations relocations module_rva relocs_a state;
							
							// a marked, absolute {definition,reference} 
							#! (reloc_symbol,state)
								= sel_symbol file_n relocation_symbol_n state;
											
								// a marked, absolute referenced symbol
								#! rva
									= module_rva + (relocation_offset - virtual_address);
								#! page_n 
									= rva / page_size;
								
								// compute fixups (=relocations)
							/*
								| True
									# s
										= "size relocs_a: " +++ toString (size relocs_a) +++
										  "page_n: " +++ toString (page_n);
									= abort s;
							*/
									
								#! (page_relocs,relocs_a)
									= replace relocs_a page_n EmptyRelocBlock;
								#! mask
									= dec (1 << i_reloc_type);
								#! offset
									= (rva - page_relocs.page_rva) bitand mask;
								#! updated_page_relocs
									= { page_relocs &
										n_relocations = inc page_relocs.n_relocations,
										relocs = [(IMAGE_REL_BASED_HIGHLOW << i_reloc_type) bitor offset : page_relocs.relocs]
									};
								#! relocs_a
									= { relocs_a & [page_n] = updated_page_relocs };
								= count_relocations (inc relocation_n) n_relocations relocations module_rva relocs_a state;
				where {
					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;
					
/*	
					is_reference (Module _ _ _ _ _ _ _)
						= False;
					is_reference _
						= True;
*/
						
				} // count_relocations
			
			} // count_relocations_per_module
		
		} // count_reloctions_per_section_kind  
	
	} // count_reloctions_per_xcoff


// count_relocations_per_xcoffs :: !Int Int {*RelocBlock} Int !*{!SectionHeader} !*State .b -> *({*RelocBlock},*State,!*{!SectionHeader});
	
	count_relocations_in_jump_table :: {*RelocBlock} !*State !*{!SectionHeader} !Int -> ({*RelocBlock},!*State,!*{!SectionHeader});
	count_relocations_in_jump_table relocs_a state section_headers n_imported_symbols //section_header_kinds
/*
		#! i_text_section
			= get_section_index is_text_section section_headers //section_header_kinds;		
		#! (text_section,section_headers)
			= section_headers![i_text_section];		
*/
		#! (ok,_,text_section,section_headers)
			= get_section_index 0 TextSectionHeader section_headers;	
		#! pd_text_section
			= sh_get_pd_section_header text_section;

			
			
			
						
		#! jump_table_rva
//	WAS:		= text_section.section_rva + text_section.s_virtual_data - (n_imported_symbols * 6);
			= pd_text_section.section_rva + (sh_get_s_virtual_data text_section) - (n_imported_symbols * 6);

		#! relocs_a
			= count_jump_relocations 0 n_imported_symbols (jump_table_rva + 2) relocs_a;
		= (relocs_a,state,section_headers);
	where {	
		count_jump_relocations imported_symbol_n n_imported_symbols jump_table_entry_rva relocs_a
			| imported_symbol_n == n_imported_symbols
				= relocs_a;
				
				#! page_n
					= jump_table_entry_rva / page_size;
				#! (page_relocs,relocs_a)
					= replace relocs_a page_n EmptyRelocBlock;
				#! mask
					= dec (1 << i_reloc_type);
				#! offset
					= (jump_table_entry_rva - page_relocs.page_rva) bitand mask;
				#! updated_page_relocs
					= { page_relocs &
						n_relocations = inc page_relocs.n_relocations,
						relocs = [(IMAGE_REL_BASED_HIGHLOW << i_reloc_type) bitor offset : page_relocs.relocs]
					};
				#! relocs_a
					= { relocs_a & [page_n] = updated_page_relocs };
				= count_jump_relocations (inc imported_symbol_n) n_imported_symbols (jump_table_entry_rva + 6) relocs_a;
	} // count_relocations_in_jump_table
}

write_reloc_section :: !*File !*[*RelocBlock] -> .File;
write_reloc_section pe_file relocs_info
	
	#! (s_relocs_section,pe_file)
		= write_reloc_blocks (reverse relocs_info) 0 pe_file; 
	= pe_file;
where {
	//write_reloc_blocks :: !*[*RelocBlock] !Int !*File -> !(!Int,!*File);
	write_reloc_blocks [] relocs_section_i pe_file
		= (relocs_section_i,pe_file);
		
	write_reloc_blocks [reloc_block=:{page_rva,n_relocations,relocs} : reloc_blocks] relocs_section_i pe_file
		#! block_size
			= s_fixup_header + n_relocations * s_fixup_entry;
		#! pe_file = pe_file
			FWI page_rva
			FWI block_size;
		#! pe_file
			= foldl (\pe_file r -> pe_file FWW r) pe_file relocs;
		= write_reloc_blocks reloc_blocks (relocs_section_i + block_size) pe_file;

/*
		#! block_size
			= s_fixup_header + n_relocations * s_fixup_entry;
		#! pe_file = pe_file
			FWI page_rva
			FWI block_size;
		#! pe_file
			= foldl (\pe_file r -> pe_file FWW r) pe_file relocs;
		
		// align
		#! unaligned_block_end
			= relocs_section_i + block_size;
			
		#! aligned_block_end
			= roundup_to_multiple unaligned_block_end 4;
		#! delta
			= aligned_block_end - unaligned_block_end;
		| delta > 3
			= abort "delta te groot";
		#! pe_file
			= write_zero_bytes_to_file delta pe_file;


		= write_reloc_blocks reloc_blocks aligned_block_end pe_file;
*/
}