|
|
1.1 root 1: /*ident "%W%" */
2: /**************************************************************************
3: Copyright (c) 1984 AT&T
4: All Rights Reserved
5:
6: THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T
7:
8: The copyright notice above does not evidence any
9: actual or intended publication of such source code.
10:
11: *****************************************************************************/
12: #include <task.h>
13: #include "hw_stack.h"
14:
15: // Intel 386 frame fudger
16:
17: /* careful -- stack frame not self-describing */
18: // STACK GROWS DOWN
19:
20: // We define a mask in which FrameLayout can record which regs are saved.
21: // We only care about the non-scratch regs edi, esi, ebx.
22: const short REG_EDI = 0x1;
23: const short REG_ESI = 0x2;
24: const short REG_EBX = 0x4;
25: #define ALL_REGS_SAVED(m) ((short)(m) == 0x0007)
26:
27: const int OPCODE_SIZE = 1;
28: const MACHINE_BYTE CALL_INSTR = 0xe8;
29: const int CALL_SIZE = 5;
30: const MACHINE_BYTE JMP_SHORT = 0xeb;
31: const MACHINE_BYTE JMP_DISPL = 0xe9;
32: // assumes we don't need to worry about jump near indirect (0xff)
33:
34: #define IS_JMP_INSTR(instr) \
35: ((MACHINE_BYTE)(instr) == (MACHINE_BYTE)JMP_SHORT || \
36: (MACHINE_BYTE)(instr) == (MACHINE_BYTE)JMP_DISPL)
37: const MACHINE_BYTE PUSH_EBP_INSTR = 0x55;
38: const int PUSH_SIZE = 1;
39: const MACHINE_BYTE MOV_INSTR = 0x8b;
40: const int MOV_SIZE = 2;
41: const MACHINE_BYTE SUB_INSTR = 0x83;
42: const int SUB_SIZE = 3;
43: const MACHINE_BYTE PUSH_EDI_INSTR = 0x57;
44: const MACHINE_BYTE PUSH_ESI_INSTR = 0x56;
45: const MACHINE_BYTE PUSH_EBX_INSTR = 0x53;
46: #define IS_PUSH_REG_INSTR(instp) \
47: ((*((MACHINE_BYTE*)(instp)) & 0xf0) == 0x50)
48: #define IS_SET_EBP_INSTR(instp) \
49: (((*(instp)) == MOV_INSTR) && (*((MACHINE_BYTE*)(instp) + 1) == 0xec))
50: #define IS_SUB_ESP_INSTR(instp) \
51: ((*((MACHINE_BYTE*)(instp)) == SUB_INSTR) \
52: && (*((MACHINE_BYTE*)(instp) + 1) == 0xec))
53:
54:
55: int* Skip_pc_p; // global to hold fudged return pc.
56: // See comments in hw_stack.c.
57:
58: /* Given a pointer to a call instruction, yield a pointer to
59: * the called function */
60: unsigned int
61: call_dst_ptr(MACHINE_BYTE* callp)
62: {
63: /*
64: * On the Intel 386, the call instruction is: call displ
65: * where the displacement is always relative to the pc.
66: */
67: MACHINE_BYTE* dst_op_p = callp + OPCODE_SIZE;
68: int offset = 0;
69: // bytes are in reverse order, must flip positions
70: offset = dst_op_p[0] |
71: dst_op_p[1] << 8 |
72: dst_op_p[2] << 16 |
73: dst_op_p[3] << 24;
74: return (unsigned int)(callp + CALL_SIZE + offset);
75: }
76:
77: FrameLayout::FrameLayout(int* fp)
78: {
79: mask = 0;
80: unsigned int* return_pc = (unsigned int*)OLD_PC(fp);
81: /*
82: * find the starting address of the function. The idea is that the
83: * instruction immediately before the return address must be the
84: * function call.
85: */
86: MACHINE_BYTE* callp = (MACHINE_BYTE*)return_pc - CALL_SIZE;
87: if (*callp != CALL_INSTR)
88: object::task_error(E_FUNCS, (object*)0);
89: MACHINE_BYTE* func_addr = (MACHINE_BYTE*) call_dst_ptr(callp);
90:
91: /* If first instruction is jmp (-O not used), add displacement to pc
92: * to get to function prologue.
93: * Function prologue should be:
94: * pushl %ebp
95: * movl %esp,%ebp
96: * subl XXX,%esp / XXX is the offset we want
97: * pushl %eXX / a push instruction for each saved reg
98: * / (sometimes first push is of scratch
99: * / reg, and then that is the offset)
100: * / stop when no more push instructions, or have saved all regs
101: */
102: if (IS_JMP_INSTR(*func_addr)) {
103: int displ;
104: if (*func_addr == JMP_SHORT) { // 1 byte displacement
105: displ = func_addr[1];
106: func_addr += 2; // next instruction
107: } else { // JMP_DISPL, 4 byte displacement
108: // bytes reversed
109: displ = func_addr[1] |
110: func_addr[2] << 8 |
111: func_addr[3] << 16 |
112: func_addr[4] << 24;
113: func_addr += 5; // next instruction
114: }
115: func_addr += displ; // jmp destination
116: }
117: if ((*func_addr == PUSH_EBP_INSTR)
118: && (IS_SET_EBP_INSTR(func_addr + PUSH_SIZE))) {
119: func_addr += MOV_SIZE + PUSH_SIZE;
120: if (IS_SUB_ESP_INSTR(func_addr)) {
121: offset = func_addr[2] / 4;
122: func_addr += SUB_SIZE;
123: } else { // assume no automatics to be allocated
124: offset = 0;
125: }
126: } else {
127: object::task_error(E_FUNCS, (object*)0);
128: }
129: while (IS_PUSH_REG_INSTR(func_addr) && (! ALL_REGS_SAVED(mask))) {
130: if (*func_addr == PUSH_EDI_INSTR) {
131: mask |= REG_EDI;
132: func_addr += PUSH_SIZE;
133: } else if (*func_addr == PUSH_ESI_INSTR) {
134: mask |= REG_ESI;
135: func_addr += PUSH_SIZE;
136: } else if (*func_addr == PUSH_EBX_INSTR) {
137: mask |= REG_EBX;
138: func_addr += PUSH_SIZE;
139: } else {
140: // Compiler might push a scratch reg before any non-
141: // scratch regs to reserve space for automatics.
142: if (mask != 0) {
143: // if non-scratch regs already saved,
144: // then this is not an offset
145: break;
146: } else {
147: offset += 1;
148: func_addr += PUSH_SIZE;
149: }
150: }
151:
152: }
153: }
154:
155: /*
156: * Fudge frame of function-defined-by-f_fp (called "f" below)
157: * so that that function returns to its grandparent,
158: * in particular, so a parent task returns to the function that
159: * called the derived constructor, skipping the derived constructor;
160: * the child will return to the derived constructor, which is its "main."
161: * To do this we will overwrite the old fp and pc (those saved by
162: * f) with the old-old ones (those saved by f's caller),
163: * and we will overwrite the register save area with registers saved by
164: * f's caller (referred to as "skip" below).
165: *
166: * There are 2 register-save cases to deal with:
167: * 1. skip_n_saved <= f_n_saved
168: * 3. skip_n_saved > f_n_saved
169: *
170: * These are handled as follows:
171: * 1. copy the saved skip_regs over the corresponding f_regs,
172: * leaving any additional saved f_regs intact.
173: * f's epilogue instructions will be correct.
174: * 2. f's epilogue instructions will restore too few regs,
175: * must take special care to see that the extras are restored properly.
176: * -Copy saved skip_regs over any corresponding f_regs,
177: * -If fudge_return saved more regs than f did, then
178: * copy saved extra saved skip_regs over any corresponding fudge_regs,
179: * -If more extra skip_regs (not saved by either f or fudge_return,
180: * and therefore not used by either) remain, restore them explicitly.
181: * They will not be disturbed by the return from fudge_return or f.
182: */
183: void
184: task::fudge_return(int* f_fp)
185: {
186: register int* fp = f_fp; // fp of frame-to-be-fudged
187: FrameLayout lo(fp); // frame to be fudged
188: register int* skip_fp = (int*)OLD_FP(fp); // fp for f's caller (skip)
189: FrameLayout skip_lo(skip_fp); // frame for skip
190: register int* fr_fp = FP(); // fp for fudge_return
191: FrameLayout fr_lo(fr_fp); // frame for fudge_return
192:
193: OLD_PC(fp) = (int)&fudge_sp; // task::task will return through
194: // fudge_sp, which will reset the sp
195: // to point at the return-pc in
196: // skip's frame
197: // copy old fp
198: OLD_FP(fp) = OLD_FP(skip_fp);
199:
200: // now copy saved registers
201: // copy any saved skip regs over corresponding f regs; if there isn't
202: // a corresponding f reg, copy over corresponding fudge_return reg;
203: // if there isn't a corresponding fr_reg, restore it explicitly.
204: register int* to = FIRST_SAVED_REG_P(fp, lo.offset);
205: register int* from = FIRST_SAVED_REG_P(skip_fp, skip_lo.offset);
206: register int* fr_to = FIRST_SAVED_REG_P(fr_fp, fr_lo.offset);
207: for (register short m = 1; m != 0x08; m <<=1) {
208: if (m & lo.mask) {
209: if (m & skip_lo.mask) {
210: *to-- = *from--;
211: if (m & fr_lo.mask) fr_to--;
212: } else { // nothing to copy
213: to--;
214: if (m & fr_lo.mask) fr_to--;
215: }
216: } else if (m & skip_lo.mask) {
217: // No slot for *from in f's frame
218: if (m & fr_lo.mask) { // copy to fudge_return's frame
219: *fr_to-- = *from--;
220: } else {
221: // Not used in f or fudge_return;
222: // restore explicitly
223: switch(m) {
224: case REG_EDI:
225: set_edi(from--);
226: break;
227: case REG_ESI:
228: set_esi(from--);
229: break;
230: case REG_EBX:
231: set_ebx(from--);
232: break;
233: default:
234: // Oops--don't expect other regs
235: // to be saved
236: from--;
237: task_error(E_REGMASK, this);
238: break;
239: }
240: }
241: }
242: }
243: }
244:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.