ROSE  0.9.6a
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PeImportDirectory.C
Go to the documentation of this file.
1 /* PE Import Directory. A PE Import Section is a list of PE Import Directories. */
2 #include "sage3basic.h"
3 #include "MemoryMap.h"
4 
17 void
18 SgAsmPEImportDirectory::ctor(SgAsmPEImportSection *section, const std::string &dll_name)
19 {
20  ROSE_ASSERT(section!=NULL); /* needed for parsing */
21  section->add_import_directory(this);
22  p_time = time(NULL);
23 
24  p_dll_name = new SgAsmBasicString(dll_name);
25  p_dll_name->set_parent(this);
26 
28  p_imports->set_parent(this);
29 }
30 
34 size_t
36 {
37  SgAsmPEFileHeader *fhdr = SageInterface::getEnclosingNode<SgAsmPEFileHeader>(this);
38  assert(fhdr!=NULL);
39  return (p_imports->get_vector().size() + 1) * fhdr->get_word_size();
40 }
41 
44 int
46 {
47  const SgAsmPEImportItemPtrList &imports = get_imports()->get_vector();
48  if (hint>=0 && (size_t)hint<imports.size() && imports[hint]==item)
49  return hint;
50  for (size_t i=0; i<imports.size(); ++i) {
51  if (imports[i]==item)
52  return i;
53  }
54  return -1;
55 }
56 
66 size_t
68 {
69  size_t retval = 0;
70  const SgAsmPEImportItemPtrList &imports = get_imports()->get_vector();
71  for (SgAsmPEImportItemPtrList::const_iterator ii=imports.begin(); ii!=imports.end(); ++ii) {
72  SgAsmPEImportItem *import = *ii;
73  if (!import->get_by_ordinal() && import->get_hintname_rva().get_rva()!=0 && import->get_hintname_nalloc()>0) {
74  size_t nbytes = std::min(import->get_hintname_nalloc(), import->hintname_required_size());
75  extent.insert(Extent(import->get_hintname_rva().get_va(), nbytes));
76  ++retval;
77  }
78  }
79  return retval;
80 }
81 
87 {
88  SgAsmPEFileHeader *fhdr = SageInterface::getEnclosingNode<SgAsmPEFileHeader>(this);
89  ROSE_ASSERT(fhdr!=NULL);
90 
91  /* Read the import directory from disk via loader map. */
92  PEImportDirectory_disk disk, zero;
93  memset(&zero, 0, sizeof zero);
94  size_t nread = fhdr->get_loader_map()->read(&disk, idir_va, sizeof disk);
95  if (nread!=sizeof disk) {
96  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory::parse: short read (%zu byte%s) of directory "
97  "at va 0x%08"PRIx64" (needed %zu bytes)\n",
98  nread, 1==nread?"":"s", idir_va, sizeof disk);
99  memset(&disk, 0, sizeof disk);
100  }
101 
102  /* An all-zero entry marks the end of the list. In this case return null. */
103  if (!memcmp(&disk, &zero, sizeof zero))
104  return NULL;
105 
111 
112  /* Bind RVAs to best sections */
113  p_ilt_rva.bind(fhdr);
114  p_dll_name_rva.bind(fhdr);
115  p_iat_rva.bind(fhdr);
116 
117  /* Library name. The PE format specification does not require the name to be contained in the import section, but we issue
118  * a warning because ROSE may have problems allocating new space for the name if it's changed. */
119  ROSE_ASSERT(p_dll_name!=NULL);
120  std::string dll_name;
121  if (0==p_dll_name_rva.get_section()) {
122  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory::parse: import directory at va 0x%08"PRIx64
123  " has bad DLL name rva %s\n", idir_va, p_dll_name_rva.to_string().c_str());
124  } else {
125  try {
127  } catch (const MemoryMap::NotMapped &e) {
128  if (SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory::parse: short read of dll name at rva %s for "
129  " import directory at va 0x%08"PRIx64"\n",
130  p_dll_name_rva.to_string().c_str(), idir_va) &&
131  e.map) {
132  fprintf(stderr, " Memory map in effect at time of error is:\n");
133  e.map->dump(stderr, " ");
134  }
135  }
136  }
137  p_dll_name->set_string(dll_name);
138  p_dll_name_nalloc = dll_name.size() + 1;
139 
140  parse_ilt_iat(p_ilt_rva, false);
142 
143  parse_ilt_iat(p_iat_rva, false); // IAT initially is a copy of the ILT (i.e., objects are not bound to addresses yet)
145 
146  return this;
147 }
148 
158 void
159 SgAsmPEImportDirectory::parse_ilt_iat(const rose_rva_t &table_start, bool assume_bound)
160 {
161  SgAsmPEImportSection *isec = SageInterface::getEnclosingNode<SgAsmPEImportSection>(this);
162  assert(isec!=NULL);
164  assert(fhdr!=NULL);
165  assert(fhdr->get_word_size() <= sizeof(uint64_t));
166  assert(get_imports()!=NULL);
168  bool processing_iat = !imports.empty(); // we always process the ILT first (but it might be empty)
169 
170  if (0==table_start.get_rva())
171  return; // no ILT/IAT present
172 
173 
174  rose_addr_t entry_va=table_start.get_va(), entry_size=fhdr->get_word_size();
175  uint64_t by_ordinal_bit = 1ull << (8*entry_size-1);
176 
177  for (size_t idx=0; 1; ++idx, entry_va+=entry_size) {
178  /* Read the 32- or 64-bit ILT/IAT entry from proces mapped memory. */
179  uint64_t entry_word = 0;
180  try {
181  isec->read_content(fhdr->get_loader_map(), entry_va, &entry_word, entry_size);
182  } catch (const MemoryMap::NotMapped &e) {
183  if (SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at VA 0x%08"PRIx64
184  ": table entry is outside mapped memory\n", idx, entry_va) && e.map) {
185  fprintf(stderr, " Memory map in effect at time of error:\n");
186  e.map->dump(stderr, " ");
187  }
188  }
189  entry_word = ByteOrder::le_to_host(entry_word);
190 
191  /* ILT/IAT are to be terminated by a zero word. However, the length of the IAT is required to be the same as the
192  * length of the ILT. Handle three cases here:
193  * (1) Termination of the ILT by a zero entry; stop now
194  * (2) Premature termination of the IAT; continue processing
195  * (3) Missing (or late) termination of the IAT; stop now */
196  if (0==entry_word) {
197  if (idx<imports.size()) {
198  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: IAT entry #%zu at va 0x%08"PRIx64
199  ": IAT zero termination occurs before end of corresponding ILT;"
200  " %scontinuing to read IAT entries.\n", idx, entry_va,
201  (assume_bound?"":"assuming this is a bound null address and "));
202  assert(idx<imports.size());
203  imports[idx]->set_bound_rva(rose_rva_t(entry_word).bind(fhdr));
204  continue;
205  } else {
206  break;
207  }
208  } else if (processing_iat && idx>=imports.size()) {
209  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: IAT entry #%zu at va 0x%08"PRIx64
210  ": IAT extends beyond end of corresponding ILT (it is not zero terminated)"
211  "; forcibly terminating here\n", idx, entry_va);
212  break;
213  }
214 
215  /* Create the new table entry if necessary. */
216  bool entry_existed = true;
217  if (idx>=imports.size()) {
218  assert(idx==imports.size());
219  new SgAsmPEImportItem(get_imports()); // adds new item to imports list
220  entry_existed = false;
221  }
222  SgAsmPEImportItem *import_item = imports[idx];
223 
224  if (assume_bound) {
225  /* The entry word is a virtual address. */
226  if (entry_existed && import_item->get_bound_rva().get_rva()>0 &&
227  import_item->get_bound_rva().get_rva()!=entry_word) {
228  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at va 0x%08"PRIx64
229  ": bound address 0x%08"PRIx64" conflicts with bound address already"
230  " discovered 0x%08"PRIx64" (using new value)\n", idx, entry_va, entry_word,
231  import_item->get_bound_rva().get_rva());
232  }
233  import_item->set_bound_rva(rose_rva_t(entry_word).bind(fhdr));
234  } else if (0!=(entry_word & by_ordinal_bit)) {
235  /* The entry is an ordinal number. */
236  if (entry_existed && import_item->get_ordinal()!=0 && import_item->get_ordinal()!=(entry_word & 0xffff)) {
237  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at va 0x%08"PRIx64
238  ": ordinal %"PRIu64" conflicts with ordinal already discovered %u"
239  " (assuming this is a bound address)\n", idx, entry_va, (entry_word&0xffff),
240  import_item->get_ordinal());
241  import_item->set_bound_rva(rose_rva_t(entry_word).bind(fhdr));
242  } else {
243  import_item->set_by_ordinal(true);
244  import_item->set_ordinal(entry_word & 0xffff);
245  if (0!=(entry_word & ~(by_ordinal_bit | 0xffff))) {
246  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at va 0x%08"PRIx64
247  ": contains extra bits in violation of PE specification"
248  " (extra bits removed)\n", idx, entry_va);
249  }
250  }
251  } else {
252  /* The entry word points to a Hint/Name pair: a 2-byte, little endian hint, followed by a NUL-terminated string,
253  * followed by an optional extra byte to make the total length a multiple of two. */
254  if (entry_existed && import_item->get_by_ordinal()) {
255  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at va 0x%08"PRIx64
256  ": entry type \"hint/name\" conflicts with entry type already discovered"
257  " \"ordinal\" (assuming this is a bound address)\n", idx, entry_va);
258  import_item->set_bound_rva(rose_rva_t(entry_word).bind(fhdr));
259  } else if (entry_existed && import_item->get_hintname_rva().get_rva()>0 &&
260  import_item->get_hintname_rva().get_rva()!=entry_word) {
261  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at va 0x%08"PRIx64
262  ": hint/name rva 0x%08"PRIx64" conflicts with hint/name rva already"
263  " discovered 0x%08"PRIx64" (assuming this is a bound address)\n",
264  idx, entry_va, entry_word, import_item->get_hintname_rva().get_rva());
265  import_item->set_bound_rva(rose_rva_t(entry_word).bind(fhdr));
266  } else {
267  import_item->set_by_ordinal(false);
268  import_item->set_hintname_rva(rose_rva_t(entry_word).bind(fhdr));
269  import_item->set_hintname_nalloc(0); // for now, will adjust after we read it
270  rose_addr_t entry_word_va = entry_word + fhdr->get_base_va();
271  uint16_t hint;
272  try {
273  isec->read_content(fhdr->get_loader_map(), entry_word_va, &hint, sizeof hint);
274  } catch (const MemoryMap::NotMapped &e) {
275  import_item->set_hint(0);
276  import_item->get_name()->set_string("");
277  if (SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at va 0x%08"PRIx64
278  ": points to unmapped Hint/Name pair at va 0x%08"PRIx64
279  " (when reading hint)\n", idx, entry_va, entry_word_va) &&
280  e.map) {
281  fprintf(stderr, " Memory map in effect at time of error:\n");
282  e.map->dump(stderr, " ");
283  }
284  continue; // to next ILT/IAT entry
285  }
286  hint = ByteOrder::le_to_host(hint);
287  import_item->set_hint(hint);
288 
289  std::string s;
290  try {
291  s = isec->read_content_str(fhdr->get_loader_map(), entry_word_va+2);
292  } catch (const MemoryMap::NotMapped &e) {
293  import_item->get_name()->set_string("");
294  if (SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at va 0x%08"PRIx64
295  ": points to an unmapped Hint/Name pair at va 0x%08"PRIx64
296  " (when reading name)\n", idx, entry_va, entry_word_va) &&
297  e.map) {
298  fprintf(stderr, " Memory map in effect at time of error:\n");
299  e.map->dump(stderr, " ");
300  }
301  continue; // to next ILT/IAT entry
302  }
303  import_item->get_name()->set_string(s);
304 
305  if ((s.size()+1) % 2) {
306  uint8_t byte = 0;
307  try {
308  isec->read_content(fhdr->get_loader_map(), entry_word_va+2+s.size()+1, &byte, 1);
309  } catch (const MemoryMap::NotMapped &e) {
310  if (SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at va 0x%08"PRIx64
311  ": points to an unmapped Hint/Name pair at va 0x%08"PRIx64
312  " (when reading pad byte)\n", idx, entry_va, entry_word_va) &&
313  e.map) {
314  fprintf(stderr, " Memory map in effect at time of error:\n");
315  e.map->dump(stderr, " ");
316  }
317  continue; // to next ILT/IAT entry
318  }
319  if (byte) {
320  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirctory: ILT/IAT entry #%zu at va 0x%08"PRIx64
321  ": has a non-zero padding byte: 0x%02x\n", idx, entry_va, byte);
322  }
323  }
324  import_item->set_hintname_nalloc(import_item->hintname_required_size());
325  }
326  }
327  }
328 }
329 
341 void
342 SgAsmPEImportDirectory::unparse_ilt_iat(std::ostream &f, const rose_rva_t &table_start, bool assume_bound,
343  size_t tablesize) const
344 {
345  SgAsmPEImportSection *isec = SageInterface::getEnclosingNode<SgAsmPEImportSection>(this);
346  assert(isec!=NULL);
348  assert(fhdr!=NULL);
349  assert(fhdr->get_word_size() <= sizeof(uint64_t));
350  const SgAsmPEImportItemPtrList &imports = get_imports()->get_vector();
351  size_t entry_size = fhdr->get_word_size();
352  uint64_t by_ordinal_bit = 1ull << (8*entry_size-1);
353 
354  if (0==table_start.get_rva() || imports.empty())
355  return;
356  if (!table_start.get_section()) {
357  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT: table is not associated with a section: %s\n",
358  table_start.to_string().c_str());
359  return;
360  }
361 
362  /* Must we limit how many entries are written? Don't forget the zero terminator. */
363  size_t nelmts = std::min(tablesize/entry_size, imports.size()+1);
364  if (nelmts<imports.size()+1) {
365  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT: table at 0x%08"PRIx64" is truncated from %zu to"
366  " %zu entries (inc. zero terminator) due to allocation constraints.\n",
367  table_start.to_string().c_str(), imports.size()+1, nelmts);
368  }
369 
370  rose_rva_t entry_rva = table_start;
371  for (size_t idx=0; idx<nelmts/*including zero terminator*/; ++idx, entry_rva.increment(entry_size)) {
372  uint64_t entry_word = 0;
373 
374  /* Write the zero terminator? */
375  if (idx+1==nelmts) {
376  uint64_t zero = 0;
377  entry_rva.get_section()->write(f, entry_rva.get_rel(), entry_size, &zero);
378  break;
379  }
380 
381  rose_rva_t hn_rva = imports[idx]->get_hintname_rva();
382 
383  /* Build the IAT/ILT entry */
384  if (assume_bound) {
385  entry_word = imports[idx]->get_bound_rva().get_rva();
386  } else if (imports[idx]->get_by_ordinal()) {
387  if (0!=(imports[idx]->get_ordinal() & ~0xffff)) {
388  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at rva 0x%08"PRIx64
389  ": ordinal is out of bounds: %u (truncated to 16 bits)\n",
390  idx, entry_rva.get_rva(), imports[idx]->get_ordinal());
391  }
392  entry_word |= by_ordinal_bit | (imports[idx]->get_ordinal() & 0xffff);
393  } else if (0==hn_rva.get_rva() || NULL==hn_rva.get_section()) {
394  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at rva 0x%08"PRIx64
395  ": non-ordinal entry has invalid hint/name pair address %s or is"
396  " not associated with any section.\n",
397  idx, entry_rva.get_rva(), hn_rva.to_string().c_str());
398  entry_word = hn_rva.get_rva(); // best we can do
399  } else {
400  size_t bufsz = std::min(imports[idx]->get_hintname_nalloc(), imports[idx]->hintname_required_size());
401  if (bufsz < imports[idx]->hintname_required_size()) {
402  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at rva 0x%08"PRIx64
403  ": insufficient space (%zu byte%s) allocated for hint/name pair at"
404  " rva %s (need %zu bytes)\n",
405  idx, entry_rva.get_rva(), bufsz, 1==bufsz?"":"s",
406  hn_rva.to_string().c_str(), imports[idx]->hintname_required_size());
407  }
408  if (bufsz>=2) {
409  uint8_t *buf = new uint8_t[bufsz];
410  unsigned hint = imports[idx]->get_hint();
411  std::string name = imports[idx]->get_name()->get_string();
412  if (0!=(hint & ~0xffff)) {
413  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at rva 0x%08"PRIx64
414  ": hint is out of bounds: %u (truncated to 16 bits)\n",
415  idx, entry_rva.get_rva(), hint);
416  }
417  ByteOrder::host_to_le(hint, &hint); // nudge, nudge. Know what I mean?
418  memcpy(buf, &hint, 2);
419  memcpy(buf+2, name.c_str(), std::min(name.size()+1, bufsz-2));
420  if (bufsz>2)
421  buf[bufsz-1] = '\0';
422  hn_rva.get_section()->write(f, hn_rva.get_rel(), bufsz, buf);
423  if (0!=(hn_rva.get_rva() & by_ordinal_bit)) {
424  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at rva 0x%08"PRIx64
425  ": hint/name pair rva 0x%08"PRIx64" has by_ordinal bit set\n",
426  idx, entry_rva.get_rva(), hn_rva.get_rva());
427  }
428  delete[] buf;
429  }
430  entry_word = hn_rva.get_rva() & ~by_ordinal_bit;
431  }
432 
433  /* Write the IAT/ILT entry */
434  if (0==entry_word) {
435  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: ILT/IAT entry #%zu at rva 0x%08"PRIx64
436  ": zero table entry will cause table to be truncated when read\n",
437  idx, entry_rva.get_rva());
438  }
439  uint64_t disk = 0; // we might write only the first few bytes
440  ByteOrder::host_to_le(entry_word, &disk);
441  entry_rva.get_section()->write(f, entry_rva.get_rel(), entry_size, &disk);
442  }
443 }
444 
446 void *
448 {
450  ByteOrder::host_to_le(p_time, &(disk->time));
454  return disk;
455 }
456 
462 size_t
464 {
465  rose_rva_t end_rva = start_rva;
466  const SgAsmPEImportItemPtrList &imports = get_imports()->get_vector();
467 
468  /* Allocate space for the name if it hasn't been allocated already; reallocate space if its allocated in the import
469  * section. Allocate space even if the name is the empty string. */
470  if (0==p_dll_name_rva.get_rva() || p_dll_name_rva.get_section()==end_rva.get_section()) {
471  p_dll_name_nalloc = get_dll_name()->get_string().size() + 1;
472  p_dll_name_rva = end_rva;
473  end_rva.increment(p_dll_name_nalloc);
474  }
475 
476  /* Allocate space for the import lookup table if it hasn't been allocated yet. The table is terminated with a zero
477  * entry (as is the IAT according to the spec even though the IAT size is implied by the ILT size). */
478  if (0==p_ilt_rva.get_rva() || p_ilt_rva.get_section()==end_rva.get_section()) {
479  p_ilt_nalloc = iat_required_size(); // ILT and IAT are always the same size
480  p_ilt_rva = end_rva;
481  end_rva.increment(p_ilt_nalloc);
482  }
483 
484  /* Allocate space for the import address table if it hasn't been allocated yet. Note, the import address table should
485  * usually be allocated explicitly because it is referenced by the program's instructions. */
486  if (0==p_iat_rva.get_rva() || p_iat_rva.get_section()==end_rva.get_section()) {
488  p_iat_rva = end_rva;
489  end_rva.increment(p_iat_nalloc);
490  }
491 
492  /* Allocate space for the Hint/Name pairs that haven't been allocated yet. The ILT and IAT will both point to the same
493  * hint/name pair when the IAT is a copy of the ILT. When the IAT is unparsed as bound addresses, then space for the
494  * hint/name pair is still needed because the ILT still points to it. We allocate space even for hint/name pairs that are
495  * zero and the empty string. We don't reallocate hint/name pairs that are stored in a different file section (so be
496  * careful that you don't increase the length of the name). */
497  for (size_t i=0; i<imports.size(); ++i) {
498  if (!imports[i]->get_by_ordinal() &&
499  (0==imports[i]->get_hintname_rva() || imports[i]->get_hintname_rva().get_section()==end_rva.get_section())) {
500  size_t sz = imports[i]->hintname_required_size();
501  imports[i]->set_hintname_nalloc(sz);
502  imports[i]->set_hintname_rva(end_rva);
503  end_rva.increment(sz);
504  }
505  }
506 
507  return end_rva.get_rva() - start_rva.get_rva();
508 }
509 
510 void
511 SgAsmPEImportDirectory::unparse(std::ostream &f, const SgAsmPEImportSection *section, size_t idx) const
512 {
513  /* The DLL name */
514  if (0==p_dll_name_rva.get_rva() || NULL==p_dll_name_rva.get_section()) {
515  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: DLL name address %s is invalid or not bound to"
516  " any file section\n", p_dll_name_rva.to_string().c_str());
517  } else if (p_dll_name->get_string().size()+1 > p_dll_name_nalloc) {
518  SgAsmPEImportSection::import_mesg("SgAsmPEImportDirectory: insufficient space allocated (%zu byte%s) for DLL name"
519  " (need %zu byte%s)\n", p_dll_name_nalloc, 1==p_dll_name_nalloc?"":"s",
520  p_dll_name->get_string().size()+1, 1==p_dll_name->get_string().size()?"":"s");
521  } else {
522  uint8_t *buf = new uint8_t[p_dll_name_nalloc];
523  memcpy(buf, p_dll_name->get_string().c_str(), p_dll_name->get_string().size());
524  memset(buf+p_dll_name->get_string().size(), 0, p_dll_name_nalloc - p_dll_name->get_string().size());
526  delete[] buf;
527  }
528 
529  /* Import Lookup Table and Import Address Table (and indirectly, Hint/Name entries). According to the PE secification: "The
530  * structure and content of the import address table are identical to those of the import lookup table, until the file is
531  * bound." */
532  if (p_ilt_rva>0)
534  if (p_iat_rva>0)
536 
538  encode(&disk);
539  section->write(f, idx*sizeof disk, sizeof disk, &disk);
540 }
541 
542 
544 void
545 SgAsmPEImportDirectory::dump(FILE *f, const char *prefix, ssize_t idx) const
546 {
547  char p[4096];
548  if (idx>=0) {
549  sprintf(p, "%sPEImportDirectory[%zd].", prefix, idx);
550  } else {
551  sprintf(p, "%sPEImportDirectory.", prefix);
552  }
553 
554  const int w = std::max(1, DUMP_FIELD_WIDTH-(int)strlen(p));
555 
556  fprintf(f, "%s%-*s = %s for %zu bytes", p, w, "dll_name_rva", p_dll_name_rva.to_string().c_str(), p_dll_name_nalloc);
557  if (p_dll_name)
558  fprintf(f, " \"%s\"", p_dll_name->get_string(true).c_str());
559  fputc('\n', f);
560 
561  fprintf(f, "%s%-*s = %lu %s", p, w, "time", (unsigned long)p_time, ctime(&p_time));
562  fprintf(f, "%s%-*s = 0x%08x (%u)\n", p, w, "forwarder_chain", p_forwarder_chain, p_forwarder_chain);
563  fprintf(f, "%s%-*s = %s for %zu bytes\n", p, w, "ilt_rva", p_ilt_rva.to_string().c_str(), p_ilt_nalloc);
564  fprintf(f, "%s%-*s = %s for %zu bytes\n", p, w, "iat_rva", p_iat_rva.to_string().c_str(), p_iat_nalloc);
565  fprintf(f, "%s%-*s = %zu\n", p, w, "nentries", p_imports->get_vector().size());
566 
567  for (size_t i=0; i<p_imports->get_vector().size(); ++i) {
568  SgAsmPEImportItem *import = p_imports->get_vector()[i];
569  import->dump(f, p, i);
570  }
571 }