File : s-traceb-cert-ppc.adb


   1 ------------------------------------------------------------------------------
   2 --                                                                          --
   3 --                         GNAT COMPILER COMPONENTS                         --
   4 --                                                                          --
   5 --                     S Y S T E M . T R A C E B A C K                      --
   6 --                                                                          --
   7 --                                 B o d y                                  --
   8 --                                                                          --
   9 --          Copyright (C) 1999-2014, Free Software Foundation, Inc.         --
  10 --                                                                          --
  11 -- GNAT 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 -- GNAT was originally developed  by the GNAT team at  New York University. --
  28 -- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). --
  29 --                                                                          --
  30 ------------------------------------------------------------------------------
  31 
  32 with System.Address_To_Access_Conversions;
  33 
  34 --  The unit is a replacement of tracebak.c for PowerPC targets with
  35 --  certification needs, providing an all Ada implementation with a very
  36 --  simple parameterization scheme (see System.Traceback_Control below).
  37 
  38 --  The general principle of operation is similar: assuming a simple memory
  39 --  organization of the call stack frames with both a return address and a
  40 --  back-link expected to be stored at a fixed offset from a given frame
  41 --  pointer. For "main" calling "A" calling "B", something like:
  42 --
  43 --  SP        <------ stack memory, frames push this way                 TOP
  44 --  |         <------ (decreasing addresses) on calls                      |
  45 --  v                                                                      v
  46 --  |<----- frame for B ----->|<----- frame for A ----->|< frame for main >|
  47 --  .                         .                         .                  .
  48 --  .    ------------------------  ------------------------                .
  49 --  .    |                    . v  |                    . v                .
  50 --  #====|====================#====|====================#==================#
  51 --  # link | ... | ret@ | ... # link | ... | ret@ | ... # link ...         #
  52 --  #===============|=========#===============|=========#==================#
  53 --  .               v         .               v         .                  .
  54 --  .      1 insn past the    .    1 insn past the      .                  .
  55 --  .      call to B in A     .    call to A in main    .                  .
  56 
  57 --  Computing a backtrace consists in walking up the link chains starting from
  58 --  the current frame, latching the address of calls at each step and stopping
  59 --  when the top frame is reached.
  60 
  61 --  This is pretty much generic except for two ABI details that vary across
  62 --  OS implementations: the frame pointer to return address offset, and
  63 --  the characterization of the top-of-stack frame. We rely on a separate
  64 --  Traceback_Control package to expose a consistent abstraction of those
  65 --  differences.
  66 
  67 with System.Traceback_Control; use System.Traceback_Control;
  68 
  69 --  We expect Traceback_Control to expose:
  70 --
  71 --     function Return_Address_Offset return System.Address;
  72 --     --  Relative to a given frame pointer, the constant offset at which the
  73 --     --  return address for this frame is stored.
  74 --
  75 --     FP  RA_Offset
  76 --      |----------->|
  77 --      v            v
  78 --     #=====================...
  79 --     # link | ... | ret@ | ...
  80 --     #=====================...
  81 --
  82 --  Then:
  83 --
  84 --     function Is_Topframe_Retaddr (Retaddr : System.Address) return boolean;
  85 --     --  Whether the provided Retaddr signals a top-of-stack frame.
  86 
  87 package body System.Traceback is
  88 
  89    package Addr is new System.Address_To_Access_Conversions (System.Address);
  90 
  91    package CTL renames System.Traceback_Control;
  92 
  93    -----------------
  94    -- Frame links --
  95    -----------------
  96 
  97    function Link_From (Frame : System.Address) return System.Address;
  98    --  For a given subprogram frame, return the address of the next
  99    --  frame in the call chain.
 100 
 101    pragma Inline (Link_From);
 102 
 103    function Link_From (Frame : System.Address) return System.Address is
 104 
 105       Frame_Link_Offset : constant := 0;
 106       --  Relative to a given frame pointer, this is the offset in bytes of
 107       --  the memory location where we always expect the address of the caller
 108       --  frame to be stored. This defines our notion of a frame pointer.
 109 
 110    begin
 111       return Addr.To_Pointer (Frame + Frame_Link_Offset).all;
 112    end Link_From;
 113 
 114    ---------------
 115    -- Frame PCs --
 116    ---------------
 117 
 118    function Retaddr_From (Frame : System.Address) return System.Address;
 119    --  Return the return address for a given frame
 120 
 121    pragma Inline (Retaddr_From);
 122 
 123    function Retaddr_From (Frame : System.Address) return System.Address is
 124    begin
 125       --  Here, the offset at which we expect to find the return address
 126       --  in the frame is part of control parameters.
 127 
 128       return Addr.To_Pointer (Frame + CTL.Return_Address_Offset).all;
 129    end Retaddr_From;
 130 
 131    ----------------------------------
 132    -- Identifying the Top of stack --
 133    ----------------------------------
 134 
 135    function Top_Of_Stack_At (Frame : System.Address) return Boolean;
 136    --  Whether the provided Frame pointer designates a call chain entry point.
 137 
 138    pragma Inline (Top_Of_Stack_At);
 139 
 140    function Top_Of_Stack_At (Frame : System.Address) return Boolean is
 141    begin
 142       --  Two indicators we expect are a null frame pointer (back-link from
 143       --  the previous frame) or a particular return address with variations
 144       --  across target ABIs. We add an extra alignment check only to prevent
 145       --  erroneous memory accesses in case something unexpected happens.
 146 
 147       return Frame = System.Null_Address
 148         or else Frame mod System.Address'Alignment /= 0
 149         or else CTL.Is_Topframe_Retaddr (Retaddr_From (Frame));
 150    end Top_Of_Stack_At;
 151 
 152    ------------------
 153    --  Call_Chain  --
 154    ------------------
 155 
 156    procedure Call_Chain
 157      (Traceback   : in out System.Traceback_Entries.Tracebacks_Array;
 158       Max_Len     : Natural;
 159       Len         : out Natural;
 160       Exclude_Min : System.Address := System.Null_Address;
 161       Exclude_Max : System.Address := System.Null_Address;
 162       Skip_Frames : Natural := 1) is
 163 
 164       pragma Assert (Return_Address_Offset mod System.Address'Alignment = 0);
 165 
 166       Retaddr_To_Call_Offset : constant := 4;
 167       --  Size of call instruction to subtract from return address to get the
 168       --  PC of the corresponding call instruction.
 169 
 170       Frame : System.Address;
 171       --  Frame being processed
 172 
 173       Last : Integer := Traceback'First - 1;
 174       --  Index of last traceback written to the buffer
 175 
 176       procedure Forced_Callee;
 177       --  Force save of return address of Call_Chain on PPC
 178 
 179       -------------------
 180       -- Forced_Callee --
 181       -------------------
 182 
 183       --  The PPC ABI has an unusual characteristic: the return address saved
 184       --  by a subprogram is located in its caller's frame, and the save
 185       --  operation only occurs if the function performs a call.
 186 
 187       --  To make Call_Chain able to consistently retrieve its own return
 188       --  address, we define Forced_Callee and call it.  This routine should
 189       --  never be inlined.
 190 
 191       procedure Forced_Callee is
 192          Dummy : aliased Integer := 0;
 193          pragma Volatile (Dummy);
 194          pragma Warnings (Off, Dummy);
 195          --  Force allocation of a frame. Dummy must be both volatile and
 196          --  referenced (achieved by declaring it aliased). Suppress warning
 197          --  that it could be declared a constant, and that pragma Volatile
 198          --  has no effect (it forces creation of the frame).
 199       begin
 200          null;
 201       end Forced_Callee;
 202 
 203       function builtin_frame_address (Level : Integer) return System.Address;
 204       pragma Import
 205         (Intrinsic, builtin_frame_address, "__builtin_frame_address");
 206 
 207    --  Start of processing for Call_Chain
 208 
 209    begin
 210       Forced_Callee;
 211       Len := 0;
 212 
 213       --  Start with the frame pointer for the current frame.
 214 
 215       Frame := builtin_frame_address (0);
 216 
 217       --  Skip the first Skip_Frames frames, as required by our spec. If we
 218       --  happen to hit the top of stack while doing so, just yield an empty
 219       --  trace. We expect this never to happen in regular circumstances of
 220       --  calls issued by the other parts of the runtime library.
 221 
 222       for J in 1 .. Skip_Frames + 1 loop
 223          if Top_Of_Stack_At (Frame) then
 224             return;
 225          end if;
 226 
 227          Frame := Link_From (Frame);
 228       end loop;
 229 
 230       pragma Assert (Frame /= System.Null_Address);
 231 
 232       --  Now walk up recording call addresses as we go. Stop when the top
 233       --  of stack is reached or when the provided trace buffer is full.
 234       --  Don't record addresses in the provided exclusion range, typically
 235       --  used to leave aside frames from our exception propagation internals.
 236 
 237       while not Top_Of_Stack_At (Frame)
 238         and then Last < Traceback'Last
 239         and then Len < Max_Len
 240       loop
 241 
 242          declare
 243             Call_Addr : constant System.Address
 244               := Retaddr_From (Frame) - Retaddr_To_Call_Offset;
 245 
 246          begin
 247             if Call_Addr not in Exclude_Min .. Exclude_Max then
 248                Last := Last + 1;
 249                Len := Len + 1;
 250                Traceback (Last) := Call_Addr;
 251             end if;
 252 
 253             Frame := Link_From (Frame);
 254          end;
 255 
 256          pragma Assert (Frame /= System.Null_Address);
 257       end loop;
 258    end Call_Chain;
 259 
 260 end System.Traceback;