ROSE  0.9.6a
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MemoryMap.C
Go to the documentation of this file.
1 #include "sage3basic.h"
2 #include "MemoryMap.h"
3 #include "rose_getline.h"
4 
5 #include <cerrno>
6 #include <fstream>
7 
8 #include <fcntl.h>
9 #include <stdio.h>
10 #ifndef _MSC_VER
11 #include <unistd.h>
12 #include <sys/mman.h>
13 #else
14 #include <io.h>
15  inline void * mmap(void*, size_t length, int, int, int, off_t)
16  {
17  return new char[length];
18  }
19  inline void munmap(void* p_data, size_t)
20  {
21  delete [] (char *)p_data;
22  }
23 #define MAP_FAILED 0
24 #undef TEMP_FAILURE_RETRY
25 #define TEMP_FAILURE_RETRY(expression) expression
26 #define PROT_WRITE 0x02
27 #define F_OK 0
28 #endif
29 
30 std::ostream& operator<<(std::ostream &o, const MemoryMap &x) { x.print(o); return o; }
31 std::ostream& operator<<(std::ostream &o, const MemoryMap::Exception &x) { x.print(o); return o; }
32 std::ostream& operator<<(std::ostream &o, const MemoryMap::Inconsistent &x) { x.print(o); return o; }
33 std::ostream& operator<<(std::ostream &o, const MemoryMap::NotMapped &x) { x.print(o); return o; }
34 std::ostream& operator<<(std::ostream &o, const MemoryMap::NoFreeSpace &x) { x.print(o); return o; }
35 std::ostream& operator<<(std::ostream &o, const MemoryMap::SyntaxError &x) { x.print(o); return o; }
36 std::ostream& operator<<(std::ostream &o, const MemoryMap::Segment &x) { x.print(o); return o; }
37 
38 /******************************************************************************************************************************
39  * Exceptions
40  ******************************************************************************************************************************/
41 
42 std::string
43 MemoryMap::Exception::leader(std::string dflt) const
44 {
45  const char *s = what();
46  return s && *s ? dflt : std::string(s);
47 }
48 
49 std::string
50 MemoryMap::Exception::details(bool verbose) const
51 {
52  std::ostringstream ss;
53  if (verbose) {
54  ss <<"\n";
55  if (map)
56  map->dump(ss, " ");
57  }
58  return ss.str();
59 }
60 
61 void
62 MemoryMap::Exception::print(std::ostream &o, bool verbose) const
63 {
64  o <<leader("problem") <<details(verbose);
65 }
66 
67 void
68 MemoryMap::Inconsistent::print(std::ostream &o, bool verbose) const
69 {
70  o <<leader("inconsistent mapping") <<" for " <<new_range <<" vs. " <<old_range <<details(verbose);
71 
72 }
73 
74 void
75 MemoryMap::NotMapped::print(std::ostream &o, bool verbose) const
76 {
77  o <<leader("no mapping") <<" at va " <<StringUtility::addrToString(va) <<details(verbose);
78 }
79 
80 void
81 MemoryMap::NoFreeSpace::print(std::ostream &o, bool verbose) const
82 {
83  o <<leader("no free space") <<" (nbytes=" <<size <<")" <<details(verbose);
84 }
85 
86 void
87 MemoryMap::SyntaxError::print(std::ostream &o, bool verbose) const
88 {
89  o <<leader("syntax error");
90  if (!filename.empty()) {
91  o <<" at " <<filename <<":" <<linenum;
92  if (colnum>0)
93  o <<"." <<colnum;
94  }
95  o <<details(verbose);
96 }
97 
98 /******************************************************************************************************************************
99  * Buffer methods
100  ******************************************************************************************************************************/
101 
102 std::string
104 {
105  std::string retval;
106  static size_t ncalls = 0;
107 
108  retval += 'a' + (ncalls/(26*26))%26;
109  retval += 'a' + (ncalls/26)%26;
110  retval += 'a' + (ncalls%26);
111  ++ncalls;
112  return retval;
113 }
114 
115 void
116 MemoryMap::Buffer::save(const std::string &filename) const
117 {
118  std::ofstream file(filename.c_str(), std::ofstream::binary);
119 
121  char buf[8192];
122 
123  while (true) {
124  size_t n = read(buf, offset, sizeof buf);
125  file.write(buf, n);
126  if (file.bad() || n<sizeof buf)
127  break;
128  }
129 }
130 
131 bool
133 {
134  uint8_t buf[8192];
135  size_t at = 0;
136  while (at<size()) {
137  size_t n = std::min(size()-at, sizeof buf);
138  size_t nread = read(buf, at, n);
139  if (nread!=n)
140  return false;
141  for (size_t i=0; i<nread; ++i) {
142  if (buf[i]!=0)
143  return false;
144  }
145  at += nread;
146  }
147  return true;
148 }
149 
152 {
153  return BufferPtr(new NullBuffer(size));
154 }
155 
158 {
159  return BufferPtr(new ExternBuffer((uint8_t*)data, size));
160 }
161 
163 MemoryMap::ExternBuffer::create(const void *data, size_t size)
164 {
165  return BufferPtr(new ExternBuffer((const uint8_t*)data, size));
166 }
167 
170 {
171  if (!p_data)
172  return ByteBuffer::create(NULL, size());
173  uint8_t *d = new uint8_t[size()];
174  memcpy(d, p_data, size());
175  return ByteBuffer::create(d, size());
176 }
177 
178 void
180 {
181  assert(!"not implemented");
182  abort();
183 }
184 
185 size_t
186 MemoryMap::ExternBuffer::read(void *buf, size_t offset, size_t nbytes) const
187 {
188  if (!p_data || offset>=size())
189  return 0;
190  size_t n = std::min(nbytes, size()-offset);
191  if (buf)
192  memcpy(buf, (uint8_t*)p_data+offset, n);
193  return n;
194 }
195 
196 size_t
197 MemoryMap::ExternBuffer::write(const void *buf, size_t offset, size_t nbytes)
198 {
199  if (!p_data || is_read_only() || offset>=size())
200  return 0;
201  size_t n = std::min(nbytes, size()-offset);
202  if (buf)
203  memcpy((uint8_t*)p_data+offset, buf, n);
204  return n;
205 }
206 
209 {
210  return BufferPtr(new ByteBuffer((uint8_t*)data, size));
211 }
212 
214 MemoryMap::ByteBuffer::create_from_file(const std::string &filename, size_t offset)
215 {
216  // Open file and seek to starting offset
217  int fd = open(filename.c_str(), O_RDONLY);
218  if (fd<0)
219  return BufferPtr();
220  struct stat sb;
221  if (fstat(fd, &sb)<0) {
222  close(fd);
223  return BufferPtr();
224  }
225  off_t position = lseek(fd, offset, SEEK_SET);
226  if (-1==position || (rose_addr_t)position!=offset) {
227  close(fd);
228  return BufferPtr();
229  }
230 
231  // Read data from file until EOF
232  rose_addr_t size = offset > (size_t)sb.st_size ? 0 : sb.st_size-offset;
233  uint8_t *data = new uint8_t[size];
234  ssize_t n = TEMP_FAILURE_RETRY(::read(fd, data, size));
235  if (-1==n || n!=sb.st_size) {
236  delete[] data;
237  close(fd);
238  return BufferPtr();
239  }
240  close(fd);
241 
242  // Create the buffer
243  return create(data, size);
244 }
245 
248 {
249  AnonymousBuffer *buf = new AnonymousBuffer(size);
250  buf->set_name("anon " + buf->get_name());
251  return BufferPtr(buf);
252 }
253 
256 {
257  if (p_data)
258  return ExternBuffer::clone();
259  return AnonymousBuffer::create(size());
260 }
261 
262 size_t
263 MemoryMap::AnonymousBuffer::read(void *buf, size_t offset, size_t nbytes) const
264 {
265  if (offset>=size())
266  return 0;
267  size_t n = std::min(nbytes, size()-offset);
268  if (buf) {
269  if (p_data) {
270  memcpy(buf, (uint8_t*)p_data+offset, n);
271  } else {
272  memset(buf, 0, n);
273  }
274  }
275  return n;
276 }
277 
278 size_t
279 MemoryMap::AnonymousBuffer::write(const void *buf, size_t offset, size_t nbytes)
280 {
281  if (offset>=size())
282  return 0;
283  size_t n = std::min(nbytes, size()-offset);
284  if (buf) {
285  if (!p_data) {
286  bool all_zero = true;
287  for (size_t i=0; i<n && all_zero; ++i)
288  all_zero = ((uint8_t*)buf)[i] == '\0';
289  if (all_zero)
290  return n;
291  p_data = new uint8_t[size()];
292  memset(p_data, 0, size());
293  }
294  memcpy((uint8_t*)p_data+offset, buf, n);
295  }
296  return n;
297 }
298 
299 const void *
301 {
302  if (!p_data) {
303  p_data = new uint8_t[size()];
304  memset(p_data, 0, size());
305  }
307 }
308 
309 bool
311 {
312  return !p_data || ExternBuffer::is_zero();
313 }
314 
316 MemoryMap::MmapBuffer::create(const std::string &filename, int oflags, int mprot, int mflags)
317 {
318  int fd = open(filename.c_str(), oflags, 0666);
319  if (fd<0)
320  throw Exception(filename + ": " + strerror(errno), NULL);
321  struct stat sb;
322  int status = fstat(fd, &sb);
323  if (status<0)
324  throw Exception(filename + ": " + strerror(errno), NULL);
325  BufferPtr buffer;
326  try {
327  buffer = create(sb.st_size, mprot, mflags, fd, 0);
328  } catch (...) {
329  close(fd);
330  return BufferPtr();
331  }
332  close(fd);
333  return buffer;
334 }
335 
337 MemoryMap::MmapBuffer::create(size_t length, int prot, int flags, int fd, off_t offset)
338 {
339  void *buf = mmap(NULL, length, prot, flags, fd, 0);
340  if (MAP_FAILED==buf)
341  throw Exception(strerror(errno), NULL);
342  return BufferPtr(new MmapBuffer((uint8_t*)buf, length, 0==(flags & PROT_WRITE)));
343 }
344 
346 {
347  if (p_data) {
348  munmap(p_data, p_size);
349  p_data = NULL;
350  }
351 }
352 
353 void
355 {
356  assert(!"cannot resize");
357  abort();
358 }
359 
360 /******************************************************************************************************************************
361  * Segment methods
362  ******************************************************************************************************************************/
363 
364 bool
365 MemoryMap::Segment::check(const Extent &range, rose_addr_t *first_bad_va/*=NULL=*/) const
366 {
367  if (range.empty()) {
368  if (first_bad_va)
369  *first_bad_va = 0;
370  return false;
371  }
372  if (!get_buffer() || 0==get_buffer()->size() || get_buffer_offset() >= get_buffer()->size()) {
373  if (first_bad_va)
374  *first_bad_va = range.first();
375  return false;
376  }
377  if (get_buffer_offset() + range.size() > get_buffer()->size()) {
378  if (first_bad_va)
379  *first_bad_va = range.first() + (get_buffer()->size() - get_buffer_offset());
380  return false;
381  }
382 
383  return true;
384 }
385 
388 {
389  assert(my_range.contains(Extent(va)));
390  return get_buffer_offset() + va - my_range.first();
391 }
392 
393 void
395 {
396  assert(!buffer || n<buffer->size());
397  buffer_offset = n;
398 }
399 
400 std::string
402 {
403  assert(pairings!=NULL);
404  std::string retval;
405  std::string::size_type i = 0;
406  while (i<name.size()) {
407  /* Extract the file name up to the left paren */
408  while (i<name.size() && isspace(name[i])) i++;
409  std::string::size_type fname_start = i;
410  while (i<name.size() && !isspace(name[i]) && !strchr("()+", name[i])) i++;
411  if (i>=name.size() || '('!=name[i] || fname_start==i) {
412  retval += name.substr(fname_start, i-fname_start);
413  break;
414  }
415  std::string fname = name.substr(fname_start, i-fname_start);
416  i++; /*skip over the left paren*/
417  int parens=0;
418 
419  /* Extract name(s) separated from one another by '+' */
420  while (i<name.size() && ')'!=name[i]) {
421  while (i<name.size() && isspace(name[i])) i++;
422  std::string::size_type gname_start = i;
423  while (i<name.size() && '+'!=name[i] && (parens>0 || ')'!=name[i])) {
424  if ('('==name[i]) {
425  parens++;
426  } else if (')'==name[i]) {
427  parens--;
428  }
429  i++;
430  }
431  if (i>gname_start)
432  (*pairings)[fname].insert(name.substr(gname_start, i-gname_start));
433  if (i<name.size() && '+'==name[i]) i++;
434  }
435 
436  /* Skip over right paren and optional space and '+' */
437  if (i<name.size() && ')'==name[i]) i++;
438  while (i<name.size() && isspace(name[i])) i++;
439  if (i<name.size() && '+'==name[i]) i++;
440  }
441  if (i<name.size())
442  retval += name.substr(i);
443  return retval;
444 }
445 
446 void
447 MemoryMap::Segment::set_name(const NamePairings &pairings, const std::string &s1, const std::string &s2)
448 {
449  std::string s;
450  for (NamePairings::const_iterator pi=pairings.begin(); pi!=pairings.end(); ++pi) {
451  s += (s.empty()?"":"+") + pi->first + "(";
452  for (std::set<std::string>::const_iterator si=pi->second.begin(); si!=pi->second.end(); ++si) {
453  if ('('!=s[s.size()-1]) s += "+";
454  s += *si;
455  }
456  s += ')';
457  }
458  if (!s1.empty())
459  s += (s.empty()?"":"+") + s1;
460  if (!s2.empty())
461  s += (s.empty()?"":"+") + s2;
462  set_name(s);
463 }
464 
465 void
467 {
468  if (get_name().empty()) {
469  set_name(other.get_name());
470  } else if (other.get_name().empty() || 0==get_name().compare(other.get_name())) {
471  /*void*/
472  } else {
473  NamePairings pairings;
474  std::string s1 = get_name_pairings(&pairings);
475  std::string s2 = other.get_name_pairings(&pairings);
476  set_name(pairings, s1, s2);
477  }
478 }
479 
480 // RangeMap API
481 void
483 {
484  assert(!my_range.empty());
485 }
486 
487 // RangeMap API
488 void
490 {
491  assert(new_end>my_range.first() && new_end<=my_range.last());
492 }
493 
494 // RangeMap API
495 bool
496 MemoryMap::Segment::merge(const Extent &my_range, const Extent &other_range, const Segment &other)
497 {
498  assert(!my_range.empty() && !other_range.empty());
499 
500 #if 1 // Relaxed version: segments are compatible if they point to the same underlying "char*" buffer
501  if (get_buffer()==NULL || other.get_buffer()==NULL)
502  return false;
503  if (get_buffer()->get_data_ptr()==NULL ||
504  get_buffer()->get_data_ptr()!=other.get_buffer()->get_data_ptr())
505  return false;
506 #else // Strict version: compatible only if segments point to the same MemoryMap::Buffer (this is what we eventually want)
507  if (get_buffer()!=other.get_buffer())
508  return false;
509 #endif
510  if (get_mapperms()!=other.get_mapperms())
511  return false;
512 
513  if (other_range.abuts_lt(my_range)) {
514  if (other.get_buffer_offset(other_range, other_range.last()) + 1 != get_buffer_offset(my_range, my_range.first()))
515  return false;
516  set_buffer_offset(other.get_buffer_offset());
517  } else {
518  assert(other_range.abuts_gt(my_range));
519  if (get_buffer_offset(my_range, my_range.last()) + 1 != other.get_buffer_offset(other_range, other_range.first()))
520  return false;
521  }
522 
523  merge_names(other);
524  return true; // "other" is now a duplicate and will be removed from the map
525 }
526 
529 {
530  Segment right = *this;
531  right.set_buffer_offset(get_buffer_offset() + new_end-range.first());
532  return right;
533 }
534 
535 // does not compare segment names
536 bool
538 {
539  return (get_buffer() == other.get_buffer() &&
540  get_buffer_offset() == other.get_buffer_offset() &&
541  get_mapperms() == other.get_mapperms());
542 }
543 
544 void
545 MemoryMap::Segment::print(std::ostream &o) const
546 {
547  std::string bufname = buffer->get_name();
548  if (bufname.find_first_of(" \t\n()")==std::string::npos)
549  bufname = "buffer " + bufname;
550 
551  o <<(0==(get_mapperms() & MM_PROT_READ) ? "-" : "r")
552  <<(0==(get_mapperms() & MM_PROT_WRITE) ? "-" : "w")
553  <<(0==(get_mapperms() & MM_PROT_EXEC) ? "-" : "x")
554  <<(0==(get_mapperms() & MM_PROT_PRIVATE) ? "-" : "p")
555  <<" at " <<(bufname+std::string(12, ' ')).substr(0, 12)
556  <<" + " <<StringUtility::addrToString(get_buffer_offset());
557 
558  if (!get_name().empty()) {
559  static const size_t limit = 55;
560  std::string name = escapeString(get_name());
561  if (name.size()>limit)
562  name = name.substr(0, limit-3) + "...";
563  o <<" " <<name;
564  }
565 }
566 
567 /******************************************************************************************************************************
568  * MemoryMap methods
569  ******************************************************************************************************************************/
570 
571 void
573 {
574  p_segments.clear();
575 }
576 
577 MemoryMap&
578 MemoryMap::init(const MemoryMap &other, CopyLevel copy_level)
579 {
580  p_segments = other.p_segments;
581  sex = other.sex;
582 
583  switch (copy_level) {
584  case COPY_SHALLOW:
585  // nothing more to do
586  break;
587  case COPY_DEEP:
588  for (Segments::iterator si=p_segments.begin(); si!=p_segments.end(); ++si) {
589  si->second.clear_cow();
590  si->second.set_buffer(si->second.get_buffer()->clone());
591  }
592  break;
593  case COPY_ON_WRITE:
594  for (Segments::iterator si=p_segments.begin(); si!=p_segments.end(); ++si)
595  si->second.set_cow();
596  break;
597  }
598  return *this;
599 }
600 
601 size_t
603 {
604  size_t retval = 0;
605  for (const_iterator si=p_segments.begin(); si!=p_segments.end(); ++si)
606  retval += si->first.size();
607  return retval;
608 }
609 
610 void
611 MemoryMap::insert(const Extent &range, const Segment &segment, bool erase_prior)
612 {
613  Segments::iterator inserted = p_segments.insert(range, segment, erase_prior);
614  if (inserted==p_segments.end()) {
615  // If the insertion failed, then we need to throw an exception. The exception should indicate the lowest address at
616  // which there was a conflict. I.e., the first mapped address above range.first().
618  assert(found!=p_segments.end());
619  assert(range.overlaps(found->first));
620  throw Inconsistent("insertion failed", this, range, segment, found->first, found->second);
621  }
622 }
623 
624 size_t
625 MemoryMap::insert_file(const std::string &filename, rose_addr_t va, bool writable, bool erase_prior, const std::string &sgmtname)
626 {
627  int o_flags = writable ? O_RDWR : O_RDONLY;
628  int m_prot = writable ? (PROT_READ|PROT_WRITE) : PROT_READ;
629  int m_flags = writable ? MAP_SHARED : MAP_PRIVATE;
630  unsigned s_prot = writable ? MM_PROT_RW : MM_PROT_READ;
631 
632  int fd = open(filename.c_str(), o_flags);
633  if (-1==fd)
634  throw Exception(filename + ": " + strerror(errno), NULL);
635  struct stat sb;
636  if (-1==fstat(fd, &sb))
637  throw Exception(filename + " stat: " + strerror(errno), NULL);
638  if (0==sb.st_size)
639  return 0;
640 
641  Extent extent(va, sb.st_size);
642  BufferPtr buf = MmapBuffer::create(sb.st_size, m_prot, m_flags, fd, 0);
643  insert(extent, Segment(buf, 0, s_prot, sgmtname.empty()?filename:sgmtname), erase_prior);
644  return sb.st_size;
645 }
646 
647 bool
648 MemoryMap::exists(Extent range, unsigned required_perms) const
649 {
650  if (!p_segments.contains(range))
651  return false;
652  if (0==required_perms)
653  return true;
654 
655  while (!range.empty()) {
657  assert(found!=p_segments.end()); // because p_segments.contains(range)
658  const Extent &found_range = found->first;
659  const Segment &found_segment = found->second;
660  assert(!range.begins_before(found_range)); // ditto
661  if ((found_segment.get_mapperms() & required_perms) != required_perms)
662  return false;
663  if (found_range.last() >= range.last())
664  return true;
665  range = Extent::inin(found_range.last()+1, range.last());
666  }
667  return true;
668 }
669 
670 void
672 {
673  p_segments.erase(range);
674 }
675 
676 void
678 {
679  for (Segments::iterator si=p_segments.begin(); si!=p_segments.end(); ++si) {
680  if (segment==si->second) {
681  erase(si->first);
682  return;
683  }
684  }
685 }
686 
687 std::pair<Extent, MemoryMap::Segment>
689 {
691  if (found==p_segments.end())
692  throw NotMapped("", this, va);
693  return *found;
694 }
695 
697 MemoryMap::find_free(rose_addr_t start_va, size_t size, rose_addr_t alignment) const
698 {
699  ExtentMap free_map = p_segments.invert<ExtentMap>();
700  ExtentMap::iterator fmi = free_map.lower_bound(start_va);
701  while ((fmi=free_map.first_fit(size, fmi)) != free_map.end()) {
702  Extent free_range = fmi->first;
703  rose_addr_t free_va = ALIGN_UP(free_range.first(), alignment);
704  rose_addr_t free_sz = free_va > free_range.last() ? 0 : free_range.last()+1-free_va;
705  if (free_sz > size)
706  return free_va;
707  ++fmi;
708  }
709  throw NoFreeSpace("find_free() failed", this, size);
710 }
711 
714 {
715  ExtentMap free_map = p_segments.invert<ExtentMap>();
716  ExtentMap::iterator fmi = free_map.find_prior(max);
717  if (fmi==free_map.end())
718  throw NoFreeSpace("find_last_free() failed", this, 1);
719  return fmi->first.first();
720 }
721 
722 void
724 {
725  for (Segments::const_iterator si=p_segments.begin(); si!=p_segments.end(); ++si)
726  (void) visitor(this, si->first, si->second);
727 }
728 
729 void
731 {
732  ExtentMap matches;
733  for (Segments::iterator si=p_segments.begin(); si!=p_segments.end(); ++si) {
734  if (predicate(this, si->first, si->second))
735  matches.insert(si->first);
736  }
737 
738  for (ExtentMap::iterator mi=matches.begin(); mi!=matches.end(); ++mi)
739  p_segments.erase(mi->first);
740 }
741 
742 void
743 MemoryMap::prune(unsigned required, unsigned prohibited)
744 {
745  struct T1: public Visitor {
746  unsigned required, prohibited;
747  T1(unsigned required, unsigned prohibited): required(required), prohibited(prohibited) {}
748  bool operator()(const MemoryMap*, const Extent &range, const Segment &segment) {
749  return ((0!=required && 0==(segment.get_mapperms() & required)) ||
750  0!=(segment.get_mapperms() & prohibited));
751  }
752  } predicate(required, prohibited);
753  prune(predicate);
754 }
755 
756 size_t
757 MemoryMap::read1(void *dst_buf/*=NULL*/, rose_addr_t start_va, size_t desired, unsigned req_perms) const
758 {
759  Segments::const_iterator found = p_segments.find(start_va);
760  if (found==p_segments.end())
761  return 0;
762 
763  const Extent &range = found->first;
764  assert(range.contains(Extent(start_va)));
765 
766  desired = std::min((rose_addr_t)desired, (range.last()-start_va)+1);
767 
768  const Segment &segment = found->second;
769  if ((segment.get_mapperms() & req_perms) != req_perms || !segment.check(range))
770  return 0;
771 
772  rose_addr_t buffer_offset = segment.get_buffer_offset(range, start_va);
773  return segment.get_buffer()->read(dst_buf, buffer_offset, desired);
774 }
775 
776 size_t
777 MemoryMap::read(void *dst_buf/*=NULL*/, rose_addr_t start_va, size_t desired, unsigned req_perms) const
778 {
779  size_t total_copied = 0;
780  while (total_copied < desired) {
781  uint8_t *ptr = dst_buf ? (uint8_t*)dst_buf + total_copied : NULL;
782  size_t n = read1(ptr, start_va+total_copied, desired-total_copied, req_perms);
783  if (0==n) break;
784  total_copied += n;
785  }
786  memset((uint8_t*)dst_buf+total_copied, 0, desired-total_copied);
787  return total_copied;
788 }
789 
791 MemoryMap::read(rose_addr_t va, size_t desired, unsigned req_perms) const
792 {
793  SgUnsignedCharList retval;
794  while (desired>0) {
795  size_t can_read = read1(NULL, va, desired, req_perms);
796  if (0==can_read)
797  break;
798  size_t n = retval.size();
799  retval.resize(retval.size()+can_read);
800  size_t did_read = read1(&retval[n], va, desired, req_perms);
801  assert(did_read==can_read);
802 
803  va += did_read;
804  desired -= can_read;
805  }
806  return retval;
807 }
808 
809 std::string
810 MemoryMap::read_string(rose_addr_t va, size_t desired, int(*valid_char)(int), int(*invalid_char)(int), unsigned req_perms) const
811 {
812  std::string retval;
813  while (desired>0) {
814  unsigned char buf[4096];
815  size_t nread = read1(buf, va, desired, req_perms);
816  if (0==nread)
817  return retval;
818  for (size_t i=0; i<nread; ++i) {
819  if (buf[i]=='\0' || (NULL!=valid_char && !valid_char(buf[i])) || (NULL!=invalid_char && invalid_char(buf[i])))
820  return retval;
821  retval += buf[i];
822  }
823  va += nread;
824  desired -= nread;
825  }
826  return retval;
827 }
828 
829 size_t
830 MemoryMap::write1(const void *src_buf/*=NULL*/, rose_addr_t start_va, size_t desired, unsigned req_perms)
831 {
832  Segments::iterator found = p_segments.find(start_va);
833  if (found==p_segments.end())
834  return 0;
835 
836  const Extent &range = found->first;
837  assert(range.contains(Extent(start_va)));
838 
839  Segment &segment = found->second;
840  if ((segment.get_mapperms() & req_perms) != req_perms || !segment.check(range))
841  return 0;
842 
843  if (segment.is_cow()) {
844  BufferPtr old_buf = segment.get_buffer();
845  BufferPtr new_buf = old_buf->clone();
846  for (Segments::iterator si=p_segments.begin(); si!=p_segments.end(); ++si) {
847  if (si->second.get_buffer()==old_buf) {
848  si->second.set_buffer(new_buf);
849  si->second.clear_cow();
850  }
851  }
852  assert(segment.get_buffer()==new_buf);
853  assert(!segment.is_cow());
854  }
855 
856  rose_addr_t buffer_offset = segment.get_buffer_offset(range, start_va);
857  return segment.get_buffer()->write(src_buf, buffer_offset, desired);
858 }
859 
860 size_t
861 MemoryMap::write(const void *src_buf/*=NULL*/, rose_addr_t start_va, size_t desired, unsigned req_perms)
862 {
863  size_t total_copied = 0;
864  while (total_copied < desired) {
865  uint8_t *ptr = src_buf ? (uint8_t*)src_buf + total_copied : NULL;
866  size_t n = write1(ptr, start_va+total_copied, desired-total_copied, req_perms);
867  if (0==n) break;
868  total_copied += n;
869  }
870  return total_copied;
871 }
872 
873 ExtentMap
875 {
876  return ExtentMap(p_segments);
877 }
878 
879 void
880 MemoryMap::mprotect(Extent range, unsigned perms, bool relax)
881 {
882  bool done = false;
883  while (!range.empty() && !done) {
885 
886  // Skip over leading part of range that's not mapped
887  if (found==p_segments.end() || found->first.right_of(range)) {
888  if (!relax)
889  throw NotMapped("", this, range.first());
890  return;
891  }
892  if (found->first.begins_after(range)) {
893  if (!relax)
894  throw NotMapped("", this, range.first());
895  range = Extent::inin(found->first.first(), range.last());
896  }
897 
898  Segment &segment = found->second;
899  const Extent segment_range = found->first; // don't use a reference; it might be deleted by MemoryMap::insert() below
900  done = segment_range.last() >= range.last();
901 
902  if (found->second.get_mapperms()!=perms) {
903  if (range.contains(segment_range)) {
904  // we can just change the segment in place
905  segment.set_mapperms(perms);
906  } else {
907  // make a hole and insert a new segment
908  assert(segment_range.begins_before(range, false/*non-strict*/));
909  Extent new_range = Extent::inin(range.first(), std::min(range.last(), segment_range.last()));
910  Segment new_segment = segment;
911  new_segment.set_mapperms(perms);
912  new_segment.set_buffer_offset(segment.get_buffer_offset(segment_range, new_range.first()));
913  p_segments.insert(new_range, new_segment, true/*make hole*/); // 'segment' is now invalid
914  }
915  }
916 
917  if (!done)
918  range = Extent::inin(segment_range.last()+1, range.last());
919  }
920 }
921 
922 void
923 MemoryMap::erase_zeros(size_t minsize)
924 {
925  ExtentMap to_remove;
926  for (Segments::const_iterator si=p_segments.begin(); si!=p_segments.end(); ++si) {
927  Extent extent = si->first;
928  const Segment &segment = si->second;
929  if (0==(segment.get_mapperms() & MM_PROT_EXEC) || extent.size() < minsize)
930  continue; // not executable or too small to remove
931  BufferPtr buffer = segment.get_buffer();
932  if (buffer->is_zero()) {
933  to_remove.insert(extent);
934  } else {
935  for (size_t i=0; i<extent.size(); /*void*/) {
936  uint8_t page[8192];
937  size_t nread = read(page, extent.first()+i, sizeof page);
938  if (0==nread)
939  break;
940  for (size_t j=0; j<nread; /*void*/) {
941  if (0==page[j]) {
942  size_t k = 1;
943  while (j+k<nread && 0==page[j+k]) ++k;
944  if (k>=minsize)
945  to_remove.insert(Extent(extent.first()+i+j, k));
946  j += k;
947  } else {
948  ++j;
949  }
950  }
951  i += nread;
952  }
953  }
954  }
955  for (ExtentMap::iterator ei=to_remove.begin(); ei!=to_remove.end(); ++ei)
956  erase(ei->first);
957 }
958 
959 void
960 MemoryMap::dump(FILE *f, const char *prefix) const
961 {
962  std::ostringstream ss;
963  dump(ss, prefix);
964  fputs(ss.str().c_str(), f);
965 }
966 
967 void
968 MemoryMap::dump(std::ostream &out, std::string prefix) const
969 {
970  if (0==p_segments.size()) {
971  out <<prefix <<"empty\n";
972  return;
973  }
974 
975  for (Segments::const_iterator si=p_segments.begin(); si!=p_segments.end(); ++si) {
976  const Extent &range = si->first;
977  const Segment &segment = si->second;
978 
979  assert(segment.get_buffer());
980  std::string basename = segment.get_buffer()->get_name();
981 
982  out <<prefix
983  <<"va " <<StringUtility::addrToString(range.first())
984  <<" + " <<StringUtility::addrToString(range.size())
985  <<" = " <<StringUtility::addrToString(range.last()+1) <<" "
986  <<segment
987  <<"\n";
988  }
989 }
990 
991 void
992 MemoryMap::dump(const std::string &basename) const
993 {
994  std::ofstream index((basename+".index").c_str());
995  index <<*this;
996 
997  for (Segments::const_iterator si=p_segments.begin(); si!=p_segments.end(); ++si) {
998  const Extent &range = si->first;
999  const Segment &segment = si->second;
1000 
1001  std::string dataname = basename + "-" + StringUtility::addrToString(range.first()).substr(2) + ".data";
1002  segment.get_buffer()->save(dataname);
1003  }
1004 }
1005 
1006 bool
1007 MemoryMap::load(const std::string &basename)
1008 {
1009  clear();
1010  std::string indexname = basename + ".index";
1011  FILE *f = fopen(indexname.c_str(), "r");
1012  if (!f) return false;
1013 
1014  char *line = NULL;
1015  size_t line_nalloc = 0;
1016  ssize_t nread;
1017  unsigned nlines=0;
1018  std::map<std::string, BufferPtr> buffers;
1019 
1020  while (0<(nread=rose_getline(&line, &line_nalloc, f))) {
1021  char *rest, *s=line;
1022  nlines++;
1023 
1024  /* Check for empty lines and comments */
1025  while (isspace(*s)) s++;
1026  if (!*s || '#'==*s) continue;
1027 
1028  /* Starting virtual address with optional "va " prefix */
1029  if (!strncmp(s, "va ", 3)) s += 3;
1030  errno = 0;
1031 #ifndef _MSC_VER
1032  rose_addr_t segment_va = strtoull(s, &rest, 0);
1033 #else
1034  rose_addr_t segment_va = 0;
1035  ROSE_ASSERT(!"lacking Windows support");
1036 #endif
1037  if (rest==s || errno)
1038  throw SyntaxError("starting virtual address expected", this, indexname, nlines, s-line);
1039  s = rest;
1040 
1041  /* Size, prefixed with optional "+" or "," */
1042  while (isspace(*s)) s++;
1043  if ('+'==*s || ','==*s) s++;
1044  while (isspace(*s)) s++;
1045  errno = 0;
1046 #ifndef _MSC_VER
1047  rose_addr_t segment_sz = strtoull(s, &rest, 0);
1048 #else
1049  rose_addr_t segment_sz = 0;
1050  ROSE_ASSERT(!"lacking Windows support");
1051 #endif
1052  if (rest==s || errno)
1053  throw SyntaxError("virtual size expected", this, indexname, nlines, s-line);
1054  s = rest;
1055 
1056  /* Optional ending virtual address prefixed with "=" */
1057  while (isspace(*s)) s++;
1058  if ('='==*s) {
1059  s++;
1060  errno = 0;
1061 #ifndef _MSC_VER
1062  (void)strtoull(s, &rest, 0);
1063 #else
1064  ROSE_ASSERT(!"lacking Windows support");
1065 #endif
1066  if (rest==s || errno)
1067  throw SyntaxError("ending virtual address expected after '='", this, indexname, nlines, s-line);
1068  s = rest;
1069  }
1070 
1071  /* Permissions with optional ',' prefix. Permissions are the letters 'r', 'w', and/or 'x'. Hyphens can appear in the
1072  * r/w/x string at any position and are ignored. */
1073  while (isspace(*s)) s++;
1074  if (','==*s) s++;
1075  while (isspace(*s)) s++;
1076  unsigned perm = 0;
1077  while (strchr("rwxp-", *s)) {
1078  switch (*s++) {
1079  case 'r': perm |= MM_PROT_READ; break;
1080  case 'w': perm |= MM_PROT_WRITE; break;
1081  case 'x': perm |= MM_PROT_EXEC; break;
1082  case 'p': perm |= MM_PROT_PRIVATE; break;
1083  case '-': break;
1084  default: break; /*to suppress a compiler warning*/
1085  }
1086  }
1087 
1088  /* File or buffer name terminated by ',' or '0x' and then strip trailing space. */
1089  while (isspace(*s)) s++;
1090  if (','==*s) s++;
1091  while (isspace(*s)) s++;
1092  if (!strncmp(s, "at", 2) && isspace(s[2])) s+= 3;
1093  while (isspace(*s)) s++;
1094  int buffer_name_col = s-line;
1095  std::string buffer_name;
1096  while (*s && ','!=*s && 0!=strncmp(s, "0x", 2))
1097  buffer_name += *s++;
1098  size_t bnsz = buffer_name.size();
1099  while (bnsz>0 && isspace(buffer_name[bnsz-1])) --bnsz;
1100  buffer_name = buffer_name.substr(0, bnsz);
1101  if (buffer_name.empty())
1102  throw SyntaxError("file/buffer name expected", this, indexname, nlines, buffer_name_col);
1103 
1104  /* Offset from base address; optional prefix of "," or "+". */
1105  while (isspace(*s)) s++;
1106  if (','==*s || '+'==*s) s++;
1107  while (isspace(*s)) s++;
1108  errno = 0;
1109 #ifndef _MSC_VER
1110  rose_addr_t offset = strtoull(s, &rest, 0);
1111 #else
1112  rose_addr_t offset = 0;
1113  ROSE_ASSERT(!"lacking Windows support");
1114 #endif
1115  if (rest==s || errno)
1116  throw SyntaxError("file/buffer offset expected", this, indexname, nlines, s-line);
1117  s = rest;
1118 
1119  /* Comment (optional) */
1120  while (isspace(*s)) s++;
1121  if (','==*s) s++;
1122  while (isspace(*s)) s++;
1123  char *end = s + strlen(s);
1124  while (end>s && isspace(end[-1])) --end;
1125  std::string comment(s, end-s);
1126 
1127  /* Create the buffer or use one we already created. */
1128  BufferPtr buffer;
1129  std::map<std::string, BufferPtr>::iterator bi = buffers.find(buffer_name);
1130  if (bi!=buffers.end() && 0==(perm & MM_PROT_PRIVATE)) {
1131  buffer = bi->second;
1132  } else if (0==access(buffer_name.c_str(), F_OK)) {
1133  buffer = ByteBuffer::create_from_file(buffer_name);
1134  buffers.insert(std::make_pair(buffer_name, buffer));
1135  } else {
1136  ROSE_ASSERT(!"not implemented yet");
1137  }
1138 
1139  Extent range(segment_va, segment_sz);
1140  Segment segment(buffer, offset, perm, comment);
1141  insert(range, segment);
1142  }
1143 
1144  fclose(f);
1145  if (line) free(line);
1146  return nread<=0;
1147 }