unit kern_reloc; {$mode ObjFPC}{$H+} {$CALLING SysV_ABI_CDecl} interface uses sysutils, elf64, kern_rtld, subr_dynlib; function relocate_one_object(obj:p_lib_info;jmpslots:Integer):Integer; function check_copy_relocations(obj:p_lib_info):Integer; function dynlib_unlink_imported_symbols_each(root,obj:p_lib_info):Integer; implementation uses errno, systm, kern_dlsym; function check_addr(obj:p_lib_info;where:Pointer;size:Integer):Integer; var map_base:Pointer; relro_addr:Pointer; begin map_base:=obj^.map_base; relro_addr:=obj^.relro_addr; if ( (map_base > where) or ((map_base + obj^.text_size) < (where + size)) ) and ( (obj^.data_addr > where) or ((obj^.data_addr + obj^.data_size) < (where + size)) ) and ( (relro_addr=nil) or (relro_addr > where) or (obj^.relro_size=0) or ((relro_addr + obj^.relro_size) < (where + size)) ) then begin //dont check with special callbacks //Exit(ENOEXEC); end; Result:=0; end; function reloc_non_plt(obj:p_lib_info):Integer; label _next, _move64; var rela:p_elf64_rela; sym_zero:p_elf64_sym; where:Pointer; data:Pointer; data32:Integer; r_type:Integer; def:p_elf64_sym; defobj:p_lib_info; cache:array of t_SymCache; i,count:Integer; begin Result:=0; cache:=nil; SetLength(cache,obj^.rel_data^.dynsymcount); sym_zero:=@dynlibs_info.sym_zero; rela :=obj^.rel_data^.rela_addr; count:=obj^.rel_data^.rela_size div SizeOf(elf64_rela); defobj:=nil; if (rela<>nil) and (count<>0) then For i:=0 to count-1 do if not check_relo_bits(obj,i) then begin where:=Pointer(obj^.relocbase)+rela^.r_offset; r_type:=ELF64_R_TYPE(rela^.r_info); case r_type of R_X86_64_NONE:; //ignore R_X86_64_COPY: if (obj^.mainprog=0) then begin Writeln(StdErr,'reloc_non_plt:','Unexpected R_X86_64_COPY relocation in shared library ',dynlib_basename(obj^.lib_path)); Exit(ENOEXEC); end; //R_X86_64_COPY R_X86_64_RELATIVE: begin Result:=check_addr(obj,where,SizeOf(Pointer)); if (Result<>0) then begin Writeln(StdErr,'reloc_non_plt:','idx=',i,' where=0x',HexStr(where),' ref=',dynlib_basename(obj^.lib_path)); Exit; end; data:=(obj^.relocbase + rela^.r_addend); defobj:=obj; goto _move64; end; //R_X86_64_RELATIVE R_X86_64_64, R_X86_64_GLOB_DAT, R_X86_64_DTPMOD64, R_X86_64_DTPOFF64, R_X86_64_TPOFF64: //64 begin Result:=check_addr(obj,where,SizeOf(Pointer)); if (Result<>0) then begin Writeln(StdErr,'reloc_non_plt:','idx=',i,' where=0x',HexStr(where),' ref=',dynlib_basename(obj^.lib_path)); Exit; end; def:=find_symdef(ELF64_R_SYM(rela^.r_info),obj,defobj,0,@cache[0]); if (def<>nil) then case r_type of R_X86_64_64: begin data:=(defobj^.relocbase + rela^.r_addend + def^.st_value); if (def<>sym_zero) then begin goto _move64; end; end; //R_X86_64_64 R_X86_64_GLOB_DAT: begin data:=(defobj^.relocbase + def^.st_value); if (def<>sym_zero) then begin goto _move64; end; end; //R_X86_64_GLOB_DAT R_X86_64_DTPMOD64: begin Result:=copyin(where,@data,SizeOf(Pointer)); //data:=where^ if (Result<>0) then begin Writeln(StdErr,'reloc_non_plt:','copyin() failed. where=0x',HexStr(where),' [',r_type,']'); Exit(ENOEXEC); end; data:=(data + defobj^.tls_index); Result:=copyout(@data,where,8); //where^:=data if (Result<>0) then begin Writeln(StdErr,'reloc_non_plt:','copyout() failed. where=0x',HexStr(where),' [',r_type,']'); Exit(ENOEXEC); end; if (def<>@dynlibs_info.sym_nops) then set_relo_bits(obj,i); end; //R_X86_64_DTPMOD64 R_X86_64_DTPOFF64: begin Result:=copyin(where,@data,SizeOf(Pointer)); //data:=where^ if (Result<>0) then begin Writeln(StdErr,'reloc_non_plt:','copyin() failed. where=0x',HexStr(where),' [',r_type,']'); Exit(ENOEXEC); end; data:=(data + rela^.r_addend + def^.st_value); Result:=copyout(@data,where,8); //where^:=data if (Result<>0) then begin Writeln(StdErr,'reloc_non_plt:','copyout() failed. where=0x',HexStr(where),' [',r_type,']'); Exit(ENOEXEC); end; if (def<>@dynlibs_info.sym_nops) then set_relo_bits(obj,i); end; //R_X86_64_DTPOFF64 R_X86_64_TPOFF64: begin if not allocate_tls_offset(defobj) then begin Writeln(StdErr,'reloc_non_plt:','No space available for static Thread Local Storage'); Exit(ENOEXEC); end; data:=Pointer(def^.st_value - defobj^.tls_offset + rela^.r_addend); Result:=copyout(@data,where,SizeOf(Pointer)); if (Result<>0) then begin Writeln(StdErr,'reloc_non_plt:','copyout() failed. where=0x',HexStr(where),' [',r_type,']'); Exit(ENOEXEC); end; if (def<>@dynlibs_info.sym_nops) then set_relo_bits(obj,i); end; //R_X86_64_TPOFF64 else; end; //case end; //R_X86_64_*64 R_X86_64_PC32, R_X86_64_DTPOFF32, R_X86_64_TPOFF32: //32 begin Result:=check_addr(obj,where,SizeOf(Integer)); if (Result<>0) then begin Writeln(StdErr,'reloc_non_plt:','idx=',i,' where=0x',HexStr(where),' ref=',dynlib_basename(obj^.lib_path)); Exit; end; def:=find_symdef(ELF64_R_SYM(rela^.r_info),obj,defobj,0,@cache[0]); if (def<>nil) then case r_type of R_X86_64_PC32: begin data32:=(Integer(QWORD(defobj^.relocbase)) - Integer(QWORD(where))) + Integer(def^.st_value) + Integer(rela^.r_addend); if (def<>sym_zero) then begin data:=Pointer(QWORD(data32)); Result:=check_addr(defobj,data,SizeOf(Integer)); if (Result<>0) then begin Writeln(StdErr,'reloc_non_plt:','idx=',i,' where32=0x',HexStr(where),' ref=',dynlib_basename(defobj^.lib_path)); Exit; end; end; Result:=relocate_text_or_data_segment(obj,@data32,where,SizeOf(Integer)); if (Result<>0) then begin Writeln(StdErr,'reloc_non_plt:','copyout() failed. where32=0x',HexStr(where),' [',r_type,']'); Exit(ENOEXEC); end; if (def<>@dynlibs_info.sym_nops) then set_relo_bits(obj,i); end; //R_X86_64_PC32 R_X86_64_DTPOFF32: begin Result:=copyin(where,@data,SizeOf(Pointer)); //data:=where^ if (Result<>0) then begin Writeln(StdErr,'reloc_non_plt:','copyin() failed. where32=0x',HexStr(where),' [',r_type,']'); Exit(ENOEXEC); end; data:=(data + rela^.r_addend + def^.st_value); Result:=copyout(@data,where,8); //where^:=data if (Result<>0) then begin Writeln(StdErr,'reloc_non_plt:','copyout() failed. where32=0x',HexStr(where),' [',r_type,']'); Exit(ENOEXEC); end; if (def<>@dynlibs_info.sym_nops) then set_relo_bits(obj,i); end; //R_X86_64_DTPOFF32 R_X86_64_TPOFF32: begin if not allocate_tls_offset(defobj) then begin Writeln(StdErr,'reloc_non_plt:','No space available for static Thread Local Storage'); Exit(ENOEXEC); end; data32:=Integer(def^.st_value) - Integer(defobj^.tls_offset) + Integer(rela^.r_addend); Result:=copyout(@data32,where,SizeOf(Integer)); if (Result<>0) then begin Writeln(StdErr,'reloc_non_plt:','copyout() failed. where32=0x',HexStr(where),' [',r_type,']'); Exit(ENOEXEC); end; if (def<>@dynlibs_info.sym_nops) then set_relo_bits(obj,i); end; //R_X86_64_TPOFF32 else; end; //case end; //R_X86_64_*32 else begin Writeln(StdErr,'reloc_non_plt:','Unsupported reloc type=',r_type); Exit(ENOEXEC); end; end; //case // _next: Inc(rela); end; Exit(0); _move64: Result:=check_addr(defobj,data,SizeOf(Pointer)); if (Result<>0) then begin Writeln(StdErr,'reloc_non_plt:','idx=',i,' where=0x',HexStr(where),' ref=',dynlib_basename(defobj^.lib_path)); Exit; end; Result:=relocate_text_or_data_segment(obj,@data,where,SizeOf(Pointer)); if (Result<>0) then begin Writeln(StdErr,'reloc_non_plt:','copyout() failed. where=0x',HexStr(where),' [',r_type,']'); Exit(ENOEXEC); end; if (def<>@dynlibs_info.sym_nops) then set_relo_bits(obj,i); goto _next; end; function reloc_jmpslot(obj:p_lib_info;i:Integer;cache:p_SymCache;flags:Integer):Integer; var idofs:Integer; entry:p_elf64_rela; where:Pointer; data:Pointer; def:p_elf64_sym; defobj:p_lib_info; begin Result:=0; if (i<0) or (i>=(obj^.rel_data^.pltrela_size div SizeOf(elf64_rela))) then begin Exit(1); end; idofs:=obj^.rel_data^.rela_size div SizeOf(elf64_rela); idofs:=idofs+i; if check_relo_bits(obj,idofs) then Exit; entry:=obj^.rel_data^.pltrela_addr+i; if (ELF64_R_TYPE(entry^.r_info)<>R_X86_64_JUMP_SLOT) then begin Writeln(StdErr,'reloc_jmpslot:','R_TYPE (',ELF64_R_TYPE(entry^.r_info),') at index ',i,' is bad. (Expected: R_X86_64_JMP_SLOT) in ',dynlib_basename(obj^.lib_path)); Exit(3); end; where:=(obj^.relocbase + entry^.r_offset); defobj:=nil; def:=find_symdef(ELF64_R_SYM(entry^.r_info),obj,defobj,1,cache); if (def=nil) then begin Exit(1); end; if (flags=1) and (obj^.jmpslots_done=0) and (defobj^.jmpslots_done=0) then begin Exit(5); end; if (ELF64_ST_VISIBILITY(def^.st_other)=STV_HIDDEN) and (defobj<>obj) then begin Exit(2); end; data:=defobj^.relocbase + entry^.r_addend + def^.st_value; Result:=copyout(@data,where,SizeOf(Pointer)); if (Result<>0) then begin Writeln(StdErr,'reloc_jmpslot:','copyout() failed. where=0x',HexStr(where)); Exit(4); end; if (def<>@dynlibs_info.sym_nops) then set_relo_bits(obj,idofs); if (flags=0) then Exit; //dl_debug_flags end; function reloc_jmpslots(obj:p_lib_info):Integer; var cache:array of t_SymCache; i,count:Integer; begin Result:=0; cache:=nil; SetLength(cache,obj^.rel_data^.dynsymcount); count:=obj^.rel_data^.pltrela_size div SizeOf(elf64_rela); if (obj^.rel_data^.pltrela_addr<>nil) and (count<>0) then For i:=0 to count-1 do begin Result:=reloc_jmpslot(obj,i,@cache[0],0); case Result of 3:Exit(EINVAL); 4:Exit(ENOEXEC); 5:Exit(ENOEXEC); else; end; end; Result:=0; end; function relocate_one_object(obj:p_lib_info;jmpslots:Integer):Integer; begin Writeln(' relocate:',dynlib_basename(obj^.lib_path)); Result:=reloc_non_plt(obj); if (Result<>0) then begin Writeln(StdErr,'relocate_one_object:','reloc_non_plt() failed. obj=',dynlib_basename(obj^.lib_path),' rv=',Result); Exit; end; if (jmpslots=1) then begin Result:=reloc_jmpslots(obj); if (Result<>0) then begin Writeln(StdErr,'relocate_one_object:','reloc_jmplots() failed. obj=',dynlib_basename(obj^.lib_path),' rv=',Result); Exit; end; end; end; function check_copy_relocations(obj:p_lib_info):Integer; var rela:p_elf64_rela; i,count:Integer; begin Result:=0; if (obj=nil) then Exit; if (obj^.rel_data=nil) then Exit; rela :=obj^.rel_data^.rela_addr; count:=obj^.rel_data^.rela_size div SizeOf(elf64_rela); if (count<>0) then For i:=0 to count-1 do begin if (ELF64_R_TYPE(rela^.r_info)=R_X86_64_COPY) then begin Writeln(StdErr,'check_copy_relocations:','R_X86_64_COPY found in ',dynlib_basename(obj^.lib_path)); Exit(EINVAL); end; Inc(rela); end; end; function dynlib_unlink_non_plt_reloc_each(root,obj:p_lib_info):Integer; var rela:p_elf64_rela; where:Pointer; data:Pointer; data32:Integer; r_type:Integer; i,count:Integer; begin Result:=0; Assert(root<>nil,'Bad dynamic library is specified.'); rela :=obj^.rel_data^.rela_addr; count:=obj^.rel_data^.rela_size div SizeOf(elf64_rela); if (rela<>nil) and (count<>0) then For i:=0 to count-1 do if check_relo_bits(obj,i) then begin where:=Pointer(obj^.relocbase)+rela^.r_offset; r_type:=ELF64_R_TYPE(rela^.r_info); case r_type of R_X86_64_NONE:; //ignore R_X86_64_COPY: if (obj^.mainprog=0) then begin Writeln(StdErr,'dynlib_unlink_non_plt_reloc_each:','Unexpected R_X86_64_COPY relocation in dynamic library ',dynlib_basename(obj^.lib_path)); Exit(-1); end; //R_X86_64_COPY R_X86_64_64, R_X86_64_GLOB_DAT, R_X86_64_RELATIVE, R_X86_64_TPOFF64: begin data:=Pointer(QWORD($840000000)); Result:=relocate_text_or_data_segment(obj,@data,where,SizeOf(Pointer)); if (Result<>0) then begin Writeln(StdErr,'dynlib_unlink_non_plt_reloc_each:','copyout() failed. where=0x',HexStr(where),' ref=',dynlib_basename(obj^.lib_path)); Exit(-1); end; reset_relo_bits(obj,i); end; //64 R_X86_64_PC32, R_X86_64_TPOFF32: begin data32:=Integer($40000000); Result:=relocate_text_or_data_segment(obj,@data32,where,SizeOf(Integer)); if (Result<>0) then begin Writeln(StdErr,'dynlib_unlink_non_plt_reloc_each:','copyout() failed. where32=0x',HexStr(where),' ref=',dynlib_basename(obj^.lib_path)); Exit(-1); end; reset_relo_bits(obj,i); end; //32 R_X86_64_DTPMOD64, R_X86_64_DTPOFF64: begin data:=nil; Result:=copyout(@data,where,SizeOf(Pointer)); if (Result<>0) then begin Writeln(StdErr,'dynlib_unlink_non_plt_reloc_each:','copyout() failed. where=0x',HexStr(where),' ref=',dynlib_basename(obj^.lib_path)); Exit(-1); end; reset_relo_bits(obj,i); end; //64 R_X86_64_DTPOFF32: begin data32:=0; Result:=copyout(@data32,where,SizeOf(Integer)); if (Result<>0) then begin Writeln(StdErr,'dynlib_unlink_non_plt_reloc_each:','copyout() failed. where32=0x',HexStr(where),' ref=',dynlib_basename(obj^.lib_path)); Exit(-1); end; reset_relo_bits(obj,i); end; //32 else begin Writeln(StdErr,'dynlib_unlink_non_plt_reloc_each:','Unsupported reloc type=',r_type); Exit(-1); end; end; end; //case end; function dynlib_unlink_plt_reloc_each(root,obj:p_lib_info):Integer; var i,count,idofs:Integer; entry:p_elf64_rela; def:p_elf64_sym; defobj:p_lib_info; where:Pointer; data:QWORD; str:pchar; begin Result:=0; count:=obj^.rel_data^.pltrela_size div SizeOf(elf64_rela); idofs:=obj^.rel_data^.rela_size div SizeOf(elf64_rela); if (obj^.rel_data^.pltrela_addr<>nil) and (count<>0) then For i:=0 to count-1 do if check_relo_bits(obj,idofs+i) then begin entry:=obj^.rel_data^.pltrela_addr+i; if (ELF64_R_TYPE(entry^.r_info)<>R_X86_64_JUMP_SLOT) then begin Writeln(StdErr,'dynlib_unlink_plt_reloc_each:','R_TYPE (',ELF64_R_TYPE(entry^.r_info),') at index ',i,' is bad. (Expected: R_X86_64_JMP_SLOT) in ',dynlib_basename(obj^.lib_path)); Exit(-1); end; defobj:=nil; def:=find_symdef(ELF64_R_SYM(entry^.r_info),obj,defobj,1,nil); if (def=nil) then begin if (ELF64_R_SYM(entry^.r_info)<=(obj^.rel_data^.symtab_size div SizeOf(elf64_sym))) then begin def:=nil; str:=''; end else begin def:=obj^.rel_data^.symtab_addr + ELF64_R_SYM(entry^.r_info); str:=obj_get_str(obj,def^.st_name); end; Writeln(StdErr,'dynlib_unlink_plt_reloc_each:','failed to lookup symbol symp=0x',HexStr(def),' name=',str); Exit(-1); end else if (defobj=root) then begin where:=Pointer(obj^.relocbase) + entry^.r_offset; data:=i or QWORD($effffffe00000000); Result:=copyout(@data,where,SizeOf(Pointer)); if (Result<>0) then begin Writeln(StdErr,'dynlib_unlink_plt_reloc_each:','copyout() failed. where=0x',HexStr(where),' ref=',dynlib_basename(obj^.lib_path)); Exit(-1); end; reset_relo_bits(obj,idofs+i); end; end; end; function dynlib_unlink_imported_symbols_each(root,obj:p_lib_info):Integer; begin Result:=dynlib_unlink_non_plt_reloc_each(root,obj); if (Result<>0) then begin Writeln(StdErr,'dynlib_unlink_imported_symbols_each:','dynlib_unlink_non_plt_reloc_each() fails ',Result); Exit; end; Result:=dynlib_unlink_plt_reloc_each(root,obj); if (Result<>0) then begin Writeln(StdErr,'dynlib_unlink_imported_symbols_each:','dynlib_unlink_plt_reloc_each() fails ',Result); Exit; end; end; end.