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