+ 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_tpoff
{$ENDIF}
{$IFDEF i386}
,addr_ntpoff
,addr_tlsgd
{$ENDIF}
);

View File

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

View File

@ -1098,6 +1098,7 @@ unit cgx86;
procedure tcgx86.a_loadaddr_ref_reg(list : TAsmList;const ref : treference;r : tregister);
var
dirref,tmpref : treference;
tmpreg : TRegister;
begin
dirref:=ref;
@ -1107,6 +1108,37 @@ unit cgx86;
with dirref do
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
begin
if assigned(dirref.symbol) then
@ -1196,26 +1228,7 @@ unit cgx86;
else
a_load_reg_reg(list,OS_16,OS_16,segment,GetNextReg(r));
{$else i8086}
if (tf_section_threadvars in target_info.flags) 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_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);
cgmessage(cg_e_cant_use_far_pointer_there);
{$endif i8086}
end;
end;
@ -2774,6 +2787,24 @@ unit cgx86;
make_simple_ref(list,srcref);
make_simple_ref(list,dstref);
{$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;
helpsize:=3*sizeof(aword);
if cs_opt_size in current_settings.optimizerswitches then
@ -2811,8 +2842,9 @@ unit cgx86;
not(len in copy_len_sizes) then
cm:=copy_string;
{$ifndef i8086}
if (srcref.segment<>NR_NO) or
(dstref.segment<>NR_NO) then
{ using %fs and %gs as segment prefixes is perfectly valid }
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;
{$endif not i8086}
case cm of
@ -3004,8 +3036,8 @@ unit cgx86;
else {copy_string, should be a good fallback in case of unhandled}
begin
getcpuregister(list,REGDI);
if (dest.segment=NR_NO) and
(segment_regs_equal(NR_SS,NR_DS) or ((dest.base<>NR_BP) and (dest.base<>NR_SP))) then
if (dstref.segment=NR_NO) and
(segment_regs_equal(NR_SS,NR_DS) or ((dstref.base<>NR_BP) and (dstref.base<>NR_SP))) then
begin
a_loadaddr_ref_reg(list,dstref,REGDI);
saved_es:=false;
@ -3024,9 +3056,9 @@ unit cgx86;
list.concat(taicpu.op_reg(A_PUSH,push_segment_size,NR_ES));
saved_es:=true;
{$endif volatile_es}
if dest.segment<>NR_NO then
list.concat(taicpu.op_reg(A_PUSH,push_segment_size,dest.segment))
else if (dest.base=NR_BP) or (dest.base=NR_SP) then
if dstref.segment<>NR_NO then
list.concat(taicpu.op_reg(A_PUSH,push_segment_size,dstref.segment))
else if (dstref.base=NR_BP) or (dstref.base=NR_SP) then
list.concat(taicpu.op_reg(A_PUSH,push_segment_size,NR_SS))
else
internalerror(2014040401);
@ -3044,8 +3076,8 @@ unit cgx86;
srcref.index:=NR_NO;
end;
{$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
(is_segment_reg(source.segment) and segment_regs_equal(source.segment,NR_DS)) then
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(srcref.segment) and segment_regs_equal(srcref.segment,NR_DS)) then
begin
srcref.segment:=NR_NO;
a_loadaddr_ref_reg(list,srcref,REGSI);
@ -3057,9 +3089,9 @@ unit cgx86;
a_loadaddr_ref_reg(list,srcref,REGSI);
list.concat(taicpu.op_reg(A_PUSH,push_segment_size,NR_DS));
saved_ds:=true;
if source.segment<>NR_NO then
list.concat(taicpu.op_reg(A_PUSH,push_segment_size,source.segment))
else if (source.base=NR_BP) or (source.base=NR_SP) then
if srcref.segment<>NR_NO then
list.concat(taicpu.op_reg(A_PUSH,push_segment_size,srcref.segment))
else if (srcref.base=NR_BP) or (srcref.base=NR_SP) then
list.concat(taicpu.op_reg(A_PUSH,push_segment_size,NR_SS))
else
internalerror(2014040402);

View File

@ -38,6 +38,7 @@ interface
implementation
uses
globals,
cutils,verbose,systems,
aasmbase,aasmtai,aasmdata,
cgutils,cgobj,
@ -88,7 +89,15 @@ implementation
begin
case target_info.system of
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;

View File

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

View File

@ -442,13 +442,56 @@ begin
end;
{$ifdef CPUARM}
Function fpset_tls(p : pointer):cint;
{$if defined(CPUARM)}
{$define INITTLS}
Function fpset_tls(p : pointer;size : SizeUInt):cint;
begin
Result:=do_syscall(__ARM_NR_set_tls,TSysParam(p));
Result:=do_syscall(__ARM_NR_set_tls,TSysParam(p));
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'];
const
@ -474,11 +517,12 @@ procedure InitTLS; [public,alias:'FPC_INITTLS'];
tls : pointer;
auxp : ppointer;
found : boolean;
size : SizeUInt;
begin
auxp:=ppointer(envp);
{ skip environment }
while assigned(auxp^) do
inc(auxp);
inc(auxp);
inc(auxp);
{ now we are at the auxillary vector }
while assigned(auxp^) do
@ -503,8 +547,14 @@ procedure InitTLS; [public,alias:'FPC_INITTLS'];
end;
if found then
begin
tls:=Fpmmap(nil,phdr^.p_memsz,3,MAP_PRIVATE+MAP_ANONYMOUS,-1,0);
fpset_tls(tls);
size:=phdr^.p_memsz;
{$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;
{$endif CPUARM}