Skip to content

Commit dabd3e3

Browse files
committed
[lldb][RISCV] add DirectToIndirectFCR pass
Function calls support in LLDB expressions for RISCV: 4 of 6 Adds RISCV specific DirectToIndirectFunctionCallsReplacement IR pass, that allows to make assembly jumps at any 64bit address without RISCV large code model, which has not been implemented yet. This pass is needed, because jitted code contains more that +-2GB jumps, which are not available in RISCV without large code model now.
1 parent 589d071 commit dabd3e3

File tree

2 files changed

+268
-0
lines changed

2 files changed

+268
-0
lines changed
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
//===--- DirectToIndirectFCR.cpp - RISC-V specific pass -------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/IR/Constants.h"
10+
#include "llvm/IR/Function.h"
11+
#include "llvm/IR/IRBuilder.h"
12+
#include "llvm/IR/InstIterator.h"
13+
#include "llvm/IR/Instructions.h"
14+
#include "llvm/IR/LLVMContext.h"
15+
#include "llvm/IR/Module.h"
16+
#include "llvm/IR/Value.h"
17+
#include "llvm/InitializePasses.h"
18+
#include "llvm/Support/Casting.h"
19+
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
20+
21+
#include "Plugins/Architecture/RISCV/DirectToIndirectFCR.h"
22+
23+
#include "lldb/Core/Architecture.h"
24+
#include "lldb/Core/Module.h"
25+
#include "lldb/Symbol/Symtab.h"
26+
#include "lldb/Target/ExecutionContext.h"
27+
#include "lldb/Target/Process.h"
28+
#include "lldb/Target/Target.h"
29+
#include "lldb/Utility/ConstString.h"
30+
#include "lldb/Utility/LLDBLog.h"
31+
#include "lldb/Utility/Log.h"
32+
33+
#include <optional>
34+
35+
using namespace llvm;
36+
using namespace lldb_private;
37+
38+
namespace {
39+
std::string GetValueTypeStr(const llvm::Type *value_ty) {
40+
assert(value_ty);
41+
std::string str_type;
42+
llvm::raw_string_ostream rso(str_type);
43+
value_ty->print(rso);
44+
return rso.str();
45+
}
46+
47+
template <typename... Args> void LogMessage(const char *msg, Args &&...args) {
48+
Log *log = GetLog(LLDBLog::Expressions);
49+
LLDB_LOG(log, msg, std::forward<Args>(args)...);
50+
}
51+
52+
std::string getFunctionName(const llvm::CallInst *ci) {
53+
return ci->isIndirectCall() ? "indirect"
54+
: ci->getCalledFunction()->getName().str();
55+
}
56+
} // namespace
57+
58+
bool DirectToIndirectFCR::canBeReplaced(const llvm::CallInst *ci) {
59+
assert(ci);
60+
auto *return_value_ty = ci->getType();
61+
if (!(return_value_ty->isIntegerTy() || return_value_ty->isVoidTy() ||
62+
return_value_ty->isPointerTy())) {
63+
LogMessage("DirectToIndirectFCR: function {0} has unsupported "
64+
"return type ({1})\n",
65+
getFunctionName(ci), GetValueTypeStr(return_value_ty));
66+
return false;
67+
}
68+
69+
const auto *arg = llvm::find_if_not(ci->args(), [](const auto &arg) {
70+
const auto *type = arg->getType();
71+
return type->isIntegerTy() || type->isPointerTy();
72+
});
73+
74+
if (arg != ci->arg_end()) {
75+
LogMessage("DirectToIndirectFCR: argument {0} of {1} function "
76+
"has unsupported type ({2})\n",
77+
(*arg)->getName(), getFunctionName(ci),
78+
GetValueTypeStr((*arg)->getType()));
79+
return false;
80+
}
81+
return true;
82+
}
83+
84+
std::vector<llvm::Value *>
85+
DirectToIndirectFCR::getFunctionArgsAsValues(const llvm::CallInst *ci) {
86+
assert(ci);
87+
std::vector<llvm::Value *> args{};
88+
llvm::transform(ci->args(), std::back_inserter(args),
89+
[](const auto &arg) { return arg.get(); });
90+
return args;
91+
}
92+
93+
std::optional<lldb::addr_t>
94+
DirectToIndirectFCR::getFunctionAddress(const llvm::CallInst *ci) const {
95+
auto *target = m_exe_ctx.GetTargetPtr();
96+
const auto &lldb_module_sp = target->GetExecutableModule();
97+
const auto &symtab = lldb_module_sp->GetSymtab();
98+
const llvm::StringRef name = ci->getCalledFunction()->getName();
99+
100+
// eSymbolTypeCode: we try to find function
101+
// eDebugNo: not a debug symbol
102+
// eVisibilityExtern: function from extern module
103+
const auto *symbol = symtab->FindFirstSymbolWithNameAndType(
104+
ConstString(name), lldb::SymbolType::eSymbolTypeCode,
105+
Symtab::Debug::eDebugNo, Symtab::Visibility::eVisibilityExtern);
106+
if (!symbol) {
107+
LogMessage("DirectToIndirectFCR: can't find {0} in symtab\n", name);
108+
return std::nullopt;
109+
}
110+
111+
lldb::addr_t addr = symbol->GetLoadAddress(target);
112+
LogMessage("DirectToIndirectFCR: found address ({0}) of symbol {1}\n", addr,
113+
name);
114+
return addr;
115+
}
116+
117+
llvm::CallInst *DirectToIndirectFCR::getInstReplace(llvm::CallInst *ci) const {
118+
assert(ci);
119+
120+
std::optional<lldb::addr_t> addr_or_null = getFunctionAddress(ci);
121+
if (!addr_or_null.has_value())
122+
return nullptr;
123+
124+
lldb::addr_t addr = addr_or_null.value();
125+
126+
llvm::IRBuilder<> builder(ci);
127+
128+
std::vector<llvm::Value *> args = getFunctionArgsAsValues(ci);
129+
llvm::Constant *func_addr = builder.getInt64(addr);
130+
llvm::PointerType *ptr_func_ty = builder.getPtrTy();
131+
auto *cast = builder.CreateIntToPtr(func_addr, ptr_func_ty);
132+
auto *new_inst =
133+
builder.CreateCall(ci->getFunctionType(), cast, ArrayRef(args));
134+
return new_inst;
135+
}
136+
137+
DirectToIndirectFCR::DirectToIndirectFCR(const ExecutionContext &exe_ctx)
138+
: FunctionPass(ID), m_exe_ctx{exe_ctx} {}
139+
140+
DirectToIndirectFCR::~DirectToIndirectFCR() = default;
141+
142+
bool DirectToIndirectFCR::runOnFunction(llvm::Function &func) {
143+
bool has_irreplaceable =
144+
llvm::any_of(instructions(func), [this](llvm::Instruction &inst) {
145+
llvm::CallInst *ci = dyn_cast<llvm::CallInst>(&inst);
146+
if (!ci)
147+
return false;
148+
149+
// The function signature does not match the call signature.
150+
if (!ci->isIndirectCall() && !ci->getCalledFunction())
151+
return true;
152+
153+
if (!ci->isIndirectCall() && ci->getCalledFunction()->isIntrinsic())
154+
return false;
155+
156+
if (DirectToIndirectFCR::canBeReplaced(ci) &&
157+
getFunctionAddress(ci).has_value())
158+
return false;
159+
160+
return true;
161+
});
162+
163+
if (has_irreplaceable) {
164+
func.getParent()->getOrInsertNamedMetadata(
165+
Architecture::s_target_incompatibility_marker);
166+
return false;
167+
}
168+
169+
std::vector<std::reference_wrapper<llvm::Instruction>>
170+
replaceable_function_calls{};
171+
llvm::copy_if(instructions(func),
172+
std::back_inserter(replaceable_function_calls),
173+
[](llvm::Instruction &inst) {
174+
llvm::CallInst *ci = dyn_cast<llvm::CallInst>(&inst);
175+
if (ci && !ci->isIndirectCall() &&
176+
!ci->getCalledFunction()->isIntrinsic())
177+
return true;
178+
return false;
179+
});
180+
181+
if (replaceable_function_calls.empty())
182+
return false;
183+
184+
std::vector<std::pair<llvm::CallInst *, llvm::CallInst *>> replaces;
185+
llvm::transform(replaceable_function_calls, std::back_inserter(replaces),
186+
[this](std::reference_wrapper<llvm::Instruction> inst)
187+
-> std::pair<llvm::CallInst *, llvm::CallInst *> {
188+
llvm::CallInst *ci = cast<llvm::CallInst>(&(inst.get()));
189+
llvm::CallInst *new_inst = getInstReplace(ci);
190+
return {ci, new_inst};
191+
});
192+
193+
for (auto &&[from, to] : replaces) {
194+
from->replaceAllUsesWith(to);
195+
from->eraseFromParent();
196+
}
197+
198+
return true;
199+
}
200+
201+
llvm::StringRef DirectToIndirectFCR::getPassName() const {
202+
return "Transform function calls to calls by address";
203+
}
204+
205+
char DirectToIndirectFCR::ID = 0;
206+
207+
llvm::FunctionPass *
208+
lldb_private::createDirectToIndirectFCR(const ExecutionContext &exe_ctx) {
209+
return new DirectToIndirectFCR(exe_ctx);
210+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//===--- DirectToIndirectFCR.h - RISC-V specific pass ---------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#pragma once
10+
11+
#include "lldb/lldb-types.h"
12+
13+
#include "llvm/IR/Instructions.h"
14+
#include "llvm/Pass.h"
15+
16+
namespace lldb_private {
17+
18+
class ExecutionContext;
19+
20+
// During the lldb expression execution lldb wraps a user expression, jittes
21+
// fabricated code and then puts it into the stack memory. Thus, if user tried
22+
// to make a function call there will be a jump from a stack address to a code
23+
// sections's address. RISC-V Architecture doesn't have a large code model yet
24+
// and can make only a +-2GiB jumps, but in 64-bit architecture a distance
25+
// between stack addresses and code sections's addresses is longer. Therefore,
26+
// relocations resolver obtains an invalid address. To avoid such problem, this
27+
// pass should be used. It replaces function calls with appropriate function's
28+
// addresses explicitly. By doing so it removes relocations related to function
29+
// calls. This pass should be cosidered as temprorary solution until a large
30+
// code model will be approved.
31+
class DirectToIndirectFCR : public llvm::FunctionPass {
32+
33+
static bool canBeReplaced(const llvm::CallInst *ci);
34+
35+
static std::vector<llvm::Value *>
36+
getFunctionArgsAsValues(const llvm::CallInst *ci);
37+
38+
std::optional<lldb::addr_t>
39+
getFunctionAddress(const llvm::CallInst *ci) const;
40+
41+
llvm::CallInst *getInstReplace(llvm::CallInst *ci) const;
42+
43+
public:
44+
static char ID;
45+
46+
DirectToIndirectFCR(const ExecutionContext &exe_ctx);
47+
~DirectToIndirectFCR() override;
48+
49+
bool runOnFunction(llvm::Function &func) override;
50+
51+
llvm::StringRef getPassName() const override;
52+
53+
private:
54+
const ExecutionContext &m_exe_ctx;
55+
};
56+
57+
llvm::FunctionPass *createDirectToIndirectFCR(const ExecutionContext &exe_ctx);
58+
} // namespace lldb_private

0 commit comments

Comments
 (0)