• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* libunwind - a platform-independent unwind library
2    Copyright (c) 2003-2005 Hewlett-Packard Development Company, L.P.
3         Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
4 
5 This file is part of libunwind.
6 
7 Permission is hereby granted, free of charge, to any person obtaining
8 a copy of this software and associated documentation files (the
9 "Software"), to deal in the Software without restriction, including
10 without limitation the rights to use, copy, modify, merge, publish,
11 distribute, sublicense, and/or sell copies of the Software, and to
12 permit persons to whom the Software is furnished to do so, subject to
13 the following conditions:
14 
15 The above copyright notice and this permission notice shall be
16 included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
25 
26 #ifndef dwarf_h
27 #define dwarf_h
28 
29 #include <libunwind.h>
30 #include <stdatomic.h>
31 
32 struct dwarf_cursor;    /* forward-declaration */
33 struct elf_dyn_info;
34 struct elf_image;
35 struct map_info;
36 
37 #include "dwarf-config.h"
38 
39 #ifdef HAVE_CONFIG_H
40 # include "config.h"
41 #endif
42 
43 #ifndef UNW_REMOTE_ONLY
44   #if defined(HAVE_LINK_H)
45     #include <link.h>
46   #elif defined(HAVE_SYS_LINK_H)
47     #include <sys/link.h>
48   #else
49     #error Could not find <link.h>
50   #endif
51   #if defined(__ANDROID__) && defined(__arm__) && __ANDROID_API__ < 21
52     int dl_iterate_phdr(int (*)(struct dl_phdr_info *, size_t, void *), void *);
53   #endif
54 #endif
55 
56 #include <pthread.h>
57 
58 /* DWARF expression opcodes.  */
59 
60 typedef enum
61   {
62     DW_OP_addr                  = 0x03,
63     DW_OP_deref                 = 0x06,
64     DW_OP_const1u               = 0x08,
65     DW_OP_const1s               = 0x09,
66     DW_OP_const2u               = 0x0a,
67     DW_OP_const2s               = 0x0b,
68     DW_OP_const4u               = 0x0c,
69     DW_OP_const4s               = 0x0d,
70     DW_OP_const8u               = 0x0e,
71     DW_OP_const8s               = 0x0f,
72     DW_OP_constu                = 0x10,
73     DW_OP_consts                = 0x11,
74     DW_OP_dup                   = 0x12,
75     DW_OP_drop                  = 0x13,
76     DW_OP_over                  = 0x14,
77     DW_OP_pick                  = 0x15,
78     DW_OP_swap                  = 0x16,
79     DW_OP_rot                   = 0x17,
80     DW_OP_xderef                = 0x18,
81     DW_OP_abs                   = 0x19,
82     DW_OP_and                   = 0x1a,
83     DW_OP_div                   = 0x1b,
84     DW_OP_minus                 = 0x1c,
85     DW_OP_mod                   = 0x1d,
86     DW_OP_mul                   = 0x1e,
87     DW_OP_neg                   = 0x1f,
88     DW_OP_not                   = 0x20,
89     DW_OP_or                    = 0x21,
90     DW_OP_plus                  = 0x22,
91     DW_OP_plus_uconst           = 0x23,
92     DW_OP_shl                   = 0x24,
93     DW_OP_shr                   = 0x25,
94     DW_OP_shra                  = 0x26,
95     DW_OP_xor                   = 0x27,
96     DW_OP_skip                  = 0x2f,
97     DW_OP_bra                   = 0x28,
98     DW_OP_eq                    = 0x29,
99     DW_OP_ge                    = 0x2a,
100     DW_OP_gt                    = 0x2b,
101     DW_OP_le                    = 0x2c,
102     DW_OP_lt                    = 0x2d,
103     DW_OP_ne                    = 0x2e,
104     DW_OP_lit0                  = 0x30,
105     DW_OP_lit1,  DW_OP_lit2,  DW_OP_lit3,  DW_OP_lit4,  DW_OP_lit5,
106     DW_OP_lit6,  DW_OP_lit7,  DW_OP_lit8,  DW_OP_lit9,  DW_OP_lit10,
107     DW_OP_lit11, DW_OP_lit12, DW_OP_lit13, DW_OP_lit14, DW_OP_lit15,
108     DW_OP_lit16, DW_OP_lit17, DW_OP_lit18, DW_OP_lit19, DW_OP_lit20,
109     DW_OP_lit21, DW_OP_lit22, DW_OP_lit23, DW_OP_lit24, DW_OP_lit25,
110     DW_OP_lit26, DW_OP_lit27, DW_OP_lit28, DW_OP_lit29, DW_OP_lit30,
111     DW_OP_lit31,
112     DW_OP_reg0                  = 0x50,
113     DW_OP_reg1,  DW_OP_reg2,  DW_OP_reg3,  DW_OP_reg4,  DW_OP_reg5,
114     DW_OP_reg6,  DW_OP_reg7,  DW_OP_reg8,  DW_OP_reg9,  DW_OP_reg10,
115     DW_OP_reg11, DW_OP_reg12, DW_OP_reg13, DW_OP_reg14, DW_OP_reg15,
116     DW_OP_reg16, DW_OP_reg17, DW_OP_reg18, DW_OP_reg19, DW_OP_reg20,
117     DW_OP_reg21, DW_OP_reg22, DW_OP_reg23, DW_OP_reg24, DW_OP_reg25,
118     DW_OP_reg26, DW_OP_reg27, DW_OP_reg28, DW_OP_reg29, DW_OP_reg30,
119     DW_OP_reg31,
120     DW_OP_breg0                 = 0x70,
121     DW_OP_breg1,  DW_OP_breg2,  DW_OP_breg3,  DW_OP_breg4,  DW_OP_breg5,
122     DW_OP_breg6,  DW_OP_breg7,  DW_OP_breg8,  DW_OP_breg9,  DW_OP_breg10,
123     DW_OP_breg11, DW_OP_breg12, DW_OP_breg13, DW_OP_breg14, DW_OP_breg15,
124     DW_OP_breg16, DW_OP_breg17, DW_OP_breg18, DW_OP_breg19, DW_OP_breg20,
125     DW_OP_breg21, DW_OP_breg22, DW_OP_breg23, DW_OP_breg24, DW_OP_breg25,
126     DW_OP_breg26, DW_OP_breg27, DW_OP_breg28, DW_OP_breg29, DW_OP_breg30,
127     DW_OP_breg31,
128     DW_OP_regx                  = 0x90,
129     DW_OP_fbreg                 = 0x91,
130     DW_OP_bregx                 = 0x92,
131     DW_OP_piece                 = 0x93,
132     DW_OP_deref_size            = 0x94,
133     DW_OP_xderef_size           = 0x95,
134     DW_OP_nop                   = 0x96,
135     DW_OP_push_object_address   = 0x97,
136     DW_OP_call2                 = 0x98,
137     DW_OP_call4                 = 0x99,
138     DW_OP_call_ref              = 0x9a,
139     DW_OP_lo_user               = 0xe0,
140     DW_OP_hi_user               = 0xff
141   }
142 dwarf_expr_op_t;
143 
144 #define DWARF_CIE_VERSION       3
145 #define DWARF_CIE_VERSION_MAX   4
146 
147 #define DWARF_CFA_OPCODE_MASK   0xc0
148 #define DWARF_CFA_OPERAND_MASK  0x3f
149 
150 typedef enum
151   {
152     DW_CFA_advance_loc          = 0x40,
153     DW_CFA_offset               = 0x80,
154     DW_CFA_restore              = 0xc0,
155     DW_CFA_nop                  = 0x00,
156     DW_CFA_set_loc              = 0x01,
157     DW_CFA_advance_loc1         = 0x02,
158     DW_CFA_advance_loc2         = 0x03,
159     DW_CFA_advance_loc4         = 0x04,
160     DW_CFA_offset_extended      = 0x05,
161     DW_CFA_restore_extended     = 0x06,
162     DW_CFA_undefined            = 0x07,
163     DW_CFA_same_value           = 0x08,
164     DW_CFA_register             = 0x09,
165     DW_CFA_remember_state       = 0x0a,
166     DW_CFA_restore_state        = 0x0b,
167     DW_CFA_def_cfa              = 0x0c,
168     DW_CFA_def_cfa_register     = 0x0d,
169     DW_CFA_def_cfa_offset       = 0x0e,
170     DW_CFA_def_cfa_expression   = 0x0f,
171     DW_CFA_expression           = 0x10,
172     DW_CFA_offset_extended_sf   = 0x11,
173     DW_CFA_def_cfa_sf           = 0x12,
174     DW_CFA_def_cfa_offset_sf    = 0x13,
175     DW_CFA_val_offset           = 0x14,
176     DW_CFA_val_offset_sf        = 0x15,
177     DW_CFA_val_expression       = 0x16,
178     DW_CFA_lo_user              = 0x1c,
179     DW_CFA_MIPS_advance_loc8    = 0x1d,
180     DW_CFA_GNU_window_save      = 0x2d,
181     DW_CFA_GNU_args_size        = 0x2e,
182     DW_CFA_GNU_negative_offset_extended = 0x2f,
183     DW_CFA_hi_user              = 0x3c
184   }
185 dwarf_cfa_t;
186 
187 /* DWARF Pointer-Encoding (PEs).
188 
189    Pointer-Encodings were invented for the GCC exception-handling
190    support for C++, but they represent a rather generic way of
191    describing the format in which an address/pointer is stored and
192    hence we include the definitions here, in the main dwarf.h file.
193    The Pointer-Encoding format is partially documented in Linux Base
194    Spec v1.3 (http://www.linuxbase.org/spec/).  The rest is reverse
195    engineered from GCC.
196 
197 */
198 #define DW_EH_PE_FORMAT_MASK    0x0f    /* format of the encoded value */
199 #define DW_EH_PE_APPL_MASK      0x70    /* how the value is to be applied */
200 /* Flag bit.  If set, the resulting pointer is the address of the word
201    that contains the final address.  */
202 #define DW_EH_PE_indirect       0x80
203 
204 /* Pointer-encoding formats: */
205 #define DW_EH_PE_omit           0xff
206 #define DW_EH_PE_ptr            0x00    /* pointer-sized unsigned value */
207 #define DW_EH_PE_uleb128        0x01    /* unsigned LE base-128 value */
208 #define DW_EH_PE_udata2         0x02    /* unsigned 16-bit value */
209 #define DW_EH_PE_udata4         0x03    /* unsigned 32-bit value */
210 #define DW_EH_PE_udata8         0x04    /* unsigned 64-bit value */
211 #define DW_EH_PE_sleb128        0x09    /* signed LE base-128 value */
212 #define DW_EH_PE_sdata2         0x0a    /* signed 16-bit value */
213 #define DW_EH_PE_sdata4         0x0b    /* signed 32-bit value */
214 #define DW_EH_PE_sdata8         0x0c    /* signed 64-bit value */
215 
216 /* Pointer-encoding application: */
217 #define DW_EH_PE_absptr         0x00    /* absolute value */
218 #define DW_EH_PE_pcrel          0x10    /* rel. to addr. of encoded value */
219 #define DW_EH_PE_textrel        0x20    /* text-relative (GCC-specific???) */
220 #define DW_EH_PE_datarel        0x30    /* data-relative */
221 /* The following are not documented by LSB v1.3, yet they are used by
222    GCC, presumably they aren't documented by LSB since they aren't
223    used on Linux:  */
224 #define DW_EH_PE_funcrel        0x40    /* start-of-procedure-relative */
225 #define DW_EH_PE_aligned        0x50    /* aligned pointer */
226 
227 extern struct mempool dwarf_reg_state_pool;
228 extern struct mempool dwarf_cie_info_pool;
229 
230 typedef enum
231   {
232     DWARF_WHERE_UNDEF,          /* register isn't saved at all */
233     DWARF_WHERE_SAME,           /* register has same value as in prev. frame */
234     DWARF_WHERE_CFAREL,         /* register saved at CFA-relative address */
235     DWARF_WHERE_REG,            /* register saved in another register */
236     DWARF_WHERE_EXPR,           /* register saved */
237     DWARF_WHERE_VAL_EXPR,       /* register has computed value */
238     DWARF_WHERE_VAL,            /* register that is the value */
239   }
240 dwarf_where_t;
241 
242 /* For uniformity, we'd like to treat the CFA save-location like any
243    other register save-location, but this doesn't quite work, because
244    the CFA can be expressed as a (REGISTER,OFFSET) pair.  To handle
245    this, we use two dwarf_save_loc structures to describe the CFA.
246    The first one (CFA_REG_COLUMN), tells us where the CFA is saved.
247    In the case of DWARF_WHERE_EXPR, the CFA is defined by a DWARF
248    location expression whose address is given by member "val".  In the
249    case of DWARF_WHERE_REG, member "val" gives the number of the
250    base-register and the "val" member of DWARF_CFA_OFF_COLUMN gives
251    the offset value.  */
252 #define DWARF_CFA_REG_COLUMN    DWARF_NUM_PRESERVED_REGS
253 #define DWARF_CFA_OFF_COLUMN    (DWARF_NUM_PRESERVED_REGS + 1)
254 
255 typedef struct dwarf_reg_only_state
256   {
257     char where[DWARF_NUM_PRESERVED_REGS + 2];        /* how is the register saved? */
258     unw_word_t val[DWARF_NUM_PRESERVED_REGS + 2];             /* where it's saved */
259   }
260 dwarf_reg_only_state_t;
261 
262 typedef struct dwarf_reg_state
263   {
264     unw_word_t ret_addr_column;	/* which column in rule table represents return address */
265     dwarf_reg_only_state_t reg;
266   }
267 dwarf_reg_state_t;
268 
269 typedef struct dwarf_stackable_reg_state
270   {
271     struct dwarf_stackable_reg_state *next;       /* for rs_stack */
272     dwarf_reg_state_t state;
273   }
274 dwarf_stackable_reg_state_t;
275 
276 typedef struct dwarf_reg_cache_entry
277   {
278     unw_word_t ip;                        /* ip this rs is for */
279     unsigned short coll_chain;  /* used for hash collisions */
280     unsigned short hint;              /* hint for next rs to try (or -1) */
281     unsigned short valid : 1;         /* optional machine-dependent signal info */
282     unsigned short signal_frame : 1;  /* optional machine-dependent signal info */
283   }
284 dwarf_reg_cache_entry_t;
285 
286 typedef struct dwarf_cie_info
287   {
288     unw_word_t cie_instr_start; /* start addr. of CIE "initial_instructions" */
289     unw_word_t cie_instr_end;   /* end addr. of CIE "initial_instructions" */
290     unw_word_t fde_instr_start; /* start addr. of FDE "instructions" */
291     unw_word_t fde_instr_end;   /* end addr. of FDE "instructions" */
292     unw_word_t code_align;      /* code-alignment factor */
293     unw_word_t data_align;      /* data-alignment factor */
294     unw_word_t ret_addr_column; /* column of return-address register */
295     unw_word_t handler;         /* address of personality-routine */
296     uint16_t abi;
297     uint16_t tag;
298     uint8_t fde_encoding;
299     uint8_t lsda_encoding;
300     unsigned int sized_augmentation : 1;
301     unsigned int have_abi_marker : 1;
302     unsigned int signal_frame : 1;
303   }
304 dwarf_cie_info_t;
305 
306 typedef struct dwarf_state_record
307   {
308     unsigned char fde_encoding;
309     unw_word_t args_size;
310 
311     dwarf_reg_state_t rs_initial;       /* reg-state after CIE instructions */
312     dwarf_reg_state_t rs_current;       /* current reg-state */
313   }
314 dwarf_state_record_t;
315 
316 typedef struct dwarf_cursor
317   {
318     void *as_arg;               /* argument to address-space callbacks */
319     unw_addr_space_t as;        /* reference to per-address-space info */
320 
321     unw_word_t cfa;     /* canonical frame address; aka frame-/stack-pointer */
322     unw_word_t ip;              /* instruction pointer */
323     unw_word_t args_size;       /* size of arguments */
324     unw_word_t eh_args[UNW_TDEP_NUM_EH_REGS];
325     unsigned int eh_valid_mask;
326 
327     dwarf_loc_t loc[DWARF_NUM_PRESERVED_REGS];
328 
329     unsigned int stash_frames :1; /* stash frames for fast lookup */
330     unsigned int use_prev_instr :1; /* use previous (= call) or current (= signal) instruction? */
331     unsigned int pi_valid :1;   /* is proc_info valid? */
332     unsigned int pi_is_dynamic :1; /* proc_info found via dynamic proc info? */
333     unw_proc_info_t pi;         /* info about current procedure */
334 
335     short hint; /* faster lookup of the rs cache */
336     short prev_rs;
337     unw_word_t rel_pc; /* pc related to the beginning of the elf, used for addr2line */
338     unw_word_t cached_ip;              /* cached instruction pointer */
339     struct map_info *cached_map; /* memory mapping info associated with cached ip */
340     // allow us set unwind context
341     int index;
342     int reg_sz;
343     unw_word_t ctx[DWARF_NUM_PRESERVED_REGS];
344     unw_word_t fp;     /* frame-pointer */
345   }
346 dwarf_cursor_t;
347 
348 #define DWARF_DEFAULT_LOG_UNW_CACHE_SIZE        7
349 #define DWARF_DEFAULT_UNW_CACHE_SIZE    (1 << DWARF_DEFAULT_LOG_UNW_CACHE_SIZE)
350 
351 #define DWARF_DEFAULT_LOG_UNW_HASH_SIZE (DWARF_DEFAULT_LOG_UNW_CACHE_SIZE + 1)
352 #define DWARF_DEFAULT_UNW_HASH_SIZE     (1 << DWARF_DEFAULT_LOG_UNW_HASH_SIZE)
353 
354 typedef unsigned char unw_hash_index_t;
355 
356 struct dwarf_rs_cache
357   {
358     pthread_mutex_t lock;
359     unsigned short rr_head;    /* index of least-recently allocated rs */
360 
361     unsigned short log_size;
362     unsigned short prev_log_size;
363 
364     /* hash table that maps instruction pointer to rs index: */
365     unsigned short *hash;
366 
367     _Atomic uint32_t generation;        /* generation number */
368 
369     /* rs cache: */
370     dwarf_reg_state_t *buckets;
371     dwarf_reg_cache_entry_t *links;
372 
373     /* default memory, loaded in BSS segment */
374     unsigned short default_hash[DWARF_DEFAULT_UNW_HASH_SIZE];
375     dwarf_reg_state_t default_buckets[DWARF_DEFAULT_UNW_CACHE_SIZE];
376     dwarf_reg_cache_entry_t default_links[DWARF_DEFAULT_UNW_CACHE_SIZE];
377   };
378 
379 /* A list of descriptors for loaded .debug_frame sections.  */
380 
381 struct unw_debug_frame_list
382   {
383     /* The start (inclusive) and end (exclusive) of the described region.  */
384     unw_word_t start;
385     unw_word_t end;
386     /* ELF load offset */
387     unw_word_t load_offset;
388     /* The debug frame itself.  */
389     char *debug_frame;
390     size_t debug_frame_size;
391     /* Index (for binary search).  */
392     struct table_entry *index;
393     size_t index_size;
394     /* Pointer to next descriptor.  */
395     struct unw_debug_frame_list *next;
396   };
397 
398 /* Convenience macros: */
399 #define dwarf_init                      UNW_ARCH_OBJ (dwarf_init)
400 #define dwarf_callback                  UNW_OBJ (dwarf_callback)
401 #define dwarf_find_proc_info            UNW_OBJ (dwarf_find_proc_info)
402 #define dwarf_find_debug_frame          UNW_OBJ (dwarf_find_debug_frame)
403 #define dwarf_search_unwind_table       UNW_OBJ (dwarf_search_unwind_table)
404 #define dwarf_find_unwind_table         UNW_OBJ (dwarf_find_unwind_table)
405 #define dwarf_put_unwind_info           UNW_OBJ (dwarf_put_unwind_info)
406 #define dwarf_put_unwind_info           UNW_OBJ (dwarf_put_unwind_info)
407 #define dwarf_eval_expr                 UNW_OBJ (dwarf_eval_expr)
408 #define dwarf_stack_aligned             UNW_OBJ (dwarf_stack_aligned)
409 #define dwarf_extract_proc_info_from_fde \
410                 UNW_OBJ (dwarf_extract_proc_info_from_fde)
411 #define dwarf_find_save_locs            UNW_OBJ (dwarf_find_save_locs)
412 #define dwarf_make_proc_info            UNW_OBJ (dwarf_make_proc_info)
413 #define dwarf_apply_reg_state           UNW_OBJ (dwarf_apply_reg_state)
414 #define dwarf_reg_states_iterate        UNW_OBJ (dwarf_reg_states_iterate)
415 #define dwarf_read_encoded_pointer      UNW_OBJ (dwarf_read_encoded_pointer)
416 #define dwarf_step                      UNW_OBJ (dwarf_step)
417 #define dwarf_flush_rs_cache            UNW_OBJ (dwarf_flush_rs_cache)
418 
419 extern int dwarf_init (void);
420 #ifndef UNW_REMOTE_ONLY
421 extern int dwarf_callback (struct dl_phdr_info *info, size_t size, void *ptr);
422 extern int dwarf_find_proc_info (unw_addr_space_t as, unw_word_t ip,
423                                  unw_proc_info_t *pi,
424                                  int need_unwind_info, void *arg);
425 #endif /* !UNW_REMOTE_ONLY */
426 extern int dwarf_find_debug_frame (int found, unw_dyn_info_t *di_debug,
427                                    unw_word_t ip, unw_word_t segbase,
428                                    const char* obj_name, unw_word_t start,
429                                    unw_word_t end);
430 extern int dwarf_search_unwind_table (unw_addr_space_t as,
431                                       unw_word_t ip,
432                                       unw_dyn_info_t *di,
433                                       unw_proc_info_t *pi,
434                                       int need_unwind_info, void *arg);
435 /* Add For Cache MAP And ELF */
436 extern int dwarf_find_unwind_table (struct elf_dyn_info *edi, struct elf_image *ei,
437 				    unw_addr_space_t as, char *path,
438 				    unw_word_t segbase, unw_word_t mapoff,
439 				    unw_word_t ip);
440 /* Add For Cache MAP And ELF */
441 extern void dwarf_put_unwind_info (unw_addr_space_t as,
442                                    unw_proc_info_t *pi, void *arg);
443 extern int dwarf_eval_expr (struct dwarf_cursor *c, unw_word_t stack_val, unw_word_t *addr,
444                             unw_word_t len, unw_word_t *valp,
445                             int *is_register);
446 extern int
447 dwarf_stack_aligned(struct dwarf_cursor *c, unw_word_t cfa_addr,
448                     unw_word_t rbp_addr, unw_word_t *offset);
449 
450 extern int dwarf_extract_proc_info_from_fde (unw_addr_space_t as,
451                                              unw_accessors_t *a,
452                                              unw_word_t *fde_addr,
453                                              unw_proc_info_t *pi,
454                                              unw_word_t base,
455                                              int need_unwind_info,
456                                              int is_debug_frame,
457                                              void *arg);
458 extern int dwarf_find_save_locs (struct dwarf_cursor *c);
459 extern int dwarf_make_proc_info (struct dwarf_cursor *c);
460 extern int dwarf_apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs);
461 extern int dwarf_reg_states_iterate (struct dwarf_cursor *c, unw_reg_states_callback cb, void *token);
462 extern int dwarf_read_encoded_pointer (unw_addr_space_t as,
463                                        unw_accessors_t *a,
464                                        unw_word_t *addr,
465                                        unsigned char encoding,
466                                        const unw_proc_info_t *pi,
467                                        unw_word_t *valp, void *arg);
468 extern int dwarf_step (struct dwarf_cursor *c);
469 extern int dwarf_flush_rs_cache (struct dwarf_rs_cache *cache);
470 
471 #endif /* dwarf_h */
472