ns-3 Direct Code Execution
API
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
elf-cache.cc
Go to the documentation of this file.
1 #include "elf-cache.h"
2 #include "ns3/log.h"
3 #include "ns3/assert.h"
4 #include "ns3/fatal-error.h"
5 #include <unistd.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9 #include <errno.h>
10 #include <sstream>
11 #include <string.h>
12 #include <sys/mman.h>
13 
14 namespace ns3 {
15 
16 NS_LOG_COMPONENT_DEFINE ("ElfCache");
17 
18 ElfCache::ElfCache (std::string directory, uint32_t uid)
19  : m_directory (directory),
20  m_uid (uid)
21 {
22  struct Overriden overriden;
23  overriden.from = "libc.so.6";
24  overriden.to = "libc-ns3.so";
25  m_overriden.push_back (overriden);
26  overriden.from = "libpthread.so.0";
27  overriden.to = "libpthread-ns3.so";
28  m_overriden.push_back (overriden);
29  overriden.from = "librt.so.1";
30  overriden.to = "librt-ns3.so";
31  m_overriden.push_back (overriden);
32  overriden.from = "libm.so.6";
33  overriden.to = "libm-ns3.so";
34  m_overriden.push_back (overriden);
35 }
36 
37 std::string
38 ElfCache::GetBasename (std::string filename) const
39 {
40  std::string::size_type tmp = filename.find_last_of ("/");
41  if (tmp == std::string::npos)
42  {
43  return filename;
44  }
45  return filename.substr (tmp + 1, filename.size () - (tmp + 1));
46 }
47 
48 void
49 ElfCache::CopyFile (std::string source, std::string destination) const
50 {
51  NS_LOG_FUNCTION (this << source << destination);
52  int src = open (source.c_str (), O_RDONLY);
53  int dst = open (destination.c_str (), O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
54  uint8_t buffer[1024];
55  ssize_t bytes_read = read (src, buffer, 1024);
56  while (bytes_read > 0)
57  {
58  ssize_t bytes_written = 0;
59  while (bytes_written != bytes_read)
60  {
61  ssize_t written = write (dst, buffer + bytes_written, bytes_read - bytes_written);
62  bytes_written += written;
63  }
64  bytes_read = read (src, buffer, 1024);
65  }
66  close (src);
67  close (dst);
68  NS_LOG_DEBUG ("copied " << source << " to " << destination);
69 }
70 
71 long
72 ElfCache::GetDtStrTab (ElfW(Dyn) *dyn, long baseAddress) const
73 {
74  bool prelinked = false;
75  long dt_strtab = 0;
76  while (dyn->d_tag != DT_NULL)
77  {
78  if (dyn->d_tag == DT_STRTAB)
79  {
80  dt_strtab = dyn->d_un.d_val;
81  }
82  else if (dyn->d_tag == DT_GNU_PRELINKED)
83  {
84  prelinked = true;
85  }
86  dyn++;
87  }
88  if (prelinked)
89  {
90  dt_strtab -= baseAddress;
91  }
92  return dt_strtab;
93 }
94 
95 unsigned long
96 ElfCache::GetBaseAddress (ElfW(Phdr) *phdr, long phnum) const
97 {
98  unsigned long end = ~0; // the max value storable in a uint64_t
99  unsigned long base = end;
100  for (long i = 0; i < phnum; i++, phdr++)
101  {
102  if (phdr->p_type == PT_LOAD)
103  {
104  unsigned long ph_base = phdr->p_vaddr & ~(phdr->p_align - 1);
105  if (ph_base < base)
106  {
107  base = ph_base;
108  }
109  }
110  }
111  if (base == end)
112  {
113  NS_LOG_ERROR ("Could not find base address.");
114  }
115  return base;
116 }
117 
118 
119 uint8_t
120 ElfCache::NumberToChar (uint8_t c) const
121 {
122  NS_ASSERT (c <= 60);
123  if (c < 10)
124  {
125  return c + 0x30;
126  }
127  else if (c < (10 + 25))
128  {
129  return c - 10 + 0x41;
130  }
131  else if (c < (10 + 25 + 25))
132  {
133  return c - (10 + 25) + 0x61;
134  }
135  return 0; // quiet compiler
136 }
137 void
138 ElfCache::WriteString (char *str, uint32_t uid) const
139 {
140  // we use base 4 chars in base 60.
141  if (uid >= 60 * 60 * 60 * 60)
142  {
143  NS_FATAL_ERROR ("Please, report a bug: not enough unique strings for loaded code.");
144  }
145  uint8_t a = uid % 60;
146  uint8_t b = (uid / 60) % 60;
147  uint8_t c = (uid / 3600) % 60;
148  uint8_t d = (uid / 216000) % 60;
149  str[0] = NumberToChar (d);
150  str[1] = NumberToChar (c);
151  str[2] = NumberToChar (b);
152  str[3] = NumberToChar (a);
153  NS_LOG_DEBUG ("wrote " << str);
154 }
155 
156 struct ElfCache::FileInfo
157 ElfCache::EditBuffer (uint8_t *map, uint32_t selfId) const
158 {
159  ElfW (Ehdr) *header = (ElfW (Ehdr) *)map;
160  ElfW (Phdr) * phdr = (ElfW (Phdr) *)(map + header->e_phoff);
161  ElfW (Dyn) * dyn = 0;
162  long base_address = GetBaseAddress (phdr, header->e_phnum);
163 
164  // find DYNAMIC and fill DataSection
165  struct FileInfo fileInfo;
166  ElfW (Phdr) * pt_load_rw = 0;
167  ElfW (Phdr) * pt_gnu_relro = 0;
168  for (uint32_t i = 0; i < header->e_phnum; i++, phdr++)
169  {
170  switch (phdr->p_type)
171  {
172  case PT_LOAD:
173  if (phdr->p_flags & PF_W)
174  {
175  // data section !
176  pt_load_rw = phdr;
177  }
178  break;
179  case PT_DYNAMIC:
180  // now, seek DT_NEEDED
181  dyn = (ElfW (Dyn) *)(map + phdr->p_offset);
182  break;
183  case PT_GNU_RELRO:
184  pt_gnu_relro = phdr;
185  break;
186  }
187  }
188  NS_ASSERT (pt_load_rw != 0);
189  fileInfo.p_vaddr = pt_load_rw->p_vaddr;
190  fileInfo.p_memsz = pt_load_rw->p_memsz;
191  if (pt_gnu_relro != 0)
192  {
193  NS_ASSERT (pt_gnu_relro->p_vaddr == pt_load_rw->p_vaddr);
194  fileInfo.p_vaddr += pt_gnu_relro->p_memsz;
195  fileInfo.p_memsz -= pt_gnu_relro->p_memsz;
196  }
197 
198  // first, Patch the DT_NEEDED, and, DT_SONAME entries
199  // and save the DT_INIT entry
200  long dt_strtab = GetDtStrTab (dyn, base_address);
201  long dt_init = 0;
202  ElfW (Dyn) * cur = dyn;
203  while (cur->d_tag != DT_NULL)
204  {
205  if (cur->d_tag == DT_NEEDED)
206  {
207  char *needed = (char *)(map + dt_strtab + cur->d_un.d_val);
208  if (std::string (needed) != "ld-linux-x86-64.so.2"
209  && std::string (needed) != "ld-linux.so.2")
210  {
211  uint32_t id = GetDepId (needed);
212  fileInfo.deps.push_back (id);
213  WriteString (needed, id);
214  }
215  }
216  else if (cur->d_tag == DT_SONAME)
217  {
218  char *soname = (char *)(map + dt_strtab + cur->d_un.d_val);
219  WriteString (soname, selfId);
220  }
221  else if (cur->d_tag == DT_INIT)
222  {
223  dt_init = cur->d_un.d_val;
224  }
225  cur++;
226  }
227  // then, eliminate DT_FINI, DT_FINI_ARRAY and DT_FINI_ARRAYSZ
228  cur = dyn;
229  while (cur->d_tag != DT_NULL)
230  {
231  if (cur->d_tag == DT_FINI)
232  {
233  cur->d_tag = DT_INIT;
234  cur->d_un.d_val = dt_init;
235  }
236  else if (cur->d_tag == DT_FINI_ARRAYSZ)
237  {
238  cur->d_un.d_val = 0;
239  }
240  cur++;
241  }
242  return fileInfo;
243 }
244 
245 struct ElfCache::FileInfo
246 ElfCache::EditFile (std::string filename, uint32_t selfId) const
247 {
248  NS_LOG_FUNCTION (this << filename);
249  int fd = ::open (filename.c_str (), O_RDWR);
250  NS_ASSERT_MSG (fd != -1, "unable to open file=" << filename << " error=" << strerror (errno));
251  struct stat st;
252  int retval = ::fstat (fd, &st);
253  NS_ASSERT_MSG (retval == 0, "unable to fstat file=" << filename << " error=" << strerror (errno));
254  uint64_t size = st.st_size;
255  uint8_t *buffer = (uint8_t *) ::mmap (0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
256  NS_ASSERT_MSG (buffer != MAP_FAILED, "unable to mmap file=" << filename << " error=" << strerror (errno));
257  close (fd);
258 
259  struct FileInfo fileInfo = EditBuffer (buffer, selfId);
260 
261  // write back changes to hard disk
262  retval = ::msync (buffer, size, MS_SYNC);
263  NS_ASSERT_MSG (retval == 0, "msync failed " << strerror (errno));
264 
265  retval = ::munmap (buffer, size);
266  NS_ASSERT_MSG (retval == 0, "munmap failed " << strerror (errno));
267 
268  return fileInfo;
269 }
270 
271 uint32_t
273 {
274  static uint32_t id = 0;
275  id++;
276  return id;
277 }
278 
279 uint32_t
280 ElfCache::GetDepId (std::string depname) const
281 {
282  for (std::vector<struct Overriden>::const_iterator i = m_overriden.begin (); i != m_overriden.end (); ++i)
283  {
284  struct Overriden overriden = *i;
285  if (overriden.from == depname)
286  {
287  depname = overriden.to;
288  }
289  NS_LOG_DEBUG ("from: " << overriden.from << ", to: " << overriden.to);
290  }
291  for (std::vector<struct ElfCachedFile>::const_iterator i = m_files.begin (); i != m_files.end (); ++i)
292  {
293  NS_LOG_DEBUG ("cache: " << i->basename);
294  if (depname == i->basename)
295  {
296  return i->id;
297  }
298  }
299  NS_ASSERT_MSG (false, "did not find " << depname);
300  return 0; // quiet compiler
301 }
302 
303 std::string
305 {
306  int retval = ::mkdir (m_directory.c_str (), S_IRWXU);
307  if (retval == 0)
308  {
309  NS_LOG_DEBUG ("Created elf loader cache directory.");
310  }
311  std::ostringstream oss;
312  oss << m_directory << "/" << m_uid;
313  retval = ::mkdir (oss.str ().c_str (), S_IRWXU);
314  if (retval == 0)
315  {
316  NS_LOG_DEBUG ("Created elf loader cache directory.");
317  }
318  return oss.str ();
319 }
320 
322 ElfCache::Add (std::string filename)
323 {
324  NS_LOG_FUNCTION (this << filename);
325  std::string basename = GetBasename (filename);
326  // check if we have an override rule for this file
327  for (std::vector<struct Overriden>::const_iterator i = m_overriden.begin (); i != m_overriden.end (); ++i)
328  {
329  struct Overriden overriden = *i;
330  if (overriden.from == basename)
331  {
332  // check if the overriden file is already in-store.
333  for (std::vector<struct ElfCachedFile>::const_iterator i = m_files.begin (); i != m_files.end (); ++i)
334  {
335  if (i->basename == overriden.to)
336  {
337  return *i;
338  }
339  }
340  NS_ASSERT (false);
341  }
342  }
343 
344  // check if the file is already in-store.
345  for (std::vector<struct ElfCachedFile>::const_iterator i = m_files.begin (); i != m_files.end (); ++i)
346  {
347  if (i->basename == basename)
348  {
349  return *i;
350  }
351  }
352 
353  std::string directory = EnsureCacheDirectory ();
354  std::string fileCopy = directory + "/" + basename;
355  CopyFile (filename, fileCopy);
356 
357  uint32_t selfId = AllocateId ();
358 
359  struct FileInfo fileInfo = EditFile (fileCopy, selfId);
360 
361  struct ElfCachedFile cached;
362  cached.cachedFilename = fileCopy;
363  cached.basename = basename;
364  cached.data_p_vaddr = fileInfo.p_vaddr;
365  cached.data_p_memsz = fileInfo.p_memsz;
366  cached.id = selfId;
367  cached.deps = fileInfo.deps;
368 
369  m_files.push_back (cached);
370  return cached;
371 }
372 
373 
374 } // namespace ns3