File : a-emstop.adb

-------------------------------------------------------------------------------- ---- GNAT COMPILER COMPONENTS ---- ---- ADA.EXCEPTIONS.MACHINE_STATE_OPERATIONS ---- ---- B o d y ---- (Version for x86) ---- ---- $Revision: 1.16 $-- ---- Copyright (C) 1999 Free Software Foundation, Inc. ---- ---- GNAT 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 2, 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. See the GNU General Public License ---- for more details. You should have received a copy of the GNU General ---- Public License distributed with GNAT; see file COPYING. If not, write ---- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, ---- MA 02111-1307, USA. ---- ---- As a special exception, if other files instantiate generics from this ---- unit, or you link this unit with other files to produce an executable, ---- this unit does not by itself cause the resulting executable to be ---- covered by the GNU General Public License. This exception does not ---- however invalidate any other reasons why the executable file might be ---- covered by the GNU Public License. ---- ---- GNAT was originally developed by the GNAT team at New York University. ---- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). ---- ---------------------------------------------------------------------------------- Note: it is very important that this unit not generate any exception-- tables of any kind. Otherwise we get a nasty rtsfind recursion problem.-- This means no subprograms, including implicitly generated ones.-- Note: this version of Machine_State_Operations normally expects to-- operate with zero cost exceptions enabled. Some of the routines can-- be called in a situation where zero cost exceptions are not enabled,-- but exception tracekbacks are called. In this case some of the routines-- in particular Set_Machine_State, Pop_Frame, Machine_State_Length, and-- Get_Code_Loc are called from Call_Chain, and what we do for these-- routines is to interface with the standard routines (see 4semstop.adb).withSystem.Machine_Code; useSystem.Machine_Code;
withUnchecked_Conversion;
separate (Ada.Exceptions)
packagebodyMachine_State_OperationsistypeUns8ismod 2 ** 8;
typeUns32ismod 2 ** 32;
typeBits5ismod 2 ** 5;
typeBits6ismod 2 ** 6;
functionTo_AddressisnewUnchecked_Conversion (Uns32, Address);
functionTo_Uns32isnewUnchecked_Conversion (Integer, Uns32);
functionTo_Uns32isnewUnchecked_Conversion (Address, Uns32);
typeUns32_PtrisaccessallUns32;
functionTo_Uns32_PtrisnewUnchecked_Conversion (Address, Uns32_Ptr);
functionTo_Uns32_PtrisnewUnchecked_Conversion (Uns32, Uns32_Ptr);
-- Note: the type Uns32 has an alignment of 4. However, in some cases-- values of type Uns32_Ptr will not be aligned (notably in the case-- where we get the immediate field from an instruction). However this-- does not matter in practice, since the x86 does not require that-- operands be aligned.------------------------ General Approach -------------------------- For the x86 version of this unit, the Subprogram_Info_Type values-- are simply the starting code address for the subprogram. Popping-- of stack frames works by analyzing the code in the prolog, and-- deriving from this analysis the necessary information for restoring-- the registers, including the return point.----------------------------- Description of Prolog ------------------------------- If a frame pointer is present, the prolog looks like-- pushl %ebp-- movl %esp,%ebp-- subl $nnn,%esp omitted if nnn = 0-- pushl %edi omitted if edi not used-- pushl %esi omitted if esi not used-- pushl %ebx omitted if ebx not used-- If a frame pointer is not present, the prolog looks like-- subl $nnn,%esp omitted if nnn = 0-- pushl %ebp omitted if ebp not used-- pushl %edi omitted if edi not used-- pushl %esi omitted if esi not used-- pushl %ebx omitted if ebx not used-- Note: any or all of the save over call registers may be used and-- if so, will be saved using pushl as shown above. The order of the-- pushl instructions will be as shown above for gcc generated code,-- but the code in this unit does not assume this.--------------------------- Description of Call ----------------------------- A call looks like:-- pushl ... push parameters-- pushl ...-- call ... perform the call-- addl $nnn,%esp omitted if no parameters-- Note that we are not absolutely guaranteed that the call is always-- followed by an addl operation that readjusts %esp for this particular-- call. There are two reasons for this:-- 1) The addl can be delayed and combined in the case where more than-- one call appears in sequence. This can be suppressed by using the-- switch -fno-defer-pop and for Ada code, we automatically use-- this switch, but we could still be dealing with C code that was-- compiled without using this switch.-- 2) Scheduling may result in moving the addl instruction away from-- the call. It is not clear if this actually can happen at the-- current time, but it is certainly conceptually possible.-- The addl after the call is important, since we need to be able to-- restore the proper %esp value when we pop the stack. However, we do-- not try to compensate for either of the above effects. As noted above,-- case 1 does not occur for Ada code, and it does not appear in practice-- that case 2 occurs with any significant frequency (we have never seen-- an example so far for gcc generated code).-- Furthermore, it is only in the case of -fomit-frame-pointer that we-- really get into trouble from not properly restoring %esp. If we have-- a frame pointer, then the worst that happens is that %esp is slightly-- more depressed than it should be. This could waste a bit of space on-- the stack, and even in some cases cause a storage leak on the stack,-- but it will not affect the functional correctness of the processing.------------------------------------------ Definitions of Instruction Formats ------------------------------------------typeRcodeis (eax, ecx, edx, ebx, esp, ebp, esi, edi);
pragma Warnings (Off, Rcode);
-- Code indicating which register is referenced in an instruction-- The following define the format of a pushl instruction
Op_pushl : constantBits5 := 2#01010#;
typeIns_pushlisrecord
Op : Bits5 := Op_pushl;
Reg : Rcode;
endrecord;
forIns_pushluserecord
Op at 0 range 3 .. 7;
Reg at 0 range 0 .. 2;
endrecord;
Ins_pushl_ebp : constantIns_pushl := (Op_pushl, Reg => ebp);
typeIns_pushl_PtrisaccessallIns_pushl;
-- For the movl %esp,%ebp instruction, we only need to know the length-- because we simply skip past it when we analyze the prolog.Ins_movl_length : constant := 2;
-- The following define the format of addl/subl esp instructions
Op_Immed : constantBits6 := 2#100000#;
Op2_addl_Immed : constantBits5 := 2#11100#;
Op2_subl_Immed : constantBits5 := 2#11101#;
typeWord_Byteis (Word, Byte);
typeIns_addl_subl_byteisrecord
Op : Bits6; -- Set to Op_Immed
w : Word_Byte; -- Word/Byte flag (set to 1 = byte)
s : Boolean; -- Sign extension bit (1 = extend)
Op2 : Bits5; -- Secondary opcode
Reg : Rcode; -- Register
Imm8 : Uns8; -- Immediate operandendrecord;
forIns_addl_subl_byteuserecord
Op at 0 range 2 .. 7;
w at 0 range 1 .. 1;
s at 0 range 0 .. 0;
Op2 at 1 range 3 .. 7;
Reg at 1 range 0 .. 2;
Imm8 at 2 range 0 .. 7;
endrecord;
typeIns_addl_subl_wordisrecord
Op : Bits6; -- Set to Op_Immed
w : Word_Byte; -- Word/Byte flag (set to 0 = word)
s : Boolean; -- Sign extension bit (1 = extend)
Op2 : Bits5; -- Secondary opcode
Reg : Rcode; -- Register
Imm32 : Uns32; -- Immediate operandendrecord;
forIns_addl_subl_worduserecord
Op at 0 range 2 .. 7;
w at 0 range 1 .. 1;
s at 0 range 0 .. 0;
Op2 at 1 range 3 .. 7;
Reg at 1 range 0 .. 2;
Imm32 at 2 range 0 .. 31;
endrecord;
typeIns_addl_subl_byte_PtrisaccessallIns_addl_subl_byte;
typeIns_addl_subl_word_PtrisaccessallIns_addl_subl_word;
----------------------- Prolog Analysis ------------------------- The analysis of the prolog answers the following questions:-- 1. Is %ebp used as a frame pointer?-- 2. How far is SP depressed (i.e. what is the stack frame size)-- 3. Which registers are saved in the prolog, and in what order-- The following data structure stores the answers to these questionssubtypeSOCisRcoderangeebx .. edi;
-- Possible save over call registersSOC_Max : constant := 4;
-- Max number of SOC registers that can be pushedtypeSOC_Push_Regs_Typeisarray (1 .. 4) ofRcode;
-- Used to hold the register codes of pushed SOC registerstypeProlog_Typeisrecord
Frame_Reg : Boolean;
-- This is set to True if %ebp is used as a frame register, and-- False otherwise (in the False case, %ebp may be saved in the-- usual manner along with the other SOC registers).
Frame_Length : Uns32;
-- Amount by which ESP is decremented on entry, includes the effects-- of push's of save over call registers as indicated above, e.g. if-- the prolog of a routine is:---- pushl %ebp-- movl %esp,%ebp-- subl $424,%esp-- pushl %edi-- pushl %esi-- pushl %ebx---- Then the value of Frame_Length would be 436 (424 + 3 * 4). A-- precise definition is that it is:---- %esp on entry minus %esp after last SOC push---- That definition applies both in the frame pointer present and-- the frame pointer absent cases.
Num_SOC_Push : Integer range 0 .. SOC_Max;
-- Number of save over call registers actually saved by pushl-- instructions (other than the initial pushl to save the frame-- pointer if a frame pointer is in use).
SOC_Push_Regs : SOC_Push_Regs_Type;
-- The First Num_SOC_Push entries of this array are used to contain-- the codes for the SOC registers, in the order in which they were-- pushed. Note that this array excludes %ebp if it is used as a frame-- register, since although %ebp is still considered an SOC register-- in this case, it is saved and restored by a separate mechanism.-- Also we will never see %esp represented in this list. Again, it is-- true that %esp is saved over call, but it is restored by a separate-- mechanism.endrecord;
procedureAnalyze_Prolog (A : Address; Prolog : outProlog_Type);
-- Given the address of the start of the prolog for a procedure,-- analyze the instructions of the prolog, and set Prolog to contain-- the information obtained from this analysis.------------------------------------ Machine_State_Representation -------------------------------------- The type Machine_State is defined in the body of Ada.Exceptions as-- a Storage_Array of length 1 .. Machine_State_Length. But really it-- has structure as defined here. We use the structureless declaration-- in Ada.Exceptions to avoid this unit from being implementation-- dependent. The actual definition of Machine_State is as follows:typeSOC_Regs_Typeisarray (SOC) ofUns32;
typeMStateisrecord
eip : Uns32;
-- The instruction pointer location (which is the return point-- value from the next level down in all cases).
Regs : SOC_Regs_Type;
-- Values of the save over call registersendrecord;
forMStateuserecord
eip at 0 range 0 .. 31;
Regs at 4 range 0 .. 5 * 32 - 1;
endrecord;
-- Note: the routines Enter_Handler, and Set_Machine_State reference-- the fields in this structure non-symbolically.typeMState_PtrisaccessallMState;
functionTo_MState_PtrisnewUnchecked_Conversion (Machine_State_Ptr, MState_Ptr);
---------------------- Analyze_Prolog ----------------------procedureAnalyze_Prolog (A : Address; Prolog : outProlog_Type) isPtr : Address;
Ppl : Ins_pushl_Ptr;
Pas : Ins_addl_subl_byte_Ptr;
functionTo_Ins_pushl_PtrisnewUnchecked_Conversion (Address, Ins_pushl_Ptr);
functionTo_Ins_addl_subl_byte_PtrisnewUnchecked_Conversion (Address, Ins_addl_subl_byte_Ptr);
functionTo_Ins_addl_subl_word_PtrisnewUnchecked_Conversion (Address, Ins_addl_subl_word_Ptr);
beginPtr := A;
Prolog.Frame_Length := 0;
ifPtr=Null_Addressthen
Prolog.Num_SOC_Push := 0;
Prolog.Frame_Reg := True;
return;
endif;
ifTo_Ins_pushl_Ptr (Ptr).all = Ins_pushl_ebp thenPtr := Ptr+ 1 +Ins_movl_length;
Prolog.Frame_Reg := True;
else
Prolog.Frame_Reg := False;
endif;
Pas := To_Ins_addl_subl_byte_Ptr (Ptr);
if Pas.Op = Op_Immed
andthen Pas.Op2 = Op2_subl_Immed
andthen Pas.Reg = espthenif Pas.w = Wordthen
Prolog.Frame_Length := Prolog.Frame_Length +
To_Ins_addl_subl_word_Ptr (Ptr).Imm32;
Ptr := Ptr+ 6;
else
Prolog.Frame_Length := Prolog.Frame_Length + Uns32 (Pas.Imm8);
Ptr := Ptr+ 3;
-- Note: we ignore sign extension, since a sign extended-- value that was negative would imply a ludicrous frame size.endif;
endif;
-- Now scan push instructions for SOC registers
Prolog.Num_SOC_Push := 0;
loop
Ppl := To_Ins_pushl_Ptr (Ptr);
if Ppl.Op = Op_pushl andthen Ppl.Reg inSOCthen
Prolog.Num_SOC_Push := Prolog.Num_SOC_Push + 1;
Prolog.SOC_Push_Regs (Prolog.Num_SOC_Push) := Ppl.Reg;
Prolog.Frame_Length := Prolog.Frame_Length + 4;
Ptr := Ptr+ 1;
elseexit;
endif;
endloop;
end Analyze_Prolog;
--------------------- Enter_Handler ---------------------procedureEnter_Handler (M : Machine_State_Ptr; Handler : Handler_Loc) isbeginAsm ("mov %0,%%edx", Inputs => Machine_State_Ptr'Asm_Input ("r", M));
Asm ("mov %0,%%eax", Inputs => Handler_Loc'Asm_Input ("r", Handler));
Asm ("mov 4(%%edx),%%ebx"); -- M.Regs (ebx)Asm ("mov 12(%%edx),%%ebp"); -- M.Regs (ebp)Asm ("mov 16(%%edx),%%esi"); -- M.Regs (esi)Asm ("mov 20(%%edx),%%edi"); -- M.Regs (edi)Asm ("mov 8(%%edx),%%esp"); -- M.Regs (esp)Asm ("jmp %*%%eax");
end Enter_Handler;
-------------------- Get_Code_Loc --------------------functionGet_Code_Loc (M : Machine_State_Ptr) returnCode_Locis
MS : constantMState_Ptr := To_MState_Ptr (M);
Asm_Call_Size : constant := 5;
begin-- Case where exception tracebacks are enabled and we are making-- use of the GNU traceback data to get the machine state.if Exception_Tracebacks /= 0 thendeclarefunctionc_get_code_loc (m : Machine_State_Ptr) returnCode_Loc;
pragma Import (C, c_get_code_loc, "__gnat_get_code_loc");
beginreturnc_get_code_loc (M);
end;
-- Case where exception tracebacks are not enabled, so we are using-- the zero cost exception handling circuits of this unit.elseif MS.eip = 0 thenreturnTo_Address (MS.eip);
else-- When doing a call the return address is pushed to the stack.-- We want to return the call point address, so we substract-- Asm_Call_Size from the return address. This value is set-- to 5 as an asm call takes 5 bytes on x86 architectures.returnTo_Address (MS.eip - Asm_Call_Size);
endif;
endif;
end Get_Code_Loc;
---------------------------- Machine_State_Length ----------------------------functionMachine_State_LengthreturnStorage_Offsetisbegin-- Case where exception tracebacks are enabled and we are making-- use of the GNU traceback data to get the machine state.if Exception_Tracebacks /= 0 thendeclarefunctionc_machine_state_lengthreturnStorage_Offset;
pragma Import (C,
c_machine_state_length, "__gnat_machine_state_length");
beginreturnc_machine_state_length;
end;
-- Case where exception tracebacks are not enabled, so we are using-- the zero cost exception handling circuits of this unit.elsereturnMState'Max_Size_In_Storage_Elements;
endif;
end Machine_State_Length;
----------------- Pop_Frame -----------------procedurePop_Frame
(M : Machine_State_Ptr;
Info : Subprogram_Info_Type)
isbegin-- Case where exception tracebacks are enabled and we are making-- use of the GNU traceback data to get the machine state.if Exception_Tracebacks /= 0 thendeclareprocedurec_pop_frame (m : Machine_State_Ptr);
pragma Import (C, c_pop_frame, "__gnat_pop_frame");
beginc_pop_frame (M);
end;
-- Case where exception tracebacks are not enabled, so we are using-- the zero cost exception handling circuits of this unit.elsedeclare
MS : constantMState_Ptr := To_MState_Ptr (M);
PL : Prolog_Type;
SOC_Ptr : Uns32;
-- Pointer to stack location after last SOC push
Rtn_Ptr : Uns32;
-- Pointer to stack location containing return addressbeginAnalyze_Prolog (Info, PL);
-- Case of frame register, use EBP, safer than ESPif PL.Frame_Reg then
SOC_Ptr := MS.Regs (ebp) - PL.Frame_Length;
Rtn_Ptr := MS.Regs (ebp) + 4;
MS.Regs (ebp) := To_Uns32_Ptr (MS.Regs (ebp)).all;
-- No frame pointer, use ESP, and hope we have it exactly right!else
SOC_Ptr := MS.Regs (esp);
Rtn_Ptr := SOC_Ptr + PL.Frame_Length;
endif;
-- Get saved values of SOC registersfor J inreverse 1 .. PL.Num_SOC_Push loop
MS.Regs (PL.SOC_Push_Regs (J)) := To_Uns32_Ptr (SOC_Ptr).all;
SOC_Ptr := SOC_Ptr + 4;
endloop;
MS.eip := To_Uns32_Ptr (Rtn_Ptr).all;
MS.Regs (esp) := Rtn_Ptr + 4;
end;
endif;
end Pop_Frame;
------------------------- Set_Machine_State -------------------------procedureSet_Machine_State (M : Machine_State_Ptr) isN : constantAsm_Output_Operand := No_Output_Operands;
begin-- Case where exception tracebacks are enabled and we are making-- use of the GNU traceback data to get the machine state.if Exception_Tracebacks /= 0 thendeclareprocedurec_set_machine_state (m : Machine_State_Ptr);
pragma Import (C, c_set_machine_state, "__gnat_set_machine_state");
beginc_set_machine_state (M);
Pop_Frame (M, System.Null_Address);
end;
-- Case where exception tracebacks are not enabled, so we are using-- the zero cost exception handling circuits of this unit.elseAsm ("mov %0,%%edx", N, Machine_State_Ptr'Asm_Input ("r", M));
-- At this stage, we have the following situation (note that we-- are assuming that the -fomit-frame-pointer switch has not been-- used in compiling this procedure.-- (value of M)-- return point-- old ebp <------ current ebp/esp value-- The values of registers ebx/esi/edi are unchanged from entry-- so they have the values we want, and %edx points to the parameter-- value M, so we can store these values directly.Asm ("mov %%ebx,4(%%edx)"); -- M.Regs (ebx)Asm ("mov %%esi,16(%%edx)"); -- M.Regs (esi)Asm ("mov %%edi,20(%%edx)"); -- M.Regs (edi)-- The desired value of ebp is the old valueAsm ("mov 0(%%ebp),%%eax");
Asm ("mov %%eax,12(%%edx)"); -- M.Regs (ebp)-- The return point is the desired eip valueAsm ("mov 4(%%ebp),%%eax");
Asm ("mov %%eax,(%%edx)"); -- M.eip-- Finally, the desired %esp value is the value at the point of-- call to this routine *before* pushing the parameter value.Asm ("lea 12(%%ebp),%%eax");
Asm ("mov %%eax,8(%%edx)"); -- M.Regs (esp)endif;
end Set_Machine_State;
-------------------------------- Set_Signal_Machine_State --------------------------------procedureSet_Signal_Machine_State
(M : Machine_State_Ptr;
Context : System.Address)
isbeginnull;
end Set_Signal_Machine_State;
end Machine_State_Operations;