Skip to content

Commit 7b9cc3b

Browse files
authored
Prevent GCC using FP registers for pointer storage on arm64 Linux (#193)
* Prohibit use of floating-point registers where gcc on arm64 Linux tends to use them to store pointers
1 parent 368555f commit 7b9cc3b

10 files changed

+96
-38
lines changed

binding.gyp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"bindings/profilers/heap.cc",
1616
"bindings/profilers/wall.cc",
1717
"bindings/per-isolate-data.cc",
18+
"bindings/profile-translator.cc",
1819
"bindings/thread-cpu-clock.cc",
1920
"bindings/translate-heap-profile.cc",
2021
"bindings/translate-time-profile.cc",

bindings/general-regs-only.hh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2024 Datadog, Inc
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#if defined(__linux__) && defined(__aarch64__)
20+
#define GENERAL_REGS_ONLY __attribute__((target("general-regs-only")))
21+
#else
22+
#define GENERAL_REGS_ONLY
23+
#endif

bindings/profile-translator.cc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright 2025 Datadog, Inc
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "profile-translator.hh"
18+
#include <v8.h>
19+
20+
v8::Local<v8::Number> dd::ProfileTranslator::NewNumber(int64_t x) {
21+
return v8::Number::New(isolate, static_cast<double>(x));
22+
}

bindings/profile-translator.hh

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,7 @@ class ProfileTranslator {
3333
return v8::Boolean::New(isolate, x);
3434
}
3535

36-
template <typename T>
37-
v8::Local<v8::Number> NewNumber(T x) {
38-
return v8::Number::New(isolate, x);
39-
}
36+
v8::Local<v8::Number> NewNumber(int64_t x);
4037

4138
v8::Local<v8::Array> NewArray(int length) {
4239
return length == 0 ? emptyArray : v8::Array::New(isolate, length);

bindings/profilers/heap.hh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#pragma once
1818

1919
#include <nan.h>
20+
#include "general-regs-only.hh"
2021

2122
namespace dd {
2223

@@ -34,7 +35,7 @@ class HeapProfiler {
3435
// getAllocationProfile(): AllocationProfileNode
3536
static NAN_METHOD(GetAllocationProfile);
3637

37-
static NAN_METHOD(MonitorOutOfMemory);
38+
static NAN_METHOD(MonitorOutOfMemory) GENERAL_REGS_ONLY;
3839

3940
static NAN_MODULE_INIT(Init);
4041
};

bindings/profilers/wall.cc

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,8 @@ void SignalHandler::HandleProfilerSignal(int sig,
321321
auto time_from = Now();
322322
old_handler(sig, info, context);
323323
auto time_to = Now();
324-
double async_id = node::AsyncHooksGetExecutionAsyncId(isolate);
324+
int64_t async_id =
325+
static_cast<int64_t>(node::AsyncHooksGetExecutionAsyncId(isolate));
325326
prof->PushContext(time_from, time_to, cpu_time, async_id);
326327
}
327328
#else
@@ -369,10 +370,17 @@ static int64_t GetV8ToEpochOffset() {
369370
return V8toEpochOffset;
370371
}
371372

372-
ContextsByNode WallProfiler::GetContextsByNode(CpuProfile* profile,
373-
ContextBuffer& contexts,
374-
int64_t startCpuTime) {
375-
ContextsByNode contextsByNode;
373+
Local<Number> NewNumberFromInt64(Isolate* isolate, int64_t value) {
374+
return Number::New(isolate, static_cast<double>(value));
375+
}
376+
377+
std::shared_ptr<ContextsByNode> CreateContextsByNode() {
378+
return std::make_shared<ContextsByNode>();
379+
}
380+
381+
std::shared_ptr<ContextsByNode> WallProfiler::GetContextsByNode(
382+
CpuProfile* profile, ContextBuffer& contexts, int64_t startCpuTime) {
383+
auto contextsByNode = CreateContextsByNode();
376384

377385
auto sampleCount = profile->GetSamplesCount();
378386
if (contexts.empty() || sampleCount == 0) {
@@ -432,11 +440,11 @@ ContextsByNode WallProfiler::GetContextsByNode(CpuProfile* profile,
432440
break;
433441
} else {
434442
// This sample context is the closest to this sample.
435-
auto it = contextsByNode.find(sample);
443+
auto it = contextsByNode->find(sample);
436444
Local<Array> array;
437-
if (it == contextsByNode.end()) {
445+
if (it == contextsByNode->end()) {
438446
array = Array::New(isolate);
439-
contextsByNode[sample] = {array, 1};
447+
(*contextsByNode)[sample] = {array, 1};
440448
} else {
441449
array = it->second.contexts;
442450
++it->second.hitcount;
@@ -459,17 +467,17 @@ ContextsByNode WallProfiler::GetContextsByNode(CpuProfile* profile,
459467
// sample
460468
if (collectCpuTime_ && !isIdleOrProgram(sample)) {
461469
timedContext
462-
->Set(
463-
v8Context,
464-
cpuTimeKey,
465-
Number::New(isolate, sampleContext.cpu_time - lastCpuTime))
470+
->Set(v8Context,
471+
cpuTimeKey,
472+
NewNumberFromInt64(isolate,
473+
sampleContext.cpu_time - lastCpuTime))
466474
.Check();
467475
lastCpuTime = sampleContext.cpu_time;
468476
}
469477
timedContext
470478
->Set(v8Context,
471479
asyncIdKey,
472-
Number::New(isolate, sampleContext.async_id))
480+
NewNumberFromInt64(isolate, sampleContext.async_id))
473481
.Check();
474482
array->Set(v8Context, array->Length(), timedContext).Check();
475483
}
@@ -877,7 +885,7 @@ Result WallProfiler::StopImpl(bool restart, v8::Local<v8::Value>& profile) {
877885

878886
profile = TranslateTimeProfile(v8_profile,
879887
includeLines_,
880-
&contextsByNode,
888+
contextsByNode,
881889
collectCpuTime_,
882890
nonJSThreadsCpuTime);
883891

@@ -1014,7 +1022,7 @@ NAN_METHOD(WallProfiler::Dispose) {
10141022
void WallProfiler::PushContext(int64_t time_from,
10151023
int64_t time_to,
10161024
int64_t cpu_time,
1017-
double async_id) {
1025+
int64_t async_id) {
10181026
// Be careful this is called in a signal handler context therefore all
10191027
// operations must be async signal safe (in particular no allocations).
10201028
// Our ring buffer avoids allocations.

bindings/profilers/wall.hh

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#pragma once
1818

1919
#include "contexts.hh"
20+
#include "general-regs-only.hh"
2021
#include "thread-cpu-clock.hh"
2122

2223
#include <nan.h>
@@ -82,7 +83,7 @@ class WallProfiler : public Nan::ObjectWrap {
8283
int64_t time_from;
8384
int64_t time_to;
8485
int64_t cpu_time;
85-
double async_id;
86+
int64_t async_id;
8687
};
8788

8889
using ContextBuffer = std::vector<SampleContext>;
@@ -95,9 +96,10 @@ class WallProfiler : public Nan::ObjectWrap {
9596
// to work around https://bugs.chromium.org/p/v8/issues/detail?id=11051.
9697
v8::CpuProfiler* CreateV8CpuProfiler();
9798

98-
ContextsByNode GetContextsByNode(v8::CpuProfile* profile,
99-
ContextBuffer& contexts,
100-
int64_t startCpuTime);
99+
std::shared_ptr<ContextsByNode> GetContextsByNode(v8::CpuProfile* profile,
100+
ContextBuffer& contexts,
101+
int64_t startCpuTime)
102+
GENERAL_REGS_ONLY;
101103

102104
bool waitForSignal(uint64_t targetCallCount = 0);
103105

@@ -122,10 +124,11 @@ class WallProfiler : public Nan::ObjectWrap {
122124
void PushContext(int64_t time_from,
123125
int64_t time_to,
124126
int64_t cpu_time,
125-
double async_id);
127+
int64_t async_id);
126128
Result StartImpl();
127129
std::string StartInternal();
128-
Result StopImpl(bool restart, v8::Local<v8::Value>& profile);
130+
Result StopImpl(bool restart,
131+
v8::Local<v8::Value>& profile) GENERAL_REGS_ONLY;
129132

130133
CollectionMode collectionMode() {
131134
auto res = collectionMode_.load(std::memory_order_relaxed);
@@ -146,12 +149,12 @@ class WallProfiler : public Nan::ObjectWrap {
146149
return threadCpuStopWatch_.GetAndReset();
147150
}
148151

149-
static NAN_METHOD(New);
152+
static NAN_METHOD(New) GENERAL_REGS_ONLY;
150153
static NAN_METHOD(Start);
151154
static NAN_METHOD(Stop);
152155
static NAN_METHOD(V8ProfilerStuckEventLoopDetected);
153156
static NAN_METHOD(Dispose);
154-
static NAN_MODULE_INIT(Init);
157+
static NAN_MODULE_INIT(Init) GENERAL_REGS_ONLY;
155158
static NAN_GETTER(GetContext);
156159
static NAN_SETTER(SetContext);
157160
static NAN_GETTER(SharedArrayGetter);

bindings/translate-heap-profile.hh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@
1717
#pragma once
1818

1919
#include <v8-profiler.h>
20+
#include "general-regs-only.hh"
2021

2122
namespace dd {
2223

2324
v8::Local<v8::Value> TranslateAllocationProfile(
24-
v8::AllocationProfile::Node* node);
25+
v8::AllocationProfile::Node* node) GENERAL_REGS_ONLY;
2526

2627
} // namespace dd

bindings/translate-time-profile.cc

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@
1515
*/
1616

1717
#include "translate-time-profile.hh"
18+
#include "general-regs-only.hh"
1819
#include "profile-translator.hh"
1920

2021
namespace dd {
2122

2223
namespace {
2324
class TimeProfileTranslator : ProfileTranslator {
2425
private:
25-
ContextsByNode* contextsByNode;
26+
std::shared_ptr<ContextsByNode> contextsByNode;
2627
v8::Local<v8::Array> emptyArray = NewArray(0);
2728
v8::Local<v8::Integer> zero = NewInteger(0);
2829

@@ -80,7 +81,7 @@ class TimeProfileTranslator : ProfileTranslator {
8081
}
8182

8283
v8::Local<v8::Array> GetLineNumberTimeProfileChildren(
83-
const v8::CpuProfileNode* node) {
84+
const v8::CpuProfileNode* node) GENERAL_REGS_ONLY {
8485
unsigned int index = 0;
8586
v8::Local<v8::Array> children;
8687
int32_t count = node->GetChildrenCount();
@@ -200,7 +201,7 @@ class TimeProfileTranslator : ProfileTranslator {
200201
}
201202

202203
public:
203-
explicit TimeProfileTranslator(ContextsByNode* nls = nullptr)
204+
explicit TimeProfileTranslator(std::shared_ptr<ContextsByNode> nls = nullptr)
204205
: contextsByNode(nls) {}
205206

206207
v8::Local<v8::Value> TranslateTimeProfile(const v8::CpuProfile* profile,
@@ -230,11 +231,12 @@ class TimeProfileTranslator : ProfileTranslator {
230231
};
231232
} // namespace
232233

233-
v8::Local<v8::Value> TranslateTimeProfile(const v8::CpuProfile* profile,
234-
bool includeLineInfo,
235-
ContextsByNode* contextsByNode,
236-
bool hasCpuTime,
237-
int64_t nonJSThreadsCpuTime) {
234+
v8::Local<v8::Value> TranslateTimeProfile(
235+
const v8::CpuProfile* profile,
236+
bool includeLineInfo,
237+
std::shared_ptr<ContextsByNode> contextsByNode,
238+
bool hasCpuTime,
239+
int64_t nonJSThreadsCpuTime) {
238240
return TimeProfileTranslator(contextsByNode)
239241
.TranslateTimeProfile(
240242
profile, includeLineInfo, hasCpuTime, nonJSThreadsCpuTime);

bindings/translate-time-profile.hh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ namespace dd {
2424
v8::Local<v8::Value> TranslateTimeProfile(
2525
const v8::CpuProfile* profile,
2626
bool includeLineInfo,
27-
ContextsByNode* contextsByNode = nullptr,
27+
std::shared_ptr<ContextsByNode> contextsByNode = nullptr,
2828
bool hasCpuTime = false,
2929
int64_t nonJSThreadsCpuTime = 0);
3030

0 commit comments

Comments
 (0)