1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
5 -- SYSTEM.MACHINE_STATE_OPERATIONS --
8 -- (Version for x86) --
10 -- Copyright (C) 1999-2004 Ada Core Technologies, Inc. --
12 -- GNAT is free software; you can redistribute it and/or modify it under --
13 -- terms of the GNU General Public License as published by the Free Soft- --
14 -- ware Foundation; either version 2, or (at your option) any later ver- --
15 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
16 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
17 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
18 -- for more details. You should have received a copy of the GNU General --
19 -- Public License distributed with GNAT; see file COPYING. If not, write --
20 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
21 -- MA 02111-1307, USA. --
23 -- As a special exception, if other files instantiate generics from this --
24 -- unit, or you link this unit with other files to produce an executable, --
25 -- this unit does not by itself cause the resulting executable to be --
26 -- covered by the GNU General Public License. This exception does not --
27 -- however invalidate any other reasons why the executable file might be --
28 -- covered by the GNU Public License. --
30 -- GNAT was originally developed by the GNAT team at New York University. --
31 -- Extensive contributions were provided by Ada Core Technologies Inc. --
33 ------------------------------------------------------------------------------
35 -- Note: it is very important that this unit not generate any exception
36 -- tables of any kind. Otherwise we get a nasty rtsfind recursion problem.
37 -- This means no subprograms, including implicitly generated ones.
39 with Unchecked_Conversion
;
40 with System
.Storage_Elements
;
41 with System
.Machine_Code
; use System
.Machine_Code
;
44 package body System
.Machine_State_Operations
is
46 function "+" (Left
, Right
: Address
) return Address
;
47 pragma Import
(Intrinsic
, "+");
48 -- Provide addition operation on type Address (this may not be directly
49 -- available if type System.Address is non-private and the operations on
50 -- the type are made abstract to hide them from public users of System).
52 use System
.Exceptions
;
54 type Uns8
is mod 2 ** 8;
55 type Uns32
is mod 2 ** 32;
57 type Bits5
is mod 2 ** 5;
58 type Bits6
is mod 2 ** 6;
60 function To_Address
is new Unchecked_Conversion
(Uns32
, Address
);
62 type Uns32_Ptr
is access all Uns32
;
63 function To_Uns32_Ptr
is new Unchecked_Conversion
(Uns32
, Uns32_Ptr
);
65 -- Note: the type Uns32 has an alignment of 4. However, in some cases
66 -- values of type Uns32_Ptr will not be aligned (notably in the case
67 -- where we get the immediate field from an instruction). However this
68 -- does not matter in practice, since the x86 does not require that
69 -- operands be aligned.
71 ----------------------
72 -- General Approach --
73 ----------------------
75 -- For the x86 version of this unit, the Subprogram_Info_Type values
76 -- are simply the starting code address for the subprogram. Popping
77 -- of stack frames works by analyzing the code in the prolog, and
78 -- deriving from this analysis the necessary information for restoring
79 -- the registers, including the return point.
81 ---------------------------
82 -- Description of Prolog --
83 ---------------------------
85 -- If a frame pointer is present, the prolog looks like
89 -- subl $nnn,%esp omitted if nnn = 0
90 -- pushl %edi omitted if edi not used
91 -- pushl %esi omitted if esi not used
92 -- pushl %ebx omitted if ebx not used
94 -- If a frame pointer is not present, the prolog looks like
96 -- subl $nnn,%esp omitted if nnn = 0
97 -- pushl %ebp omitted if ebp not used
98 -- pushl %edi omitted if edi not used
99 -- pushl %esi omitted if esi not used
100 -- pushl %ebx omitted if ebx not used
102 -- Note: any or all of the save over call registers may be used and
103 -- if so, will be saved using pushl as shown above. The order of the
104 -- pushl instructions will be as shown above for gcc generated code,
105 -- but the code in this unit does not assume this.
107 -------------------------
108 -- Description of Call --
109 -------------------------
111 -- A call looks like:
113 -- pushl ... push parameters
115 -- call ... perform the call
116 -- addl $nnn,%esp omitted if no parameters
118 -- Note that we are not absolutely guaranteed that the call is always
119 -- followed by an addl operation that readjusts %esp for this particular
120 -- call. There are two reasons for this:
122 -- 1) The addl can be delayed and combined in the case where more than
123 -- one call appears in sequence. This can be suppressed by using the
124 -- switch -fno-defer-pop and for Ada code, we automatically use
125 -- this switch, but we could still be dealing with C code that was
126 -- compiled without using this switch.
128 -- 2) Scheduling may result in moving the addl instruction away from
129 -- the call. It is not clear if this actually can happen at the
130 -- current time, but it is certainly conceptually possible.
132 -- The addl after the call is important, since we need to be able to
133 -- restore the proper %esp value when we pop the stack. However, we do
134 -- not try to compensate for either of the above effects. As noted above,
135 -- case 1 does not occur for Ada code, and it does not appear in practice
136 -- that case 2 occurs with any significant frequency (we have never seen
137 -- an example so far for gcc generated code).
139 -- Furthermore, it is only in the case of -fomit-frame-pointer that we
140 -- really get into trouble from not properly restoring %esp. If we have
141 -- a frame pointer, then the worst that happens is that %esp is slightly
142 -- more depressed than it should be. This could waste a bit of space on
143 -- the stack, and even in some cases cause a storage leak on the stack,
144 -- but it will not affect the functional correctness of the processing.
146 ----------------------------------------
147 -- Definitions of Instruction Formats --
148 ----------------------------------------
150 type Rcode
is (eax
, ecx
, edx
, ebx
, esp
, ebp
, esi
, edi
);
151 pragma Warnings
(Off
, Rcode
);
152 -- Code indicating which register is referenced in an instruction
154 -- The following define the format of a pushl instruction
156 Op_pushl
: constant Bits5
:= 2#
01010#
;
158 type Ins_pushl
is record
159 Op
: Bits5
:= Op_pushl
;
163 for Ins_pushl
use record
164 Op
at 0 range 3 .. 7;
165 Reg
at 0 range 0 .. 2;
168 Ins_pushl_ebp
: constant Ins_pushl
:= (Op_pushl
, Reg
=> ebp
);
170 type Ins_pushl_Ptr
is access all Ins_pushl
;
172 -- For the movl %esp,%ebp instruction, we only need to know the length
173 -- because we simply skip past it when we analyze the prolog.
175 Ins_movl_length
: constant := 2;
177 -- The following define the format of addl/subl esp instructions
179 Op_Immed
: constant Bits6
:= 2#
100000#
;
181 Op2_addl_Immed
: constant Bits5
:= 2#
11100#
;
182 pragma Unreferenced
(Op2_addl_Immed
);
184 Op2_subl_Immed
: constant Bits5
:= 2#
11101#
;
186 type Word_Byte
is (Word
, Byte
);
187 pragma Unreferenced
(Byte
);
189 type Ins_addl_subl_byte
is record
190 Op
: Bits6
; -- Set to Op_Immed
191 w
: Word_Byte
; -- Word/Byte flag (set to 1 = byte)
192 s
: Boolean; -- Sign extension bit (1 = extend)
193 Op2
: Bits5
; -- Secondary opcode
194 Reg
: Rcode
; -- Register
195 Imm8
: Uns8
; -- Immediate operand
198 for Ins_addl_subl_byte
use record
199 Op
at 0 range 2 .. 7;
202 Op2
at 1 range 3 .. 7;
203 Reg
at 1 range 0 .. 2;
204 Imm8
at 2 range 0 .. 7;
207 type Ins_addl_subl_word
is record
208 Op
: Bits6
; -- Set to Op_Immed
209 w
: Word_Byte
; -- Word/Byte flag (set to 0 = word)
210 s
: Boolean; -- Sign extension bit (1 = extend)
211 Op2
: Bits5
; -- Secondary opcode
212 Reg
: Rcode
; -- Register
213 Imm32
: Uns32
; -- Immediate operand
216 for Ins_addl_subl_word
use record
217 Op
at 0 range 2 .. 7;
220 Op2
at 1 range 3 .. 7;
221 Reg
at 1 range 0 .. 2;
222 Imm32
at 2 range 0 .. 31;
225 type Ins_addl_subl_byte_Ptr
is access all Ins_addl_subl_byte
;
226 type Ins_addl_subl_word_Ptr
is access all Ins_addl_subl_word
;
228 ---------------------
229 -- Prolog Analysis --
230 ---------------------
232 -- The analysis of the prolog answers the following questions:
234 -- 1. Is %ebp used as a frame pointer?
235 -- 2. How far is SP depressed (i.e. what is the stack frame size)
236 -- 3. Which registers are saved in the prolog, and in what order
238 -- The following data structure stores the answers to these questions
240 subtype SOC
is Rcode
range ebx
.. edi
;
241 -- Possible save over call registers
243 SOC_Max
: constant := 4;
244 -- Max number of SOC registers that can be pushed
246 type SOC_Push_Regs_Type
is array (1 .. 4) of Rcode
;
247 -- Used to hold the register codes of pushed SOC registers
249 type Prolog_Type
is record
252 -- This is set to True if %ebp is used as a frame register, and
253 -- False otherwise (in the False case, %ebp may be saved in the
254 -- usual manner along with the other SOC registers).
256 Frame_Length
: Uns32
;
257 -- Amount by which ESP is decremented on entry, includes the effects
258 -- of push's of save over call registers as indicated above, e.g. if
259 -- the prolog of a routine is:
268 -- Then the value of Frame_Length would be 436 (424 + 3 * 4). A
269 -- precise definition is that it is:
271 -- %esp on entry minus %esp after last SOC push
273 -- That definition applies both in the frame pointer present and
274 -- the frame pointer absent cases.
276 Num_SOC_Push
: Integer range 0 .. SOC_Max
;
277 -- Number of save over call registers actually saved by pushl
278 -- instructions (other than the initial pushl to save the frame
279 -- pointer if a frame pointer is in use).
281 SOC_Push_Regs
: SOC_Push_Regs_Type
;
282 -- The First Num_SOC_Push entries of this array are used to contain
283 -- the codes for the SOC registers, in the order in which they were
284 -- pushed. Note that this array excludes %ebp if it is used as a frame
285 -- register, since although %ebp is still considered an SOC register
286 -- in this case, it is saved and restored by a separate mechanism.
287 -- Also we will never see %esp represented in this list. Again, it is
288 -- true that %esp is saved over call, but it is restored by a separate
293 procedure Analyze_Prolog
(A
: Address
; Prolog
: out Prolog_Type
);
294 -- Given the address of the start of the prolog for a procedure,
295 -- analyze the instructions of the prolog, and set Prolog to contain
296 -- the information obtained from this analysis.
298 ----------------------------------
299 -- Machine_State_Representation --
300 ----------------------------------
302 -- The type Machine_State is defined in the body of Ada.Exceptions as
303 -- a Storage_Array of length 1 .. Machine_State_Length. But really it
304 -- has structure as defined here. We use the structureless declaration
305 -- in Ada.Exceptions to avoid this unit from being implementation
306 -- dependent. The actual definition of Machine_State is as follows:
308 type SOC_Regs_Type
is array (SOC
) of Uns32
;
310 type MState
is record
312 -- The instruction pointer location (which is the return point
313 -- value from the next level down in all cases).
315 Regs
: SOC_Regs_Type
;
316 -- Values of the save over call registers
319 for MState
use record
320 eip
at 0 range 0 .. 31;
321 Regs
at 4 range 0 .. 5 * 32 - 1;
323 -- Note: the routines Enter_Handler, and Set_Machine_State reference
324 -- the fields in this structure non-symbolically.
326 type MState_Ptr
is access all MState
;
328 function To_MState_Ptr
is
329 new Unchecked_Conversion
(Machine_State
, MState_Ptr
);
331 ----------------------------
332 -- Allocate_Machine_State --
333 ----------------------------
335 function Allocate_Machine_State
return Machine_State
is
336 use System
.Storage_Elements
;
340 (Memory
.Alloc
(MState
'Max_Size_In_Storage_Elements));
341 end Allocate_Machine_State
;
347 procedure Analyze_Prolog
(A
: Address
; Prolog
: out Prolog_Type
) is
350 Pas
: Ins_addl_subl_byte_Ptr
;
352 function To_Ins_pushl_Ptr
is
353 new Unchecked_Conversion
(Address
, Ins_pushl_Ptr
);
355 function To_Ins_addl_subl_byte_Ptr
is
356 new Unchecked_Conversion
(Address
, Ins_addl_subl_byte_Ptr
);
358 function To_Ins_addl_subl_word_Ptr
is
359 new Unchecked_Conversion
(Address
, Ins_addl_subl_word_Ptr
);
363 Prolog
.Frame_Length
:= 0;
365 if Ptr
= Null_Address
then
366 Prolog
.Num_SOC_Push
:= 0;
367 Prolog
.Frame_Reg
:= True;
371 if To_Ins_pushl_Ptr
(Ptr
).all = Ins_pushl_ebp
then
372 Ptr
:= Ptr
+ 1 + Ins_movl_length
;
373 Prolog
.Frame_Reg
:= True;
375 Prolog
.Frame_Reg
:= False;
378 Pas
:= To_Ins_addl_subl_byte_Ptr
(Ptr
);
381 and then Pas
.Op2
= Op2_subl_Immed
382 and then Pas
.Reg
= esp
385 Prolog
.Frame_Length
:= Prolog
.Frame_Length
+
386 To_Ins_addl_subl_word_Ptr
(Ptr
).Imm32
;
390 Prolog
.Frame_Length
:= Prolog
.Frame_Length
+ Uns32
(Pas
.Imm8
);
393 -- Note: we ignore sign extension, since a sign extended
394 -- value that was negative would imply a ludicrous frame size.
398 -- Now scan push instructions for SOC registers
400 Prolog
.Num_SOC_Push
:= 0;
403 Ppl
:= To_Ins_pushl_Ptr
(Ptr
);
405 if Ppl
.Op
= Op_pushl
and then Ppl
.Reg
in SOC
then
406 Prolog
.Num_SOC_Push
:= Prolog
.Num_SOC_Push
+ 1;
407 Prolog
.SOC_Push_Regs
(Prolog
.Num_SOC_Push
) := Ppl
.Reg
;
408 Prolog
.Frame_Length
:= Prolog
.Frame_Length
+ 4;
422 procedure Enter_Handler
(M
: Machine_State
; Handler
: Handler_Loc
) is
424 Asm
("mov %0,%%edx", Inputs
=> Machine_State
'Asm_Input ("r", M
));
425 Asm
("mov %0,%%eax", Inputs
=> Handler_Loc
'Asm_Input ("r", Handler
));
427 Asm
("mov 4(%%edx),%%ebx"); -- M.Regs (ebx)
428 Asm
("mov 12(%%edx),%%ebp"); -- M.Regs (ebp)
429 Asm
("mov 16(%%edx),%%esi"); -- M.Regs (esi)
430 Asm
("mov 20(%%edx),%%edi"); -- M.Regs (edi)
431 Asm
("mov 8(%%edx),%%esp"); -- M.Regs (esp)
439 function Fetch_Code
(Loc
: Code_Loc
) return Code_Loc
is
444 ------------------------
445 -- Free_Machine_State --
446 ------------------------
448 procedure Free_Machine_State
(M
: in out Machine_State
) is
450 Memory
.Free
(Address
(M
));
451 M
:= Machine_State
(Null_Address
);
452 end Free_Machine_State
;
458 function Get_Code_Loc
(M
: Machine_State
) return Code_Loc
is
460 Asm_Call_Size
: constant := 2;
461 -- Minimum size for a call instruction under ix86. Using the minimum
462 -- size is safe here as the call point computed from the return point
463 -- will always be inside the call instruction.
465 MS
: constant MState_Ptr
:= To_MState_Ptr
(M
);
469 return To_Address
(MS
.eip
);
471 -- When doing a call the return address is pushed to the stack.
472 -- We want to return the call point address, so we subtract
473 -- Asm_Call_Size from the return address. This value is set
474 -- to 5 as an asm call takes 5 bytes on x86 architectures.
476 return To_Address
(MS
.eip
- Asm_Call_Size
);
480 --------------------------
481 -- Machine_State_Length --
482 --------------------------
484 function Machine_State_Length
485 return System
.Storage_Elements
.Storage_Offset
488 return MState
'Max_Size_In_Storage_Elements;
489 end Machine_State_Length
;
497 Info
: Subprogram_Info_Type
)
499 MS
: constant MState_Ptr
:= To_MState_Ptr
(M
);
503 -- Pointer to stack location after last SOC push
506 -- Pointer to stack location containing return address
509 Analyze_Prolog
(Info
, PL
);
511 -- Case of frame register, use EBP, safer than ESP
514 SOC_Ptr
:= MS
.Regs
(ebp
) - PL
.Frame_Length
;
515 Rtn_Ptr
:= MS
.Regs
(ebp
) + 4;
516 MS
.Regs
(ebp
) := To_Uns32_Ptr
(MS
.Regs
(ebp
)).all;
518 -- No frame pointer, use ESP, and hope we have it exactly right!
521 SOC_Ptr
:= MS
.Regs
(esp
);
522 Rtn_Ptr
:= SOC_Ptr
+ PL
.Frame_Length
;
525 -- Get saved values of SOC registers
527 for J
in reverse 1 .. PL
.Num_SOC_Push
loop
528 MS
.Regs
(PL
.SOC_Push_Regs
(J
)) := To_Uns32_Ptr
(SOC_Ptr
).all;
529 SOC_Ptr
:= SOC_Ptr
+ 4;
532 MS
.eip
:= To_Uns32_Ptr
(Rtn_Ptr
).all;
533 MS
.Regs
(esp
) := Rtn_Ptr
+ 4;
536 -----------------------
537 -- Set_Machine_State --
538 -----------------------
540 procedure Set_Machine_State
(M
: Machine_State
) is
541 N
: constant Asm_Output_Operand
:= No_Output_Operands
;
544 Asm
("mov %0,%%edx", N
, Machine_State
'Asm_Input ("r", M
));
546 -- At this stage, we have the following situation (note that we
547 -- are assuming that the -fomit-frame-pointer switch has not been
548 -- used in compiling this procedure.
552 -- old ebp <------ current ebp/esp value
554 -- The values of registers ebx/esi/edi are unchanged from entry
555 -- so they have the values we want, and %edx points to the parameter
556 -- value M, so we can store these values directly.
558 Asm
("mov %%ebx,4(%%edx)"); -- M.Regs (ebx)
559 Asm
("mov %%esi,16(%%edx)"); -- M.Regs (esi)
560 Asm
("mov %%edi,20(%%edx)"); -- M.Regs (edi)
562 -- The desired value of ebp is the old value
564 Asm
("mov 0(%%ebp),%%eax");
565 Asm
("mov %%eax,12(%%edx)"); -- M.Regs (ebp)
567 -- The return point is the desired eip value
569 Asm
("mov 4(%%ebp),%%eax");
570 Asm
("mov %%eax,(%%edx)"); -- M.eip
572 -- Finally, the desired %esp value is the value at the point of
573 -- call to this routine *before* pushing the parameter value.
575 Asm
("lea 12(%%ebp),%%eax");
576 Asm
("mov %%eax,8(%%edx)"); -- M.Regs (esp)
577 end Set_Machine_State
;
579 ------------------------------
580 -- Set_Signal_Machine_State --
581 ------------------------------
583 procedure Set_Signal_Machine_State
585 Context
: System
.Address
)
587 pragma Warnings
(Off
, M
);
588 pragma Warnings
(Off
, Context
);
592 end Set_Signal_Machine_State
;
594 end System
.Machine_State_Operations
;