ROSE  0.9.6a
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ElfErrorFrame.C
Go to the documentation of this file.
1 /* ELF Error Handling Frames (SgAsmElfEHFrameSection and related classes) */
2 
3 #include "sage3basic.h"
4 
5 static const size_t WARNING_LIMIT=10;
6 static size_t nwarnings=0;
7 
9 void
11 {
12  ROSE_ASSERT(ehframe->get_ci_entries()!=NULL);
13  ehframe->get_ci_entries()->get_entries().push_back(this);
14  ROSE_ASSERT(ehframe->get_ci_entries()->get_entries().size()>0);
15  set_parent(ehframe->get_ci_entries());
16 
18  p_fd_entries->set_parent(this);
19 }
20 
23 std::string
25 {
26  SgAsmElfFileHeader *fhdr = ehframe->get_elf_header();
27  ROSE_ASSERT(fhdr!=NULL);
28 
29  /* Allocate worst-case size for results */
30  size_t worst_size = (4+
31  1+
32  get_augmentation_string().size()+1+
33  10+
34  10+
35  10+
37  get_instructions().size()+
38  fhdr->get_word_size());
39  unsigned char *buf = new unsigned char[worst_size];
40 
41  rose_addr_t at = 0;
42  uint32_t u32_disk;
43  unsigned char u8_disk;
44 
45  /* CIE back offset (always zero) */
46  u32_disk=0;
47  memcpy(buf+at, &u32_disk, 4); at+=4;
48 
49  /* Version */
50  u8_disk = get_version();
51  memcpy(buf+at, &u8_disk, 1); at+=1;
52 
53  /* NUL-terminated Augmentation String */
54  size_t sz = get_augmentation_string().size()+1;
55  memcpy(buf+at, get_augmentation_string().c_str(), sz); at+=sz;
56 
57  /* Alignment factors */
58  at = ehframe->write_uleb128(buf, at, get_code_alignment_factor());
59  at = ehframe->write_sleb128(buf, at, get_data_alignment_factor());
60 
61  /* Augmentation data */
62  at = ehframe->write_uleb128(buf, at, get_augmentation_data_length());
63  std::string astr = get_augmentation_string();
64  if (!astr.empty() && astr[0]=='z') {
65  for (size_t i=1; i<astr.size(); i++) {
66  if ('L'==astr[i]) {
67  u8_disk = get_lsda_encoding();
68  buf[at++] = u8_disk;
69  } else if ('P'==astr[i]) {
70  u8_disk = get_prh_encoding();
71  buf[at++] = u8_disk;
72  switch (get_prh_encoding()) {
73  case 0x05:
74  case 0x06:
75  case 0x07:
76  buf[at++] = get_prh_arg();
77  ByteOrder::host_to_le(get_prh_addr(), &u32_disk);
78  memcpy(buf+at, &u32_disk, 4); at+=4;
79  break;
80  default:
81  /* See parser */
82  if (++nwarnings<=WARNING_LIMIT) {
83  fprintf(stderr, "%s:%u: warning: unknown PRH encoding (0x%02x)\n",
84  __FILE__, __LINE__, get_prh_encoding());
86  fprintf(stderr, " (additional frame warnings will be suppressed)\n");
87  }
88  break;
89  }
90  } else if ('R'==astr[i]) {
91  u8_disk = get_addr_encoding();
92  buf[at++] = u8_disk;
93  } else if ('S'==astr[i]) {
94  /* Signal frame; no auxilliary data */
95  } else {
96  ROSE_ASSERT(!"invalid .eh_frame augmentation string");
97  abort();
98  }
99  }
100  }
101 
102  /* Initial instructions */
103  sz = get_instructions().size();
104  if (sz>0) {
105  memcpy(buf+at, &(get_instructions()[0]), sz);
106  at += sz;
107  }
108 
109  std::string retval((char*)buf, at);
110  delete[] buf;
111  return retval;
112 }
113 
115 void
116 SgAsmElfEHFrameEntryCI::dump(FILE *f, const char *prefix, ssize_t idx) const
117 {
118  char p[4096];
119  if (idx>=0) {
120  sprintf(p, "%sCIE[%zd].", prefix, idx);
121  } else {
122  sprintf(p, "%sCIE.", prefix);
123  }
124  const int w = std::max(1, DUMP_FIELD_WIDTH-(int)strlen(p));
125 
126  fprintf(f, "%s%-*s = %d\n", p, w, "version", get_version());
127  fprintf(f, "%s%-*s = \"%s\"\n", p, w, "augStr", get_augmentation_string().c_str());
128  fprintf(f, "%s%-*s = %s\n", p, w, "sig_frame", get_sig_frame()?"yes":"no");
129  fprintf(f, "%s%-*s = 0x%08"PRIx64" (%"PRIu64")\n", p, w, "code_align",
131  fprintf(f, "%s%-*s = 0x%08"PRIx64" (%"PRId64")\n", p, w, "data_align",
133  fprintf(f, "%s%-*s = 0x%08"PRIx64" (%"PRIu64")\n", p, w, "aug_length",
135  fprintf(f, "%s%-*s = %d\n", p, w, "lsda_encoding", get_lsda_encoding());
136  fprintf(f, "%s%-*s = %d\n", p, w, "prh_encoding", get_prh_encoding());
137  if (get_prh_encoding()>=0) {
138  fprintf(f, "%s%-*s = 0x%02x (%u)\n", p, w, "prh_arg", get_prh_arg(), get_prh_arg());
139  fprintf(f, "%s%-*s = 0x%08"PRIx64" (%"PRIu64")\n", p, w, "prh_addr", get_prh_addr(), get_prh_addr());
140  }
141  fprintf(f, "%s%-*s = %d\n", p, w, "addr_encoding", get_addr_encoding());
142  if (get_instructions().size()>0) {
143  fprintf(f, "%s%-*s = 0x%08zx (%zu) bytes\n", p, w, "instructions",
144  get_instructions().size(), get_instructions().size());
145  hexdump(f, 0, std::string(p)+"insns at ", get_instructions());
146  }
147  for (size_t i=0; i<get_fd_entries()->get_entries().size(); i++) {
149  fde->dump(f, p, i);
150  }
151 }
152 
154 void
156 {
157  ROSE_ASSERT(cie->get_fd_entries()!=NULL);
158  cie->get_fd_entries()->get_entries().push_back(this);
159  ROSE_ASSERT(cie->get_fd_entries()->get_entries().size()>0);
160  set_parent(cie->get_fd_entries());
161 }
162 
165 std::string
167 {
168  SgAsmElfFileHeader *fhdr = ehframe->get_elf_header();
169  ROSE_ASSERT(fhdr!=NULL);
170 
171  /* Allocate worst-case size for results */
172  size_t worst_size = 8 + get_augmentation_data().size() + get_instructions().size() + fhdr->get_word_size();
173  unsigned char *buf = new unsigned char[worst_size];
174 
175  size_t sz;
176  rose_addr_t at = 0;
177  uint32_t u32_disk;
178 
179  /* PC Begin (begin_rva) and size */
180  switch (cie->get_addr_encoding()) {
181  case -1: /* No address encoding specified */
182  case 0x01:
183  case 0x03:
184  case 0x1b: {
185  ByteOrder::host_to_le(get_begin_rva().get_rva(), &u32_disk);
186  memcpy(buf+at, &u32_disk, 4); at+=4;
187  ByteOrder::host_to_le(get_size(), &u32_disk);
188  memcpy(buf+at, &u32_disk, 4); at+=4;
189  break;
190  }
191  default:
192  /* See parser */
193  if (++nwarnings<=WARNING_LIMIT) {
194  fprintf(stderr, "%s:%u: warning: unknown FDE address encoding (0x%02x)\n",
195  __FILE__, __LINE__, cie->get_addr_encoding());
197  fprintf(stderr, " (additional frame warnings will be suppressed)\n");
198  }
199  break;
200  }
201 
202  /* Augmentation Data */
203  std::string astr = cie->get_augmentation_string();
204  if (astr.size()>0 && astr[0]=='z') {
205  at = ehframe->write_uleb128(buf, at, get_augmentation_data().size());
206  sz = get_augmentation_data().size();
207  if (sz>0) {
208  memcpy(buf+at, &(get_augmentation_data()[0]), sz);
209  at += sz;
210  }
211  }
212 
213  /* Call frame instructions */
214  sz = get_instructions().size();
215  if (sz>0) {
216  memcpy(buf+at, &(get_instructions()[0]), sz);
217  at += sz;
218  }
219 
220  std::string retval((char*)buf, at);
221  delete[] buf;
222  return retval;
223 }
224 
225 
227 void
228 SgAsmElfEHFrameEntryFD::dump(FILE *f, const char *prefix, ssize_t idx) const
229 {
230  char p[4096];
231  if (idx>=0) {
232  sprintf(p, "%sFDE[%zd].", prefix, idx);
233  } else {
234  sprintf(p, "%sFDE.", prefix);
235  }
236  const int w = std::max(1, DUMP_FIELD_WIDTH-(int)strlen(p));
237 
238  fprintf(f, "%s%-*s = %s\n", p, w, "begin_rva", get_begin_rva().to_string().c_str());
239  fprintf(f, "%s%-*s = 0x%08"PRIx64" (%"PRIu64") bytes\n", p, w, "size", get_size(), get_size());
240  fprintf(f, "%s%-*s = 0x%08zx (%zu) bytes\n", p, w, "aug_data",
241  get_augmentation_data().size(), get_augmentation_data().size());
242  hexdump(f, 0, std::string(p)+"data at ", get_augmentation_data());
243  fprintf(f, "%s%-*s = 0x%08zx (%zu) bytes\n", p, w, "instructions",
244  get_instructions().size(), get_instructions().size());
245  hexdump(f, 0, std::string(p)+"insns at ", get_instructions());
246 }
247 
249 void
251 {
253  p_ci_entries->set_parent(this);
254 }
255 
259 {
262  ROSE_ASSERT(fhdr!=NULL);
263 
264  rose_addr_t record_offset=0;
265  std::map<rose_addr_t, SgAsmElfEHFrameEntryCI*> cies;
266 
267  while (record_offset<get_size()) {
268  rose_addr_t at = record_offset;
269  unsigned char u8_disk;
270  uint32_t u32_disk;
271  uint64_t u64_disk;
272 
273  /* Length or extended length */
274  rose_addr_t length_field_size = 4; /*number of bytes not counted in length*/
275  read_content_local(at, &u32_disk, 4); at += 4;
276  rose_addr_t record_size = disk_to_host(fhdr->get_sex(), u32_disk);
277  if (record_size==0xffffffff) {
278  read_content_local(at, &u64_disk, 8); at += 8;
279  record_size = disk_to_host(fhdr->get_sex(), u64_disk);
280  length_field_size += 8; /*FIXME: it's not entirely clear whether ExtendedLength includes this field*/
281  }
282  if (0==record_size)
283  break;
284 
285  /* Backward offset to CIE record, or zero if this is a CIE record. */
286  read_content_local(at, &u32_disk, 4); at += 4;
287  rose_addr_t cie_back_offset = disk_to_host(fhdr->get_sex(), u32_disk);
288  if (0==cie_back_offset) {
289  /* This is a CIE record */
291  cies[record_offset] = cie;
292 
293  /* Version */
294  uint8_t cie_version;
295  read_content_local(at++, &cie_version, 1);
296  cie->set_version(cie_version);
297 
298  /* Augmentation String */
299  std::string astr = read_content_local_str(at);
300  at += astr.size() + 1;
301  cie->set_augmentation_string(astr);
302 
303  /* Alignment factors */
306 
307  /* Augmentation data length. This is apparently the length of the data described by the Augmentation String plus
308  * the Initial Instructions plus any padding. [RPM 2009-01-15] */
310 
311  /* Augmentation data. The format of the augmentation data in the CIE record is determined by reading the
312  * characters of the augmentation string. */
313  if (!astr.empty() && astr[0]=='z') {
314  for (size_t i=1; i<astr.size(); i++) {
315  if ('L'==astr[i]) {
316  read_content_local(at++, &u8_disk, 1);
317  cie->set_lsda_encoding(u8_disk);
318  } else if ('P'==astr[i]) {
319  /* The first byte is an encoding method which describes the following bytes, which are the address of
320  * a Personality Routine Handler. There appears to be very little documentation about these fields. */
321  read_content_local(at++, &u8_disk, 1);
322  cie->set_prh_encoding(u8_disk);
323  switch (cie->get_prh_encoding()) {
324  case 0x05: /* See Ubuntu 32bit /usr/bin/aptitude */
325  case 0x06: /* See second CIE record for Gentoo-Amd64 /usr/bin/addftinfo */
326  case 0x07: /* See first CIE record for Gentoo-Amd64 /usr/bin/addftinfo */
327  read_content_local(at++, &u8_disk, 1); /* not sure what this is; arg for __gxx_personality_v0? */
328  cie->set_prh_arg(u8_disk);
329  read_content_local(at, &u32_disk, 4); at+=4; /* address of <__gxx_personality_v0@plt> */
330  cie->set_prh_addr(ByteOrder::le_to_host(u32_disk));
331  break;
332  case 0x09: /* *.o file generated by gcc-4.0.x */
333  /* FIXME: Cannot find any info about this entry. Fix SgAsmElfEHFrameSection::parse() if we
334  * ever figure this out. [RPM 2009-09-29] */
335  /*fallthrough*/
336  default: {
337  if (++nwarnings<=WARNING_LIMIT) {
338  fprintf(stderr, "%s:%u: warning: ELF CIE 0x%08"PRIx64" has unknown PRH encoding 0x%02x\n",
339  __FILE__, __LINE__, get_offset()+record_offset, cie->get_prh_encoding());
341  fprintf(stderr, " (additional frame warnings will be suppressed)\n");
342  }
343  break;
344  }
345  }
346  } else if ('R'==astr[i]) {
347  read_content_local(at++, &u8_disk, 1);
348  cie->set_addr_encoding(u8_disk);
349  } else if ('S'==astr[i]) {
350  /* See http://lkml.indiana.edu/hypermail/linux/kernel/0602.3/1144.html and GCC PR #26208*/
351  cie->set_sig_frame(true);
352  } else {
353  /* Some stuff we don't handle yet. Warn about it and don't read anything. */
354  if (++nwarnings<=WARNING_LIMIT) {
355  fprintf(stderr, "%s:%u: warning: ELF CIE 0x%08"PRIx64" has invalid augmentation string \"%s\"\n",
356  __FILE__, __LINE__, get_offset()+record_offset, escapeString(astr).c_str());
358  fprintf(stderr, " (additional frame warnings will be suppressed)\n");
359  }
360  }
361  }
362  }
363 
364  /* Initial instructions. These are apparently included in the augmentation_data_length. The final instructions can
365  * be zero padding (no-op instructions) to bring the record up to a multiple of the word size. */
366  rose_addr_t init_insn_size = (length_field_size + record_size) - (at - record_offset);
367  cie->get_instructions() = read_content_local_ucl(at, init_insn_size);
368  ROSE_ASSERT(cie->get_instructions().size()==init_insn_size);
369 
370  } else {
371  /* This is a FDE record */
372  rose_addr_t cie_offset = record_offset + length_field_size - cie_back_offset;
373  assert(cies.find(cie_offset)!=cies.end());
374  SgAsmElfEHFrameEntryCI *cie = cies[cie_offset];
376 
377  /* PC Begin (begin_rva) and size */
378  switch (cie->get_addr_encoding()) {
379  case -1: /* No address encoding specified */
380  case 0x01:
381  case 0x03:
382  case 0x1b: /* Address doesn't look valid (e.g., 0xfffd74e8) but still four bytes [RPM 2008-01-16]*/
383  {
384  read_content_local(at, &u32_disk, 4); at+=4;
385  fde->set_begin_rva(ByteOrder::le_to_host(u32_disk));
386  read_content_local(at, &u32_disk, 4); at+=4;
387  fde->set_size(ByteOrder::le_to_host(u32_disk));
388  break;
389  }
390  default:
391  fprintf(stderr, "%s:%u: ELF CIE 0x%08"PRIx64", FDE 0x%08"PRIx64": unknown address encoding: 0x%02x\n",
392  __FILE__, __LINE__, get_offset()+cie_offset, get_offset()+record_offset, cie->get_addr_encoding());
393  abort();
394  }
395 
396  /* Augmentation Data */
397  std::string astring = cie->get_augmentation_string();
398  if (astring.size()>0 && astring[0]=='z') {
399  rose_addr_t aug_length = read_content_local_uleb128(&at);
400  fde->get_augmentation_data() = read_content_local_ucl(at, aug_length);
401  at += aug_length;
402  ROSE_ASSERT(fde->get_augmentation_data().size()==aug_length);
403  }
404 
405  /* Call frame instructions */
406  rose_addr_t cf_insn_size = (length_field_size + record_size) - (at - record_offset);
407  fde->get_instructions() = read_content_local_ucl(at, cf_insn_size);
408  ROSE_ASSERT(fde->get_instructions().size()==cf_insn_size);
409  }
410 
411  record_offset += length_field_size + record_size;
412  }
413  return this;
414 }
415 
419 SgAsmElfEHFrameSection::calculate_sizes(size_t *entsize, size_t *required, size_t *optional, size_t *entcount) const
420 {
421  rose_addr_t whole = unparse(NULL);
422  if (entsize)
423  *entsize = 0;
424  if (required)
425  *required = 0;
426  if (optional)
427  *optional = 0;
428  if (entcount)
429  *entcount = 0;
430  return whole;
431 }
432 
434 void
435 SgAsmElfEHFrameSection::unparse(std::ostream &f) const
436 {
437  unparse(&f);
438 }
439 
444 SgAsmElfEHFrameSection::unparse(std::ostream *fp) const
445 {
447  ROSE_ASSERT(fhdr!=NULL);
448 
449  rose_addr_t at=0;
450  uint32_t u32_disk;
451  uint64_t u64_disk;
452 
453  for (size_t i=0; i<get_ci_entries()->get_entries().size(); i++) {
454  rose_addr_t last_cie_offset = at;
456  std::string s = cie->unparse(this);
457  if (s.size()<0xffffffff) {
458  host_to_disk(fhdr->get_sex(), s.size(), &u32_disk);
459  if (fp)
460  write(*fp, at, 4, &u32_disk);
461  at += 4;
462  } else {
463  u32_disk = 0xffffffff;
464  if (fp)
465  write(*fp, at, 4, &u32_disk);
466  at += 4;
467  host_to_disk(fhdr->get_sex(), s.size(), &u64_disk);
468  if (fp)
469  write(*fp, at, 8, &u64_disk);
470  at += 8;
471  }
472  if (fp)
473  write(*fp, at, s);
474  at += s.size();
475 
476  for (size_t j=0; j<cie->get_fd_entries()->get_entries().size(); j++) {
478  std::string s = fde->unparse(this, cie);
479 
480  /* Record size, not counting run-length coded size field, but counting CIE back offset. */
481  rose_addr_t record_size = 4 + s.size();
482  if (record_size<0xffffffff) {
483  host_to_disk(fhdr->get_sex(), record_size, &u32_disk);
484  if (fp)
485  write(*fp, at, 4, &u32_disk);
486  at += 4;
487  } else {
488  u32_disk = 0xffffffff;
489  if (fp)
490  write(*fp, at, 4, &u32_disk);
491  at += 4;
492  host_to_disk(fhdr->get_sex(), record_size, &u64_disk);
493  if (fp)
494  write(*fp, at, 8, &u64_disk);
495  at += 8;
496  }
497 
498  /* CIE back offset. Number of bytes from the beginning of the current CIE record (including the Size fields) to
499  * the beginning of the FDE record (excluding the Size fields but including the CIE back offset). */
500  rose_addr_t cie_back_offset = at - last_cie_offset;
501  host_to_disk(fhdr->get_sex(), cie_back_offset, &u32_disk);
502  if (fp)
503  write(*fp, at, 4, &u32_disk);
504  at += 4;
505 
506  /* The FDE record itself */
507  if (fp)
508  write(*fp, at, s);
509  at += s.size();
510  }
511  }
512 
513  /* Write a zero length to indicate the end of the CIE list */
514  u32_disk = 0;
515  if (fp)
516  write(*fp, at, 4, &u32_disk);
517  at += 4;
518 
519  return at;
520 }
521 
522 /* Print some debugging info */
523 void
524 SgAsmElfEHFrameSection::dump(FILE *f, const char *prefix, ssize_t idx) const
525 {
526  char p[4096];
527  if (idx>=0) {
528  sprintf(p, "%sElfEHFrameSection[%zd].", prefix, idx);
529  } else {
530  sprintf(p, "%sElfEHFrameSection.", prefix);
531  }
532 
533  SgAsmElfSection::dump(f, p, -1);
534  for (size_t i=0; i<get_ci_entries()->get_entries().size(); i++) {
536  cie->dump(f, p, i);
537  }
538 }