1// Copyright 2012 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Platform-specific code for FreeBSD goes here. For the POSIX-compatible
6// parts, the implementation is in platform-posix.cc.
7
8#include <pthread.h>
9#include <semaphore.h>
10#include <signal.h>
11#include <stdlib.h>
12#include <sys/resource.h>
13#include <sys/time.h>
14#include <sys/types.h>
15#include <sys/ucontext.h>
16
17#include <sys/fcntl.h>  // open
18#include <sys/mman.h>   // mmap & munmap
19#include <sys/stat.h>   // open
20#include <sys/types.h>  // mmap & munmap
21#include <unistd.h>     // getpagesize
22// If you don't have execinfo.h then you need devel/libexecinfo from ports.
23#include <errno.h>
24#include <limits.h>
25#include <stdarg.h>
26#include <strings.h>    // index
27
28#include <cmath>
29
30#undef MAP_TYPE
31
32#include "src/base/macros.h"
33#include "src/base/platform/platform.h"
34
35
36namespace v8 {
37namespace base {
38
39
40const char* OS::LocalTimezone(double time, TimezoneCache* cache) {
41  if (std::isnan(time)) return "";
42  time_t tv = static_cast<time_t>(std::floor(time/msPerSecond));
43  struct tm* t = localtime(&tv);
44  if (NULL == t) return "";
45  return t->tm_zone;
46}
47
48
49double OS::LocalTimeOffset(TimezoneCache* cache) {
50  time_t tv = time(NULL);
51  struct tm* t = localtime(&tv);
52  // tm_gmtoff includes any daylight savings offset, so subtract it.
53  return static_cast<double>(t->tm_gmtoff * msPerSecond -
54                             (t->tm_isdst > 0 ? 3600 * msPerSecond : 0));
55}
56
57
58void* OS::Allocate(const size_t requested,
59                   size_t* allocated,
60                   bool executable) {
61  const size_t msize = RoundUp(requested, getpagesize());
62  int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
63  void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
64
65  if (mbase == MAP_FAILED) return NULL;
66  *allocated = msize;
67  return mbase;
68}
69
70
71class PosixMemoryMappedFile : public OS::MemoryMappedFile {
72 public:
73  PosixMemoryMappedFile(FILE* file, void* memory, int size)
74    : file_(file), memory_(memory), size_(size) { }
75  virtual ~PosixMemoryMappedFile();
76  virtual void* memory() { return memory_; }
77  virtual int size() { return size_; }
78 private:
79  FILE* file_;
80  void* memory_;
81  int size_;
82};
83
84
85OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
86  FILE* file = fopen(name, "r+");
87  if (file == NULL) return NULL;
88
89  fseek(file, 0, SEEK_END);
90  int size = ftell(file);
91
92  void* memory =
93      mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
94  return new PosixMemoryMappedFile(file, memory, size);
95}
96
97
98OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
99    void* initial) {
100  FILE* file = fopen(name, "w+");
101  if (file == NULL) return NULL;
102  int result = fwrite(initial, size, 1, file);
103  if (result < 1) {
104    fclose(file);
105    return NULL;
106  }
107  void* memory =
108      mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
109  return new PosixMemoryMappedFile(file, memory, size);
110}
111
112
113PosixMemoryMappedFile::~PosixMemoryMappedFile() {
114  if (memory_) munmap(memory_, size_);
115  fclose(file_);
116}
117
118
119static unsigned StringToLong(char* buffer) {
120  return static_cast<unsigned>(strtol(buffer, NULL, 16));  // NOLINT
121}
122
123
124std::vector<OS::SharedLibraryAddress> OS::GetSharedLibraryAddresses() {
125  std::vector<SharedLibraryAddress> result;
126  static const int MAP_LENGTH = 1024;
127  int fd = open("/proc/self/maps", O_RDONLY);
128  if (fd < 0) return result;
129  while (true) {
130    char addr_buffer[11];
131    addr_buffer[0] = '0';
132    addr_buffer[1] = 'x';
133    addr_buffer[10] = 0;
134    ssize_t bytes_read = read(fd, addr_buffer + 2, 8);
135    if (bytes_read < 8) break;
136    unsigned start = StringToLong(addr_buffer);
137    bytes_read = read(fd, addr_buffer + 2, 1);
138    if (bytes_read < 1) break;
139    if (addr_buffer[2] != '-') break;
140    bytes_read = read(fd, addr_buffer + 2, 8);
141    if (bytes_read < 8) break;
142    unsigned end = StringToLong(addr_buffer);
143    char buffer[MAP_LENGTH];
144    int bytes_read = -1;
145    do {
146      bytes_read++;
147      if (bytes_read >= MAP_LENGTH - 1)
148        break;
149      bytes_read = read(fd, buffer + bytes_read, 1);
150      if (bytes_read < 1) break;
151    } while (buffer[bytes_read] != '\n');
152    buffer[bytes_read] = 0;
153    // Ignore mappings that are not executable.
154    if (buffer[3] != 'x') continue;
155    char* start_of_path = index(buffer, '/');
156    // There may be no filename in this line.  Skip to next.
157    if (start_of_path == NULL) continue;
158    buffer[bytes_read] = 0;
159    result.push_back(SharedLibraryAddress(start_of_path, start, end));
160  }
161  close(fd);
162  return result;
163}
164
165
166void OS::SignalCodeMovingGC() {
167}
168
169
170
171// Constants used for mmap.
172static const int kMmapFd = -1;
173static const int kMmapFdOffset = 0;
174
175
176VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { }
177
178
179VirtualMemory::VirtualMemory(size_t size)
180    : address_(ReserveRegion(size)), size_(size) { }
181
182
183VirtualMemory::VirtualMemory(size_t size, size_t alignment)
184    : address_(NULL), size_(0) {
185  DCHECK((alignment % OS::AllocateAlignment()) == 0);
186  size_t request_size = RoundUp(size + alignment,
187                                static_cast<intptr_t>(OS::AllocateAlignment()));
188  void* reservation = mmap(OS::GetRandomMmapAddr(),
189                           request_size,
190                           PROT_NONE,
191                           MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
192                           kMmapFd,
193                           kMmapFdOffset);
194  if (reservation == MAP_FAILED) return;
195
196  uint8_t* base = static_cast<uint8_t*>(reservation);
197  uint8_t* aligned_base = RoundUp(base, alignment);
198  DCHECK_LE(base, aligned_base);
199
200  // Unmap extra memory reserved before and after the desired block.
201  if (aligned_base != base) {
202    size_t prefix_size = static_cast<size_t>(aligned_base - base);
203    OS::Free(base, prefix_size);
204    request_size -= prefix_size;
205  }
206
207  size_t aligned_size = RoundUp(size, OS::AllocateAlignment());
208  DCHECK_LE(aligned_size, request_size);
209
210  if (aligned_size != request_size) {
211    size_t suffix_size = request_size - aligned_size;
212    OS::Free(aligned_base + aligned_size, suffix_size);
213    request_size -= suffix_size;
214  }
215
216  DCHECK(aligned_size == request_size);
217
218  address_ = static_cast<void*>(aligned_base);
219  size_ = aligned_size;
220}
221
222
223VirtualMemory::~VirtualMemory() {
224  if (IsReserved()) {
225    bool result = ReleaseRegion(address(), size());
226    DCHECK(result);
227    USE(result);
228  }
229}
230
231
232bool VirtualMemory::IsReserved() {
233  return address_ != NULL;
234}
235
236
237void VirtualMemory::Reset() {
238  address_ = NULL;
239  size_ = 0;
240}
241
242
243bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
244  return CommitRegion(address, size, is_executable);
245}
246
247
248bool VirtualMemory::Uncommit(void* address, size_t size) {
249  return UncommitRegion(address, size);
250}
251
252
253bool VirtualMemory::Guard(void* address) {
254  OS::Guard(address, OS::CommitPageSize());
255  return true;
256}
257
258
259void* VirtualMemory::ReserveRegion(size_t size) {
260  void* result = mmap(OS::GetRandomMmapAddr(),
261                      size,
262                      PROT_NONE,
263                      MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
264                      kMmapFd,
265                      kMmapFdOffset);
266
267  if (result == MAP_FAILED) return NULL;
268
269  return result;
270}
271
272
273bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) {
274  int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
275  if (MAP_FAILED == mmap(base,
276                         size,
277                         prot,
278                         MAP_PRIVATE | MAP_ANON | MAP_FIXED,
279                         kMmapFd,
280                         kMmapFdOffset)) {
281    return false;
282  }
283  return true;
284}
285
286
287bool VirtualMemory::UncommitRegion(void* base, size_t size) {
288  return mmap(base,
289              size,
290              PROT_NONE,
291              MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_FIXED,
292              kMmapFd,
293              kMmapFdOffset) != MAP_FAILED;
294}
295
296
297bool VirtualMemory::ReleaseRegion(void* base, size_t size) {
298  return munmap(base, size) == 0;
299}
300
301
302bool VirtualMemory::HasLazyCommits() {
303  // TODO(alph): implement for the platform.
304  return false;
305}
306
307} }  // namespace v8::base
308