ehopt.c revision 38889
1/* ehopt.c--optimize gcc exception frame information.
2   Copyright (C) 1998 Free Software Foundation, Inc.
3   Written by Ian Lance Taylor <ian@cygnus.com>.
4
5This file is part of GAS, the GNU Assembler.
6
7GAS is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2, or (at your option)
10any later version.
11
12GAS is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GAS; see the file COPYING.  If not, write to the Free
19Software Foundation, 59 Temple Place - Suite 330, Boston, MA
2002111-1307, USA. */
21
22#include "as.h"
23#include "subsegs.h"
24
25/* We include this ELF file, even though we may not be assembling for
26   ELF, since the exception frame information is always in a format
27   derived from DWARF.  */
28
29#include "elf/dwarf2.h"
30
31/* Try to optimize gcc 2.8 exception frame information.
32
33   Exception frame information is emitted for every function in the
34   .eh_frame section.  Simple information for a function with no
35   exceptions looks like this:
36
37__FRAME_BEGIN__:
38	.4byte	.LLCIE1	/ Length of Common Information Entry
39.LSCIE1:
40	.4byte	0x0	/ CIE Identifier Tag
41	.byte	0x1	/ CIE Version
42	.byte	0x0	/ CIE Augmentation (none)
43	.byte	0x1	/ ULEB128 0x1 (CIE Code Alignment Factor)
44	.byte	0x7c	/ SLEB128 -4 (CIE Data Alignment Factor)
45	.byte	0x8	/ CIE RA Column
46	.byte	0xc	/ DW_CFA_def_cfa
47	.byte	0x4	/ ULEB128 0x4
48	.byte	0x4	/ ULEB128 0x4
49	.byte	0x88	/ DW_CFA_offset, column 0x8
50	.byte	0x1	/ ULEB128 0x1
51	.align 4
52.LECIE1:
53	.set	.LLCIE1,.LECIE1-.LSCIE1	/ CIE Length Symbol
54	.4byte	.LLFDE1	/ FDE Length
55.LSFDE1:
56	.4byte	.LSFDE1-__FRAME_BEGIN__	/ FDE CIE offset
57	.4byte	.LFB1	/ FDE initial location
58	.4byte	.LFE1-.LFB1	/ FDE address range
59	.byte	0x4	/ DW_CFA_advance_loc4
60	.4byte	.LCFI0-.LFB1
61	.byte	0xe	/ DW_CFA_def_cfa_offset
62	.byte	0x8	/ ULEB128 0x8
63	.byte	0x85	/ DW_CFA_offset, column 0x5
64	.byte	0x2	/ ULEB128 0x2
65	.byte	0x4	/ DW_CFA_advance_loc4
66	.4byte	.LCFI1-.LCFI0
67	.byte	0xd	/ DW_CFA_def_cfa_register
68	.byte	0x5	/ ULEB128 0x5
69	.byte	0x4	/ DW_CFA_advance_loc4
70	.4byte	.LCFI2-.LCFI1
71	.byte	0x2e	/ DW_CFA_GNU_args_size
72	.byte	0x4	/ ULEB128 0x4
73	.byte	0x4	/ DW_CFA_advance_loc4
74	.4byte	.LCFI3-.LCFI2
75	.byte	0x2e	/ DW_CFA_GNU_args_size
76	.byte	0x0	/ ULEB128 0x0
77	.align 4
78.LEFDE1:
79	.set	.LLFDE1,.LEFDE1-.LSFDE1	/ FDE Length Symbol
80
81   The immediate issue we can address in the assembler is the
82   DW_CFA_advance_loc4 followed by a four byte value.  The value is
83   the difference of two addresses in the function.  Since gcc does
84   not know this value, it always uses four bytes.  We will know the
85   value at the end of assembly, so we can do better.  */
86
87static int eh_frame_code_alignment PARAMS ((void));
88
89/* Get the code alignment factor from the CIE.  */
90
91static int
92eh_frame_code_alignment ()
93{
94  static int code_alignment;
95  segT current_seg;
96  subsegT current_subseg;
97  fragS *f;
98  fixS *fix;
99  int offset;
100  char augmentation[10];
101  int iaug;
102
103  if (code_alignment != 0)
104    return code_alignment;
105
106  /* We should find the CIE at the start of the .eh_frame section.  */
107
108  current_seg = now_seg;
109  current_subseg = now_subseg;
110  subseg_new (".eh_frame", 0);
111#if defined (BFD_ASSEMBLER) || defined (MANY_SEGMENTS)
112  f = seg_info (now_seg)->frchainP->frch_root;
113#else
114  f = frchain_now->frch_root;
115#endif
116#ifdef BFD_ASSEMBLER
117  fix = seg_info (now_seg)->frchainP->fix_root;
118#else
119  fix = *seg_fix_rootP;
120#endif
121  subseg_set (current_seg, current_subseg);
122
123  /* Look through the frags of the section to find the code alignment.  */
124
125  /* First make sure that the CIE Identifier Tag is 0.  */
126
127  offset = 4;
128  while (f != NULL && offset >= f->fr_fix)
129    {
130      offset -= f->fr_fix;
131      f = f->fr_next;
132    }
133  if (f == NULL
134      || f->fr_fix - offset < 4
135      || f->fr_literal[offset] != 0
136      || f->fr_literal[offset + 1] != 0
137      || f->fr_literal[offset + 2] != 0
138      || f->fr_literal[offset + 3] != 0)
139    {
140      code_alignment = -1;
141      return -1;
142    }
143
144  /* Next make sure the CIE version number is 1.  */
145
146  offset += 4;
147  while (f != NULL && offset >= f->fr_fix)
148    {
149      offset -= f->fr_fix;
150      f = f->fr_next;
151    }
152  if (f == NULL
153      || f->fr_fix - offset < 1
154      || f->fr_literal[offset] != 1)
155    {
156      code_alignment = -1;
157      return -1;
158    }
159
160  /* Skip the augmentation (a null terminated string).  */
161
162  iaug = 0;
163  ++offset;
164  while (1)
165    {
166      while (f != NULL && offset >= f->fr_fix)
167	{
168	  offset -= f->fr_fix;
169	  f = f->fr_next;
170	}
171      if (f == NULL)
172	{
173	  code_alignment = -1;
174	  return -1;
175	}
176      while (offset < f->fr_fix && f->fr_literal[offset] != '\0')
177	{
178	  if ((size_t) iaug < (sizeof augmentation) - 1)
179	    {
180	      augmentation[iaug] = f->fr_literal[offset];
181	      ++iaug;
182	    }
183	  ++offset;
184	}
185      if (offset < f->fr_fix)
186	break;
187    }
188  ++offset;
189  while (f != NULL && offset >= f->fr_fix)
190    {
191      offset -= f->fr_fix;
192      f = f->fr_next;
193    }
194  if (f == NULL)
195    {
196      code_alignment = -1;
197      return -1;
198    }
199
200  augmentation[iaug] = '\0';
201  if (augmentation[0] == '\0')
202    {
203      /* No augmentation.  */
204    }
205  else if (strcmp (augmentation, "eh") == 0)
206    {
207      /* We have to skip a pointer.  Unfortunately, we don't know how
208	 large it is.  We find out by looking for a matching fixup.  */
209      while (fix != NULL
210	     && (fix->fx_frag != f || fix->fx_where != offset))
211	fix = fix->fx_next;
212      if (fix == NULL)
213	offset += 4;
214      else
215	offset += fix->fx_size;
216      while (f != NULL && offset >= f->fr_fix)
217	{
218	  offset -= f->fr_fix;
219	  f = f->fr_next;
220	}
221      if (f == NULL)
222	{
223	  code_alignment = -1;
224	  return -1;
225	}
226    }
227  else
228    {
229      code_alignment = -1;
230      return -1;
231    }
232
233  /* We're now at the code alignment factor, which is a ULEB128.  If
234     it isn't a single byte, forget it.  */
235
236  code_alignment = f->fr_literal[offset] & 0xff;
237  if ((code_alignment & 0x80) != 0 || code_alignment == 0)
238    {
239      code_alignment = -1;
240      return -1;
241    }
242
243  return code_alignment;
244}
245
246/* This function is called from emit_expr.  It looks for cases which
247   we can optimize.
248
249   Rather than try to parse all this information as we read it, we
250   look for a single byte DW_CFA_advance_loc4 followed by a 4 byte
251   difference.  We turn that into a rs_cfa_advance frag, and handle
252   those frags at the end of the assembly.  If the gcc output changes
253   somewhat, this optimization may stop working.
254
255   This function returns non-zero if it handled the expression and
256   emit_expr should not do anything, or zero otherwise.  It can also
257   change *EXP and *PNBYTES.  */
258
259int
260check_eh_frame (exp, pnbytes)
261     expressionS *exp;
262     unsigned int *pnbytes;
263{
264  static int saw_advance_loc4;
265  static fragS *loc4_frag;
266  static int loc4_fix;
267
268  if (flag_traditional_format)
269    {
270      /* Don't optimize.  */
271    }
272  else if (strcmp (segment_name (now_seg), ".eh_frame") != 0)
273    saw_advance_loc4 = 0;
274  else if (*pnbytes == 1
275	   && exp->X_op == O_constant
276	   && exp->X_add_number == DW_CFA_advance_loc4)
277    {
278      /* This might be a DW_CFA_advance_loc4.  Record the frag and the
279         position within the frag, so that we can change it later.  */
280      saw_advance_loc4 = 1;
281      frag_grow (1);
282      loc4_frag = frag_now;
283      loc4_fix = frag_now_fix ();
284    }
285  else if (saw_advance_loc4
286	   && *pnbytes == 4
287	   && exp->X_op == O_constant)
288    {
289      int ca;
290
291      /* This is a case which we can optimize.  The two symbols being
292         subtracted were in the same frag and the expression was
293         reduced to a constant.  We can do the optimization entirely
294         in this function.  */
295
296      saw_advance_loc4 = 0;
297
298      ca = eh_frame_code_alignment ();
299      if (ca < 0)
300	{
301	  /* Don't optimize.  */
302	}
303      else if (exp->X_add_number % ca == 0
304	       && exp->X_add_number / ca < 0x40)
305	{
306	  loc4_frag->fr_literal[loc4_fix]
307	    = DW_CFA_advance_loc | (exp->X_add_number / ca);
308	  /* No more bytes needed.  */
309	  return 1;
310	}
311      else if (exp->X_add_number < 0x100)
312	{
313	  loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc1;
314	  *pnbytes = 1;
315	}
316      else if (exp->X_add_number < 0x10000)
317	{
318	  loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc2;
319	  *pnbytes = 2;
320	}
321    }
322  else if (saw_advance_loc4
323	   && *pnbytes == 4
324	   && exp->X_op == O_subtract)
325    {
326
327      /* This is a case we can optimize.  The expression was not
328         reduced, so we can not finish the optimization until the end
329         of the assembly.  We set up a variant frag which we handle
330         later.  */
331
332      saw_advance_loc4 = 0;
333
334      frag_var (rs_cfa, 4, 0, 0, make_expr_symbol (exp),
335		loc4_fix, (char *) loc4_frag);
336
337      return 1;
338    }
339  else
340    saw_advance_loc4 = 0;
341
342  return 0;
343}
344
345/* The function estimates the size of a rs_cfa variant frag based on
346   the current values of the symbols.  It is called before the
347   relaxation loop.  We set fr_subtype to the expected length.  */
348
349int
350eh_frame_estimate_size_before_relax (frag)
351     fragS *frag;
352{
353  int ca;
354  offsetT diff;
355  int ret;
356
357  ca = eh_frame_code_alignment ();
358  diff = resolve_symbol_value (frag->fr_symbol, 0);
359
360  if (ca < 0)
361    ret = 4;
362  else if (diff % ca == 0 && diff / ca < 0x40)
363    ret = 0;
364  else if (diff < 0x100)
365    ret = 1;
366  else if (diff < 0x10000)
367    ret = 2;
368  else
369    ret = 4;
370
371  frag->fr_subtype = ret;
372
373  return ret;
374}
375
376/* This function relaxes a rs_cfa variant frag based on the current
377   values of the symbols.  fr_subtype is the current length of the
378   frag.  This returns the change in frag length.  */
379
380int
381eh_frame_relax_frag (frag)
382     fragS *frag;
383{
384  int oldsize, newsize;
385
386  oldsize = frag->fr_subtype;
387  newsize = eh_frame_estimate_size_before_relax (frag);
388  return newsize - oldsize;
389}
390
391/* This function converts a rs_cfa variant frag into a normal fill
392   frag.  This is called after all relaxation has been done.
393   fr_subtype will be the desired length of the frag.  */
394
395void
396eh_frame_convert_frag (frag)
397     fragS *frag;
398{
399  offsetT diff;
400  fragS *loc4_frag;
401  int loc4_fix;
402
403  loc4_frag = (fragS *) frag->fr_opcode;
404  loc4_fix = (int) frag->fr_offset;
405
406  diff = resolve_symbol_value (frag->fr_symbol, 1);
407
408  if (frag->fr_subtype == 0)
409    {
410      int ca;
411
412      ca = eh_frame_code_alignment ();
413      assert (ca > 0 && diff % ca == 0 && diff / ca < 0x40);
414      loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc | (diff / ca);
415    }
416  else if (frag->fr_subtype == 1)
417    {
418      assert (diff < 0x100);
419      loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc1;
420      frag->fr_literal[frag->fr_fix] = diff;
421    }
422  else if (frag->fr_subtype == 2)
423    {
424      assert (diff < 0x10000);
425      loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc2;
426      md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 2);
427    }
428  else
429    md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 4);
430
431  frag->fr_fix += frag->fr_subtype;
432  frag->fr_type = rs_fill;
433  frag->fr_offset = 0;
434}
435