implementation module elf_relocations;

import StdInt,StdChar,StdClass,StdArray,StdFile,StdBool;
import elf_linker_types;

R_X86_64_NONE:==0;
R_X86_64_64:==1;
R_X86_64_PC32:==2;
R_X86_64_32:==10;
R_X86_64_32S:==11;

TEXT_SECTION:==1;

(BYTE) string i :== toInt (string.[i]);
                                                                                                                        
(WORD) :: !{#Char} !Int -> Int;
(WORD) string i = (string BYTE (i+1)<<8) bitor (string BYTE i);
                                                                                                                        
(LONG) :: !{#Char} !Int -> Int;
(LONG) string i
    = (string BYTE (i+3)<<24) bitor (string BYTE (i+2)<<16) bitor (string BYTE (i+1)<<8) bitor (string BYTE i);

(POINTER) :: !{#Char} !Int -> Int;
(POINTER) string i
	= (string BYTE (i+7)<<56) bitor (string BYTE (i+6)<<48) bitor (string BYTE (i+5)<<40) bitor (string BYTE (i+4)<<32) bitor
	  (string BYTE (i+3)<<24) bitor (string BYTE (i+2)<<16) bitor (string BYTE (i+1)<<8) bitor (string BYTE i);

(TBYTE) :: !{#Char} !Int -> Int;
(TBYTE) string i = (string BYTE (i+2)<<16) bitor (string BYTE (i+1)<<8) bitor (string BYTE i);

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

(FWL) infixl;
(FWL) f i :== fwritei (i>>32) (fwritei i f);

(FWP) infixl;
(FWP) f i :== fwritei (i>>32) (fwritei i f);

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

write_elf_header :: !*File -> *File;
write_elf_header file
	= file
			FWI 0x464c457f
			FWI 0x00010102
			FWI 0
			FWI 0
			FWI 0x003e0001
			FWI 1
			FWL 0
			FWL 0
			FWL 0x40
			FWI 0
			FWI 0x00000040
			FWI 0x00400000
			FWI 0x00010009;

write_shstrtab_end :: !*File -> *File;
write_shstrtab_end file
	= file
            FWS ".rela.text\000"
            FWS ".rela.data\000"
            FWS ".symtab\000"
            FWS ".strtab\000"
            FWS "\000\000"
            ;

write_symbol :: !Int !Int !Int !Int !*File -> *File;
write_symbol string_offset info_other_shndx value size file
	= file FWI string_offset FWI info_other_shndx FWL value FWL size;

count_relocations :: !Int !{#Char} !{!Symbol} !XcoffArray -> Int;
count_relocations old_n_module_relocations relocations symbol_array xcoff_a
	= count_relocations 0 0;
{
		count_relocations :: !Int !Int -> Int;
		count_relocations relocation_n n_module_relocations
			| relocation_n==old_n_module_relocations
				= n_module_relocations;
			# relocation_index=relocation_n * SIZE_OF_RELOCATION;
			  relocation_type=get_relocation_type relocations relocation_index;
			| relocation_type<>R_X86_64_PC32
				| relocation_type==R_X86_64_NONE
					= count_relocations (relocation_n+1) n_module_relocations;
					= count_relocations (relocation_n+1) (n_module_relocations+1);
			# relocation_symbol_n=get_relocation_symbol_n relocations relocation_index;
			= case symbol_array.[relocation_symbol_n] of {
				UndefinedLabel symbol_n
					= count_relocations (relocation_n+1) (n_module_relocations+1);
				Module section_n virtual_label_offset _ _ _ _ _ _
					| section_n==TEXT_SECTION
						= count_relocations (relocation_n+1) n_module_relocations;
						= count_relocations (relocation_n+1) (n_module_relocations+1);
				Label section_n label_offset section_symbol_n
					# (Module section_n virtual_label_offset _ _ _ _ _ _) = symbol_array.[section_symbol_n];
					| section_n==TEXT_SECTION
						= count_relocations (relocation_n+1) n_module_relocations;
						= count_relocations (relocation_n+1) (n_module_relocations+1);
				ImportedLabel file_n symbol_n
					# (Module section_n _ _ _ _ _ _ _) = xcoff_a.[file_n].symbol_table.symbols.[symbol_n];
					| section_n==TEXT_SECTION
						= count_relocations (relocation_n+1) n_module_relocations;
						= count_relocations (relocation_n+1) (n_module_relocations+1);
				ImportedLabelPlusOffset file_n symbol_n label_offset
					# (Module section_n _ _ _ _ _ _ _) = xcoff_a.[file_n].symbol_table.symbols.[symbol_n];
					| section_n==TEXT_SECTION
						= count_relocations (relocation_n+1) n_module_relocations;
						= count_relocations (relocation_n+1) (n_module_relocations+1);
					= count_relocations (relocation_n+1) n_module_relocations;
			  };
}

write_module_relocations :: !Symbol !Int !Int !SSymbolArray !XcoffArray !{#Int} !{#Int} !*File -> *File;
write_module_relocations (Module _ _ _ _ _ n_module_relocations relocations _) module_offset first_symbol_n symbol_array xcoff_a module_offset_a offset_a pe_file
	= write_relocations 0 n_module_relocations relocations pe_file;
{
	write_relocations relocation_n n_relocations relocations pe_file
		| relocation_n==n_relocations
			= pe_file;
			#	relocation_index=relocation_n * SIZE_OF_RELOCATION;
				relocation_type=get_relocation_type relocations relocation_index;
			| relocation_type==R_X86_64_NONE
				= write_relocations_none relocation_n n_relocations relocations pe_file;
			#	relocation_symbol_n=get_relocation_symbol_n relocations relocation_index;
				relocation_offset=relocations LONG relocation_index;
				relocation_addend=relocations POINTER (relocation_index+16);
			= case symbol_array.[relocation_symbol_n] of {
				Module section_n _ _ _ _ _ _ _
					| relocation_type==R_X86_64_PC32 && section_n==TEXT_SECTION
						= write_relocations_none relocation_n n_relocations relocations pe_file;						
						# real_label_offset=module_offset_a.[first_symbol_n+relocation_symbol_n];
						# relocation_addend=relocation_addend+real_label_offset;
						= write_relocations2 relocation_offset relocation_type section_n relocation_addend relocation_n n_relocations relocations pe_file;
				Label section_n label_offset section_symbol_n
					# (Module section_n virtual_label_offset _ _ _ _ _ _) = symbol_array.[section_symbol_n];
					| relocation_type==R_X86_64_PC32 && section_n==TEXT_SECTION
						= write_relocations_none relocation_n n_relocations relocations pe_file;
						# real_label_offset=module_offset_a.[first_symbol_n+section_symbol_n];
						# relocation_addend=relocation_addend+real_label_offset+label_offset-virtual_label_offset;
						= write_relocations2 relocation_offset relocation_type section_n relocation_addend relocation_n n_relocations relocations pe_file;
				ImportedLabelPlusOffset file_n symbol_n label_offset
					# (Module section_n _ _ _ _ _ _ _) = xcoff_a.[file_n].symbol_table.symbols.[symbol_n];
					| relocation_type==R_X86_64_PC32 && section_n==TEXT_SECTION
						= write_relocations_none relocation_n n_relocations relocations pe_file;
						# real_label_offset = module_offset_a.[offset_a.[file_n]+symbol_n];
						# relocation_addend=relocation_addend+real_label_offset+label_offset;
						= write_relocations2 relocation_offset relocation_type section_n relocation_addend relocation_n n_relocations relocations pe_file;
				UndefinedLabel symbol_n
					# relocation_symbol_n=symbol_n+4;
					= write_relocations2 relocation_offset relocation_type relocation_symbol_n relocation_addend relocation_n n_relocations relocations pe_file;
				ImportedLabel file_n symbol_n // for commons in .bss
					# (Module section_n _ _ _ _ _ _ _) = xcoff_a.[file_n].symbol_table.symbols.[symbol_n];
					| relocation_type==R_X86_64_PC32 && section_n==TEXT_SECTION
						= write_relocations_none relocation_n n_relocations relocations pe_file;
						# real_label_offset = module_offset_a.[offset_a.[file_n]+symbol_n];
						# relocation_addend=relocation_addend+real_label_offset;
						= write_relocations2 relocation_offset relocation_type section_n relocation_addend relocation_n n_relocations relocations pe_file;
//				_
//					= write_relocations2 relocation_offset relocation_type relocation_symbol_n relocation_addend relocation_n n_relocations relocations pe_file;
			};

	write_relocations2 relocation_offset relocation_type relocation_symbol_n relocation_addend relocation_n n_relocations relocations pe_file
		#	pe_file = pe_file FWL (module_offset+relocation_offset) FWL (relocation_type bitor (relocation_symbol_n<<32)) FWL relocation_addend;
		=	write_relocations (relocation_n+1) n_relocations relocations pe_file;

	write_relocations_none relocation_n n_relocations relocations pe_file
		=	write_relocations (relocation_n+1) n_relocations relocations pe_file;
}

relocate_text :: !.Int Int Int Int Int {#Char} {#SXcoff} {#Int} {#Int} {!Symbol} !*{#.Char} -> .{#Char};
relocate_text n_relocations virtual_address real_module_offset virtual_module_offset first_symbol_n relocations xcoff_a marked_offset_a0 module_offset_a  symbol_a text_a0
	= relocate_text 0 symbol_a text_a0;
	{
		relocate_text relocation_n symbol_a text_a0
			| relocation_n==n_relocations
				= text_a0;
				#	relocation_index=relocation_n * SIZE_OF_RELOCATION;
					relocation_type=get_relocation_type relocations relocation_index;
					text1 = case relocation_type of {
						R_X86_64_PC32
							#	relocation_symbol_n=get_relocation_symbol_n relocations relocation_index;
								relocation_offset=relocations LONG relocation_index;
								relocation_addend=relocations POINTER (relocation_index+16);
							-> relocate_branch symbol_a.[relocation_symbol_n] relocation_offset relocation_addend (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;
						R_X86_64_32S
							-> text_a0;
						R_X86_64_32
							-> text_a0;
						R_X86_64_NONE
							-> text_a0;
					}
				= relocate_text (relocation_n+1) symbol_a text1;
	}

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;
		#	relocation_index=relocation_n * SIZE_OF_RELOCATION;
			relocation_type=get_relocation_type text_relocations relocation_index;
			relocation_symbol_n=get_relocation_symbol_n text_relocations relocation_index;
			relocation_offset=text_relocations LONG relocation_index;

			data1 = relocate_symbol relocation_type data0;
					with {
						relocate_symbol :: Int *{#Char} -> *{#Char};
						relocate_symbol reloction_type data0
							| relocation_type==R_X86_64_32S || relocation_type==R_X86_64_32 || relocation_type==R_X86_64_64
								= data0;
					}
		= relocate_data (relocation_n+1) 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;

relocate_branch :: Symbol Int Int Int Int Int {#Int} Int Int {!Symbol} {#Int} XcoffArray *{#Char} -> *{#Char};
relocate_branch (Module section_n virtual_label_offset _ _ _ _ _ _) relocation_offset relocation_addend index virtual_module_offset
		real_module_offset module_offset_a first_symbol_n symbol_n symbol_a marked_offset_a0 xcoff_a text0
	| section_n==TEXT_SECTION
		# real_label_offset = module_offset_a.[first_symbol_n+symbol_n];
		= store_long_at_offset ((virtual_module_offset-relocation_offset)+(real_label_offset-real_module_offset)+relocation_addend) index text0;
		= text0;
relocate_branch (Label _ offset module_n) relocation_offset relocation_addend 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 section_n virtual_label_offset _ _ _ _ _ _
			| section_n==TEXT_SECTION
				# real_label_offset = module_offset_a.[first_symbol_n+ /*symbol_n*/ module_n]+offset;
				-> store_long_at_offset ((virtual_label_offset-relocation_offset)+(real_label_offset-real_module_offset)+relocation_addend) index text0;
				-> text0;
	};
relocate_branch (ImportedLabel file_n symbol_n) relocation_offset relocation_addend index virtual_module_offset
		real_module_offset module_offset_a first_symbol_n _ symbol_a marked_offset_a0 xcoff_a text0
	| file_n<0
		# real_label_offset = module_offset_a.[marked_offset_a0.[file_n + size marked_offset_a0]+symbol_n];
		= store_long_at_offset ((virtual_module_offset-relocation_offset)+(real_label_offset-real_module_offset)+relocation_addend) index text0;
		# first_symbol_n = marked_offset_a0.[file_n];
		  symbol_a=xcoff_a.[file_n].symbol_table.symbols;
		= case (symbol_a.[symbol_n]) of {
			Module section_n _ _ _ _ _ _ _
				| section_n==TEXT_SECTION
					# real_label_offset = module_offset_a.[first_symbol_n+symbol_n];
					-> store_long_at_offset ((virtual_module_offset-relocation_offset)+(real_label_offset-real_module_offset)+relocation_addend) index text0;
					-> text0;
//			_ -> abort ("relocate_branch "+++file_name+++" "+++toString file_n+++" "+++toString symbol_n);
			};
relocate_branch (ImportedLabelPlusOffset file_n symbol_n label_offset) relocation_offset relocation_addend index virtual_module_offset
		real_module_offset module_offset_a first_symbol_n _ symbol_a marked_offset_a0 xcoff_a text0
	# first_symbol_n = marked_offset_a0.[file_n];
	  symbol_a=xcoff_a.[file_n].symbol_table.symbols;
	=	case (symbol_a.[symbol_n]) of {
			Module section_n _ _ _ _ _ _ _
				| section_n==TEXT_SECTION
					# real_label_offset = module_offset_a.[first_symbol_n+symbol_n];
					->	store_long_at_offset ((virtual_module_offset-relocation_offset)+(real_label_offset-real_module_offset)+label_offset+relocation_addend) index text0;
					-> text0;
		};
relocate_branch (UndefinedLabel _) relocation_offset relocation_addend index virtual_module_offset
		real_module_offset module_offset_a first_symbol_n _ symbol_a marked_offset_a0 xcoff_a text
		= text;

store_long_at_offset :: !Int !Int !*{#Char} -> *{#Char};
store_long_at_offset v index array
	= {array & [index]=toChar v,[index+1]=toChar (v>>8),[index+2]=toChar (v>>16),[index+3]=toChar (v>>24)};
