1 // Copyright 2014 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 AIX goes here. For the POSIX comaptible parts
6 // the implementation is in platform-posix.cc.
7
8 #include <pthread.h>
9 #include <semaphore.h>
10 #include <signal.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <sys/resource.h>
14 #include <sys/time.h>
15 #include <sys/ucontext.h>
16
17 #include <errno.h>
18 #include <fcntl.h> // open
19 #include <limits.h>
20 #include <stdarg.h>
21 #include <strings.h> // index
22 #include <sys/mman.h> // mmap & munmap
23 #include <sys/stat.h> // open
24 #include <sys/types.h> // mmap & munmap
25 #include <unistd.h> // getpagesize
26
27 #include <cmath>
28
29 #undef MAP_TYPE
30
31 #include "src/base/macros.h"
32 #include "src/base/platform/platform-posix.h"
33 #include "src/base/platform/platform.h"
34
35 namespace v8 {
36 namespace base {
37
38
get_gmt_offset(const tm & localtm)39 int64_t get_gmt_offset(const tm& localtm) {
40 // replacement for tm->tm_gmtoff field in glibc
41 // returns seconds east of UTC, taking DST into account
42 struct timeval tv;
43 struct timezone tz;
44 int ret_code = gettimeofday(&tv, &tz);
45 // 0 = success, -1 = failure
46 DCHECK_NE(ret_code, -1);
47 if (ret_code == -1) {
48 return 0;
49 }
50 return (-tz.tz_minuteswest * 60) + (localtm.tm_isdst > 0 ? 3600 : 0);
51 }
52
53 class AIXTimezoneCache : public PosixTimezoneCache {
54 const char* LocalTimezone(double time) override;
55
56 double LocalTimeOffset(double time_ms, bool is_utc) override;
57
~AIXTimezoneCache()58 ~AIXTimezoneCache() override {}
59 };
60
LocalTimezone(double time_ms)61 const char* AIXTimezoneCache::LocalTimezone(double time_ms) {
62 if (std::isnan(time_ms)) return "";
63 time_t tv = static_cast<time_t>(floor(time_ms / msPerSecond));
64 struct tm tm;
65 struct tm* t = localtime_r(&tv, &tm);
66 if (nullptr == t) return "";
67 return tzname[0]; // The location of the timezone string on AIX.
68 }
69
LocalTimeOffset(double time_ms,bool is_utc)70 double AIXTimezoneCache::LocalTimeOffset(double time_ms, bool is_utc) {
71 // On AIX, struct tm does not contain a tm_gmtoff field, use get_gmt_offset
72 // helper function
73 time_t utc = time(nullptr);
74 DCHECK_NE(utc, -1);
75 struct tm tm;
76 struct tm* loc = localtime_r(&utc, &tm);
77 DCHECK_NOT_NULL(loc);
78 return static_cast<double>(get_gmt_offset(*loc) * msPerSecond -
79 (loc->tm_isdst > 0 ? 3600 * msPerSecond : 0));
80 }
81
CreateTimezoneCache()82 TimezoneCache* OS::CreateTimezoneCache() { return new AIXTimezoneCache(); }
83
StringToLong(char * buffer)84 static unsigned StringToLong(char* buffer) {
85 return static_cast<unsigned>(strtol(buffer, nullptr, 16));
86 }
87
GetSharedLibraryAddresses()88 std::vector<OS::SharedLibraryAddress> OS::GetSharedLibraryAddresses() {
89 std::vector<SharedLibraryAddress> result;
90 static const int MAP_LENGTH = 1024;
91 int fd = open("/proc/self/maps", O_RDONLY);
92 if (fd < 0) return result;
93 while (true) {
94 char addr_buffer[11];
95 addr_buffer[0] = '0';
96 addr_buffer[1] = 'x';
97 addr_buffer[10] = 0;
98 ssize_t rc = read(fd, addr_buffer + 2, 8);
99 if (rc < 8) break;
100 unsigned start = StringToLong(addr_buffer);
101 rc = read(fd, addr_buffer + 2, 1);
102 if (rc < 1) break;
103 if (addr_buffer[2] != '-') break;
104 rc = read(fd, addr_buffer + 2, 8);
105 if (rc < 8) break;
106 unsigned end = StringToLong(addr_buffer);
107 char buffer[MAP_LENGTH];
108 int bytes_read = -1;
109 do {
110 bytes_read++;
111 if (bytes_read >= MAP_LENGTH - 1) break;
112 rc = read(fd, buffer + bytes_read, 1);
113 if (rc < 1) break;
114 } while (buffer[bytes_read] != '\n');
115 buffer[bytes_read] = 0;
116 // Ignore mappings that are not executable.
117 if (buffer[3] != 'x') continue;
118 char* start_of_path = index(buffer, '/');
119 // There may be no filename in this line. Skip to next.
120 if (start_of_path == nullptr) continue;
121 buffer[bytes_read] = 0;
122 result.push_back(SharedLibraryAddress(start_of_path, start, end));
123 }
124 close(fd);
125 return result;
126 }
127
SignalCodeMovingGC()128 void OS::SignalCodeMovingGC() {}
129
AdjustSchedulingParams()130 void OS::AdjustSchedulingParams() {}
131
GetFreeMemoryRangesWithin(OS::Address boundary_start,OS::Address boundary_end,size_t minimum_size,size_t alignment)132 std::vector<OS::MemoryRange> OS::GetFreeMemoryRangesWithin(
133 OS::Address boundary_start, OS::Address boundary_end, size_t minimum_size,
134 size_t alignment) {
135 return {};
136 }
137
138 // static
GetStackStart()139 Stack::StackSlot Stack::GetStackStart() {
140 // pthread_getthrds_np creates 3 values:
141 // __pi_stackaddr, __pi_stacksize, __pi_stackend
142
143 // higher address ----- __pi_stackend, stack base
144 //
145 // |
146 // | __pi_stacksize, stack grows downwards
147 // |
148 // V
149 //
150 // lower address ----- __pi_stackaddr, current sp
151
152 pthread_t tid = pthread_self();
153 struct __pthrdsinfo buf;
154 // clear buf
155 memset(&buf, 0, sizeof(buf));
156 char regbuf[1];
157 int regbufsize = sizeof(regbuf);
158 const int rc = pthread_getthrds_np(&tid, PTHRDSINFO_QUERY_ALL, &buf,
159 sizeof(buf), regbuf, ®bufsize);
160 CHECK(!rc);
161 if (buf.__pi_stackend == NULL || buf.__pi_stackaddr == NULL) {
162 return nullptr;
163 }
164 return reinterpret_cast<void*>(buf.__pi_stackend);
165 }
166
167 // static
DecommitPages(void * address,size_t size)168 bool OS::DecommitPages(void* address, size_t size) {
169 // The difference between this implementation and the alternative under
170 // platform-posix.cc is that on AIX, calling mmap on a pre-designated address
171 // with MAP_FIXED will fail and return -1 unless the application has requested
172 // SPEC1170 compliant behaviour:
173 // https://www.ibm.com/docs/en/aix/7.3?topic=m-mmap-mmap64-subroutine
174 // Therefore in case if failure we need to unmap the address before trying to
175 // map it again. The downside is another thread could place another mapping at
176 // the same address after the munmap but before the mmap, therefore a CHECK is
177 // also added to assure the address is mapped successfully. Refer to the
178 // comments under https://crrev.com/c/3010195 for more details.
179 #define MMAP() \
180 mmap(address, size, PROT_NONE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)
181 DCHECK_EQ(0, reinterpret_cast<uintptr_t>(address) % CommitPageSize());
182 DCHECK_EQ(0, size % CommitPageSize());
183 void* ptr;
184 // Try without mapping first.
185 ptr = MMAP();
186 if (ptr != address) {
187 DCHECK_EQ(ptr, MAP_FAILED);
188 // Returns 0 when successful.
189 if (munmap(address, size)) {
190 return false;
191 }
192 // Try again after unmap.
193 ptr = MMAP();
194 // If this check fails it's most likely due to a racing condition where
195 // another thread has mapped the same address right before we do.
196 // Since this could cause hard-to-debug issues, potentially with security
197 // impact, and we can't recover from this, the best we can do is abort the
198 // process.
199 CHECK_EQ(ptr, address);
200 }
201 #undef MMAP
202 return true;
203 }
204
205 } // namespace base
206 } // namespace v8
207