mirror of
https://github.com/JakeHillion/object-introspection.git
synced 2024-11-13 22:06:55 +00:00
129 lines
3.3 KiB
C++
129 lines
3.3 KiB
C++
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#include "oi/Descs.h"
|
|
|
|
#include <glog/logging.h>
|
|
|
|
#include <algorithm>
|
|
#include <boost/scope_exit.hpp>
|
|
#include <charconv>
|
|
#include <iostream>
|
|
#include <utility>
|
|
|
|
extern "C" {
|
|
#include <drgn.h>
|
|
#include <sys/user.h>
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os, const FuncDesc::Range& r) {
|
|
return os << (void*)r.start << ':' << (void*)r.end;
|
|
}
|
|
|
|
/*
|
|
* Given a register set return the address where the supplied argument
|
|
* position can be found at the given pc (what about if we don't have this
|
|
* location?).
|
|
*/
|
|
std::optional<uintptr_t> FuncDesc::Arg::findAddress(
|
|
struct user_regs_struct* regs, uintptr_t pc) const {
|
|
auto prevRip = std::exchange(regs->rip, pc);
|
|
BOOST_SCOPE_EXIT_ALL(&) {
|
|
regs->rip = prevRip;
|
|
};
|
|
|
|
struct drgn_object object {};
|
|
BOOST_SCOPE_EXIT_ALL(&) {
|
|
drgn_object_deinit(&object);
|
|
};
|
|
|
|
if (auto* err = drgn_object_locate(&locator, regs, &object)) {
|
|
LOG(ERROR) << "Error while finding address of argument: " << err->message;
|
|
drgn_error_destroy(err);
|
|
return std::nullopt;
|
|
}
|
|
|
|
return object.address;
|
|
}
|
|
|
|
std::optional<uint8_t> FuncDesc::getArgumentIndex(const std::string& arg,
|
|
bool validateIndex) const {
|
|
if (arg == "retval") {
|
|
return std::nullopt;
|
|
}
|
|
|
|
if (arg == "this") {
|
|
if (!isMethod) {
|
|
LOG(ERROR) << "Function " << symName << " has no 'this' parameter";
|
|
return std::nullopt;
|
|
}
|
|
return 0;
|
|
}
|
|
//
|
|
// Extract arg's number
|
|
auto it = arg.find_first_of("0123456789");
|
|
if (it == std::string::npos) {
|
|
LOG(ERROR) << "Invalid argument: " << arg;
|
|
return std::nullopt;
|
|
}
|
|
|
|
const auto* argIdxBegin = arg.data() + it;
|
|
const auto* argIdxEnd = arg.data() + arg.size();
|
|
|
|
uint8_t argIdx = 0;
|
|
if (auto res = std::from_chars(argIdxBegin, argIdxEnd, argIdx);
|
|
res.ec != std::errc{}) {
|
|
LOG(ERROR) << "Failed to convert " << arg
|
|
<< " digits: " << strerror((int)res.ec);
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Check and offset for methods
|
|
if (validateIndex && argIdx >= numArgs()) {
|
|
LOG(ERROR) << "Argument index " << (int)argIdx
|
|
<< " too large. Args count: " << numArgs();
|
|
return std::nullopt;
|
|
}
|
|
|
|
if (isMethod) {
|
|
argIdx += 1;
|
|
}
|
|
|
|
return argIdx;
|
|
}
|
|
|
|
std::shared_ptr<FuncDesc::TargetObject> FuncDesc::getArgument(
|
|
const std::string& arg) {
|
|
std::shared_ptr<FuncDesc::TargetObject> outArg;
|
|
|
|
if (arg == "retval") {
|
|
outArg = retval;
|
|
} else {
|
|
auto argIdx = getArgumentIndex(arg);
|
|
if (!argIdx.has_value()) {
|
|
return nullptr;
|
|
}
|
|
|
|
outArg = arguments[*argIdx];
|
|
}
|
|
|
|
if (!outArg || !outArg->valid) {
|
|
LOG(ERROR) << "Argument " << arg << " for " << symName << " is invalid";
|
|
return nullptr;
|
|
}
|
|
|
|
return outArg;
|
|
}
|