platform-cygwin.cc 8.14 KB
Newer Older
1
// Copyright 2012 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
vegorov@chromium.org's avatar
vegorov@chromium.org committed
4

5 6
// Platform-specific code for Cygwin goes here. For the POSIX-compatible
// parts, the implementation is in platform-posix.cc.
vegorov@chromium.org's avatar
vegorov@chromium.org committed
7 8 9 10 11 12 13

#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdarg.h>
#include <strings.h>    // index
#include <sys/mman.h>   // mmap & munmap
14
#include <sys/time.h>
vegorov@chromium.org's avatar
vegorov@chromium.org committed
15 16
#include <unistd.h>     // sysconf

17
#include <cmath>
vegorov@chromium.org's avatar
vegorov@chromium.org committed
18

19
#undef MAP_TYPE
vegorov@chromium.org's avatar
vegorov@chromium.org committed
20

21
#include "src/base/macros.h"
22
#include "src/base/platform/platform-posix.h"
23
#include "src/base/platform/platform.h"
24
#include "src/base/win32-headers.h"
vegorov@chromium.org's avatar
vegorov@chromium.org committed
25 26

namespace v8 {
27
namespace base {
vegorov@chromium.org's avatar
vegorov@chromium.org committed
28

29 30
namespace {

31
// The memory allocation implementation is taken from platform-win32.cc.
32

33 34 35 36
DWORD GetProtectionFromMemoryPermission(OS::MemoryPermission access) {
  switch (access) {
    case OS::MemoryPermission::kNoAccess:
      return PAGE_NOACCESS;
37 38
    case OS::MemoryPermission::kRead:
      return PAGE_READONLY;
39 40
    case OS::MemoryPermission::kReadWrite:
      return PAGE_READWRITE;
41 42
    case OS::MemoryPermission::kReadWriteExecute:
      return PAGE_EXECUTE_READWRITE;
43 44
    case OS::MemoryPermission::kReadExecute:
      return PAGE_EXECUTE_READ;
45 46 47 48 49 50
  }
  UNREACHABLE();
}

uint8_t* RandomizedVirtualAlloc(size_t size, DWORD flags, DWORD protect,
                                void* hint) {
51
  LPVOID base = nullptr;
52

53 54
  // For executable or reserved pages try to use the address hint.
  if (protect != PAGE_READWRITE) {
55
    base = VirtualAlloc(hint, size, flags, protect);
56 57
  }

58 59 60 61
  // If that fails, let the OS find an address to use.
  if (base == nullptr) {
    base = VirtualAlloc(nullptr, size, flags, protect);
  }
62

63
  return reinterpret_cast<uint8_t*>(base);
64 65 66 67
}

}  // namespace

68 69
class CygwinTimezoneCache : public PosixTimezoneCache {
  const char* LocalTimezone(double time) override;
vegorov@chromium.org's avatar
vegorov@chromium.org committed
70

71
  double LocalTimeOffset(double time_ms, bool is_utc) override;
72 73 74 75 76

  ~CygwinTimezoneCache() override {}
};

const char* CygwinTimezoneCache::LocalTimezone(double time) {
77
  if (std::isnan(time)) return "";
78
  time_t tv = static_cast<time_t>(std::floor(time/msPerSecond));
79 80
  struct tm tm;
  struct tm* t = localtime_r(&tv, &tm);
81
  if (nullptr == t) return "";
vegorov@chromium.org's avatar
vegorov@chromium.org committed
82 83 84
  return tzname[0];  // The location of the timezone string on Cygwin.
}

85
double LocalTimeOffset(double time_ms, bool is_utc) {
vegorov@chromium.org's avatar
vegorov@chromium.org committed
86
  // On Cygwin, struct tm does not contain a tm_gmtoff field.
87
  time_t utc = time(nullptr);
88
  DCHECK_NE(utc, -1);
89 90
  struct tm tm;
  struct tm* loc = localtime_r(&utc, &tm);
91
  DCHECK_NOT_NULL(loc);
vegorov@chromium.org's avatar
vegorov@chromium.org committed
92 93 94 95 96
  // time - localtime includes any daylight savings offset, so subtract it.
  return static_cast<double>((mktime(loc) - utc) * msPerSecond -
                             (loc->tm_isdst > 0 ? 3600 * msPerSecond : 0));
}

97
// static
98 99 100 101 102 103 104 105
void* OS::Allocate(void* address, size_t size, size_t alignment,
                   MemoryPermission access) {
  size_t page_size = AllocatePageSize();
  DCHECK_EQ(0, size % page_size);
  DCHECK_EQ(0, alignment % page_size);
  DCHECK_LE(page_size, alignment);
  address = AlignedAddress(address, alignment);

106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
  DWORD flags = (access == OS::MemoryPermission::kNoAccess)
                    ? MEM_RESERVE
                    : MEM_RESERVE | MEM_COMMIT;
  DWORD protect = GetProtectionFromMemoryPermission(access);

  // First, try an exact size aligned allocation.
  uint8_t* base = RandomizedVirtualAlloc(size, flags, protect, address);
  if (base == nullptr) return nullptr;  // Can't allocate, we're OOM.

  // If address is suitably aligned, we're done.
  uint8_t* aligned_base = RoundUp(base, alignment);
  if (base == aligned_base) return reinterpret_cast<void*>(base);

  // Otherwise, free it and try a larger allocation.
  CHECK(Free(base, size));

  // Clear the hint. It's unlikely we can allocate at this address.
  address = nullptr;
124

125 126 127
  // Add the maximum misalignment so we are guaranteed an aligned base address
  // in the allocated region.
  size_t padded_size = size + (alignment - page_size);
128
  const int kMaxAttempts = 3;
129
  aligned_base = nullptr;
130 131
  for (int i = 0; i < kMaxAttempts; ++i) {
    base = RandomizedVirtualAlloc(padded_size, flags, protect, address);
132 133 134 135
    if (base == nullptr) return nullptr;  // Can't allocate, we're OOM.

    // Try to trim the allocation by freeing the padded allocation and then
    // calling VirtualAlloc at the aligned base.
136
    CHECK(Free(base, padded_size));
137
    aligned_base = RoundUp(base, alignment);
138 139 140 141 142 143
    base = reinterpret_cast<uint8_t*>(
        VirtualAlloc(aligned_base, size, flags, protect));
    // We might not get the reduced allocation due to a race. In that case,
    // base will be nullptr.
    if (base != nullptr) break;
  }
144
  DCHECK_IMPLIES(base, base == aligned_base);
145
  return reinterpret_cast<void*>(base);
146 147
}

148
// static
149
bool OS::Free(void* address, const size_t size) {
150 151 152
  DCHECK_EQ(0, static_cast<uintptr_t>(address) % AllocatePageSize());
  DCHECK_EQ(0, size % AllocatePageSize());
  USE(size);
153
  return VirtualFree(address, 0, MEM_RELEASE) != 0;
154 155
}

156 157 158 159 160 161 162
// static
bool OS::Release(void* address, size_t size) {
  DCHECK_EQ(0, reinterpret_cast<uintptr_t>(address) % CommitPageSize());
  DCHECK_EQ(0, size % CommitPageSize());
  return VirtualFree(address, size, MEM_DECOMMIT) != 0;
}

163
// static
164 165 166 167 168
bool OS::SetPermissions(void* address, size_t size, MemoryPermission access) {
  DCHECK_EQ(0, reinterpret_cast<uintptr_t>(address) % CommitPageSize());
  DCHECK_EQ(0, size % CommitPageSize());
  if (access == MemoryPermission::kNoAccess) {
    return VirtualFree(address, size, MEM_DECOMMIT) != 0;
169
  }
170 171 172 173
  DWORD protect = GetProtectionFromMemoryPermission(access);
  return VirtualAlloc(address, size, MEM_COMMIT, protect) != nullptr;
}

174 175 176 177 178
// static
bool OS::HasLazyCommits() {
  // TODO(alph): implement for the platform.
  return false;
}
179

180 181
std::vector<OS::SharedLibraryAddress> OS::GetSharedLibraryAddresses() {
  std::vector<SharedLibraryAddresses> result;
vegorov@chromium.org's avatar
vegorov@chromium.org committed
182 183 184 185
  // This function assumes that the layout of the file is as follows:
  // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name]
  // If we encounter an unexpected situation we abort scanning further entries.
  FILE* fp = fopen("/proc/self/maps", "r");
186
  if (fp == nullptr) return result;
vegorov@chromium.org's avatar
vegorov@chromium.org committed
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213

  // Allocate enough room to be able to store a full file name.
  const int kLibNameLen = FILENAME_MAX + 1;
  char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen));

  // This loop will terminate once the scanning hits an EOF.
  while (true) {
    uintptr_t start, end;
    char attr_r, attr_w, attr_x, attr_p;
    // Parse the addresses and permission bits at the beginning of the line.
    if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break;
    if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break;

    int c;
    if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') {
      // Found a read-only executable entry. Skip characters until we reach
      // the beginning of the filename or the end of the line.
      do {
        c = getc(fp);
      } while ((c != EOF) && (c != '\n') && (c != '/'));
      if (c == EOF) break;  // EOF: Was unexpected, just exit.

      // Process the filename if found.
      if (c == '/') {
        ungetc(c, fp);  // Push the '/' back into the stream to be read below.

        // Read to the end of the line. Exit if the read fails.
214
        if (fgets(lib_name, kLibNameLen, fp) == nullptr) break;
vegorov@chromium.org's avatar
vegorov@chromium.org committed
215 216 217 218 219 220 221 222 223 224

        // Drop the newline character read by fgets. We do not need to check
        // for a zero-length string because we know that we at least read the
        // '/' character.
        lib_name[strlen(lib_name) - 1] = '\0';
      } else {
        // No library name found, just record the raw address range.
        snprintf(lib_name, kLibNameLen,
                 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end);
      }
225
      result.push_back(SharedLibraryAddress(lib_name, start, end));
vegorov@chromium.org's avatar
vegorov@chromium.org committed
226
    } else {
227
      // Entry not describing executable data. Skip to end of line to set up
vegorov@chromium.org's avatar
vegorov@chromium.org committed
228 229 230 231 232 233 234 235 236
      // reading the next entry.
      do {
        c = getc(fp);
      } while ((c != EOF) && (c != '\n'));
      if (c == EOF) break;
    }
  }
  free(lib_name);
  fclose(fp);
237
  return result;
vegorov@chromium.org's avatar
vegorov@chromium.org committed
238 239
}

240
void OS::SignalCodeMovingGC() {
vegorov@chromium.org's avatar
vegorov@chromium.org committed
241 242 243
  // Nothing to do on Cygwin.
}

244 245
}  // namespace base
}  // namespace v8