1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
5 -- SYSTEM.MACHINE_STATE_OPERATIONS --
8 -- (Version for x86) --
11 -- Copyright (C) 1999-2002 Ada Core Technologies, Inc. --
13 -- GNAT is free software; you can redistribute it and/or modify it under --
14 -- terms of the GNU General Public License as published by the Free Soft- --
15 -- ware Foundation; either version 2, or (at your option) any later ver- --
16 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
17 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
18 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
19 -- for more details. You should have received a copy of the GNU General --
20 -- Public License distributed with GNAT; see file COPYING. If not, write --
21 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
22 -- MA 02111-1307, USA. --
24 -- As a special exception, if other files instantiate generics from this --
25 -- unit, or you link this unit with other files to produce an executable, --
26 -- this unit does not by itself cause the resulting executable to be --
27 -- covered by the GNU General Public License. This exception does not --
28 -- however invalidate any other reasons why the executable file might be --
29 -- covered by the GNU Public License. --
31 -- GNAT was originally developed by the GNAT team at New York University. --
32 -- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). --
34 ------------------------------------------------------------------------------
36 -- Note: it is very important that this unit not generate any exception
37 -- tables of any kind. Otherwise we get a nasty rtsfind recursion problem.
38 -- This means no subprograms, including implicitly generated ones.
40 with Unchecked_Conversion
;
41 with System
.Storage_Elements
;
42 with System
.Machine_Code
; use System
.Machine_Code
;
45 package body System
.Machine_State_Operations
is
47 use System
.Exceptions
;
49 type Uns8
is mod 2 ** 8;
50 type Uns32
is mod 2 ** 32;
52 type Bits5
is mod 2 ** 5;
53 type Bits6
is mod 2 ** 6;
55 function To_Address
is new Unchecked_Conversion
(Uns32
, Address
);
57 type Uns32_Ptr
is access all Uns32
;
58 function To_Uns32_Ptr
is new Unchecked_Conversion
(Uns32
, Uns32_Ptr
);
60 -- Note: the type Uns32 has an alignment of 4. However, in some cases
61 -- values of type Uns32_Ptr will not be aligned (notably in the case
62 -- where we get the immediate field from an instruction). However this
63 -- does not matter in practice, since the x86 does not require that
64 -- operands be aligned.
66 ----------------------
67 -- General Approach --
68 ----------------------
70 -- For the x86 version of this unit, the Subprogram_Info_Type values
71 -- are simply the starting code address for the subprogram. Popping
72 -- of stack frames works by analyzing the code in the prolog, and
73 -- deriving from this analysis the necessary information for restoring
74 -- the registers, including the return point.
76 ---------------------------
77 -- Description of Prolog --
78 ---------------------------
80 -- If a frame pointer is present, the prolog looks like
84 -- subl $nnn,%esp omitted if nnn = 0
85 -- pushl %edi omitted if edi not used
86 -- pushl %esi omitted if esi not used
87 -- pushl %ebx omitted if ebx not used
89 -- If a frame pointer is not present, the prolog looks like
91 -- subl $nnn,%esp omitted if nnn = 0
92 -- pushl %ebp omitted if ebp not used
93 -- pushl %edi omitted if edi not used
94 -- pushl %esi omitted if esi not used
95 -- pushl %ebx omitted if ebx not used
97 -- Note: any or all of the save over call registers may be used and
98 -- if so, will be saved using pushl as shown above. The order of the
99 -- pushl instructions will be as shown above for gcc generated code,
100 -- but the code in this unit does not assume this.
102 -------------------------
103 -- Description of Call --
104 -------------------------
106 -- A call looks like:
108 -- pushl ... push parameters
110 -- call ... perform the call
111 -- addl $nnn,%esp omitted if no parameters
113 -- Note that we are not absolutely guaranteed that the call is always
114 -- followed by an addl operation that readjusts %esp for this particular
115 -- call. There are two reasons for this:
117 -- 1) The addl can be delayed and combined in the case where more than
118 -- one call appears in sequence. This can be suppressed by using the
119 -- switch -fno-defer-pop and for Ada code, we automatically use
120 -- this switch, but we could still be dealing with C code that was
121 -- compiled without using this switch.
123 -- 2) Scheduling may result in moving the addl instruction away from
124 -- the call. It is not clear if this actually can happen at the
125 -- current time, but it is certainly conceptually possible.
127 -- The addl after the call is important, since we need to be able to
128 -- restore the proper %esp value when we pop the stack. However, we do
129 -- not try to compensate for either of the above effects. As noted above,
130 -- case 1 does not occur for Ada code, and it does not appear in practice
131 -- that case 2 occurs with any significant frequency (we have never seen
132 -- an example so far for gcc generated code).
134 -- Furthermore, it is only in the case of -fomit-frame-pointer that we
135 -- really get into trouble from not properly restoring %esp. If we have
136 -- a frame pointer, then the worst that happens is that %esp is slightly
137 -- more depressed than it should be. This could waste a bit of space on
138 -- the stack, and even in some cases cause a storage leak on the stack,
139 -- but it will not affect the functional correctness of the processing.
141 ----------------------------------------
142 -- Definitions of Instruction Formats --
143 ----------------------------------------
145 type Rcode
is (eax
, ecx
, edx
, ebx
, esp
, ebp
, esi
, edi
);
146 pragma Warnings
(Off
, Rcode
);
147 -- Code indicating which register is referenced in an instruction
149 -- The following define the format of a pushl instruction
151 Op_pushl
: constant Bits5
:= 2#
01010#
;
153 type Ins_pushl
is record
154 Op
: Bits5
:= Op_pushl
;
158 for Ins_pushl
use record
159 Op
at 0 range 3 .. 7;
160 Reg
at 0 range 0 .. 2;
163 Ins_pushl_ebp
: constant Ins_pushl
:= (Op_pushl
, Reg
=> ebp
);
165 type Ins_pushl_Ptr
is access all Ins_pushl
;
167 -- For the movl %esp,%ebp instruction, we only need to know the length
168 -- because we simply skip past it when we analyze the prolog.
170 Ins_movl_length
: constant := 2;
172 -- The following define the format of addl/subl esp instructions
174 Op_Immed
: constant Bits6
:= 2#
100000#
;
176 Op2_addl_Immed
: constant Bits5
:= 2#
11100#
;
177 pragma Unreferenced
(Op2_addl_Immed
);
179 Op2_subl_Immed
: constant Bits5
:= 2#
11101#
;
181 type Word_Byte
is (Word
, Byte
);
182 pragma Unreferenced
(Byte
);
184 type Ins_addl_subl_byte
is record
185 Op
: Bits6
; -- Set to Op_Immed
186 w
: Word_Byte
; -- Word/Byte flag (set to 1 = byte)
187 s
: Boolean; -- Sign extension bit (1 = extend)
188 Op2
: Bits5
; -- Secondary opcode
189 Reg
: Rcode
; -- Register
190 Imm8
: Uns8
; -- Immediate operand
193 for Ins_addl_subl_byte
use record
194 Op
at 0 range 2 .. 7;
197 Op2
at 1 range 3 .. 7;
198 Reg
at 1 range 0 .. 2;
199 Imm8
at 2 range 0 .. 7;
202 type Ins_addl_subl_word
is record
203 Op
: Bits6
; -- Set to Op_Immed
204 w
: Word_Byte
; -- Word/Byte flag (set to 0 = word)
205 s
: Boolean; -- Sign extension bit (1 = extend)
206 Op2
: Bits5
; -- Secondary opcode
207 Reg
: Rcode
; -- Register
208 Imm32
: Uns32
; -- Immediate operand
211 for Ins_addl_subl_word
use record
212 Op
at 0 range 2 .. 7;
215 Op2
at 1 range 3 .. 7;
216 Reg
at 1 range 0 .. 2;
217 Imm32
at 2 range 0 .. 31;
220 type Ins_addl_subl_byte_Ptr
is access all Ins_addl_subl_byte
;
221 type Ins_addl_subl_word_Ptr
is access all Ins_addl_subl_word
;
223 ---------------------
224 -- Prolog Analysis --
225 ---------------------
227 -- The analysis of the prolog answers the following questions:
229 -- 1. Is %ebp used as a frame pointer?
230 -- 2. How far is SP depressed (i.e. what is the stack frame size)
231 -- 3. Which registers are saved in the prolog, and in what order
233 -- The following data structure stores the answers to these questions
235 subtype SOC
is Rcode
range ebx
.. edi
;
236 -- Possible save over call registers
238 SOC_Max
: constant := 4;
239 -- Max number of SOC registers that can be pushed
241 type SOC_Push_Regs_Type
is array (1 .. 4) of Rcode
;
242 -- Used to hold the register codes of pushed SOC registers
244 type Prolog_Type
is record
247 -- This is set to True if %ebp is used as a frame register, and
248 -- False otherwise (in the False case, %ebp may be saved in the
249 -- usual manner along with the other SOC registers).
251 Frame_Length
: Uns32
;
252 -- Amount by which ESP is decremented on entry, includes the effects
253 -- of push's of save over call registers as indicated above, e.g. if
254 -- the prolog of a routine is:
263 -- Then the value of Frame_Length would be 436 (424 + 3 * 4). A
264 -- precise definition is that it is:
266 -- %esp on entry minus %esp after last SOC push
268 -- That definition applies both in the frame pointer present and
269 -- the frame pointer absent cases.
271 Num_SOC_Push
: Integer range 0 .. SOC_Max
;
272 -- Number of save over call registers actually saved by pushl
273 -- instructions (other than the initial pushl to save the frame
274 -- pointer if a frame pointer is in use).
276 SOC_Push_Regs
: SOC_Push_Regs_Type
;
277 -- The First Num_SOC_Push entries of this array are used to contain
278 -- the codes for the SOC registers, in the order in which they were
279 -- pushed. Note that this array excludes %ebp if it is used as a frame
280 -- register, since although %ebp is still considered an SOC register
281 -- in this case, it is saved and restored by a separate mechanism.
282 -- Also we will never see %esp represented in this list. Again, it is
283 -- true that %esp is saved over call, but it is restored by a separate
288 procedure Analyze_Prolog
(A
: Address
; Prolog
: out Prolog_Type
);
289 -- Given the address of the start of the prolog for a procedure,
290 -- analyze the instructions of the prolog, and set Prolog to contain
291 -- the information obtained from this analysis.
293 ----------------------------------
294 -- Machine_State_Representation --
295 ----------------------------------
297 -- The type Machine_State is defined in the body of Ada.Exceptions as
298 -- a Storage_Array of length 1 .. Machine_State_Length. But really it
299 -- has structure as defined here. We use the structureless declaration
300 -- in Ada.Exceptions to avoid this unit from being implementation
301 -- dependent. The actual definition of Machine_State is as follows:
303 type SOC_Regs_Type
is array (SOC
) of Uns32
;
305 type MState
is record
307 -- The instruction pointer location (which is the return point
308 -- value from the next level down in all cases).
310 Regs
: SOC_Regs_Type
;
311 -- Values of the save over call registers
314 for MState
use record
315 eip
at 0 range 0 .. 31;
316 Regs
at 4 range 0 .. 5 * 32 - 1;
318 -- Note: the routines Enter_Handler, and Set_Machine_State reference
319 -- the fields in this structure non-symbolically.
321 type MState_Ptr
is access all MState
;
323 function To_MState_Ptr
is
324 new Unchecked_Conversion
(Machine_State
, MState_Ptr
);
326 ----------------------------
327 -- Allocate_Machine_State --
328 ----------------------------
330 function Allocate_Machine_State
return Machine_State
is
331 use System
.Storage_Elements
;
335 (Memory
.Alloc
(MState
'Max_Size_In_Storage_Elements));
336 end Allocate_Machine_State
;
342 procedure Analyze_Prolog
(A
: Address
; Prolog
: out Prolog_Type
) is
345 Pas
: Ins_addl_subl_byte_Ptr
;
347 function To_Ins_pushl_Ptr
is
348 new Unchecked_Conversion
(Address
, Ins_pushl_Ptr
);
350 function To_Ins_addl_subl_byte_Ptr
is
351 new Unchecked_Conversion
(Address
, Ins_addl_subl_byte_Ptr
);
353 function To_Ins_addl_subl_word_Ptr
is
354 new Unchecked_Conversion
(Address
, Ins_addl_subl_word_Ptr
);
358 Prolog
.Frame_Length
:= 0;
360 if Ptr
= Null_Address
then
361 Prolog
.Num_SOC_Push
:= 0;
362 Prolog
.Frame_Reg
:= True;
366 if To_Ins_pushl_Ptr
(Ptr
).all = Ins_pushl_ebp
then
367 Ptr
:= Ptr
+ 1 + Ins_movl_length
;
368 Prolog
.Frame_Reg
:= True;
370 Prolog
.Frame_Reg
:= False;
373 Pas
:= To_Ins_addl_subl_byte_Ptr
(Ptr
);
376 and then Pas
.Op2
= Op2_subl_Immed
377 and then Pas
.Reg
= esp
380 Prolog
.Frame_Length
:= Prolog
.Frame_Length
+
381 To_Ins_addl_subl_word_Ptr
(Ptr
).Imm32
;
385 Prolog
.Frame_Length
:= Prolog
.Frame_Length
+ Uns32
(Pas
.Imm8
);
388 -- Note: we ignore sign extension, since a sign extended
389 -- value that was negative would imply a ludicrous frame size.
393 -- Now scan push instructions for SOC registers
395 Prolog
.Num_SOC_Push
:= 0;
398 Ppl
:= To_Ins_pushl_Ptr
(Ptr
);
400 if Ppl
.Op
= Op_pushl
and then Ppl
.Reg
in SOC
then
401 Prolog
.Num_SOC_Push
:= Prolog
.Num_SOC_Push
+ 1;
402 Prolog
.SOC_Push_Regs
(Prolog
.Num_SOC_Push
) := Ppl
.Reg
;
403 Prolog
.Frame_Length
:= Prolog
.Frame_Length
+ 4;
417 procedure Enter_Handler
(M
: Machine_State
; Handler
: Handler_Loc
) is
419 Asm
("mov %0,%%edx", Inputs
=> Machine_State
'Asm_Input ("r", M
));
420 Asm
("mov %0,%%eax", Inputs
=> Handler_Loc
'Asm_Input ("r", Handler
));
422 Asm
("mov 4(%%edx),%%ebx"); -- M.Regs (ebx)
423 Asm
("mov 12(%%edx),%%ebp"); -- M.Regs (ebp)
424 Asm
("mov 16(%%edx),%%esi"); -- M.Regs (esi)
425 Asm
("mov 20(%%edx),%%edi"); -- M.Regs (edi)
426 Asm
("mov 8(%%edx),%%esp"); -- M.Regs (esp)
434 function Fetch_Code
(Loc
: Code_Loc
) return Code_Loc
is
439 ------------------------
440 -- Free_Machine_State --
441 ------------------------
443 procedure Free_Machine_State
(M
: in out Machine_State
) is
445 Memory
.Free
(Address
(M
));
446 M
:= Machine_State
(Null_Address
);
447 end Free_Machine_State
;
453 function Get_Code_Loc
(M
: Machine_State
) return Code_Loc
is
455 Asm_Call_Size
: constant := 2;
456 -- Minimum size for a call instruction under ix86. Using the minimum
457 -- size is safe here as the call point computed from the return point
458 -- will always be inside the call instruction.
460 MS
: constant MState_Ptr
:= To_MState_Ptr
(M
);
464 return To_Address
(MS
.eip
);
466 -- When doing a call the return address is pushed to the stack.
467 -- We want to return the call point address, so we subtract
468 -- Asm_Call_Size from the return address. This value is set
469 -- to 5 as an asm call takes 5 bytes on x86 architectures.
471 return To_Address
(MS
.eip
- Asm_Call_Size
);
475 --------------------------
476 -- Machine_State_Length --
477 --------------------------
479 function Machine_State_Length
480 return System
.Storage_Elements
.Storage_Offset
483 return MState
'Max_Size_In_Storage_Elements;
484 end Machine_State_Length
;
492 Info
: Subprogram_Info_Type
)
494 MS
: constant MState_Ptr
:= To_MState_Ptr
(M
);
498 -- Pointer to stack location after last SOC push
501 -- Pointer to stack location containing return address
504 Analyze_Prolog
(Info
, PL
);
506 -- Case of frame register, use EBP, safer than ESP
509 SOC_Ptr
:= MS
.Regs
(ebp
) - PL
.Frame_Length
;
510 Rtn_Ptr
:= MS
.Regs
(ebp
) + 4;
511 MS
.Regs
(ebp
) := To_Uns32_Ptr
(MS
.Regs
(ebp
)).all;
513 -- No frame pointer, use ESP, and hope we have it exactly right!
516 SOC_Ptr
:= MS
.Regs
(esp
);
517 Rtn_Ptr
:= SOC_Ptr
+ PL
.Frame_Length
;
520 -- Get saved values of SOC registers
522 for J
in reverse 1 .. PL
.Num_SOC_Push
loop
523 MS
.Regs
(PL
.SOC_Push_Regs
(J
)) := To_Uns32_Ptr
(SOC_Ptr
).all;
524 SOC_Ptr
:= SOC_Ptr
+ 4;
527 MS
.eip
:= To_Uns32_Ptr
(Rtn_Ptr
).all;
528 MS
.Regs
(esp
) := Rtn_Ptr
+ 4;
531 -----------------------
532 -- Set_Machine_State --
533 -----------------------
535 procedure Set_Machine_State
(M
: Machine_State
) is
536 N
: constant Asm_Output_Operand
:= No_Output_Operands
;
539 Asm
("mov %0,%%edx", N
, Machine_State
'Asm_Input ("r", M
));
541 -- At this stage, we have the following situation (note that we
542 -- are assuming that the -fomit-frame-pointer switch has not been
543 -- used in compiling this procedure.
547 -- old ebp <------ current ebp/esp value
549 -- The values of registers ebx/esi/edi are unchanged from entry
550 -- so they have the values we want, and %edx points to the parameter
551 -- value M, so we can store these values directly.
553 Asm
("mov %%ebx,4(%%edx)"); -- M.Regs (ebx)
554 Asm
("mov %%esi,16(%%edx)"); -- M.Regs (esi)
555 Asm
("mov %%edi,20(%%edx)"); -- M.Regs (edi)
557 -- The desired value of ebp is the old value
559 Asm
("mov 0(%%ebp),%%eax");
560 Asm
("mov %%eax,12(%%edx)"); -- M.Regs (ebp)
562 -- The return point is the desired eip value
564 Asm
("mov 4(%%ebp),%%eax");
565 Asm
("mov %%eax,(%%edx)"); -- M.eip
567 -- Finally, the desired %esp value is the value at the point of
568 -- call to this routine *before* pushing the parameter value.
570 Asm
("lea 12(%%ebp),%%eax");
571 Asm
("mov %%eax,8(%%edx)"); -- M.Regs (esp)
572 end Set_Machine_State
;
574 ------------------------------
575 -- Set_Signal_Machine_State --
576 ------------------------------
578 procedure Set_Signal_Machine_State
580 Context
: System
.Address
)
582 pragma Warnings
(Off
, M
);
583 pragma Warnings
(Off
, Context
);
587 end Set_Signal_Machine_State
;
589 end System
.Machine_State_Operations
;