+ i386-linux support for tls-based threadvars

git-svn-id: trunk@40272 -
This commit is contained in:
florian 2018-11-07 22:03:02 +00:00
parent 0d50a63c7d
commit 063415fa72
6 changed files with 156 additions and 53 deletions

View File

@ -126,7 +126,10 @@ interface
,addr_gottpoff ,addr_gottpoff
,addr_tpoff ,addr_tpoff
{$ENDIF} {$ENDIF}
{$IFDEF i386}
,addr_ntpoff
,addr_tlsgd
{$ENDIF}
); );

View File

@ -164,20 +164,25 @@ interface
owner.writer.AsmWrite(symbol.name); owner.writer.AsmWrite(symbol.name);
if assigned(relsymbol) then if assigned(relsymbol) then
owner.writer.AsmWrite('-'+relsymbol.name); owner.writer.AsmWrite('-'+relsymbol.name);
if ref.refaddr=addr_pic then case ref.refaddr of
begin addr_pic:
{ @GOT and @GOTPCREL references are only allowed for symbol alone, begin
indexing, relsymbol or offset cannot be present. } { @GOT and @GOTPCREL references are only allowed for symbol alone,
if assigned(relsymbol) or (offset<>0) or (index<>NR_NO) then indexing, relsymbol or offset cannot be present. }
InternalError(2015011801); if assigned(relsymbol) or (offset<>0) or (index<>NR_NO) then
InternalError(2015011801);
{$ifdef x86_64} {$ifdef x86_64}
if (base<>NR_RIP) then if (base<>NR_RIP) then
InternalError(2015011802); InternalError(2015011802);
owner.writer.AsmWrite('@GOTPCREL'); owner.writer.AsmWrite('@GOTPCREL');
{$else x86_64} {$else x86_64}
owner.writer.AsmWrite('@GOT'); owner.writer.AsmWrite('@GOT');
{$endif x86_64} {$endif x86_64}
end; end;
addr_ntpoff:
owner.writer.AsmWrite('@ntpoff');
end;
if offset<0 then if offset<0 then
owner.writer.AsmWrite(tostr(offset)) owner.writer.AsmWrite(tostr(offset))
else else
@ -222,7 +227,7 @@ interface
else else
owner.writer.AsmWrite(gas_regname(o.reg)); owner.writer.AsmWrite(gas_regname(o.reg));
top_ref : top_ref :
if o.ref^.refaddr in [addr_no,addr_pic,addr_pic_no_got] then if o.ref^.refaddr in [addr_no,addr_pic,addr_pic_no_got,addr_ntpoff] then
WriteReference(o.ref^) WriteReference(o.ref^)
else else
begin begin

View File

@ -1098,6 +1098,7 @@ unit cgx86;
procedure tcgx86.a_loadaddr_ref_reg(list : TAsmList;const ref : treference;r : tregister); procedure tcgx86.a_loadaddr_ref_reg(list : TAsmList;const ref : treference;r : tregister);
var var
dirref,tmpref : treference; dirref,tmpref : treference;
tmpreg : TRegister;
begin begin
dirref:=ref; dirref:=ref;
@ -1107,6 +1108,37 @@ unit cgx86;
with dirref do with dirref do
begin begin
if refaddr=addr_ntpoff then
begin
{ Convert thread local address to a process global addres
as we cannot handle far pointers.}
case target_info.system of
system_i386_linux,system_i386_android:
if segment=NR_GS then
begin
reference_reset(tmpref,1,[]);
tmpref.segment:=NR_GS;
tmpreg:=getaddressregister(list);
a_load_ref_reg(list,OS_ADDR,OS_ADDR,tmpref,tmpreg);
reference_reset(tmpref,1,[]);
tmpref.symbol:=symbol;
tmpref.refaddr:=refaddr;
tmpref.base:=tmpreg;
if base<>NR_NO then
tmpref.index:=base;
list.concat(Taicpu.op_ref_reg(A_LEA,tcgsize2opsize[OS_ADDR],tmpref,tmpreg));
segment:=NR_NO;
base:=tmpreg;
symbol:=nil;
refaddr:=addr_no;
end
else
Internalerror(2018110402);
else
Internalerror(2018110403);
end;
end;
if (base=NR_NO) and (index=NR_NO) then if (base=NR_NO) and (index=NR_NO) then
begin begin
if assigned(dirref.symbol) then if assigned(dirref.symbol) then
@ -1196,26 +1228,7 @@ unit cgx86;
else else
a_load_reg_reg(list,OS_16,OS_16,segment,GetNextReg(r)); a_load_reg_reg(list,OS_16,OS_16,segment,GetNextReg(r));
{$else i8086} {$else i8086}
if (tf_section_threadvars in target_info.flags) then cgmessage(cg_e_cant_use_far_pointer_there);
begin
{ Convert thread local address to a process global addres
as we cannot handle far pointers.}
case target_info.system of
system_i386_linux,system_i386_android:
if segment=NR_GS then
begin
reference_reset_symbol(tmpref,current_asmdata.RefAsmSymbol('___fpc_threadvar_offset',AT_DATA),0,sizeof(pint),[]);
tmpref.segment:=NR_GS;
list.concat(Taicpu.op_ref_reg(A_ADD,tcgsize2opsize[OS_ADDR],tmpref,r));
end
else
cgmessage(cg_e_cant_use_far_pointer_there);
else
cgmessage(cg_e_cant_use_far_pointer_there);
end;
end
else
cgmessage(cg_e_cant_use_far_pointer_there);
{$endif i8086} {$endif i8086}
end; end;
end; end;
@ -2774,6 +2787,24 @@ unit cgx86;
make_simple_ref(list,srcref); make_simple_ref(list,srcref);
make_simple_ref(list,dstref); make_simple_ref(list,dstref);
{$endif not i8086} {$endif not i8086}
{$ifdef i386}
{ we could handle "far" pointers here, but reloading es/ds is probably much slower
than just resolving the tls segment }
if (srcref.refaddr=addr_ntpoff) and (srcref.segment=NR_GS) then
begin
r:=getaddressregister(list);
a_loadaddr_ref_reg(list,srcref,r);
reference_reset(srcref,srcref.alignment,srcref.volatility);
srcref.base:=r;
end;
if (dstref.refaddr=addr_ntpoff) and (dstref.segment=NR_GS) then
begin
r:=getaddressregister(list);
a_loadaddr_ref_reg(list,dstref,r);
reference_reset(dstref,dstref.alignment,dstref.volatility);
dstref.base:=r;
end;
{$endif i386}
cm:=copy_move; cm:=copy_move;
helpsize:=3*sizeof(aword); helpsize:=3*sizeof(aword);
if cs_opt_size in current_settings.optimizerswitches then if cs_opt_size in current_settings.optimizerswitches then
@ -2811,8 +2842,9 @@ unit cgx86;
not(len in copy_len_sizes) then not(len in copy_len_sizes) then
cm:=copy_string; cm:=copy_string;
{$ifndef i8086} {$ifndef i8086}
if (srcref.segment<>NR_NO) or { using %fs and %gs as segment prefixes is perfectly valid }
(dstref.segment<>NR_NO) then if ((srcref.segment<>NR_NO) and (srcref.segment<>NR_FS) and (srcref.segment<>NR_GS)) or
((dstref.segment<>NR_NO) and (dstref.segment<>NR_FS) and (dstref.segment<>NR_GS)) then
cm:=copy_string; cm:=copy_string;
{$endif not i8086} {$endif not i8086}
case cm of case cm of
@ -3004,8 +3036,8 @@ unit cgx86;
else {copy_string, should be a good fallback in case of unhandled} else {copy_string, should be a good fallback in case of unhandled}
begin begin
getcpuregister(list,REGDI); getcpuregister(list,REGDI);
if (dest.segment=NR_NO) and if (dstref.segment=NR_NO) and
(segment_regs_equal(NR_SS,NR_DS) or ((dest.base<>NR_BP) and (dest.base<>NR_SP))) then (segment_regs_equal(NR_SS,NR_DS) or ((dstref.base<>NR_BP) and (dstref.base<>NR_SP))) then
begin begin
a_loadaddr_ref_reg(list,dstref,REGDI); a_loadaddr_ref_reg(list,dstref,REGDI);
saved_es:=false; saved_es:=false;
@ -3024,9 +3056,9 @@ unit cgx86;
list.concat(taicpu.op_reg(A_PUSH,push_segment_size,NR_ES)); list.concat(taicpu.op_reg(A_PUSH,push_segment_size,NR_ES));
saved_es:=true; saved_es:=true;
{$endif volatile_es} {$endif volatile_es}
if dest.segment<>NR_NO then if dstref.segment<>NR_NO then
list.concat(taicpu.op_reg(A_PUSH,push_segment_size,dest.segment)) list.concat(taicpu.op_reg(A_PUSH,push_segment_size,dstref.segment))
else if (dest.base=NR_BP) or (dest.base=NR_SP) then else if (dstref.base=NR_BP) or (dstref.base=NR_SP) then
list.concat(taicpu.op_reg(A_PUSH,push_segment_size,NR_SS)) list.concat(taicpu.op_reg(A_PUSH,push_segment_size,NR_SS))
else else
internalerror(2014040401); internalerror(2014040401);
@ -3044,8 +3076,8 @@ unit cgx86;
srcref.index:=NR_NO; srcref.index:=NR_NO;
end; end;
{$endif i8086} {$endif i8086}
if ((source.segment=NR_NO) and (segment_regs_equal(NR_SS,NR_DS) or ((source.base<>NR_BP) and (source.base<>NR_SP)))) or if ((srcref.segment=NR_NO) and (segment_regs_equal(NR_SS,NR_DS) or ((srcref.base<>NR_BP) and (srcref.base<>NR_SP)))) or
(is_segment_reg(source.segment) and segment_regs_equal(source.segment,NR_DS)) then (is_segment_reg(srcref.segment) and segment_regs_equal(srcref.segment,NR_DS)) then
begin begin
srcref.segment:=NR_NO; srcref.segment:=NR_NO;
a_loadaddr_ref_reg(list,srcref,REGSI); a_loadaddr_ref_reg(list,srcref,REGSI);
@ -3057,9 +3089,9 @@ unit cgx86;
a_loadaddr_ref_reg(list,srcref,REGSI); a_loadaddr_ref_reg(list,srcref,REGSI);
list.concat(taicpu.op_reg(A_PUSH,push_segment_size,NR_DS)); list.concat(taicpu.op_reg(A_PUSH,push_segment_size,NR_DS));
saved_ds:=true; saved_ds:=true;
if source.segment<>NR_NO then if srcref.segment<>NR_NO then
list.concat(taicpu.op_reg(A_PUSH,push_segment_size,source.segment)) list.concat(taicpu.op_reg(A_PUSH,push_segment_size,srcref.segment))
else if (source.base=NR_BP) or (source.base=NR_SP) then else if (srcref.base=NR_BP) or (srcref.base=NR_SP) then
list.concat(taicpu.op_reg(A_PUSH,push_segment_size,NR_SS)) list.concat(taicpu.op_reg(A_PUSH,push_segment_size,NR_SS))
else else
internalerror(2014040402); internalerror(2014040402);

View File

@ -38,6 +38,7 @@ interface
implementation implementation
uses uses
globals,
cutils,verbose,systems, cutils,verbose,systems,
aasmbase,aasmtai,aasmdata, aasmbase,aasmtai,aasmdata,
cgutils,cgobj, cgutils,cgobj,
@ -88,7 +89,15 @@ implementation
begin begin
case target_info.system of case target_info.system of
system_i386_linux,system_i386_android: system_i386_linux,system_i386_android:
location.reference.segment:=NR_GS; begin
location.reference.segment:=NR_GS;
case current_settings.tlsmodel of
tlsm_local:
location.reference.refaddr:=addr_ntpoff;
else
Internalerror(2018110401);
end;
end;
end; end;
end; end;
end; end;

View File

@ -45,6 +45,8 @@ var
function fpc_geteipasebxlocal : pointer; [external name 'fpc_geteipasebx']; function fpc_geteipasebxlocal : pointer; [external name 'fpc_geteipasebx'];
{$endif} {$endif}
procedure InitTLS; [external name 'FPC_INITTLS'];
procedure _FPC_proc_start; assembler; nostackframe; public name '_start'; procedure _FPC_proc_start; assembler; nostackframe; public name '_start';
asm asm
{ First locate the start of the environment variables } { First locate the start of the environment variables }
@ -93,6 +95,8 @@ asm
movl %esp,initialstkptr movl %esp,initialstkptr
{$endif FPC_PIC} {$endif FPC_PIC}
call InitTLS
xorl %ebp,%ebp xorl %ebp,%ebp
call PASCALMAIN call PASCALMAIN
end; end;

View File

@ -442,13 +442,56 @@ begin
end; end;
{$ifdef CPUARM} {$if defined(CPUARM)}
{$define INITTLS}
Function fpset_tls(p : pointer):cint; Function fpset_tls(p : pointer;size : SizeUInt):cint;
begin begin
Result:=do_syscall(__ARM_NR_set_tls,TSysParam(p)); Result:=do_syscall(__ARM_NR_set_tls,TSysParam(p));
end; end;
{$endif defined(CPUARM)}
{$if defined(CPUI386)}
{$define INITTLS}
Function fpset_tls(p : pointer;size : SizeUInt):cint;
var
desc : record
entry_number : dword;
base_addr : dword;
limit : dword;
flags : dword;
end;
selector : word;
begin
// get descriptor from the kernel
desc.entry_number:=$ffffffff;
// TLS is accessed by negative offsets, only the TCB pointer is at offset 0
desc.base_addr:=dword(p)+size-SizeOf(Pointer);
// 4 GB, limit is given in pages
desc.limit:=$fffff;
// seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1
desc.flags:=%1010001;
Result:=do_syscall(syscall_nr_set_thread_area,TSysParam(@desc));
if Result=0 then
begin
selector:=desc.entry_number*8+3;
asm
movw selector,%gs
movl desc.base_addr,%eax
movl %eax,%gs:0
end;
end;
end;
{$endif defined(CPUI386)}
{$ifdef INITTLS}
{ This code initialized the TLS segment for single threaded and static programs.
In case of multithreaded and/or dynamically linked programs it is assumed that they
dependent anyways on glibc which initializes tls properly.
As soon as a purely FPC dynamic loading or multithreading is implemented, the code
needs to be extended to handle these cases as well.
}
procedure InitTLS; [public,alias:'FPC_INITTLS']; procedure InitTLS; [public,alias:'FPC_INITTLS'];
const const
@ -474,11 +517,12 @@ procedure InitTLS; [public,alias:'FPC_INITTLS'];
tls : pointer; tls : pointer;
auxp : ppointer; auxp : ppointer;
found : boolean; found : boolean;
size : SizeUInt;
begin begin
auxp:=ppointer(envp); auxp:=ppointer(envp);
{ skip environment } { skip environment }
while assigned(auxp^) do while assigned(auxp^) do
inc(auxp); inc(auxp);
inc(auxp); inc(auxp);
{ now we are at the auxillary vector } { now we are at the auxillary vector }
while assigned(auxp^) do while assigned(auxp^) do
@ -503,8 +547,14 @@ procedure InitTLS; [public,alias:'FPC_INITTLS'];
end; end;
if found then if found then
begin begin
tls:=Fpmmap(nil,phdr^.p_memsz,3,MAP_PRIVATE+MAP_ANONYMOUS,-1,0); size:=phdr^.p_memsz;
fpset_tls(tls); {$ifdef CPUI386}
{ threadvars should start at a page boundary,
add extra space for the TCB }
size:=Align(size,4096)+sizeof(Pointer);
{$endif CPUI386}
tls:=Fpmmap(nil,size,3,MAP_PRIVATE+MAP_ANONYMOUS,-1,0);
fpset_tls(tls,size);
end; end;
end; end;
{$endif CPUARM} {$endif CPUARM}