File : s-stchop.adb


   1 ------------------------------------------------------------------------------
   2 --                                                                          --
   3 --                 GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS                 --
   4 --                                                                          --
   5 --     S Y S T E M . S T A C K _ C H E C K I N G . O P E R A T I O N S      --
   6 --                                                                          --
   7 --                                  B o d y                                 --
   8 --                                                                          --
   9 --          Copyright (C) 1999-2014, Free Software Foundation, Inc.         --
  10 --                                                                          --
  11 -- GNARL is free software; you can  redistribute it  and/or modify it under --
  12 -- terms of the  GNU General Public License as published  by the Free Soft- --
  13 -- ware  Foundation;  either version 3,  or (at your option) any later ver- --
  14 -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
  15 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
  16 -- or FITNESS FOR A PARTICULAR PURPOSE.                                     --
  17 --                                                                          --
  18 --                                                                          --
  19 --                                                                          --
  20 --                                                                          --
  21 --                                                                          --
  22 -- You should have received a copy of the GNU General Public License and    --
  23 -- a copy of the GCC Runtime Library Exception along with this program;     --
  24 -- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
  25 -- <http://www.gnu.org/licenses/>.                                          --
  26 --                                                                          --
  27 -- GNARL was developed by the GNARL team at Florida State University.       --
  28 -- Extensive contributions were provided by Ada Core Technologies, Inc.     --
  29 --                                                                          --
  30 ------------------------------------------------------------------------------
  31 
  32 --  This is the general implementation of this package. There is a VxWorks
  33 --  specific version of this package (s-stchop-vxworks.adb). This file should
  34 --  be kept synchronized with it.
  35 
  36 pragma Restrictions (No_Elaboration_Code);
  37 --  We want to guarantee the absence of elaboration code because the
  38 --  binder does not handle references to this package.
  39 
  40 with System.Storage_Elements; use System.Storage_Elements;
  41 with System.Parameters; use System.Parameters;
  42 with System.Soft_Links;
  43 with System.CRTL;
  44 
  45 package body System.Stack_Checking.Operations is
  46 
  47    Kilobyte : constant := 1024;
  48 
  49    function Set_Stack_Info
  50      (Stack : not null access Stack_Access) return Stack_Access;
  51    --  The function Set_Stack_Info is the actual function that updates the
  52    --  cache containing a pointer to the Stack_Info. It may also be used for
  53    --  detecting asynchronous abort in combination with Invalidate_Self_Cache.
  54    --
  55    --  Set_Stack_Info should do the following things in order:
  56    --     1) Get the Stack_Access value for the current task
  57    --     2) Set Stack.all to the value obtained in 1)
  58    --     3) Optionally Poll to check for asynchronous abort
  59    --
  60    --  This order is important because if at any time a write to the stack
  61    --  cache is pending, that write should be followed by a Poll to prevent
  62    --  losing signals.
  63    --
  64    --  Note: This function must be compiled with Polling turned off
  65    --
  66    --  Note: on systems with real thread-local storage, Set_Stack_Info should
  67    --  return an access value for such local storage. In those cases the cache
  68    --  will always be up-to-date.
  69 
  70    ----------------------------
  71    -- Invalidate_Stack_Cache --
  72    ----------------------------
  73 
  74    procedure Invalidate_Stack_Cache (Any_Stack : Stack_Access) is
  75       pragma Warnings (Off, Any_Stack);
  76    begin
  77       Cache := Null_Stack;
  78    end Invalidate_Stack_Cache;
  79 
  80    -----------------------------
  81    -- Notify_Stack_Attributes --
  82    -----------------------------
  83 
  84    procedure Notify_Stack_Attributes
  85      (Initial_SP : System.Address;
  86       Size       : System.Storage_Elements.Storage_Offset)
  87    is
  88       My_Stack : constant Stack_Access := Soft_Links.Get_Stack_Info.all;
  89 
  90       --  We piggyback on the 'Limit' field to store what will be used as the
  91       --  'Base' and leave the 'Size' alone to not interfere with the logic in
  92       --  Set_Stack_Info below.
  93 
  94       pragma Unreferenced (Size);
  95 
  96    begin
  97       My_Stack.Limit := Initial_SP;
  98    end Notify_Stack_Attributes;
  99 
 100    --------------------
 101    -- Set_Stack_Info --
 102    --------------------
 103 
 104    function Set_Stack_Info
 105      (Stack : not null access Stack_Access) return Stack_Access
 106    is
 107       type Frame_Mark is null record;
 108       Frame_Location : Frame_Mark;
 109       Frame_Address  : constant Address := Frame_Location'Address;
 110 
 111       My_Stack    : Stack_Access;
 112       Limit_Chars : System.Address;
 113       Limit       : Integer;
 114 
 115    begin
 116       --  The order of steps 1 .. 3 is important, see specification
 117 
 118       --  1) Get the Stack_Access value for the current task
 119 
 120       My_Stack := Soft_Links.Get_Stack_Info.all;
 121 
 122       if My_Stack.Base = Null_Address then
 123 
 124          --  First invocation, initialize based on the assumption that there
 125          --  are Environment_Stack_Size bytes available beyond the current
 126          --  frame address.
 127 
 128          if My_Stack.Size = 0 then
 129             My_Stack.Size := Storage_Offset (Default_Env_Stack_Size);
 130 
 131             --  When the environment variable GNAT_STACK_LIMIT is set, set
 132             --  Environment_Stack_Size to that number of kB.
 133 
 134             Limit_Chars := System.CRTL.getenv ("GNAT_STACK_LIMIT" & ASCII.NUL);
 135 
 136             if Limit_Chars /= Null_Address then
 137                Limit := System.CRTL.atoi (Limit_Chars);
 138 
 139                if Limit >= 0 then
 140                   My_Stack.Size := Storage_Offset (Limit) * Kilobyte;
 141                end if;
 142             end if;
 143          end if;
 144 
 145          --  If a stack base address has been registered, honor it. Fallback to
 146          --  the address of a local object otherwise.
 147 
 148          My_Stack.Base :=
 149            (if My_Stack.Limit /= System.Null_Address
 150             then My_Stack.Limit else Frame_Address);
 151 
 152          if Stack_Grows_Down then
 153 
 154             --  Prevent wrap-around on too big stack sizes
 155 
 156             My_Stack.Limit := My_Stack.Base - My_Stack.Size;
 157 
 158             if My_Stack.Limit > My_Stack.Base then
 159                My_Stack.Limit := Address'First;
 160             end if;
 161 
 162          else
 163             My_Stack.Limit := My_Stack.Base + My_Stack.Size;
 164 
 165             --  Prevent wrap-around on too big stack sizes
 166 
 167             if My_Stack.Limit < My_Stack.Base then
 168                My_Stack.Limit := Address'Last;
 169             end if;
 170          end if;
 171       end if;
 172 
 173       --  2) Set Stack.all to the value obtained in 1)
 174 
 175       Stack.all := My_Stack;
 176 
 177       --  3) Optionally Poll to check for asynchronous abort
 178 
 179       if Soft_Links.Check_Abort_Status.all /= 0 then
 180          raise Standard'Abort_Signal;
 181       end if;
 182 
 183       --  Never trust the cached value, but return local copy
 184 
 185       return My_Stack;
 186    end Set_Stack_Info;
 187 
 188    -----------------
 189    -- Stack_Check --
 190    -----------------
 191 
 192    function Stack_Check
 193      (Stack_Address : System.Address) return Stack_Access
 194    is
 195       type Frame_Marker is null record;
 196       Marker        : Frame_Marker;
 197       Cached_Stack  : constant Stack_Access := Cache;
 198       Frame_Address : constant System.Address := Marker'Address;
 199 
 200    begin
 201       --  The parameter may have wrapped around in System.Address arithmetics.
 202       --  In that case, we have no other choices than raising the exception.
 203 
 204       if (Stack_Grows_Down and then
 205             Stack_Address > Frame_Address)
 206         or else
 207          (not Stack_Grows_Down and then
 208             Stack_Address < Frame_Address)
 209       then
 210          raise Storage_Error with "stack overflow detected";
 211       end if;
 212 
 213       --  This function first does a "cheap" check which is correct if it
 214       --  succeeds. In case of failure, the full check is done. Ideally the
 215       --  cheap check should be done in an optimized manner, or be inlined.
 216 
 217       if (Stack_Grows_Down and then
 218             (Frame_Address <= Cached_Stack.Base
 219                and then
 220              Stack_Address > Cached_Stack.Limit))
 221         or else
 222          (not Stack_Grows_Down and then
 223             (Frame_Address >= Cached_Stack.Base
 224                and then
 225              Stack_Address < Cached_Stack.Limit))
 226       then
 227          --  Cached_Stack is valid as it passed the stack check
 228 
 229          return Cached_Stack;
 230       end if;
 231 
 232       Full_Check :
 233       declare
 234          My_Stack : constant Stack_Access := Set_Stack_Info (Cache'Access);
 235          --  At this point Stack.all might already be invalid, so
 236          --  it is essential to use our local copy of Stack.
 237 
 238       begin
 239          if (Stack_Grows_Down and then
 240                (not (Frame_Address <= My_Stack.Base)))
 241            or else
 242             (not Stack_Grows_Down and then
 243                (not (Frame_Address >= My_Stack.Base)))
 244          then
 245             --  The returned Base is lower than the stored one, so assume that
 246             --  the original one wasn't right and use the current Frame_Address
 247             --  as new one. This allows Base to be initialized with the
 248             --  Frame_Address as approximation. During initialization the
 249             --  Frame_Address will be close to the stack base anyway: the
 250             --  difference should be compensated for in the stack reserve.
 251 
 252             My_Stack.Base := Frame_Address;
 253          end if;
 254 
 255          if (Stack_Grows_Down
 256               and then Stack_Address < My_Stack.Limit)
 257            or else
 258             (not Stack_Grows_Down
 259               and then Stack_Address > My_Stack.Limit)
 260          then
 261             raise Storage_Error with "stack overflow detected";
 262          end if;
 263 
 264          return My_Stack;
 265       end Full_Check;
 266    end Stack_Check;
 267 
 268    ------------------------
 269    -- Update_Stack_Cache --
 270    ------------------------
 271 
 272    procedure Update_Stack_Cache (Stack : Stack_Access) is
 273    begin
 274       if not Multi_Processor then
 275          Cache := Stack;
 276       end if;
 277    end Update_Stack_Cache;
 278 
 279 end System.Stack_Checking.Operations;