/src/binutils-gdb/gas/config/tc-i386-ginsn.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* tc-i386-ginsn.c -- Ginsn generation for the x86-64 ISA |
2 | | |
3 | | Copyright (C) 2024-2025 Free Software Foundation, Inc. |
4 | | |
5 | | This file is part of GAS. |
6 | | |
7 | | GAS is free software; you can redistribute it and/or modify |
8 | | it under the terms of the GNU General Public License as published by |
9 | | the Free Software Foundation; either version 3 of the license, or |
10 | | (at your option) any later version. |
11 | | |
12 | | GAS is distributed in the hope that it will be useful, |
13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | GNU General Public License for more details. |
16 | | |
17 | | You should have received a copy of the GNU General Public License |
18 | | along with this program; see the file COPYING3. If not, |
19 | | see <http://www.gnu.org/licenses/>. */ |
20 | | |
21 | | /* This file contains the implementation of the ginsn creation for x86-64 |
22 | | instructions. */ |
23 | | |
24 | | /* DWARF register number for EFLAGS. Used for pushf/popf insns. */ |
25 | 0 | #define GINSN_DW2_REGNUM_EFLAGS 49 |
26 | | /* DWARF register number for RSI. Used as dummy value when RegIP/RegIZ. */ |
27 | 0 | #define GINSN_DW2_REGNUM_RSI_DUMMY 4 |
28 | | |
29 | | /* Identify the callee-saved registers in System V AMD64 ABI. */ |
30 | | |
31 | | bool |
32 | | x86_scfi_callee_saved_p (unsigned int dw2reg_num) |
33 | 0 | { |
34 | 0 | if (dw2reg_num == 3 /* rbx. */ |
35 | 0 | || dw2reg_num == REG_FP /* rbp. */ |
36 | 0 | || dw2reg_num == REG_SP /* rsp. */ |
37 | 0 | || (dw2reg_num >= 12 && dw2reg_num <= 15) /* r12 - r15. */) |
38 | 0 | return true; |
39 | | |
40 | 0 | return false; |
41 | 0 | } |
42 | | |
43 | | /* Check whether an instruction prefix which affects operation size |
44 | | accompanies. For insns in the legacy space, setting REX.W takes precedence |
45 | | over the operand-size prefix (66H) when both are used. |
46 | | |
47 | | The current users of this API are in the handlers for PUSH, POP or other |
48 | | instructions which affect the stack pointer implicitly: the operation size |
49 | | (16, 32, or 64 bits) determines the amount by which the stack pointer is |
50 | | incremented / decremented (2, 4 or 8). */ |
51 | | |
52 | | static bool |
53 | | ginsn_opsize_prefix_p (void) |
54 | 0 | { |
55 | 0 | return (!(i.prefix[REX_PREFIX] & REX_W) && i.prefix[DATA_PREFIX]); |
56 | 0 | } |
57 | | |
58 | | /* Get the DWARF register number for the given register entry. |
59 | | For specific byte/word/dword register accesses like al, cl, ah, ch, r8d, |
60 | | r20w etc., we need to identify the DWARF register number for the |
61 | | corresponding 8-byte GPR. |
62 | | |
63 | | This function is a hack - it relies on relative ordering of reg entries in |
64 | | the i386_regtab. FIXME - it will be good to allow a more direct way to get |
65 | | this information. */ |
66 | | |
67 | | static unsigned int |
68 | | ginsn_dw2_regnum (const reg_entry *ireg) |
69 | 0 | { |
70 | 0 | const reg_entry *temp = ireg; |
71 | 0 | unsigned int dwarf_reg = Dw2Inval, idx = 0; |
72 | | |
73 | | /* ginsn creation is available for AMD64 abi only ATM. Other flag_code |
74 | | are not expected. */ |
75 | 0 | gas_assert (ireg && flag_code == CODE_64BIT); |
76 | | |
77 | | /* Watch out for RegIP, RegIZ. These are expected to appear only with |
78 | | base/index addressing modes. Although creating inaccurate data |
79 | | dependencies, using a dummy value (lets say volatile register rsi) will |
80 | | not hurt SCFI. TBD_GINSN_GEN_NOT_SCFI. */ |
81 | 0 | if (ireg->reg_num == RegIP || ireg->reg_num == RegIZ) |
82 | 0 | return GINSN_DW2_REGNUM_RSI_DUMMY; |
83 | | |
84 | 0 | dwarf_reg = ireg->dw2_regnum[object_64bit]; |
85 | |
|
86 | 0 | if (dwarf_reg == Dw2Inval) |
87 | 0 | { |
88 | 0 | if (ireg <= &i386_regtab[3]) |
89 | | /* For al, cl, dl, bl, bump over to axl, cxl, dxl, bxl respectively by |
90 | | adding 8. */ |
91 | 0 | temp = ireg + 8; |
92 | 0 | else if (ireg <= &i386_regtab[7]) |
93 | | /* For ah, ch, dh, bh, bump over to axl, cxl, dxl, bxl respectively by |
94 | | adding 4. */ |
95 | 0 | temp = ireg + 4; |
96 | 0 | else |
97 | 0 | { |
98 | | /* The code relies on the relative ordering of the reg entries in |
99 | | i386_regtab. There are 32 register entries between axl-r31b, |
100 | | ax-r31w etc. The assertions here ensures the code does not |
101 | | recurse indefinitely. */ |
102 | 0 | gas_assert ((temp - &i386_regtab[0]) >= 0); |
103 | 0 | idx = temp - &i386_regtab[0]; |
104 | 0 | gas_assert (idx + 32 < i386_regtab_size - 1); |
105 | | |
106 | 0 | temp = temp + 32; |
107 | 0 | } |
108 | | |
109 | 0 | dwarf_reg = ginsn_dw2_regnum (temp); |
110 | 0 | } |
111 | | |
112 | | /* Sanity check - failure may indicate state corruption, bad ginsn or |
113 | | perhaps the i386-reg table and the current function got out of sync. */ |
114 | 0 | gas_assert (dwarf_reg < Dw2Inval); |
115 | | |
116 | 0 | return dwarf_reg; |
117 | 0 | } |
118 | | |
119 | | static ginsnS * |
120 | | x86_ginsn_addsub_reg_mem (const symbolS *insn_end_sym) |
121 | 0 | { |
122 | 0 | unsigned int dw2_regnum; |
123 | 0 | unsigned int src1_dw2_regnum; |
124 | 0 | ginsnS *ginsn = NULL; |
125 | 0 | ginsnS * (*ginsn_func) (const symbolS *, bool, |
126 | 0 | enum ginsn_src_type, unsigned int, offsetT, |
127 | 0 | enum ginsn_src_type, unsigned int, offsetT, |
128 | 0 | enum ginsn_dst_type, unsigned int, offsetT); |
129 | 0 | uint16_t opcode = i.tm.base_opcode; |
130 | |
|
131 | 0 | gas_assert (i.tm.opcode_space == SPACE_BASE |
132 | 0 | && (opcode == 0x1 || opcode == 0x29)); |
133 | 0 | ginsn_func = (opcode == 0x1) ? ginsn_new_add : ginsn_new_sub; |
134 | | |
135 | | /* op %reg, symbol or even other cases where destination involves indirect |
136 | | access are unnecessary for SCFI correctness. TBD_GINSN_GEN_NOT_SCFI. */ |
137 | 0 | if (i.mem_operands) |
138 | 0 | return ginsn; |
139 | | |
140 | | /* Skip detection of 8/16/32-bit op size; 'add/sub reg, reg/mem' ops always |
141 | | make the dest reg untraceable for SCFI. */ |
142 | | |
143 | | /* op reg, reg/mem. */ |
144 | 0 | src1_dw2_regnum = ginsn_dw2_regnum (i.op[0].regs); |
145 | | /* Of interest only when second opnd is not memory. */ |
146 | 0 | if (i.reg_operands == 2) |
147 | 0 | { |
148 | 0 | dw2_regnum = ginsn_dw2_regnum (i.op[1].regs); |
149 | 0 | ginsn = ginsn_func (insn_end_sym, true, |
150 | 0 | GINSN_SRC_REG, src1_dw2_regnum, 0, |
151 | 0 | GINSN_SRC_REG, dw2_regnum, 0, |
152 | 0 | GINSN_DST_REG, dw2_regnum, 0); |
153 | 0 | ginsn_set_where (ginsn); |
154 | 0 | } |
155 | |
|
156 | 0 | return ginsn; |
157 | 0 | } |
158 | | |
159 | | static ginsnS * |
160 | | x86_ginsn_addsub_mem_reg (const symbolS *insn_end_sym) |
161 | 0 | { |
162 | 0 | unsigned int dw2_regnum; |
163 | 0 | unsigned int src1_dw2_regnum; |
164 | 0 | const reg_entry *mem_reg; |
165 | 0 | int32_t gdisp = 0; |
166 | 0 | ginsnS *ginsn = NULL; |
167 | 0 | ginsnS * (*ginsn_func) (const symbolS *, bool, |
168 | 0 | enum ginsn_src_type, unsigned int, offsetT, |
169 | 0 | enum ginsn_src_type, unsigned int, offsetT, |
170 | 0 | enum ginsn_dst_type, unsigned int, offsetT); |
171 | 0 | uint16_t opcode = i.tm.base_opcode; |
172 | |
|
173 | 0 | gas_assert (i.tm.opcode_space == SPACE_BASE |
174 | 0 | && (opcode == 0x3 || opcode == 0x2b)); |
175 | 0 | ginsn_func = (opcode == 0x3) ? ginsn_new_add : ginsn_new_sub; |
176 | | |
177 | | /* op symbol, %reg. */ |
178 | 0 | if (i.mem_operands && !i.base_reg && !i.index_reg) |
179 | 0 | return ginsn; |
180 | | |
181 | | /* Skip detection of 8/16/32-bit op size; 'add/sub reg/mem, reg' ops always |
182 | | make the dest reg untraceable for SCFI. */ |
183 | | |
184 | | /* op reg/mem, %reg. */ |
185 | 0 | dw2_regnum = ginsn_dw2_regnum (i.op[1].regs); |
186 | |
|
187 | 0 | if (i.reg_operands == 2) |
188 | 0 | { |
189 | 0 | src1_dw2_regnum = ginsn_dw2_regnum (i.op[0].regs); |
190 | 0 | ginsn = ginsn_func (insn_end_sym, true, |
191 | 0 | GINSN_SRC_REG, src1_dw2_regnum, 0, |
192 | 0 | GINSN_SRC_REG, dw2_regnum, 0, |
193 | 0 | GINSN_DST_REG, dw2_regnum, 0); |
194 | 0 | ginsn_set_where (ginsn); |
195 | 0 | } |
196 | 0 | else if (i.mem_operands) |
197 | 0 | { |
198 | 0 | mem_reg = (i.base_reg) ? i.base_reg : i.index_reg; |
199 | 0 | src1_dw2_regnum = ginsn_dw2_regnum (mem_reg); |
200 | 0 | if (i.disp_operands == 1) |
201 | 0 | gdisp = i.op[0].disps->X_add_number; |
202 | 0 | ginsn = ginsn_func (insn_end_sym, true, |
203 | 0 | GINSN_SRC_INDIRECT, src1_dw2_regnum, gdisp, |
204 | 0 | GINSN_SRC_REG, dw2_regnum, 0, |
205 | 0 | GINSN_DST_REG, dw2_regnum, 0); |
206 | 0 | ginsn_set_where (ginsn); |
207 | 0 | } |
208 | |
|
209 | 0 | return ginsn; |
210 | 0 | } |
211 | | |
212 | | static ginsnS * |
213 | | x86_ginsn_alu_imm (const symbolS *insn_end_sym) |
214 | 0 | { |
215 | 0 | offsetT src_imm; |
216 | 0 | unsigned int dw2_regnum; |
217 | 0 | ginsnS *ginsn = NULL; |
218 | 0 | enum ginsn_src_type src_type = GINSN_SRC_REG; |
219 | 0 | enum ginsn_dst_type dst_type = GINSN_DST_REG; |
220 | |
|
221 | 0 | ginsnS * (*ginsn_func) (const symbolS *, bool, |
222 | 0 | enum ginsn_src_type, unsigned int, offsetT, |
223 | 0 | enum ginsn_src_type, unsigned int, offsetT, |
224 | 0 | enum ginsn_dst_type, unsigned int, offsetT); |
225 | | |
226 | | /* FIXME - create ginsn where dest is REG_SP / REG_FP only ? */ |
227 | | /* Map for insn.tm.extension_opcode |
228 | | 000 ADD 100 AND |
229 | | 001 OR 101 SUB |
230 | | 010 ADC 110 XOR |
231 | | 011 SBB 111 CMP */ |
232 | | |
233 | | /* add/sub/and imm, %reg only at this time for SCFI. |
234 | | Although all three ('and', 'or' , 'xor') make the destination reg |
235 | | untraceable, 'and' op is handled but not 'or' / 'xor' because we will look |
236 | | into supporting the DRAP pattern at some point. Other opcodes ('adc', |
237 | | 'sbb' and 'cmp') are not generated here either. The ginsn representation |
238 | | does not have support for the latter three opcodes; GINSN_TYPE_OTHER may |
239 | | be added for these after x86_ginsn_unhandled () invocation if the |
240 | | destination register is REG_SP or REG_FP. */ |
241 | 0 | if (i.tm.extension_opcode == 5) |
242 | 0 | ginsn_func = ginsn_new_sub; |
243 | 0 | else if (i.tm.extension_opcode == 4) |
244 | 0 | ginsn_func = ginsn_new_and; |
245 | 0 | else if (i.tm.extension_opcode == 0) |
246 | 0 | ginsn_func = ginsn_new_add; |
247 | 0 | else |
248 | 0 | return ginsn; |
249 | | |
250 | | /* TBD_GINSN_REPRESENTATION_LIMIT: There is no representation for when a |
251 | | symbol is used as an operand, like so: |
252 | | addq $simd_cmp_op+8, %rdx |
253 | | Skip generating any ginsn for this. */ |
254 | 0 | if (i.imm_operands == 1 |
255 | 0 | && i.op[0].imms->X_op != O_constant) |
256 | 0 | return ginsn; |
257 | | |
258 | | /* addq $1, symbol |
259 | | addq $1, -16(%rbp) |
260 | | These are not of interest for SCFI. Also, TBD_GINSN_GEN_NOT_SCFI. */ |
261 | 0 | if (i.mem_operands == 1) |
262 | 0 | return ginsn; |
263 | | |
264 | | /* 8/16/32-bit op size makes the destination reg untraceable for SCFI. |
265 | | Deal with this via the x86_ginsn_unhandled () code path. */ |
266 | 0 | if (i.suffix != QWORD_MNEM_SUFFIX) |
267 | 0 | return ginsn; |
268 | | |
269 | 0 | gas_assert (i.imm_operands == 1); |
270 | 0 | src_imm = i.op[0].imms->X_add_number; |
271 | | /* The second operand may be a register or indirect access. For SCFI, only |
272 | | the case when the second opnd is a register is interesting. Revisit this |
273 | | if generating ginsns for a different gen mode TBD_GINSN_GEN_NOT_SCFI. */ |
274 | 0 | if (i.reg_operands == 1) |
275 | 0 | { |
276 | 0 | dw2_regnum = ginsn_dw2_regnum (i.op[1].regs); |
277 | | /* For ginsn, keep the imm as second src operand. */ |
278 | 0 | ginsn = ginsn_func (insn_end_sym, true, |
279 | 0 | src_type, dw2_regnum, 0, |
280 | 0 | GINSN_SRC_IMM, 0, src_imm, |
281 | 0 | dst_type, dw2_regnum, 0); |
282 | |
|
283 | 0 | ginsn_set_where (ginsn); |
284 | 0 | } |
285 | |
|
286 | 0 | return ginsn; |
287 | 0 | } |
288 | | |
289 | | /* Create ginsn(s) for MOV operations. |
290 | | |
291 | | The generated ginsns corresponding to mov with indirect access to memory |
292 | | (src or dest) suffer with loss of information: when both index and base |
293 | | registers are at play, only base register gets conveyed in ginsn. Note |
294 | | this TBD_GINSN_GEN_NOT_SCFI. */ |
295 | | |
296 | | static ginsnS * |
297 | | x86_ginsn_move (const symbolS *insn_end_sym) |
298 | 0 | { |
299 | 0 | ginsnS *ginsn = NULL; |
300 | 0 | unsigned int dst_reg; |
301 | 0 | unsigned int src_reg; |
302 | 0 | offsetT src_disp = 0; |
303 | 0 | offsetT dst_disp = 0; |
304 | 0 | const reg_entry *dst = NULL; |
305 | 0 | const reg_entry *src = NULL; |
306 | 0 | uint16_t opcode = i.tm.base_opcode; |
307 | 0 | enum ginsn_src_type src_type = GINSN_SRC_REG; |
308 | 0 | enum ginsn_dst_type dst_type = GINSN_DST_REG; |
309 | | |
310 | | /* mov %reg, symbol or mov symbol, %reg. |
311 | | Not of interest for SCFI. Also, TBD_GINSN_GEN_NOT_SCFI. */ |
312 | 0 | if (i.mem_operands == 1 && !i.base_reg && !i.index_reg) |
313 | 0 | return ginsn; |
314 | | |
315 | | /* 8/16/32-bit op size makes the destination reg untraceable for SCFI. |
316 | | Handle mov reg, reg only. mov to or from a memory operand will make |
317 | | dest reg, when present, untraceable, irrespective of the op size. */ |
318 | 0 | if (i.reg_operands == 2 && i.suffix != QWORD_MNEM_SUFFIX) |
319 | 0 | return ginsn; |
320 | | |
321 | 0 | gas_assert (i.tm.opcode_space == SPACE_BASE); |
322 | 0 | if (opcode == 0x8b || opcode == 0x8a) |
323 | 0 | { |
324 | | /* mov disp(%reg), %reg. */ |
325 | 0 | if (i.mem_operands) |
326 | 0 | { |
327 | 0 | src = (i.base_reg) ? i.base_reg : i.index_reg; |
328 | 0 | if (i.disp_operands == 1) |
329 | 0 | src_disp = i.op[0].disps->X_add_number; |
330 | 0 | src_type = GINSN_SRC_INDIRECT; |
331 | 0 | } |
332 | 0 | else |
333 | 0 | src = i.op[0].regs; |
334 | |
|
335 | 0 | dst = i.op[1].regs; |
336 | 0 | } |
337 | 0 | else if (opcode == 0x89 || opcode == 0x88) |
338 | 0 | { |
339 | | /* mov %reg, disp(%reg). */ |
340 | 0 | src = i.op[0].regs; |
341 | 0 | if (i.mem_operands) |
342 | 0 | { |
343 | 0 | dst = (i.base_reg) ? i.base_reg : i.index_reg; |
344 | 0 | if (i.disp_operands == 1) |
345 | 0 | dst_disp = i.op[1].disps->X_add_number; |
346 | 0 | dst_type = GINSN_DST_INDIRECT; |
347 | 0 | } |
348 | 0 | else |
349 | 0 | dst = i.op[1].regs; |
350 | 0 | } |
351 | |
|
352 | 0 | src_reg = ginsn_dw2_regnum (src); |
353 | 0 | dst_reg = ginsn_dw2_regnum (dst); |
354 | |
|
355 | 0 | ginsn = ginsn_new_mov (insn_end_sym, true, |
356 | 0 | src_type, src_reg, src_disp, |
357 | 0 | dst_type, dst_reg, dst_disp); |
358 | 0 | ginsn_set_where (ginsn); |
359 | |
|
360 | 0 | return ginsn; |
361 | 0 | } |
362 | | |
363 | | /* Generate appropriate ginsn for lea. |
364 | | |
365 | | Unhandled sub-cases (marked with TBD_GINSN_GEN_NOT_SCFI) also suffer with |
366 | | some loss of information in the final ginsn chosen eventually (type |
367 | | GINSN_TYPE_OTHER). But this is fine for now for GINSN_GEN_SCFI generation |
368 | | mode. */ |
369 | | |
370 | | static ginsnS * |
371 | | x86_ginsn_lea (const symbolS *insn_end_sym) |
372 | 0 | { |
373 | 0 | offsetT src_disp = 0; |
374 | 0 | ginsnS *ginsn = NULL; |
375 | 0 | unsigned int src1_reg; |
376 | 0 | const reg_entry *src1; |
377 | 0 | offsetT index_scale; |
378 | 0 | unsigned int dst_reg; |
379 | 0 | bool index_regiz_p; |
380 | |
|
381 | 0 | if ((!i.base_reg) != (!i.index_reg || i.index_reg->reg_num == RegIZ)) |
382 | 0 | { |
383 | | /* lea disp(%base), %dst or lea disp(,%index,imm), %dst. |
384 | | Either index_reg or base_reg exists, but not both. Further, as per |
385 | | above, the case when just %index exists but is equal to RegIZ is |
386 | | excluded. If not excluded, a GINSN_TYPE_MOV of %rsi |
387 | | (GINSN_DW2_REGNUM_RSI_DUMMY) to %dst will be generated by this block. |
388 | | Such a mov ginsn is imprecise; so, exclude now and generate |
389 | | GINSN_TYPE_OTHER instead later via the x86_ginsn_unhandled (). |
390 | | Excluding other cases is required due to |
391 | | TBD_GINSN_REPRESENTATION_LIMIT. */ |
392 | |
|
393 | 0 | index_scale = i.log2_scale_factor; |
394 | 0 | index_regiz_p = i.index_reg && i.index_reg->reg_num == RegIZ; |
395 | 0 | src1 = i.base_reg ? i.base_reg : i.index_reg; |
396 | 0 | src1_reg = ginsn_dw2_regnum (src1); |
397 | 0 | dst_reg = ginsn_dw2_regnum (i.op[1].regs); |
398 | | /* It makes sense to represent a scale factor of 1 precisely here |
399 | | (i.e., not using GINSN_TYPE_OTHER, but rather similar to the |
400 | | base-without-index case). A non-zero scale factor is still OK if |
401 | | the index reg is zero reg. |
402 | | However, skip from here the case when disp has a symbol instead. |
403 | | TBD_GINSN_REPRESENTATION_LIMIT. */ |
404 | 0 | if ((!index_scale || index_regiz_p) |
405 | 0 | && (!i.disp_operands || i.op[0].disps->X_op == O_constant)) |
406 | 0 | { |
407 | 0 | if (i.disp_operands) |
408 | 0 | src_disp = i.op[0].disps->X_add_number; |
409 | |
|
410 | 0 | if (src_disp) |
411 | | /* Generate an ADD ginsn. */ |
412 | 0 | ginsn = ginsn_new_add (insn_end_sym, true, |
413 | 0 | GINSN_SRC_REG, src1_reg, 0, |
414 | 0 | GINSN_SRC_IMM, 0, src_disp, |
415 | 0 | GINSN_DST_REG, dst_reg, 0); |
416 | 0 | else |
417 | | /* Generate a MOV ginsn. */ |
418 | 0 | ginsn = ginsn_new_mov (insn_end_sym, true, |
419 | 0 | GINSN_SRC_REG, src1_reg, 0, |
420 | 0 | GINSN_DST_REG, dst_reg, 0); |
421 | |
|
422 | 0 | ginsn_set_where (ginsn); |
423 | 0 | } |
424 | 0 | } |
425 | | /* Skip handling other cases here, |
426 | | - when (i.index_reg && i.base_reg) is true, |
427 | | e.g., lea disp(%base,%index,imm), %dst |
428 | | We do not have a ginsn representation for multiply. |
429 | | - or, when (!i.index_reg && !i.base_reg) is true, |
430 | | e.g., lea symbol, %dst |
431 | | Not a frequent pattern. If %dst is a register of interest, the user is |
432 | | likely to use a MOV op anyway. |
433 | | Deal with these via the x86_ginsn_unhandled () code path to generate |
434 | | GINSN_TYPE_OTHER when necessary. TBD_GINSN_GEN_NOT_SCFI. */ |
435 | |
|
436 | 0 | return ginsn; |
437 | 0 | } |
438 | | |
439 | | static ginsnS * |
440 | | x86_ginsn_jump (const symbolS *insn_end_sym, bool cond_p) |
441 | 0 | { |
442 | 0 | ginsnS *ginsn = NULL; |
443 | 0 | const symbolS *src_symbol; |
444 | 0 | ginsnS * (*ginsn_func) (const symbolS *sym, bool real_p, |
445 | 0 | enum ginsn_src_type src_type, unsigned int src_reg, |
446 | 0 | const symbolS *src_ginsn_sym); |
447 | |
|
448 | 0 | gas_assert (i.disp_operands == 1); |
449 | | |
450 | 0 | ginsn_func = cond_p ? ginsn_new_jump_cond : ginsn_new_jump; |
451 | 0 | if (i.op[0].disps->X_op == O_symbol && !i.op[0].disps->X_add_number) |
452 | 0 | { |
453 | 0 | src_symbol = i.op[0].disps->X_add_symbol; |
454 | 0 | ginsn = ginsn_func (insn_end_sym, true, |
455 | 0 | GINSN_SRC_SYMBOL, 0, src_symbol); |
456 | |
|
457 | 0 | ginsn_set_where (ginsn); |
458 | 0 | } |
459 | 0 | else |
460 | 0 | { |
461 | | /* A non-zero addend in jump/JCC target makes control-flow tracking |
462 | | difficult. Skip SCFI for now. */ |
463 | 0 | as_bad (_("SCFI: `%s' insn with non-zero addend to sym not supported"), |
464 | 0 | cond_p ? "JCC" : "jmp"); |
465 | 0 | return ginsn; |
466 | 0 | } |
467 | | |
468 | 0 | return ginsn; |
469 | 0 | } |
470 | | |
471 | | static ginsnS * |
472 | | x86_ginsn_indirect_branch (const symbolS *insn_end_sym) |
473 | 0 | { |
474 | 0 | ginsnS *ginsn = NULL; |
475 | 0 | const reg_entry *mem_reg; |
476 | 0 | unsigned int dw2_regnum; |
477 | |
|
478 | 0 | ginsnS * (*ginsn_func) (const symbolS *sym, bool real_p, |
479 | 0 | enum ginsn_src_type src_type, unsigned int src_reg, |
480 | 0 | const symbolS *src_ginsn_sym); |
481 | | |
482 | | /* Other cases are not expected. */ |
483 | 0 | gas_assert (i.tm.extension_opcode == 4 || i.tm.extension_opcode == 2); |
484 | | |
485 | 0 | if (i.tm.extension_opcode == 4) |
486 | | /* 0xFF /4 (jmp r/m). */ |
487 | 0 | ginsn_func = ginsn_new_jump; |
488 | 0 | else if (i.tm.extension_opcode == 2) |
489 | | /* 0xFF /2 (call r/m). */ |
490 | 0 | ginsn_func = ginsn_new_call; |
491 | 0 | else |
492 | 0 | return ginsn; |
493 | | |
494 | 0 | if (i.reg_operands) |
495 | 0 | { |
496 | 0 | dw2_regnum = ginsn_dw2_regnum (i.op[0].regs); |
497 | 0 | ginsn = ginsn_func (insn_end_sym, true, |
498 | 0 | GINSN_SRC_REG, dw2_regnum, NULL); |
499 | 0 | ginsn_set_where (ginsn); |
500 | 0 | } |
501 | 0 | else if (i.mem_operands) |
502 | 0 | { |
503 | | /* Handle jump/call near, absolute indirect, address. |
504 | | E.g., jmp/call *imm(%rN), jmp/call *sym(,%rN,imm) |
505 | | or jmp/call *sym(%rN) etc. */ |
506 | 0 | mem_reg = i.base_reg ? i.base_reg : i.index_reg; |
507 | | /* Generate a ginsn, even if it is with TBD_GINSN_INFO_LOSS. Otherwise, |
508 | | the user gets the impression of missing functionality due to this |
509 | | being a COFI and alerted for via the x86_ginsn_unhandled () workflow |
510 | | as unhandled operation (which can be misleading for users). |
511 | | |
512 | | Indirect branches make the code block ineligible for SCFI; Hence, an |
513 | | approximate ginsn will not affect SCFI correctness: |
514 | | - Use dummy register if no base or index |
515 | | - Skip symbol information, if any. |
516 | | Note this case of TBD_GINSN_GEN_NOT_SCFI. */ |
517 | 0 | dw2_regnum = (mem_reg |
518 | 0 | ? ginsn_dw2_regnum (mem_reg) |
519 | 0 | : GINSN_DW2_REGNUM_RSI_DUMMY); |
520 | 0 | ginsn = ginsn_func (insn_end_sym, true, |
521 | 0 | GINSN_SRC_REG, dw2_regnum, NULL); |
522 | 0 | ginsn_set_where (ginsn); |
523 | 0 | } |
524 | |
|
525 | 0 | return ginsn; |
526 | 0 | } |
527 | | |
528 | | static ginsnS * |
529 | | x86_ginsn_enter (const symbolS *insn_end_sym) |
530 | 0 | { |
531 | 0 | ginsnS *ginsn = NULL; |
532 | 0 | ginsnS *ginsn_next = NULL; |
533 | 0 | ginsnS *ginsn_last = NULL; |
534 | | /* In 64-bit mode, the default stack update size is 8 bytes. */ |
535 | 0 | int stack_opnd_size = 8; |
536 | |
|
537 | 0 | gas_assert (i.imm_operands == 2); |
538 | | |
539 | | /* For non-zero size operands, bail out as untraceable for SCFI. */ |
540 | 0 | if (i.op[0].imms->X_op != O_constant || i.op[0].imms->X_add_symbol != 0 |
541 | 0 | || i.op[1].imms->X_op != O_constant || i.op[1].imms->X_add_symbol != 0) |
542 | 0 | { |
543 | 0 | as_bad ("SCFI: enter insn with non-zero operand not supported"); |
544 | 0 | return ginsn; |
545 | 0 | } |
546 | | |
547 | | /* Check if this is a 16-bit op. */ |
548 | 0 | if (ginsn_opsize_prefix_p ()) |
549 | 0 | stack_opnd_size = 2; |
550 | | |
551 | | /* If the nesting level is 0, the processor pushes the frame pointer from |
552 | | the BP/EBP/RBP register onto the stack, copies the current stack |
553 | | pointer from the SP/ESP/RSP register into the BP/EBP/RBP register, and |
554 | | loads the SP/ESP/RSP register with the current stack-pointer value |
555 | | minus the value in the size operand. */ |
556 | 0 | ginsn = ginsn_new_sub (insn_end_sym, false, |
557 | 0 | GINSN_SRC_REG, REG_SP, 0, |
558 | 0 | GINSN_SRC_IMM, 0, stack_opnd_size, |
559 | 0 | GINSN_DST_REG, REG_SP, 0); |
560 | 0 | ginsn_set_where (ginsn); |
561 | 0 | ginsn_next = ginsn_new_store (insn_end_sym, false, |
562 | 0 | GINSN_SRC_REG, REG_FP, |
563 | 0 | GINSN_DST_INDIRECT, REG_SP, 0); |
564 | 0 | ginsn_set_where (ginsn_next); |
565 | 0 | gas_assert (!ginsn_link_next (ginsn, ginsn_next)); |
566 | 0 | ginsn_last = ginsn_new_mov (insn_end_sym, false, |
567 | 0 | GINSN_SRC_REG, REG_SP, 0, |
568 | 0 | GINSN_DST_REG, REG_FP, 0); |
569 | 0 | ginsn_set_where (ginsn_last); |
570 | 0 | gas_assert (!ginsn_link_next (ginsn_next, ginsn_last)); |
571 | | |
572 | 0 | return ginsn; |
573 | 0 | } |
574 | | |
575 | | static ginsnS * |
576 | | x86_ginsn_leave (const symbolS *insn_end_sym) |
577 | 0 | { |
578 | 0 | ginsnS *ginsn = NULL; |
579 | 0 | ginsnS *ginsn_next = NULL; |
580 | 0 | ginsnS *ginsn_last = NULL; |
581 | | /* In 64-bit mode, the default stack update size is 8 bytes. */ |
582 | 0 | int stack_opnd_size = 8; |
583 | | |
584 | | /* Check if this is a 16-bit op. */ |
585 | 0 | if (ginsn_opsize_prefix_p ()) |
586 | 0 | stack_opnd_size = 2; |
587 | | |
588 | | /* The 'leave' instruction copies the contents of the RBP register |
589 | | into the RSP register to release all stack space allocated to the |
590 | | procedure. */ |
591 | 0 | ginsn = ginsn_new_mov (insn_end_sym, false, |
592 | 0 | GINSN_SRC_REG, REG_FP, 0, |
593 | 0 | GINSN_DST_REG, REG_SP, 0); |
594 | 0 | ginsn_set_where (ginsn); |
595 | | /* Then it restores the old value of the RBP register from the stack. */ |
596 | 0 | ginsn_next = ginsn_new_load (insn_end_sym, false, |
597 | 0 | GINSN_SRC_INDIRECT, REG_SP, 0, |
598 | 0 | GINSN_DST_REG, REG_FP); |
599 | 0 | ginsn_set_where (ginsn_next); |
600 | 0 | gas_assert (!ginsn_link_next (ginsn, ginsn_next)); |
601 | 0 | ginsn_last = ginsn_new_add (insn_end_sym, false, |
602 | 0 | GINSN_SRC_REG, REG_SP, 0, |
603 | 0 | GINSN_SRC_IMM, 0, stack_opnd_size, |
604 | 0 | GINSN_DST_REG, REG_SP, 0); |
605 | 0 | ginsn_set_where (ginsn_next); |
606 | 0 | gas_assert (!ginsn_link_next (ginsn_next, ginsn_last)); |
607 | | |
608 | 0 | return ginsn; |
609 | 0 | } |
610 | | |
611 | | /* Check if an instruction is whitelisted. |
612 | | |
613 | | Some instructions may appear with REG_SP or REG_FP as destination, because |
614 | | which they are deemed 'interesting' for SCFI. Whitelist them here if they |
615 | | do not affect SCFI correctness. */ |
616 | | |
617 | | static bool |
618 | | x86_ginsn_safe_to_skip_p (void) |
619 | 0 | { |
620 | 0 | bool skip_p = false; |
621 | 0 | uint16_t opcode = i.tm.base_opcode; |
622 | |
|
623 | 0 | switch (opcode) |
624 | 0 | { |
625 | 0 | case 0x80: |
626 | 0 | case 0x81: |
627 | 0 | case 0x83: |
628 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
629 | 0 | break; |
630 | | /* cmp imm, reg/rem. */ |
631 | 0 | if (i.tm.extension_opcode == 7) |
632 | 0 | skip_p = true; |
633 | 0 | break; |
634 | | |
635 | 0 | case 0x38: |
636 | 0 | case 0x39: |
637 | 0 | case 0x3a: |
638 | 0 | case 0x3b: |
639 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
640 | 0 | break; |
641 | | /* cmp imm/reg/mem, reg/rem. */ |
642 | 0 | skip_p = true; |
643 | 0 | break; |
644 | | |
645 | 0 | case 0xf6: |
646 | 0 | case 0xf7: |
647 | 0 | case 0x84: |
648 | 0 | case 0x85: |
649 | | /* test imm/reg/mem, reg/mem. */ |
650 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
651 | 0 | break; |
652 | 0 | skip_p = true; |
653 | 0 | break; |
654 | | |
655 | 0 | default: |
656 | 0 | break; |
657 | 0 | } |
658 | | |
659 | 0 | return skip_p; |
660 | 0 | } |
661 | | |
662 | 0 | #define X86_GINSN_UNHANDLED_NONE 0 |
663 | 0 | #define X86_GINSN_UNHANDLED_DEST_REG 1 |
664 | 0 | #define X86_GINSN_UNHANDLED_CFG 2 |
665 | 0 | #define X86_GINSN_UNHANDLED_STACKOP 3 |
666 | 0 | #define X86_GINSN_UNHANDLED_UNEXPECTED 4 |
667 | | |
668 | | /* Check the input insn for its impact on the correctness of the synthesized |
669 | | CFI. Returns an error code to the caller. */ |
670 | | |
671 | | static int |
672 | | x86_ginsn_unhandled (void) |
673 | 0 | { |
674 | 0 | int err = X86_GINSN_UNHANDLED_NONE; |
675 | 0 | const reg_entry *reg_op; |
676 | 0 | unsigned int dw2_regnum; |
677 | | |
678 | | /* Keep an eye out for instructions affecting control flow. */ |
679 | 0 | if (i.tm.opcode_modifier.jump) |
680 | 0 | err = X86_GINSN_UNHANDLED_CFG; |
681 | | /* Also, for any instructions involving an implicit update to the stack |
682 | | pointer. */ |
683 | 0 | else if (i.tm.opcode_modifier.operandconstraint == IMPLICIT_STACK_OP) |
684 | 0 | err = X86_GINSN_UNHANDLED_STACKOP; |
685 | | /* Finally, also check if the missed instructions are affecting REG_SP or |
686 | | REG_FP. The destination operand is the last at all stages of assembly |
687 | | (due to following AT&T syntax layout in the internal representation). In |
688 | | case of Intel syntax input, this still remains true as swap_operands () |
689 | | is done by now. |
690 | | PS: These checks do not involve index / base reg, as indirect memory |
691 | | accesses via REG_SP or REG_FP do not affect SCFI correctness. |
692 | | (Also note these instructions are candidates for other ginsn generation |
693 | | modes in future. TBD_GINSN_GEN_NOT_SCFI.) */ |
694 | 0 | else if (i.operands && i.reg_operands |
695 | 0 | && !(i.flags[i.operands - 1] & Operand_Mem)) |
696 | 0 | { |
697 | 0 | reg_op = i.op[i.operands - 1].regs; |
698 | 0 | if (reg_op) |
699 | 0 | { |
700 | 0 | dw2_regnum = ginsn_dw2_regnum (reg_op); |
701 | 0 | if (dw2_regnum == REG_SP || dw2_regnum == REG_FP) |
702 | 0 | err = X86_GINSN_UNHANDLED_DEST_REG; |
703 | 0 | } |
704 | 0 | else |
705 | | /* Something unexpected. Indicate to caller. */ |
706 | 0 | err = X86_GINSN_UNHANDLED_UNEXPECTED; |
707 | 0 | } |
708 | |
|
709 | 0 | return err; |
710 | 0 | } |
711 | | |
712 | | /* Generate one or more generic GAS instructions, a.k.a, ginsns for the current |
713 | | machine instruction. |
714 | | |
715 | | Returns the head of linked list of ginsn(s) added, if success; Returns NULL |
716 | | if failure. |
717 | | |
718 | | The input ginsn_gen_mode GMODE determines the set of minimal necessary |
719 | | ginsns necessary for correctness of any passes applicable for that mode. |
720 | | For supporting the GINSN_GEN_SCFI generation mode, following is the list of |
721 | | machine instructions that must be translated into the corresponding ginsns |
722 | | to ensure correctness of SCFI: |
723 | | - All instructions affecting the two registers that could potentially |
724 | | be used as the base register for CFA tracking. For SCFI, the base |
725 | | register for CFA tracking is limited to REG_SP and REG_FP only for |
726 | | now. |
727 | | - All change of flow instructions: conditional and unconditional branches, |
728 | | call and return from functions. |
729 | | - All instructions that can potentially be a register save / restore |
730 | | operation. |
731 | | - All instructions that perform stack manipulation implicitly: the CALL, |
732 | | RET, PUSH, POP, ENTER, and LEAVE instructions. |
733 | | |
734 | | The function currently supports GINSN_GEN_SCFI ginsn generation mode only. |
735 | | To support other generation modes will require work on this target-specific |
736 | | process of creation of ginsns: |
737 | | - Some of such places are tagged with TBD_GINSN_GEN_NOT_SCFI to serve as |
738 | | possible starting points. |
739 | | - Also note that ginsn representation may need enhancements. Specifically, |
740 | | note some TBD_GINSN_INFO_LOSS and TBD_GINSN_REPRESENTATION_LIMIT markers. |
741 | | */ |
742 | | |
743 | | static ginsnS * |
744 | | x86_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode) |
745 | 0 | { |
746 | 0 | int err = 0; |
747 | 0 | uint16_t opcode; |
748 | 0 | unsigned int dw2_regnum; |
749 | 0 | const reg_entry *mem_reg; |
750 | 0 | ginsnS *ginsn = NULL; |
751 | 0 | ginsnS *ginsn_next = NULL; |
752 | | /* In 64-bit mode, the default stack update size is 8 bytes. */ |
753 | 0 | int stack_opnd_size = 8; |
754 | | |
755 | | /* Currently supports generation of selected ginsns, sufficient for |
756 | | the use-case of SCFI only. */ |
757 | 0 | if (gmode != GINSN_GEN_SCFI) |
758 | 0 | return ginsn; |
759 | | |
760 | 0 | opcode = i.tm.base_opcode; |
761 | | |
762 | | /* Until it is clear how to handle APX NDD and other new opcodes, disallow |
763 | | them from SCFI. */ |
764 | 0 | if (is_apx_rex2_encoding () |
765 | 0 | || (i.tm.opcode_modifier.evex && is_apx_evex_encoding ())) |
766 | 0 | { |
767 | 0 | as_bad (_("SCFI: unsupported APX op %#x may cause incorrect CFI"), |
768 | 0 | opcode); |
769 | 0 | return ginsn; |
770 | 0 | } |
771 | | |
772 | 0 | switch (opcode) |
773 | 0 | { |
774 | | |
775 | | /* Add opcodes 0x0/0x2 and sub opcodes 0x28/0x2a (with opcode_space |
776 | | SPACE_BASE) are 8-bit ops. While they are relevant for SCFI |
777 | | correctness, skip handling them here and use the x86_ginsn_unhandled |
778 | | code path to generate GINSN_TYPE_OTHER when necessary. */ |
779 | | |
780 | 0 | case 0x1: /* add reg, reg/mem. */ |
781 | 0 | case 0x29: /* sub reg, reg/mem. */ |
782 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
783 | 0 | break; |
784 | 0 | ginsn = x86_ginsn_addsub_reg_mem (insn_end_sym); |
785 | 0 | break; |
786 | | |
787 | 0 | case 0x3: /* add reg/mem, reg. */ |
788 | 0 | case 0x2b: /* sub reg/mem, reg. */ |
789 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
790 | 0 | break; |
791 | 0 | ginsn = x86_ginsn_addsub_mem_reg (insn_end_sym); |
792 | 0 | break; |
793 | | |
794 | 0 | case 0xa0: /* push fs. */ |
795 | 0 | case 0xa8: /* push gs. */ |
796 | | /* push fs / push gs have opcode_space == SPACE_0F. */ |
797 | 0 | if (i.tm.opcode_space != SPACE_0F) |
798 | 0 | break; |
799 | 0 | dw2_regnum = ginsn_dw2_regnum (i.op[0].regs); |
800 | | /* Check if operation size is 16-bit. */ |
801 | 0 | if (ginsn_opsize_prefix_p ()) |
802 | 0 | stack_opnd_size = 2; |
803 | 0 | ginsn = ginsn_new_sub (insn_end_sym, false, |
804 | 0 | GINSN_SRC_REG, REG_SP, 0, |
805 | 0 | GINSN_SRC_IMM, 0, stack_opnd_size, |
806 | 0 | GINSN_DST_REG, REG_SP, 0); |
807 | 0 | ginsn_set_where (ginsn); |
808 | 0 | ginsn_next = ginsn_new_store (insn_end_sym, false, |
809 | 0 | GINSN_SRC_REG, dw2_regnum, |
810 | 0 | GINSN_DST_INDIRECT, REG_SP, 0); |
811 | 0 | ginsn_set_where (ginsn_next); |
812 | 0 | gas_assert (!ginsn_link_next (ginsn, ginsn_next)); |
813 | 0 | break; |
814 | | |
815 | 0 | case 0xa1: /* pop fs. */ |
816 | 0 | case 0xa9: /* pop gs. */ |
817 | | /* pop fs / pop gs have opcode_space == SPACE_0F. */ |
818 | 0 | if (i.tm.opcode_space != SPACE_0F) |
819 | 0 | break; |
820 | 0 | dw2_regnum = ginsn_dw2_regnum (i.op[0].regs); |
821 | | /* Check if operation size is 16-bit. */ |
822 | 0 | if (ginsn_opsize_prefix_p ()) |
823 | 0 | stack_opnd_size = 2; |
824 | 0 | ginsn = ginsn_new_load (insn_end_sym, false, |
825 | 0 | GINSN_SRC_INDIRECT, REG_SP, 0, |
826 | 0 | GINSN_DST_REG, dw2_regnum); |
827 | 0 | ginsn_set_where (ginsn); |
828 | 0 | ginsn_next = ginsn_new_add (insn_end_sym, false, |
829 | 0 | GINSN_SRC_REG, REG_SP, 0, |
830 | 0 | GINSN_SRC_IMM, 0, stack_opnd_size, |
831 | 0 | GINSN_DST_REG, REG_SP, 0); |
832 | 0 | ginsn_set_where (ginsn_next); |
833 | 0 | gas_assert (!ginsn_link_next (ginsn, ginsn_next)); |
834 | 0 | break; |
835 | | |
836 | 0 | case 0x50 ... 0x57: |
837 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
838 | 0 | break; |
839 | | /* push reg. */ |
840 | 0 | dw2_regnum = ginsn_dw2_regnum (i.op[0].regs); |
841 | | /* Check if operation size is 16-bit. */ |
842 | 0 | if (ginsn_opsize_prefix_p ()) |
843 | 0 | stack_opnd_size = 2; |
844 | 0 | ginsn = ginsn_new_sub (insn_end_sym, false, |
845 | 0 | GINSN_SRC_REG, REG_SP, 0, |
846 | 0 | GINSN_SRC_IMM, 0, stack_opnd_size, |
847 | 0 | GINSN_DST_REG, REG_SP, 0); |
848 | 0 | ginsn_set_where (ginsn); |
849 | 0 | ginsn_next = ginsn_new_store (insn_end_sym, false, |
850 | 0 | GINSN_SRC_REG, dw2_regnum, |
851 | 0 | GINSN_DST_INDIRECT, REG_SP, 0); |
852 | 0 | ginsn_set_where (ginsn_next); |
853 | 0 | gas_assert (!ginsn_link_next (ginsn, ginsn_next)); |
854 | 0 | break; |
855 | | |
856 | 0 | case 0x58 ... 0x5f: |
857 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
858 | 0 | break; |
859 | | /* pop reg. */ |
860 | 0 | dw2_regnum = ginsn_dw2_regnum (i.op[0].regs); |
861 | 0 | ginsn = ginsn_new_load (insn_end_sym, false, |
862 | 0 | GINSN_SRC_INDIRECT, REG_SP, 0, |
863 | 0 | GINSN_DST_REG, dw2_regnum); |
864 | 0 | ginsn_set_where (ginsn); |
865 | | /* Check if operation size is 16-bit. */ |
866 | 0 | if (ginsn_opsize_prefix_p ()) |
867 | 0 | stack_opnd_size = 2; |
868 | 0 | ginsn_next = ginsn_new_add (insn_end_sym, false, |
869 | 0 | GINSN_SRC_REG, REG_SP, 0, |
870 | 0 | GINSN_SRC_IMM, 0, stack_opnd_size, |
871 | 0 | GINSN_DST_REG, REG_SP, 0); |
872 | 0 | ginsn_set_where (ginsn_next); |
873 | 0 | gas_assert (!ginsn_link_next (ginsn, ginsn_next)); |
874 | 0 | break; |
875 | | |
876 | 0 | case 0x6a: /* push imm8. */ |
877 | 0 | case 0x68: /* push imm16/imm32. */ |
878 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
879 | 0 | break; |
880 | | /* Check if operation size is 16-bit. */ |
881 | 0 | if (ginsn_opsize_prefix_p ()) |
882 | 0 | stack_opnd_size = 2; |
883 | | /* Skip getting the value of imm from machine instruction |
884 | | because this is not important for SCFI. */ |
885 | 0 | ginsn = ginsn_new_sub (insn_end_sym, false, |
886 | 0 | GINSN_SRC_REG, REG_SP, 0, |
887 | 0 | GINSN_SRC_IMM, 0, stack_opnd_size, |
888 | 0 | GINSN_DST_REG, REG_SP, 0); |
889 | 0 | ginsn_set_where (ginsn); |
890 | 0 | ginsn_next = ginsn_new_store (insn_end_sym, false, |
891 | 0 | GINSN_SRC_IMM, 0, |
892 | 0 | GINSN_DST_INDIRECT, REG_SP, 0); |
893 | 0 | ginsn_set_where (ginsn_next); |
894 | 0 | gas_assert (!ginsn_link_next (ginsn, ginsn_next)); |
895 | 0 | break; |
896 | | |
897 | | /* PS: Opcodes 0x80 ... 0x8f with opcode_space SPACE_0F are present |
898 | | only after relaxation. They do not need to be handled for ginsn |
899 | | creation. */ |
900 | 0 | case 0x70 ... 0x7f: |
901 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
902 | 0 | break; |
903 | 0 | ginsn = x86_ginsn_jump (insn_end_sym, true); |
904 | 0 | break; |
905 | | |
906 | 0 | case 0x80: |
907 | 0 | case 0x81: |
908 | 0 | case 0x83: |
909 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
910 | 0 | break; |
911 | 0 | ginsn = x86_ginsn_alu_imm (insn_end_sym); |
912 | 0 | break; |
913 | | |
914 | 0 | case 0x8a: /* mov r/m8, r8. */ |
915 | 0 | case 0x8b: /* mov r/m(16/32/64), r(16/32/64). */ |
916 | 0 | case 0x88: /* mov r8, r/m8. */ |
917 | 0 | case 0x89: /* mov r(16/32/64), r/m(16/32/64). */ |
918 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
919 | 0 | break; |
920 | 0 | ginsn = x86_ginsn_move (insn_end_sym); |
921 | 0 | break; |
922 | | |
923 | 0 | case 0x8d: |
924 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
925 | 0 | break; |
926 | | /* lea disp(%base,%index,imm), %dst. */ |
927 | 0 | ginsn = x86_ginsn_lea (insn_end_sym); |
928 | 0 | break; |
929 | | |
930 | 0 | case 0x8f: |
931 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
932 | 0 | break; |
933 | | /* pop to reg/mem. */ |
934 | 0 | if (i.mem_operands) |
935 | 0 | { |
936 | 0 | mem_reg = (i.base_reg) ? i.base_reg : i.index_reg; |
937 | | /* Use dummy register if no base or index. Unlike other opcodes, |
938 | | ginsns must be generated as this affect stack pointer. */ |
939 | 0 | dw2_regnum = (mem_reg |
940 | 0 | ? ginsn_dw2_regnum (mem_reg) |
941 | 0 | : GINSN_DW2_REGNUM_RSI_DUMMY); |
942 | 0 | } |
943 | 0 | else |
944 | 0 | dw2_regnum = ginsn_dw2_regnum (i.op[0].regs); |
945 | 0 | ginsn = ginsn_new_load (insn_end_sym, false, |
946 | 0 | GINSN_SRC_INDIRECT, REG_SP, 0, |
947 | 0 | GINSN_DST_INDIRECT, dw2_regnum); |
948 | 0 | ginsn_set_where (ginsn); |
949 | | /* Check if operation size is 16-bit. */ |
950 | 0 | if (ginsn_opsize_prefix_p ()) |
951 | 0 | stack_opnd_size = 2; |
952 | 0 | ginsn_next = ginsn_new_add (insn_end_sym, false, |
953 | 0 | GINSN_SRC_REG, REG_SP, 0, |
954 | 0 | GINSN_SRC_IMM, 0, stack_opnd_size, |
955 | 0 | GINSN_DST_REG, REG_SP, 0); |
956 | 0 | ginsn_set_where (ginsn_next); |
957 | 0 | gas_assert (!ginsn_link_next (ginsn, ginsn_next)); |
958 | 0 | break; |
959 | | |
960 | 0 | case 0x9c: |
961 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
962 | 0 | break; |
963 | | /* pushf / pushfq. */ |
964 | | /* Check if operation size is 16-bit. */ |
965 | 0 | if (ginsn_opsize_prefix_p ()) |
966 | 0 | stack_opnd_size = 2; |
967 | 0 | ginsn = ginsn_new_sub (insn_end_sym, false, |
968 | 0 | GINSN_SRC_REG, REG_SP, 0, |
969 | 0 | GINSN_SRC_IMM, 0, stack_opnd_size, |
970 | 0 | GINSN_DST_REG, REG_SP, 0); |
971 | 0 | ginsn_set_where (ginsn); |
972 | | /* FIXME - hardcode the actual DWARF reg number value. As for SCFI |
973 | | correctness, although this behaves simply a placeholder value; its |
974 | | just clearer if the value is correct. */ |
975 | 0 | dw2_regnum = GINSN_DW2_REGNUM_EFLAGS; |
976 | 0 | ginsn_next = ginsn_new_store (insn_end_sym, false, |
977 | 0 | GINSN_SRC_REG, dw2_regnum, |
978 | 0 | GINSN_DST_INDIRECT, REG_SP, 0); |
979 | 0 | ginsn_set_where (ginsn_next); |
980 | 0 | gas_assert (!ginsn_link_next (ginsn, ginsn_next)); |
981 | 0 | break; |
982 | | |
983 | 0 | case 0x9d: |
984 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
985 | 0 | break; |
986 | | /* popf / popfq. */ |
987 | | /* Check if operation size is 16-bit. */ |
988 | 0 | if (ginsn_opsize_prefix_p ()) |
989 | 0 | stack_opnd_size = 2; |
990 | | /* FIXME - hardcode the actual DWARF reg number value. As for SCFI |
991 | | correctness, although this behaves simply a placeholder value; its |
992 | | just clearer if the value is correct. */ |
993 | 0 | dw2_regnum = GINSN_DW2_REGNUM_EFLAGS; |
994 | 0 | ginsn = ginsn_new_load (insn_end_sym, false, |
995 | 0 | GINSN_SRC_INDIRECT, REG_SP, 0, |
996 | 0 | GINSN_DST_REG, dw2_regnum); |
997 | 0 | ginsn_set_where (ginsn); |
998 | 0 | ginsn_next = ginsn_new_add (insn_end_sym, false, |
999 | 0 | GINSN_SRC_REG, REG_SP, 0, |
1000 | 0 | GINSN_SRC_IMM, 0, stack_opnd_size, |
1001 | 0 | GINSN_DST_REG, REG_SP, 0); |
1002 | 0 | ginsn_set_where (ginsn_next); |
1003 | 0 | gas_assert (!ginsn_link_next (ginsn, ginsn_next)); |
1004 | 0 | break; |
1005 | | |
1006 | 0 | case 0xff: |
1007 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
1008 | 0 | break; |
1009 | | /* push from reg/mem. */ |
1010 | 0 | if (i.tm.extension_opcode == 6) |
1011 | 0 | { |
1012 | | /* Check if operation size is 16-bit. */ |
1013 | 0 | if (ginsn_opsize_prefix_p ()) |
1014 | 0 | stack_opnd_size = 2; |
1015 | 0 | ginsn = ginsn_new_sub (insn_end_sym, false, |
1016 | 0 | GINSN_SRC_REG, REG_SP, 0, |
1017 | 0 | GINSN_SRC_IMM, 0, stack_opnd_size, |
1018 | 0 | GINSN_DST_REG, REG_SP, 0); |
1019 | 0 | ginsn_set_where (ginsn); |
1020 | 0 | if (i.mem_operands) |
1021 | 0 | { |
1022 | 0 | mem_reg = (i.base_reg) ? i.base_reg : i.index_reg; |
1023 | | /* Use dummy register if no base or index. Unlike other opcodes, |
1024 | | ginsns must be generated as this affect stack pointer. */ |
1025 | 0 | dw2_regnum = (mem_reg |
1026 | 0 | ? ginsn_dw2_regnum (mem_reg) |
1027 | 0 | : GINSN_DW2_REGNUM_RSI_DUMMY); |
1028 | 0 | } |
1029 | 0 | else |
1030 | 0 | dw2_regnum = ginsn_dw2_regnum (i.op[0].regs); |
1031 | 0 | ginsn_next = ginsn_new_store (insn_end_sym, false, |
1032 | 0 | GINSN_SRC_INDIRECT, dw2_regnum, |
1033 | 0 | GINSN_DST_INDIRECT, REG_SP, 0); |
1034 | 0 | ginsn_set_where (ginsn_next); |
1035 | 0 | gas_assert (!ginsn_link_next (ginsn, ginsn_next)); |
1036 | 0 | } |
1037 | 0 | else if (i.tm.extension_opcode == 4 || i.tm.extension_opcode == 2) |
1038 | 0 | ginsn = x86_ginsn_indirect_branch (insn_end_sym); |
1039 | 0 | break; |
1040 | | |
1041 | 0 | case 0xc2: /* ret imm16. */ |
1042 | 0 | case 0xc3: /* ret. */ |
1043 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
1044 | 0 | break; |
1045 | | /* Near ret. */ |
1046 | 0 | ginsn = ginsn_new_return (insn_end_sym, true); |
1047 | 0 | ginsn_set_where (ginsn); |
1048 | 0 | break; |
1049 | | |
1050 | 0 | case 0xc8: |
1051 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
1052 | 0 | break; |
1053 | | /* enter. */ |
1054 | 0 | ginsn = x86_ginsn_enter (insn_end_sym); |
1055 | 0 | break; |
1056 | | |
1057 | 0 | case 0xc9: |
1058 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
1059 | 0 | break; |
1060 | | /* leave. */ |
1061 | 0 | ginsn = x86_ginsn_leave (insn_end_sym); |
1062 | 0 | break; |
1063 | | |
1064 | 0 | case 0xe0 ... 0xe2: /* loop / loope / loopne. */ |
1065 | 0 | case 0xe3: /* jecxz / jrcxz. */ |
1066 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
1067 | 0 | break; |
1068 | 0 | ginsn = x86_ginsn_jump (insn_end_sym, true); |
1069 | 0 | ginsn_set_where (ginsn); |
1070 | 0 | break; |
1071 | | |
1072 | 0 | case 0xe8: |
1073 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
1074 | 0 | break; |
1075 | | /* PS: SCFI machinery does not care about which func is being |
1076 | | called. OK to skip that info. */ |
1077 | 0 | ginsn = ginsn_new_call (insn_end_sym, true, |
1078 | 0 | GINSN_SRC_SYMBOL, 0, NULL); |
1079 | 0 | ginsn_set_where (ginsn); |
1080 | 0 | break; |
1081 | | |
1082 | | /* PS: opcode 0xe9 appears only after relaxation. Skip here. */ |
1083 | 0 | case 0xeb: |
1084 | | /* If opcode_space != SPACE_BASE, this is not a jmp insn. Skip it |
1085 | | for GINSN_GEN_SCFI. */ |
1086 | 0 | if (i.tm.opcode_space != SPACE_BASE) |
1087 | 0 | break; |
1088 | | /* Unconditional jmp. */ |
1089 | 0 | ginsn = x86_ginsn_jump (insn_end_sym, false); |
1090 | 0 | ginsn_set_where (ginsn); |
1091 | 0 | break; |
1092 | | |
1093 | 0 | default: |
1094 | | /* TBD_GINSN_GEN_NOT_SCFI: Skip all other opcodes uninteresting for |
1095 | | GINSN_GEN_SCFI mode. */ |
1096 | 0 | break; |
1097 | 0 | } |
1098 | | |
1099 | 0 | if (!ginsn && !x86_ginsn_safe_to_skip_p ()) |
1100 | 0 | { |
1101 | | /* For all unhandled insns that are not whitelisted, check that they do |
1102 | | not impact SCFI correctness. */ |
1103 | 0 | err = x86_ginsn_unhandled (); |
1104 | 0 | switch (err) |
1105 | 0 | { |
1106 | 0 | case X86_GINSN_UNHANDLED_NONE: |
1107 | 0 | break; |
1108 | 0 | case X86_GINSN_UNHANDLED_DEST_REG: |
1109 | | /* Not all writes to REG_FP are harmful in context of SCFI. Simply |
1110 | | generate a GINSN_TYPE_OTHER with destination set to the |
1111 | | appropriate register. The SCFI machinery will bail out if this |
1112 | | ginsn affects SCFI correctness. */ |
1113 | 0 | dw2_regnum = ginsn_dw2_regnum (i.op[i.operands - 1].regs); |
1114 | 0 | ginsn = ginsn_new_other (insn_end_sym, true, |
1115 | 0 | GINSN_SRC_IMM, 0, |
1116 | 0 | GINSN_SRC_IMM, 0, |
1117 | 0 | GINSN_DST_REG, dw2_regnum); |
1118 | 0 | ginsn_set_where (ginsn); |
1119 | 0 | break; |
1120 | 0 | case X86_GINSN_UNHANDLED_CFG: |
1121 | 0 | case X86_GINSN_UNHANDLED_STACKOP: |
1122 | 0 | as_bad (_("SCFI: unhandled op %#x may cause incorrect CFI"), opcode); |
1123 | 0 | break; |
1124 | 0 | case X86_GINSN_UNHANDLED_UNEXPECTED: |
1125 | 0 | as_bad (_("SCFI: unexpected op %#x may cause incorrect CFI"), |
1126 | 0 | opcode); |
1127 | 0 | break; |
1128 | 0 | default: |
1129 | 0 | abort (); |
1130 | 0 | break; |
1131 | 0 | } |
1132 | 0 | } |
1133 | | |
1134 | 0 | return ginsn; |
1135 | 0 | } |