Ver Fonte

+ i386-linux support for tls-based threadvars

git-svn-id: trunk@40272 -
florian há 6 anos atrás
pai
commit
063415fa72

+ 4 - 1
compiler/cgbase.pas

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

+ 17 - 12
compiler/x86/agx86att.pas

@@ -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

+ 64 - 32
compiler/x86/cgx86.pas

@@ -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);

+ 10 - 1
compiler/x86/nx86ld.pas

@@ -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;

+ 4 - 0
rtl/linux/i386/si_prc.inc

@@ -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;

+ 56 - 6
rtl/linux/system.pp

@@ -442,13 +442,56 @@ begin
 end;
 
 
-{$ifdef CPUARM}
+{$if defined(CPUARM)}
+{$define INITTLS}
+Function fpset_tls(p : pointer;size : SizeUInt):cint;
+begin
+  Result:=do_syscall(__ARM_NR_set_tls,TSysParam(p));
+end;
+{$endif defined(CPUARM)}
 
-Function fpset_tls(p : pointer):cint;
+{$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
- Result:=do_syscall(__ARM_NR_set_tls,TSysParam(p));
+  // 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}