|
|
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 <stdio.h>
14: #include "hw_stack.h"
15:
16: // 3B frame fudger
17:
18: //Frames not self-describing!
19: //STACK GROWS UP
20:
21: #ifdef u3b
22: /*
23: * On the 3B20, the call instruction is call num,dst
24: * where num is the number of arguments to the function
25: * and dst is the address of the called function.
26: * There are 4 possible opcodes for the call instruction:
27: * 3 are optimizations possible when num can be represented in 4 bits
28: * (the usual case), and further depend on the addressing mode used.
29: * This code assumes that destination addresses will only be
30: * pc-relative, absolute, or absolute deferred.
31: */
32:
33: const GEN_CALL = 0x78;
34: const OPT_GEN_CALL = 0x79;
35: const PC_REL_CALL = 0x77;
36: const IMM_CALL = 0xB9;
37:
38: const ABSOLUTE_DESC = 0x08;
39: const PC_REL_DESC = 0x06;
40: const ABSOLUTE_DEF_DESC = 0x09;
41:
42: #define IS_CALL_OPCODE(instr) \
43: (((MACHINE_BYTE)(instr) == (MACHINE_BYTE)GEN_CALL) || \
44: ((MACHINE_BYTE)(instr) == (MACHINE_BYTE)OPT_GEN_CALL) || \
45: ((MACHINE_BYTE)(instr) == (MACHINE_BYTE)PC_REL_CALL) || \
46: ((MACHINE_BYTE)(instr) == (MACHINE_BYTE)IMM_CALL))
47:
48: const N_CALL_INSTR_SIZES = 3;
49: short call_size[N_CALL_INSTR_SIZES] = {4, 6, 7};
50:
51: #undef SAVE_OPERAND /* not needed for u3b */
52:
53: /* src op is not important in decoding the call instruction on u3b */
54: #define SRC_OP_DESC(instr) /* not needed for u3b */
55: #define IS_LEGAL_SRC_OP() 1
56: #undef SRC_OP_SIZE /* not needed for u3b */
57: const OPCODE_SIZE = 1;
58: const OPERAND1_SIZE = 1;
59:
60: #define DST_OP_DESC(instr) \
61: (((unsigned char *)(instr))[OPCODE_SIZE] & 0x0f)
62:
63: #define IS_LEGAL_DST_OP(desc) \
64: (((desc) == ABSOLUTE_DESC) || \
65: ((desc) == PC_REL_DESC) || \
66: ((desc) == ABSOLUTE_DEF_DESC) \
67: )
68: #define HAS_LEGAL_OPERANDS(call_instr) \
69: (IS_LEGAL_DST_OP((DST_OP_DESC(poss_callp))) || \
70: (call_instr[0] == PC_REL_CALL))
71:
72:
73: /* Given a pointer to a call instruction,
74: * yield a pointer to the called function */
75: unsigned int
76: call_dst_ptr(unsigned char* callp)
77: {
78: int offset = 0;
79: // points past operand1 and/or descriptor to address or offset
80: unsigned char* dst_op_p = callp + OPCODE_SIZE + OPERAND1_SIZE;
81: switch(*callp) {
82: case PC_REL_CALL: // 16-bit pc-relative address
83: offset = dst_op_p[0] << 8 | dst_op_p[1];
84: if (callp[1] & 0x80) // negative offset
85: offset = -(offset);
86: unsigned char* next_pc = callp + 4; // relative to NEXT pc
87: return (unsigned int)((short*)next_pc + offset);
88: case GEN_CALL:
89: case OPT_GEN_CALL:
90: switch(DST_OP_DESC(callp)) {
91: case ABSOLUTE_DESC: // 24-bit absolute address
92: return (dst_op_p[0] << 20 |
93: dst_op_p[1] << 12 |
94: dst_op_p[2] << 4 |
95: dst_op_p[3] >> 4 ) & 0x00ffffff;
96: case ABSOLUTE_DEF_DESC: // 24-bit absolute deferred address
97: int* addr_p =
98: (int*) ((dst_op_p[0] << 20 |
99: dst_op_p[1] << 12 |
100: dst_op_p[2] << 4 |
101: dst_op_p[3] >> 4 ) & 0x00ffffff);
102: return (unsigned int)(*addr_p);
103: case PC_REL_DESC: // 24-bit pc-relative address
104: offset = (dst_op_p[0] << 20 |
105: dst_op_p[1] << 12 |
106: dst_op_p[2] << 4 |
107: dst_op_p[3] >> 4 );
108: if (dst_op_p[0] & 0x08) // negative offset
109: offset |= 0xff000000;
110: else // positive offset
111: offset &= 0x00ffffff;
112: return (unsigned int)(callp + offset);
113: default: // illegal descriptor
114: object::task_error(E_FUNCS, (object*)0);
115: }
116: case IMM_CALL: // 24-bit immediate address
117: return (dst_op_p[0] << 20 |
118: dst_op_p[1] << 12 |
119: dst_op_p[2] << 4 |
120: dst_op_p[3] >> 4 ) & 0x00ffffff;
121: default: // illegal call instruction
122: object::task_error(E_FUNCS, (object*)0);
123: }
124: } /* call_dst_ptr */
125:
126:
127: #else /* m32: u3b2, u3b5, u3b15 */
128:
129: /*
130: * On the 3B2, the call instruction is CALL src,dst
131: * where src is the address of the new ap (where the args were
132: * already pushed on the stack) and dst is the address of the
133: * called function. src and dst can be various lengths:
134: * src will be a byte, halfword, or word displacement
135: * and dst will be absolute, or be a byte, halfword, or word displacement.
136: * Byte displacement = 2 bytes, halfword = 3, word = 5;
137: * the opcode = 1 byte; absolute = 5 bytes--12 possible combinations
138: * and 6 possible instr lengths.
139: */
140:
141:
142: #define IS_CALL_OPCODE(instr) ((MACHINE_BYTE)(instr) == (MACHINE_BYTE)0x2c)
143:
144: const BYTE_DISPL = 0xC0;
145: const HALFWORD_DISPL = 0xA0;
146: const WORD_DISPL = 0x80;
147: const ABSOLUTE = 0x70;
148: const REG_SP = 0x0C;
149: const REG_PC = 0x0F;
150:
151: const N_CALL_INSTR_SIZES = 6;
152: short call_size[N_CALL_INSTR_SIZES] = {5, 6, 7, 8, 9, 11};
153:
154: /* CALL's src operand descriptor needed to determine size of src operand */
155: #define SRC_OP_DESC(instr) (((unsigned char *)(instr))[1])
156: #define SRC_OP_SIZE(desc) \
157: ((MODE_FIELD(desc) == BYTE_DISPL) ? 2 : \
158: (MODE_FIELD(desc) == HALFWORD_DISPL) ? 3 : \
159: (MODE_FIELD(desc) == WORD_DISPL) ? 5 : \
160: ((object::task_error(E_FUNCS, (object*)0))) \
161: )
162: const OPCODE_SIZE = 1;
163: const DESC_SIZE = 1;
164:
165: #define DST_OP_DESC(instr) \
166: (((unsigned char *)(instr)) \
167: [OPCODE_SIZE + SRC_OP_SIZE(SRC_OP_DESC(instr))])
168: #define MODE_FIELD(desc) ((desc) & 0xf0)
169: #define REG_FIELD(desc) ((desc) & 0x0f)
170:
171: #define IS_LEGAL_SRC_OP(desc) /* assumes sp-relative displ */ \
172: (((MODE_FIELD(desc) == BYTE_DISPL) || \
173: (MODE_FIELD(desc) == HALFWORD_DISPL) || \
174: (MODE_FIELD(desc) == WORD_DISPL) \
175: ) && (REG_FIELD(desc) == REG_SP) \
176: )
177: /* destination displacement is probably pc-relative, but we don't
178: * make that assumption. It could be a call through a function pointer.
179: */
180: #define IS_LEGAL_DST_OP(desc) \
181: ((MODE_FIELD(desc) == BYTE_DISPL) || \
182: (MODE_FIELD(desc) == HALFWORD_DISPL) || \
183: (MODE_FIELD(desc) == WORD_DISPL) || \
184: ((MODE_FIELD(desc) == ABSOLUTE) && (REG_FIELD(desc) == REG_PC))\
185: )
186: #define HAS_LEGAL_OPERANDS(call_instr) \
187: (IS_LEGAL_SRC_OP((SRC_OP_DESC(call_instr))) \
188: && IS_LEGAL_DST_OP((DST_OP_DESC(call_instr))))
189:
190:
191: /* Given a pointer to a call instruction, yield a pointer to
192: * the called function */
193: unsigned int
194: call_dst_ptr(unsigned char* callp)
195: {
196: int sop_size = SRC_OP_SIZE(SRC_OP_DESC(callp));
197: unsigned char* dst_op_p =
198: /* points past the dst desc, to addr or offset */
199: callp + OPCODE_SIZE + sop_size + DESC_SIZE;
200: int offset = 0; // offset for displacement modes
201: switch(MODE_FIELD(DST_OP_DESC(callp))) {
202: case BYTE_DISPL:
203: offset = dst_op_p[0];
204: if(*dst_op_p & 0x80) {
205: /* negative--do sign extension for offset */
206: offset |= 0xffffff00;
207: }
208: return (unsigned int)(callp + offset);
209: case HALFWORD_DISPL:
210: // bytes are in reverse order, must flip positions
211: offset = dst_op_p[0] | dst_op_p[1] << 8;
212: if(dst_op_p[1] & 0x80) {
213: /* negative--do sign extension for offset */
214: offset |= 0xffff0000;
215: }
216: return (unsigned int)(callp + offset);
217: case WORD_DISPL:
218: // bytes are in reverse order, must flip positions
219: offset = dst_op_p[0] |
220: dst_op_p[1] << 8 |
221: dst_op_p[2] << 16 |
222: dst_op_p[3] << 24;
223: /* don't need sign extension */
224: return (unsigned int)(callp + offset);
225: case ABSOLUTE:
226: return
227: dst_op_p[0] |
228: dst_op_p[1] << 8 |
229: dst_op_p[2] << 16 |
230: dst_op_p[3] << 24;
231: default:
232: object::task_error(E_FUNCS, (object*)0);
233: }
234: } /* call_dst_ptr */
235:
236: #endif /* m32: u3b2, u3b5, u3b15 */
237:
238: FrameLayout::FrameLayout(int* fp)
239: {
240: /*
241: * Given a frame pointer, find the number of regs saved in the frame.
242: * The idea is that the instruction immediately before the return pc
243: * is the function call, and contains a pointer to the first
244: * instruction in the function denoted by the fp, which will be a save
245: * instruction. We can decode the save instruction to find how many
246: * registers were saved.
247: */
248:
249: int* return_pc = (int*)OLD_PC(fp);
250:
251: unsigned char* callp = NULL;
252: /* Because this method is nondeterministic, try all combinations
253: instead of stopping when one fits */
254: for(int i = 0; i < N_CALL_INSTR_SIZES; i++) {
255: unsigned char* poss_callp =
256: (unsigned char *)return_pc - call_size[i];
257: if ((IS_CALL_OPCODE(*poss_callp))
258: && HAS_LEGAL_OPERANDS(poss_callp)) {
259: if(!callp) callp = poss_callp;
260: else { // try some heuristics to disambiguate
261: // (Don't try to dereference dst ptr
262: // until sure, or necessary to disambiguate,
263: // to avoid unnecessary core dumps.)
264: // If first callp dst points to a save,
265: // assume that is the instruction we want.
266: if (!IS_SAVE_OPCODE(
267: *(unsigned char *)(call_dst_ptr(callp))) ) {
268: if (IS_SAVE_OPCODE(
269: *(unsigned char *)(call_dst_ptr(poss_callp))) ) {
270: callp = poss_callp;
271: } else {
272: // neither is right, go on
273: callp = 0;
274: }
275:
276: }
277: }
278: }
279: }
280: if (callp == NULL) object::task_error(E_FUNCS, (object*)0); //No match!
281: unsigned int func_addr = call_dst_ptr(callp);
282: if (!(IS_SAVE_OPCODE( *(unsigned char *)func_addr) ))
283: object::task_error(E_FUNCS, (object*)0);
284: n_saved = N_SAVED_REGS(func_addr);
285: }
286:
287: /*
288: * Fudge frame of function-defined-by-f_fp (called "f" below)
289: * so that that function returns to its grandparent,
290: * in particular, so a parent task returns to the function that
291: * called the derived constructor (de_ctor), skipping de_ctor;
292: * the child will return to the derived constructor, which is its "main."
293: * To do this we will overwrite the old fp, ap, and pc (those saved by
294: * f) with the old-old ones (those saved by f's caller),
295: * and we will overwrite the register save area with registers saved by
296: * f's caller (referred to as "skip" below).
297: *
298: * There are 2 register-save cases to deal with:
299: * 1. skip_n_saved <= f_n_saved
300: * 2. skip_n_saved > f_n_saved
301: *
302: * These are handled as follows:
303: * 1. copy the saved skip_regs over the corresponding f_regs,
304: * leaving any additional saved f_regs intact.
305: * f's restore instruction will be correct.
306: * 2. f's restore instruction will restore too few regs, must take special
307: * care to see that the extras are restored properly.
308: * -Copy saved skip_regs over any corresponding f_regs,
309: * -If fudge_return saved more regs than f did, then
310: * copy saved extra saved skip_regs over any corresponding fudge_regs,
311: * -If more extra skip_regs (not saved by either f or fudge_return,
312: * and therefore not used by either) remain, restore them explicitly.
313: * They will not be disturbed by the return from fudge_return or f,
314: */
315: void
316: task::fudge_return(int* f_fp)
317: {
318: register int* fp = f_fp; // fp of frame-to-be-fudged
319: FrameLayout lo(fp); // frame to be fudged
320: register int* skip_fp = (int*)OLD_FP(fp); // fp for f's caller (skip)
321: FrameLayout skip_lo(skip_fp); // frame for skip
322:
323: OLD_PC(fp) = OLD_PC(skip_fp);
324: OLD_AP(fp) = OLD_AP(skip_fp);
325: OLD_FP(fp) = OLD_FP(skip_fp);
326:
327: // finally copy saved regs
328: register int* from = LAST_SAVED_REG_P(skip_fp, skip_lo.n_saved);
329: register int* to = LAST_SAVED_REG_P(fp, lo.n_saved);
330: register int i;
331: if(lo.n_saved >= skip_lo.n_saved) {
332: // copy the saved skip regs over the corresponding f regs,
333: // leaving any additional saved f regs intact.
334: for(i = skip_lo.n_saved; i > 0; i--) {
335: *to-- = *from--;
336: }
337: } else { // lo.n_saved < skip_lo.n_saved--take care!
338: // copy the saved skip regs over any corresponding
339: // f regs.
340: for(i = lo.n_saved; i > 0; i--) {
341: *to-- = *from--;
342: }
343: int extra = skip_lo.n_saved - lo.n_saved;
344: // If fudge_return saved more regs than f, extra skip_regs
345: // should be copied over any corresponding fudge_return regs.
346: int* fr_fp = FP(); // fp for fudge_return
347: FrameLayout fr_lo(fr_fp); // frame for fudge_return
348: if (fr_lo.n_saved >= lo.n_saved) {
349: to = LAST_SAVED_REG_P(fr_fp, fr_lo.n_saved) - lo.n_saved;
350: int n;
351: int d = fr_lo.n_saved - lo.n_saved;
352: if (d >= extra) { // room for all extra skip_regs
353: n = extra;
354: } else {
355: n = d;
356: }
357: for(i = n; i > 0; i--) {
358: *to-- = *from--;
359: extra--;
360: }
361: }
362: if (extra) { // Remaining skip regs must be explicitly restored.
363: int r = skip_lo.n_saved - extra + 1;
364: for (i = extra; i > 0; i--) {
365: switch (r++) {
366: case 6:
367: set_r3(from);
368: from--;
369: break;
370: case 5:
371: set_r4(from);
372: from--;
373: break;
374: case 4:
375: set_r5(from);
376: from--;
377: break;
378: case 3:
379: set_r6(from);
380: from--;
381: break;
382: case 2:
383: set_r7(from);
384: from--;
385: break;
386: case 1:
387: set_r8(from);
388: from--;
389: break;
390: }
391: }
392: }
393: }
394: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.