mirror of
https://github.com/rizinorg/cutter.git
synced 2025-01-20 11:28:51 +00:00
Merge branch 'dev' into Rohan-branch
This commit is contained in:
commit
3c7a987b0d
@ -1,4 +1,4 @@
|
|||||||
version: '2.2.0-git-{build}'
|
version: '2.4.0-git-{build}'
|
||||||
image: 'Visual Studio 2017'
|
image: 'Visual Studio 2017'
|
||||||
clone_depth: 1
|
clone_depth: 1
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ if(NOT CUTTER_ENABLE_PYTHON)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CUTTER_VERSION_MAJOR 2)
|
set(CUTTER_VERSION_MAJOR 2)
|
||||||
set(CUTTER_VERSION_MINOR 2)
|
set(CUTTER_VERSION_MINOR 4)
|
||||||
set(CUTTER_VERSION_PATCH 0)
|
set(CUTTER_VERSION_PATCH 0)
|
||||||
|
|
||||||
set(CUTTER_VERSION "${CUTTER_VERSION_MAJOR}.${CUTTER_VERSION_MINOR}.${CUTTER_VERSION_PATCH}")
|
set(CUTTER_VERSION "${CUTTER_VERSION_MAJOR}.${CUTTER_VERSION_MINOR}.${CUTTER_VERSION_PATCH}")
|
||||||
|
@ -57,12 +57,11 @@ endif()
|
|||||||
|
|
||||||
# TODO: This version number should be fetched automatically
|
# TODO: This version number should be fetched automatically
|
||||||
# instead of being hardcoded.
|
# instead of being hardcoded.
|
||||||
set (Rizin_VERSION 0.7)
|
set (Rizin_VERSION 0.8)
|
||||||
|
|
||||||
set (RZ_LIBS rz_core rz_config rz_cons rz_io rz_util rz_flag rz_asm rz_debug
|
set (RZ_LIBS rz_core rz_config rz_cons rz_io rz_util rz_flag rz_arch rz_debug
|
||||||
rz_hash rz_bin rz_lang rz_il rz_analysis rz_parse rz_bp rz_egg rz_reg
|
rz_hash rz_bin rz_lang rz_il rz_bp rz_egg rz_reg rz_search rz_syscall
|
||||||
rz_search rz_syscall rz_socket rz_magic rz_crypto rz_type rz_diff rz_sign
|
rz_socket rz_magic rz_crypto rz_type rz_diff rz_sign rz_demangler)
|
||||||
rz_demangler)
|
|
||||||
set (RZ_EXTRA_LIBS rz_main)
|
set (RZ_EXTRA_LIBS rz_main)
|
||||||
set (RZ_BIN rz-bin rizin rz-diff rz-find rz-gg rz-hash rz-run rz-asm rz-ax)
|
set (RZ_BIN rz-bin rizin rz-diff rz-find rz-gg rz-hash rz-run rz-asm rz-ax)
|
||||||
|
|
||||||
|
11
dist/CMakeLists.txt
vendored
11
dist/CMakeLists.txt
vendored
@ -24,7 +24,9 @@ if(WIN32)
|
|||||||
install(CODE "
|
install(CODE "
|
||||||
set(ENV{RZ_PREFIX} \"\${CMAKE_INSTALL_PREFIX}\")
|
set(ENV{RZ_PREFIX} \"\${CMAKE_INSTALL_PREFIX}\")
|
||||||
set(ENV{PATH} \"\${CMAKE_INSTALL_PREFIX};\$ENV{PATH}\")
|
set(ENV{PATH} \"\${CMAKE_INSTALL_PREFIX};\$ENV{PATH}\")
|
||||||
execute_process(COMMAND powershell \"${CMAKE_CURRENT_SOURCE_DIR}/bundle_jsdec.ps1\" \"\${CMAKE_INSTALL_PREFIX}\"
|
execute_process(COMMAND powershell \"${CMAKE_CURRENT_SOURCE_DIR}/bundle_jsdec.ps1\"
|
||||||
|
\"\${CMAKE_INSTALL_PREFIX}\"
|
||||||
|
\"-DCUTTER_INSTALL_PLUGDIR=plugins/native\"
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
RESULT_VARIABLE SCRIPT_RESULT)
|
RESULT_VARIABLE SCRIPT_RESULT)
|
||||||
if (SCRIPT_RESULT)
|
if (SCRIPT_RESULT)
|
||||||
@ -124,9 +126,14 @@ endif()
|
|||||||
|
|
||||||
if(CUTTER_ENABLE_DEPENDENCY_DOWNLOADS AND (NOT WIN32))
|
if(CUTTER_ENABLE_DEPENDENCY_DOWNLOADS AND (NOT WIN32))
|
||||||
if (CUTTER_PACKAGE_JSDEC)
|
if (CUTTER_PACKAGE_JSDEC)
|
||||||
|
if(APPLE)
|
||||||
|
set (JSDEC_PLUGIN_OPTIONS "-DCUTTER_INSTALL_PLUGDIR=plugins/native")
|
||||||
|
else()
|
||||||
|
set (JSDEC_PLUGIN_OPTIONS "")
|
||||||
|
endif()
|
||||||
install(CODE "
|
install(CODE "
|
||||||
execute_process(COMMAND \"${CMAKE_CURRENT_SOURCE_DIR}/../scripts/jsdec.sh\"
|
execute_process(COMMAND \"${CMAKE_CURRENT_SOURCE_DIR}/../scripts/jsdec.sh\"
|
||||||
--pkg-config-path=\${CMAKE_INSTALL_PREFIX}/lib/pkgconfig --prefix=\${CMAKE_INSTALL_PREFIX}
|
\"\${CMAKE_INSTALL_PREFIX}\" \"${JSDEC_PLUGIN_OPTIONS}\"
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
RESULT_VARIABLE SCRIPT_RESULT)
|
RESULT_VARIABLE SCRIPT_RESULT)
|
||||||
if (SCRIPT_RESULT)
|
if (SCRIPT_RESULT)
|
||||||
|
24
dist/bundle_jsdec.ps1
vendored
24
dist/bundle_jsdec.ps1
vendored
@ -2,16 +2,26 @@ $dist = $args[0]
|
|||||||
$python = Split-Path((Get-Command python.exe).Path)
|
$python = Split-Path((Get-Command python.exe).Path)
|
||||||
|
|
||||||
if (-not (Test-Path -Path 'jsdec' -PathType Container)) {
|
if (-not (Test-Path -Path 'jsdec' -PathType Container)) {
|
||||||
git clone https://github.com/rizinorg/jsdec.git --depth 1 --branch "v0.7.0"
|
git clone https://github.com/rizinorg/jsdec.git --depth 1 --branch "dev"
|
||||||
}
|
}
|
||||||
cd jsdec
|
cd jsdec
|
||||||
& meson.exe --buildtype=release --prefix="$dist" build
|
$jsdecdir = (Get-Item .).FullName
|
||||||
ninja -C build install
|
|
||||||
|
& meson.exe setup --buildtype=release -Dbuild_type=cutter "$jsdecdir\build_lib"
|
||||||
|
ninja -C "$jsdecdir\build_lib"
|
||||||
|
|
||||||
|
# cmake is silly and expects .lib but meson generates the static lib as .a
|
||||||
|
Copy-Item "$jsdecdir\build_lib\libjsdec.a" -Destination "$jsdecdir\build_lib\jsdec.lib"
|
||||||
|
|
||||||
|
mkdir build_plugin
|
||||||
|
cd build_plugin
|
||||||
|
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DJSDEC_BUILD_DIR="$jsdecdir\build_lib" -DCMAKE_INSTALL_PREFIX="$dist" $cmake_opts "$jsdecdir\cutter-plugin"
|
||||||
|
ninja install
|
||||||
|
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
$pathdll = "$dist\lib\rizin\plugins\core_pdd.dll"
|
$pathdll = "$dist\share\rizin\cutter\plugins\native\jsdec_cutter.dll"
|
||||||
if(![System.IO.File]::Exists($pathdll)) {
|
if(![System.IO.File]::Exists($pathdll)) {
|
||||||
type build\meson-logs\meson-log.txt
|
echo "files: $dist\share\rizin\cutter\plugins\native\"
|
||||||
ls "$dist\lib\rizin\plugins\"
|
ls "$dist\share\rizin\cutter\plugins\native\"
|
||||||
throw (New-Object System.IO.FileNotFoundException("File not found: $pathdll", $pathdll))
|
throw (New-Object System.IO.FileNotFoundException("File not found: $pathdll", $pathdll))
|
||||||
}
|
}
|
||||||
Remove-Item -Recurse -Force "$dist\lib\rizin\plugins\core_pdd.lib"
|
|
||||||
|
2
dist/bundle_rz_libyara.ps1
vendored
2
dist/bundle_rz_libyara.ps1
vendored
@ -3,7 +3,7 @@ $cmake_opts = $args[1]
|
|||||||
$python = Split-Path((Get-Command python.exe).Path)
|
$python = Split-Path((Get-Command python.exe).Path)
|
||||||
|
|
||||||
if (-not (Test-Path -Path 'rz_libyara' -PathType Container)) {
|
if (-not (Test-Path -Path 'rz_libyara' -PathType Container)) {
|
||||||
git clone https://github.com/rizinorg/rz-libyara.git --depth 1 rz_libyara
|
git clone https://github.com/rizinorg/rz-libyara.git --depth 1 --branch main rz_libyara
|
||||||
git -C rz_libyara submodule init
|
git -C rz_libyara submodule init
|
||||||
git -C rz_libyara submodule update
|
git -C rz_libyara submodule update
|
||||||
}
|
}
|
||||||
|
@ -24,9 +24,9 @@ copyright = '2020, The Cutter Developers'
|
|||||||
author = 'The Cutter Developers'
|
author = 'The Cutter Developers'
|
||||||
|
|
||||||
# The short X.Y version
|
# The short X.Y version
|
||||||
version = '2.2'
|
version = '2.4'
|
||||||
# The full version, including a2lpha/beta/rc tags
|
# The full version, including a2lpha/beta/rc tags
|
||||||
release = '2.2.0'
|
release = '2.4.0'
|
||||||
|
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
# -- General configuration ---------------------------------------------------
|
||||||
|
2
rizin
2
rizin
@ -1 +1 @@
|
|||||||
Subproject commit fa455f8b5244ad0cebe2fa8aca1c71096f55dfa1
|
Subproject commit 34f1a9e7b40e289cdf8e7f03c145bdbd5d41dc89
|
@ -1,20 +1,24 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
INSTALL_PREFIX="$1"
|
||||||
|
EXTRA_CMAKE_OPTS="$2"
|
||||||
|
|
||||||
SCRIPTPATH=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
|
SCRIPTPATH=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
|
||||||
|
|
||||||
cd "$SCRIPTPATH/.."
|
cd "$SCRIPTPATH/.."
|
||||||
|
|
||||||
if [ ! -d jsdec ]; then
|
if [ ! -d jsdec ]; then
|
||||||
git clone https://github.com/rizinorg/jsdec.git --depth 1 --branch "v0.7.0"
|
git clone https://github.com/rizinorg/jsdec.git --depth 1 --branch "dev"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd jsdec
|
cd jsdec
|
||||||
if [ -d build ]; then
|
if [ -d build_lib ]; then
|
||||||
rm -rf build
|
rm -rf build_lib
|
||||||
fi
|
fi
|
||||||
meson --buildtype=release "$@" build
|
meson setup --buildtype=release --pkg-config-path="$INSTALL_PREFIX/lib/pkgconfig" -Dbuild_type=cutter build_lib
|
||||||
ninja -C build
|
ninja -C build_lib
|
||||||
ninja -C build install
|
|
||||||
|
|
||||||
|
mkdir build_plugin && cd build_plugin
|
||||||
|
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DJSDEC_BUILD_DIR="../build_lib" -DCMAKE_INSTALL_PREFIX="$INSTALL_PREFIX" $EXTRA_CMAKE_OPTS ../cutter-plugin
|
||||||
|
ninja install
|
||||||
|
@ -8,14 +8,14 @@ EXTRA_CMAKE_OPTS="$2"
|
|||||||
cd "$SCRIPTPATH/.."
|
cd "$SCRIPTPATH/.."
|
||||||
|
|
||||||
if [[ ! -d rz_libyara ]]; then
|
if [[ ! -d rz_libyara ]]; then
|
||||||
git clone https://github.com/rizinorg/rz-libyara.git --depth 1 rz_libyara
|
git clone https://github.com/rizinorg/rz-libyara.git --depth 1 --branch main rz_libyara
|
||||||
git -C rz_libyara submodule init
|
git -C rz_libyara submodule init
|
||||||
git -C rz_libyara submodule update
|
git -C rz_libyara submodule update
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd rz_libyara
|
cd rz_libyara
|
||||||
|
|
||||||
meson --buildtype=release --pkg-config-path="$INSTALL_PREFIX/lib/pkgconfig" --prefix="$INSTALL_PREFIX" -Duse_sys_yara=disabled build
|
meson --buildtype=release --pkg-config-path="$INSTALL_PREFIX/lib/pkgconfig" --prefix="$INSTALL_PREFIX" -Denable_openssl=false -Duse_sys_yara=disabled build
|
||||||
ninja -C build install
|
ninja -C build install
|
||||||
|
|
||||||
cd cutter-plugin
|
cd cutter-plugin
|
||||||
|
@ -120,10 +120,6 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc
|
|||||||
|
|
||||||
Config()->setOutputRedirectionEnabled(clOptions.outputRedirectionEnabled);
|
Config()->setOutputRedirectionEnabled(clOptions.outputRedirectionEnabled);
|
||||||
|
|
||||||
if (JSDecDecompiler::isAvailable()) {
|
|
||||||
Core()->registerDecompiler(new JSDecDecompiler(Core()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if CUTTER_RZGHIDRA_STATIC
|
#if CUTTER_RZGHIDRA_STATIC
|
||||||
Core()->registerDecompiler(new RzGhidraDecompiler(Core()));
|
Core()->registerDecompiler(new RzGhidraDecompiler(Core()));
|
||||||
#endif
|
#endif
|
||||||
|
@ -10,7 +10,8 @@ void openIssue()
|
|||||||
{
|
{
|
||||||
RzCoreLocked core(Core());
|
RzCoreLocked core(Core());
|
||||||
RzBinFile *bf = rz_bin_cur(core->bin);
|
RzBinFile *bf = rz_bin_cur(core->bin);
|
||||||
RzBinInfo *info = rz_bin_get_info(core->bin);
|
RzBinObject *bobj = rz_bin_cur_object(core->bin);
|
||||||
|
const RzBinInfo *info = bobj ? rz_bin_object_get_info(bobj) : nullptr;
|
||||||
RzBinPlugin *plugin = rz_bin_file_cur_plugin(bf);
|
RzBinPlugin *plugin = rz_bin_file_cur_plugin(bf);
|
||||||
|
|
||||||
QString url, osInfo, format, arch, type;
|
QString url, osInfo, format, arch, type;
|
||||||
|
@ -10,107 +10,8 @@ Decompiler::Decompiler(const QString &id, const QString &name, QObject *parent)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *jsonToStrdup(const CutterJson &str)
|
|
||||||
{
|
|
||||||
const RzJson *j = str.lowLevelValue();
|
|
||||||
if (!j || j->type != RZ_JSON_STRING) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return rz_str_dup(j->str_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static RzAnnotatedCode *parseJsonCode(CutterJson &json)
|
|
||||||
{
|
|
||||||
char *raw_code = jsonToStrdup(json["code"]);
|
|
||||||
if (!raw_code) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
RzAnnotatedCode *code = rz_annotated_code_new(raw_code);
|
|
||||||
if (!code) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
for (const auto &jsonAnnotation : json["annotations"]) {
|
|
||||||
RzCodeAnnotation annotation = {};
|
|
||||||
annotation.start = jsonAnnotation["start"].toUt64();
|
|
||||||
annotation.end = jsonAnnotation["end"].toUt64();
|
|
||||||
QString type = jsonAnnotation["type"].toString();
|
|
||||||
if (type == "offset") {
|
|
||||||
annotation.type = RZ_CODE_ANNOTATION_TYPE_OFFSET;
|
|
||||||
annotation.offset.offset = jsonAnnotation["offset"].toString().toULongLong();
|
|
||||||
} else if (type == "function_name") {
|
|
||||||
annotation.type = RZ_CODE_ANNOTATION_TYPE_FUNCTION_NAME;
|
|
||||||
annotation.reference.name = jsonToStrdup(jsonAnnotation["name"]);
|
|
||||||
annotation.reference.offset = jsonAnnotation["offset"].toString().toULongLong();
|
|
||||||
} else if (type == "global_variable") {
|
|
||||||
annotation.type = RZ_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE;
|
|
||||||
annotation.reference.offset = jsonAnnotation["offset"].toString().toULongLong();
|
|
||||||
} else if (type == "constant_variable") {
|
|
||||||
annotation.type = RZ_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE;
|
|
||||||
annotation.reference.offset = jsonAnnotation["offset"].toString().toULongLong();
|
|
||||||
} else if (type == "local_variable") {
|
|
||||||
annotation.type = RZ_CODE_ANNOTATION_TYPE_LOCAL_VARIABLE;
|
|
||||||
annotation.variable.name = jsonToStrdup(jsonAnnotation["name"]);
|
|
||||||
} else if (type == "function_parameter") {
|
|
||||||
annotation.type = RZ_CODE_ANNOTATION_TYPE_FUNCTION_PARAMETER;
|
|
||||||
annotation.variable.name = jsonToStrdup(jsonAnnotation["name"]);
|
|
||||||
} else if (type == "syntax_highlight") {
|
|
||||||
annotation.type = RZ_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT;
|
|
||||||
QString highlightType = jsonAnnotation["syntax_highlight"].toString();
|
|
||||||
if (highlightType == "keyword") {
|
|
||||||
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_KEYWORD;
|
|
||||||
} else if (highlightType == "comment") {
|
|
||||||
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_COMMENT;
|
|
||||||
} else if (highlightType == "datatype") {
|
|
||||||
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_DATATYPE;
|
|
||||||
} else if (highlightType == "function_name") {
|
|
||||||
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_NAME;
|
|
||||||
} else if (highlightType == "function_parameter") {
|
|
||||||
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_PARAMETER;
|
|
||||||
} else if (highlightType == "local_variable") {
|
|
||||||
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_LOCAL_VARIABLE;
|
|
||||||
} else if (highlightType == "constant_variable") {
|
|
||||||
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_CONSTANT_VARIABLE;
|
|
||||||
} else if (highlightType == "global_variable") {
|
|
||||||
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_GLOBAL_VARIABLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rz_annotated_code_add_annotation(code, &annotation);
|
|
||||||
}
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
RzAnnotatedCode *Decompiler::makeWarning(QString warningMessage)
|
RzAnnotatedCode *Decompiler::makeWarning(QString warningMessage)
|
||||||
{
|
{
|
||||||
std::string temporary = warningMessage.toStdString();
|
std::string temporary = warningMessage.toStdString();
|
||||||
return rz_annotated_code_new(strdup(temporary.c_str()));
|
return rz_annotated_code_new(strdup(temporary.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
JSDecDecompiler::JSDecDecompiler(QObject *parent) : Decompiler("jsdec", "jsdec", parent)
|
|
||||||
{
|
|
||||||
task = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool JSDecDecompiler::isAvailable()
|
|
||||||
{
|
|
||||||
return Core()->getConfigVariableSpaces().contains("jsdec");
|
|
||||||
}
|
|
||||||
|
|
||||||
void JSDecDecompiler::decompileAt(RVA addr)
|
|
||||||
{
|
|
||||||
if (task) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
task = new RizinCmdTask("pddA @ " + QString::number(addr));
|
|
||||||
connect(task, &RizinCmdTask::finished, this, [this]() {
|
|
||||||
CutterJson json = task->getResultJson();
|
|
||||||
delete task;
|
|
||||||
task = nullptr;
|
|
||||||
if (!json.size()) {
|
|
||||||
emit finished(Decompiler::makeWarning(tr("Failed to parse JSON from jsdec")));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
RzAnnotatedCode *code = parseJsonCode(json);
|
|
||||||
emit finished(code);
|
|
||||||
});
|
|
||||||
task->startTask();
|
|
||||||
}
|
|
||||||
|
@ -37,20 +37,4 @@ signals:
|
|||||||
void finished(RzAnnotatedCode *codeDecompiled);
|
void finished(RzAnnotatedCode *codeDecompiled);
|
||||||
};
|
};
|
||||||
|
|
||||||
class JSDecDecompiler : public Decompiler
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
private:
|
|
||||||
RizinCmdTask *task;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit JSDecDecompiler(QObject *parent = nullptr);
|
|
||||||
void decompileAt(RVA addr) override;
|
|
||||||
|
|
||||||
bool isRunning() override { return task != nullptr; }
|
|
||||||
|
|
||||||
static bool isAvailable();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // DECOMPILER_H
|
#endif // DECOMPILER_H
|
||||||
|
@ -62,7 +62,7 @@ CutterJson RizinCmdTask::getResultJson()
|
|||||||
}
|
}
|
||||||
char *copy = static_cast<char *>(rz_mem_alloc(strlen(res) + 1));
|
char *copy = static_cast<char *>(rz_mem_alloc(strlen(res) + 1));
|
||||||
strcpy(copy, res);
|
strcpy(copy, res);
|
||||||
return Core()->parseJson(copy, nullptr);
|
return Core()->parseJson("task", copy, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *RizinCmdTask::getResultRaw()
|
const char *RizinCmdTask::getResultRaw()
|
||||||
|
@ -273,6 +273,7 @@ void CutterCore::loadCutterRC()
|
|||||||
}
|
}
|
||||||
qInfo() << tr("Loading initialization file from ") << cutterRCFilePath;
|
qInfo() << tr("Loading initialization file from ") << cutterRCFilePath;
|
||||||
rz_core_cmd_file(core, cutterRCFilePath.toUtf8().constData());
|
rz_core_cmd_file(core, cutterRCFilePath.toUtf8().constData());
|
||||||
|
rz_cons_flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,6 +287,7 @@ void CutterCore::loadDefaultCutterRC()
|
|||||||
}
|
}
|
||||||
qInfo() << tr("Loading initialization file from ") << cutterRCFilePath;
|
qInfo() << tr("Loading initialization file from ") << cutterRCFilePath;
|
||||||
rz_core_cmd_file(core, cutterRCFilePath.toUtf8().constData());
|
rz_core_cmd_file(core, cutterRCFilePath.toUtf8().constData());
|
||||||
|
rz_cons_flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QString> CutterCore::sdbList(QString path)
|
QList<QString> CutterCore::sdbList(QString path)
|
||||||
@ -473,19 +475,7 @@ QString CutterCore::cmdRaw(const char *cmd)
|
|||||||
{
|
{
|
||||||
QString res;
|
QString res;
|
||||||
CORE_LOCK();
|
CORE_LOCK();
|
||||||
rz_cons_push();
|
return rz_core_cmd_str(core, cmd);
|
||||||
|
|
||||||
// rz_core_cmd does not return the output of the command
|
|
||||||
rz_core_cmd(core, cmd, 0);
|
|
||||||
|
|
||||||
// we grab the output straight from rz_cons
|
|
||||||
res = rz_cons_get_buffer();
|
|
||||||
|
|
||||||
// cleaning up
|
|
||||||
rz_cons_pop();
|
|
||||||
rz_cons_echo(NULL);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CutterJson CutterCore::cmdj(const char *str)
|
CutterJson CutterCore::cmdj(const char *str)
|
||||||
@ -496,7 +486,7 @@ CutterJson CutterCore::cmdj(const char *str)
|
|||||||
res = rz_core_cmd_str(core, str);
|
res = rz_core_cmd_str(core, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseJson(res, str);
|
return parseJson("cmdj", res, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CutterCore::cmdTask(const QString &str)
|
QString CutterCore::cmdTask(const QString &str)
|
||||||
@ -507,9 +497,9 @@ QString CutterCore::cmdTask(const QString &str)
|
|||||||
return task.getResult();
|
return task.getResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
CutterJson CutterCore::parseJson(char *res, const char *cmd)
|
CutterJson CutterCore::parseJson(const char *name, char *res, const char *cmd)
|
||||||
{
|
{
|
||||||
if (!res) {
|
if (RZ_STR_ISEMPTY(res)) {
|
||||||
return CutterJson();
|
return CutterJson();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,24 +507,17 @@ CutterJson CutterCore::parseJson(char *res, const char *cmd)
|
|||||||
|
|
||||||
if (!doc) {
|
if (!doc) {
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
eprintf("Failed to parse JSON for command \"%s\"\n", cmd);
|
RZ_LOG_ERROR("%s: Failed to parse JSON for command \"%s\"\n%s\n", name, cmd, res);
|
||||||
} else {
|
} else {
|
||||||
eprintf("Failed to parse JSON\n");
|
RZ_LOG_ERROR("%s: Failed to parse JSON %s\n", name, res);
|
||||||
}
|
|
||||||
const int MAX_JSON_DUMP_SIZE = 8 * 1024;
|
|
||||||
size_t originalSize = strlen(res);
|
|
||||||
if (originalSize > MAX_JSON_DUMP_SIZE) {
|
|
||||||
res[MAX_JSON_DUMP_SIZE] = 0;
|
|
||||||
eprintf("%zu bytes total: %s ...\n", originalSize, res);
|
|
||||||
} else {
|
|
||||||
eprintf("%s\n", res);
|
|
||||||
}
|
}
|
||||||
|
RZ_LOG_ERROR("%s: %s\n", name, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CutterJson(doc, QSharedPointer<CutterJsonOwner>::create(doc, res));
|
return CutterJson(doc, QSharedPointer<CutterJsonOwner>::create(doc, res));
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList CutterCore::autocomplete(const QString &cmd, RzLinePromptType promptType, size_t limit)
|
QStringList CutterCore::autocomplete(const QString &cmd, RzLinePromptType promptType)
|
||||||
{
|
{
|
||||||
RzLineBuffer buf;
|
RzLineBuffer buf;
|
||||||
int c = snprintf(buf.data, sizeof(buf.data), "%s", cmd.toUtf8().constData());
|
int c = snprintf(buf.data, sizeof(buf.data), "%s", cmd.toUtf8().constData());
|
||||||
@ -543,18 +526,17 @@ QStringList CutterCore::autocomplete(const QString &cmd, RzLinePromptType prompt
|
|||||||
}
|
}
|
||||||
buf.index = buf.length = std::min((int)(sizeof(buf.data) - 1), c);
|
buf.index = buf.length = std::min((int)(sizeof(buf.data) - 1), c);
|
||||||
|
|
||||||
RzLineCompletion completion;
|
RzLineNSCompletionResult *compr = rz_core_autocomplete_rzshell(core(), &buf, promptType);
|
||||||
rz_line_completion_init(&completion, limit);
|
|
||||||
rz_core_autocomplete(core(), &completion, &buf, promptType);
|
|
||||||
|
|
||||||
QStringList r;
|
QStringList r;
|
||||||
r.reserve(rz_pvector_len(&completion.args));
|
auto optslen = rz_pvector_len(&compr->options);
|
||||||
for (size_t i = 0; i < rz_pvector_len(&completion.args); i++) {
|
r.reserve(optslen);
|
||||||
|
for (size_t i = 0; i < optslen; i++) {
|
||||||
r.push_back(QString::fromUtf8(
|
r.push_back(QString::fromUtf8(
|
||||||
reinterpret_cast<const char *>(rz_pvector_at(&completion.args, i))));
|
reinterpret_cast<const char *>(rz_pvector_at(&compr->options, i))));
|
||||||
}
|
}
|
||||||
|
rz_line_ns_completion_result_free(compr);
|
||||||
|
|
||||||
rz_line_completion_fini(&completion);
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -602,8 +584,6 @@ bool CutterCore::loadFile(QString path, ut64 baddr, ut64 mapaddr, int perms, int
|
|||||||
rz_bin_select_idx(core->bin, NULL, idx);
|
rz_bin_select_idx(core->bin, NULL, idx);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} else {
|
|
||||||
// Not loading RzBin info coz va = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto iod = core->io ? core->io->desc : NULL;
|
auto iod = core->io ? core->io->desc : NULL;
|
||||||
@ -611,7 +591,8 @@ bool CutterCore::loadFile(QString path, ut64 baddr, ut64 mapaddr, int perms, int
|
|||||||
core->file && iod && (core->file->fd == iod->fd) && iod->plugin && iod->plugin->isdbg;
|
core->file && iod && (core->file->fd == iod->fd) && iod->plugin && iod->plugin->isdbg;
|
||||||
|
|
||||||
if (!debug && rz_flag_get(core->flags, "entry0")) {
|
if (!debug && rz_flag_get(core->flags, "entry0")) {
|
||||||
rz_core_cmd0(core, "s entry0");
|
ut64 addr = rz_num_math(core->num, "entry0");
|
||||||
|
rz_core_seek_and_save(core, addr, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (perms & RZ_PERM_W) {
|
if (perms & RZ_PERM_W) {
|
||||||
@ -621,6 +602,7 @@ bool CutterCore::loadFile(QString path, ut64 baddr, ut64 mapaddr, int perms, int
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rz_cons_flush();
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -715,19 +697,16 @@ void CutterCore::delFlag(const QString &name)
|
|||||||
emit flagsChanged();
|
emit flagsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
PRzAnalysisBytes CutterCore::getRzAnalysisBytesSingle(RVA addr)
|
CutterRzIter<RzAnalysisBytes> CutterCore::getRzAnalysisBytesSingle(RVA addr)
|
||||||
{
|
{
|
||||||
CORE_LOCK();
|
CORE_LOCK();
|
||||||
ut8 buf[128];
|
ut8 buf[128];
|
||||||
rz_io_read_at(core->io, addr, buf, sizeof(buf));
|
rz_io_read_at(core->io, addr, buf, sizeof(buf));
|
||||||
|
|
||||||
auto seek = seekTemp(addr);
|
// Warning! only safe to use with stack buffer, due to instruction count being 1
|
||||||
auto vec = fromOwned(rz_core_analysis_bytes(core, addr, buf, sizeof(buf), 1));
|
auto result =
|
||||||
|
CutterRzIter<RzAnalysisBytes>(rz_core_analysis_bytes(core, addr, buf, sizeof(buf), 1));
|
||||||
auto ab = vec && rz_pvector_len(vec.get()) > 0
|
return result;
|
||||||
? reinterpret_cast<RzAnalysisBytes *>(rz_pvector_pop_front(vec.get()))
|
|
||||||
: nullptr;
|
|
||||||
return { ab, rz_analysis_bytes_free };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CutterCore::getInstructionBytes(RVA addr)
|
QString CutterCore::getInstructionBytes(RVA addr)
|
||||||
@ -1027,18 +1006,10 @@ RVA CutterCore::nextOpAddr(RVA startAddr, int count)
|
|||||||
{
|
{
|
||||||
CORE_LOCK();
|
CORE_LOCK();
|
||||||
auto seek = seekTemp(startAddr);
|
auto seek = seekTemp(startAddr);
|
||||||
auto vec = fromOwned(rz_core_analysis_bytes(core, core->offset, core->block,
|
auto consumed =
|
||||||
(int)core->blocksize, count + 1));
|
rz_core_analysis_ops_size(core, core->offset, core->block, (int)core->blocksize, count);
|
||||||
|
|
||||||
RVA addr = startAddr + 1;
|
RVA addr = startAddr + consumed;
|
||||||
if (!vec) {
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
auto ab = reinterpret_cast<RzAnalysisBytes *>(rz_pvector_tail(vec.get()));
|
|
||||||
if (!(ab && ab->op)) {
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
addr = ab->op->addr;
|
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1324,7 +1295,7 @@ RVA CutterCore::getLastFunctionInstruction(RVA addr)
|
|||||||
if (!fcn) {
|
if (!fcn) {
|
||||||
return RVA_INVALID;
|
return RVA_INVALID;
|
||||||
}
|
}
|
||||||
RzAnalysisBlock *lastBB = (RzAnalysisBlock *)rz_list_last(fcn->bbs);
|
RzAnalysisBlock *lastBB = (RzAnalysisBlock *)rz_pvector_tail(fcn->bbs);
|
||||||
return lastBB ? rz_analysis_block_get_op_addr(lastBB, lastBB->ninstr - 1) : RVA_INVALID;
|
return lastBB ? rz_analysis_block_get_op_addr(lastBB, lastBB->ninstr - 1) : RVA_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1399,13 +1370,17 @@ CutterJson CutterCore::getSignatureInfo()
|
|||||||
if (!signature) {
|
if (!signature) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return parseJson(signature, nullptr);
|
return parseJson("signature", signature, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CutterCore::existsFileInfo()
|
bool CutterCore::existsFileInfo()
|
||||||
{
|
{
|
||||||
CORE_LOCK();
|
CORE_LOCK();
|
||||||
const RzBinInfo *info = rz_bin_get_info(core->bin);
|
RzBinObject *bobj = rz_bin_cur_object(core->bin);
|
||||||
|
if (!bobj) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const RzBinInfo *info = rz_bin_object_get_info(bobj);
|
||||||
if (!(info && info->rclass)) {
|
if (!(info && info->rclass)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1666,7 +1641,7 @@ QVector<Chunk> CutterCore::getHeapChunks(RVA arena_addr)
|
|||||||
rz_list_free(arenas);
|
rz_list_free(arenas);
|
||||||
return chunks_vector;
|
return chunks_vector;
|
||||||
}
|
}
|
||||||
m_arena = ((RzArenaListItem *)rz_list_get_head_data(arenas))->addr;
|
m_arena = ((RzArenaListItem *)rz_list_first(arenas))->addr;
|
||||||
rz_list_free(arenas);
|
rz_list_free(arenas);
|
||||||
} else {
|
} else {
|
||||||
m_arena = arena_addr;
|
m_arena = arena_addr;
|
||||||
@ -3086,7 +3061,7 @@ QList<FunctionDescription> CutterCore::getAllFunctions()
|
|||||||
function.linearSize = rz_analysis_function_linear_size(fcn);
|
function.linearSize = rz_analysis_function_linear_size(fcn);
|
||||||
function.nargs = rz_analysis_arg_count(fcn);
|
function.nargs = rz_analysis_arg_count(fcn);
|
||||||
function.nlocals = rz_analysis_var_local_count(fcn);
|
function.nlocals = rz_analysis_var_local_count(fcn);
|
||||||
function.nbbs = rz_list_length(fcn->bbs);
|
function.nbbs = rz_pvector_len(fcn->bbs);
|
||||||
function.calltype = fcn->cc ? QString::fromUtf8(fcn->cc) : QString();
|
function.calltype = fcn->cc ? QString::fromUtf8(fcn->cc) : QString();
|
||||||
function.name = fcn->name ? QString::fromUtf8(fcn->name) : QString();
|
function.name = fcn->name ? QString::fromUtf8(fcn->name) : QString();
|
||||||
function.edges = rz_analysis_function_count_edges(fcn, nullptr);
|
function.edges = rz_analysis_function_count_edges(fcn, nullptr);
|
||||||
@ -3207,11 +3182,11 @@ QList<SymbolDescription> CutterCore::getAllSymbols()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const RzList *entries = rz_bin_object_get_entries(bf->o);
|
const RzPVector *entries = rz_bin_object_get_entries(bf->o);
|
||||||
if (entries) {
|
if (symbols) {
|
||||||
/* list entrypoints as symbols too */
|
/* list entrypoints as symbols too */
|
||||||
int n = 0;
|
int n = 0;
|
||||||
for (const auto &entry : CutterRzList<RzBinSymbol>(entries)) {
|
for (const auto &entry : CutterPVector<RzBinAddr>(entries)) {
|
||||||
SymbolDescription symbol;
|
SymbolDescription symbol;
|
||||||
symbol.vaddr = entry->vaddr;
|
symbol.vaddr = entry->vaddr;
|
||||||
symbol.name = QString("entry") + QString::number(n++);
|
symbol.name = QString("entry") + QString::number(n++);
|
||||||
@ -3443,13 +3418,13 @@ QList<SectionDescription> CutterCore::getAllSections()
|
|||||||
section.size = sect->size;
|
section.size = sect->size;
|
||||||
section.perm = rz_str_rwx_i(sect->perm);
|
section.perm = rz_str_rwx_i(sect->perm);
|
||||||
if (sect->size > 0) {
|
if (sect->size > 0) {
|
||||||
HtPP *digests = rz_core_bin_create_digests(core, sect->paddr, sect->size, hashnames);
|
HtSS *digests = rz_core_bin_create_digests(core, sect->paddr, sect->size, hashnames);
|
||||||
if (!digests) {
|
if (!digests) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const char *entropy = (const char *)ht_pp_find(digests, "entropy", NULL);
|
const char *entropy = (const char *)ht_ss_find(digests, "entropy", NULL);
|
||||||
section.entropy = rz_str_get(entropy);
|
section.entropy = rz_str_get(entropy);
|
||||||
ht_pp_free(digests);
|
ht_ss_free(digests);
|
||||||
}
|
}
|
||||||
|
|
||||||
sections << section;
|
sections << section;
|
||||||
@ -3524,10 +3499,8 @@ QList<EntrypointDescription> CutterCore::getAllEntrypoint()
|
|||||||
ut64 laddr = rz_bin_get_laddr(core->bin);
|
ut64 laddr = rz_bin_get_laddr(core->bin);
|
||||||
|
|
||||||
QList<EntrypointDescription> qList;
|
QList<EntrypointDescription> qList;
|
||||||
const RzList *entries = rz_bin_object_get_entries(bf->o);
|
const RzPVector *entries = rz_bin_object_get_entries(bf->o);
|
||||||
RzListIter *iter;
|
for (const auto &entry : CutterPVector<RzBinAddr>(entries)) {
|
||||||
RzBinAddr *entry;
|
|
||||||
CutterRzListForeach (entries, iter, RzBinAddr, entry) {
|
|
||||||
if (entry->type != RZ_BIN_ENTRY_TYPE_PROGRAM) {
|
if (entry->type != RZ_BIN_ENTRY_TYPE_PROGRAM) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -4342,6 +4315,7 @@ void CutterCore::loadScript(const QString &scriptname)
|
|||||||
{
|
{
|
||||||
CORE_LOCK();
|
CORE_LOCK();
|
||||||
rz_core_cmd_file(core, scriptname.toUtf8().constData());
|
rz_core_cmd_file(core, scriptname.toUtf8().constData());
|
||||||
|
rz_cons_flush();
|
||||||
}
|
}
|
||||||
triggerRefreshAll();
|
triggerRefreshAll();
|
||||||
}
|
}
|
||||||
@ -4360,10 +4334,9 @@ QString CutterCore::getVersionInformation()
|
|||||||
const char *name;
|
const char *name;
|
||||||
const char *(*callback)();
|
const char *(*callback)();
|
||||||
} vcs[] = {
|
} vcs[] = {
|
||||||
{ "rz_analysis", &rz_analysis_version },
|
{ "rz_arch", &rz_arch_version },
|
||||||
{ "rz_lib", &rz_lib_version },
|
{ "rz_lib", &rz_lib_version },
|
||||||
{ "rz_egg", &rz_egg_version },
|
{ "rz_egg", &rz_egg_version },
|
||||||
{ "rz_asm", &rz_asm_version },
|
|
||||||
{ "rz_bin", &rz_bin_version },
|
{ "rz_bin", &rz_bin_version },
|
||||||
{ "rz_cons", &rz_cons_version },
|
{ "rz_cons", &rz_cons_version },
|
||||||
{ "rz_flag", &rz_flag_version },
|
{ "rz_flag", &rz_flag_version },
|
||||||
@ -4376,7 +4349,6 @@ QString CutterCore::getVersionInformation()
|
|||||||
#if !USE_LIB_MAGIC
|
#if !USE_LIB_MAGIC
|
||||||
{ "rz_magic", &rz_magic_version },
|
{ "rz_magic", &rz_magic_version },
|
||||||
#endif
|
#endif
|
||||||
{ "rz_parse", &rz_parse_version },
|
|
||||||
{ "rz_reg", &rz_reg_version },
|
{ "rz_reg", &rz_reg_version },
|
||||||
{ "rz_sign", &rz_sign_version },
|
{ "rz_sign", &rz_sign_version },
|
||||||
{ "rz_search", &rz_search_version },
|
{ "rz_search", &rz_search_version },
|
||||||
|
@ -62,8 +62,6 @@ struct CUTTER_EXPORT RegisterRef
|
|||||||
QString name;
|
QString name;
|
||||||
};
|
};
|
||||||
|
|
||||||
using PRzAnalysisBytes = std::unique_ptr<RzAnalysisBytes, decltype(rz_analysis_bytes_free) *>;
|
|
||||||
|
|
||||||
class CUTTER_EXPORT CutterCore : public QObject
|
class CUTTER_EXPORT CutterCore : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -183,13 +181,13 @@ public:
|
|||||||
QString getRizinVersionReadable(const char *program = nullptr);
|
QString getRizinVersionReadable(const char *program = nullptr);
|
||||||
QString getVersionInformation();
|
QString getVersionInformation();
|
||||||
|
|
||||||
CutterJson parseJson(char *res, const char *cmd = nullptr);
|
CutterJson parseJson(const char *name, char *res, const char *cmd = nullptr);
|
||||||
CutterJson parseJson(char *res, const QString &cmd = QString())
|
CutterJson parseJson(const char *name, char *res, const QString &cmd = QString())
|
||||||
{
|
{
|
||||||
return parseJson(res, cmd.isNull() ? nullptr : cmd.toLocal8Bit().constData());
|
return parseJson(name, res, cmd.isNull() ? nullptr : cmd.toLocal8Bit().constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList autocomplete(const QString &cmd, RzLinePromptType promptType, size_t limit = 4096);
|
QStringList autocomplete(const QString &cmd, RzLinePromptType promptType);
|
||||||
|
|
||||||
/* Functions methods */
|
/* Functions methods */
|
||||||
void renameFunction(const RVA offset, const QString &newName);
|
void renameFunction(const RVA offset, const QString &newName);
|
||||||
@ -248,7 +246,7 @@ public:
|
|||||||
QString getGlobalVariableType(RVA offset);
|
QString getGlobalVariableType(RVA offset);
|
||||||
|
|
||||||
/* Edition functions */
|
/* Edition functions */
|
||||||
PRzAnalysisBytes getRzAnalysisBytesSingle(RVA addr);
|
CutterRzIter<RzAnalysisBytes> getRzAnalysisBytesSingle(RVA addr);
|
||||||
QString getInstructionBytes(RVA addr);
|
QString getInstructionBytes(RVA addr);
|
||||||
QString getInstructionOpcode(RVA addr);
|
QString getInstructionOpcode(RVA addr);
|
||||||
void editInstruction(RVA addr, const QString &inst, bool fillWithNops = false);
|
void editInstruction(RVA addr, const QString &inst, bool fillWithNops = false);
|
||||||
|
@ -164,4 +164,30 @@ public:
|
|||||||
iterator end() const { return iterator(nullptr); }
|
iterator end() const { return iterator(nullptr); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class CutterRzIter
|
||||||
|
{
|
||||||
|
UniquePtrC<RzIterator, &rz_iterator_free> rzIter;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CutterRzIter(RzIterator *rzIter) : rzIter(rzIter)
|
||||||
|
{
|
||||||
|
// immediately attempt advancing by 1, otherwise it's hard to distinguish whether current
|
||||||
|
// element is null due to not having called next, or due to having run out of elements
|
||||||
|
if (rzIter) {
|
||||||
|
++*this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CutterRzIter<T> &operator++()
|
||||||
|
{
|
||||||
|
rz_iterator_next(rzIter.get());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
operator bool() { return rzIter && rzIter->cur; }
|
||||||
|
T &operator*() { return *reinterpret_cast<RzAnalysisBytes *>(rzIter->cur); }
|
||||||
|
T *get() { return reinterpret_cast<RzAnalysisBytes *>(rzIter->cur); }
|
||||||
|
T *operator->() { return reinterpret_cast<RzAnalysisBytes *>(rzIter->cur); }
|
||||||
|
};
|
||||||
|
|
||||||
#endif // RIZINCPP_H
|
#endif // RIZINCPP_H
|
||||||
|
@ -24,7 +24,11 @@ VersionInfoDialog::~VersionInfoDialog() {}
|
|||||||
void VersionInfoDialog::fillVersionInfo()
|
void VersionInfoDialog::fillVersionInfo()
|
||||||
{
|
{
|
||||||
RzCoreLocked core(Core());
|
RzCoreLocked core(Core());
|
||||||
const RzBinInfo *info = rz_bin_get_info(core->bin);
|
RzBinObject *bobj = rz_bin_cur_object(core->bin);
|
||||||
|
if (!bobj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const RzBinInfo *info = rz_bin_object_get_info(bobj);
|
||||||
if (!info || !info->rclass) {
|
if (!info || !info->rclass) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -520,7 +520,7 @@ void DisassemblyContextMenu::aboutToShowSlot()
|
|||||||
|
|
||||||
if (ab && ab->op) {
|
if (ab && ab->op) {
|
||||||
const char *opexstr = RZ_STRBUF_SAFEGET(&ab->op->opex);
|
const char *opexstr = RZ_STRBUF_SAFEGET(&ab->op->opex);
|
||||||
CutterJson operands = Core()->parseJson(strdup(opexstr), nullptr);
|
CutterJson operands = Core()->parseJson("opex", strdup(opexstr), nullptr);
|
||||||
|
|
||||||
// Loop through both the operands of the instruction
|
// Loop through both the operands of the instruction
|
||||||
for (const CutterJson operand : operands) {
|
for (const CutterJson operand : operands) {
|
||||||
|
@ -25,6 +25,11 @@
|
|||||||
<update_contact>xarkes</update_contact>
|
<update_contact>xarkes</update_contact>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="2.3.3" date="2024-02-24" />
|
||||||
|
<release version="2.3.2" date="2023-09-14" />
|
||||||
|
<release version="2.3.1" date="2023-08-20" />
|
||||||
|
<release version="2.3.0" date="2023-08-05" />
|
||||||
|
<release version="2.2.1" date="2023-05-15" />
|
||||||
<release version="2.2.0" date="2023-02-22" />
|
<release version="2.2.0" date="2023-02-22" />
|
||||||
<release version="2.1.2" date="2022-09-11" />
|
<release version="2.1.2" date="2022-09-11" />
|
||||||
<release version="2.1.1" date="2022-09-10" />
|
<release version="2.1.1" date="2022-09-10" />
|
||||||
|
@ -52,7 +52,8 @@ void Dashboard::updateContents()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add file hashes, analysis info and libraries
|
// Add file hashes, analysis info and libraries
|
||||||
RzBinInfo *binInfo = rz_bin_get_info(core->bin);
|
RzBinObject *bobj = rz_bin_cur_object(core->bin);
|
||||||
|
const RzBinInfo *binInfo = bobj ? rz_bin_object_get_info(bobj) : nullptr;
|
||||||
|
|
||||||
setPlainText(ui->fileEdit, binInfo ? binInfo->file : "");
|
setPlainText(ui->fileEdit, binInfo ? binInfo->file : "");
|
||||||
setPlainText(ui->formatEdit, binInfo ? binInfo->rclass : "");
|
setPlainText(ui->formatEdit, binInfo ? binInfo->rclass : "");
|
||||||
@ -211,7 +212,7 @@ void Dashboard::setPlainText(QLineEdit *textBox, const QString &text)
|
|||||||
* @brief Setting boolean values of binary information in dashboard
|
* @brief Setting boolean values of binary information in dashboard
|
||||||
* @param RzBinInfo
|
* @param RzBinInfo
|
||||||
*/
|
*/
|
||||||
void Dashboard::setRzBinInfo(RzBinInfo *binInfo)
|
void Dashboard::setRzBinInfo(const RzBinInfo *binInfo)
|
||||||
{
|
{
|
||||||
setPlainText(ui->vaEdit, binInfo ? setBoolText(binInfo->has_va) : "");
|
setPlainText(ui->vaEdit, binInfo ? setBoolText(binInfo->has_va) : "");
|
||||||
setPlainText(ui->canaryEdit, binInfo ? setBoolText(binInfo->has_canary) : "");
|
setPlainText(ui->canaryEdit, binInfo ? setBoolText(binInfo->has_canary) : "");
|
||||||
|
@ -33,7 +33,7 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
std::unique_ptr<Ui::Dashboard> ui;
|
std::unique_ptr<Ui::Dashboard> ui;
|
||||||
void setPlainText(QLineEdit *textBox, const QString &text);
|
void setPlainText(QLineEdit *textBox, const QString &text);
|
||||||
void setRzBinInfo(RzBinInfo *binInfo);
|
void setRzBinInfo(const RzBinInfo *binInfo);
|
||||||
const char *setBoolText(bool value);
|
const char *setBoolText(bool value);
|
||||||
|
|
||||||
QWidget *hashesWidget = nullptr;
|
QWidget *hashesWidget = nullptr;
|
||||||
|
@ -225,7 +225,7 @@ void DisassemblerGraphView::loadCurrentGraph()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &bbi : CutterRzList<RzAnalysisBlock>(fcn->bbs)) {
|
for (const auto &bbi : CutterPVector<RzAnalysisBlock>(fcn->bbs)) {
|
||||||
RVA bbiFail = bbi->fail;
|
RVA bbiFail = bbi->fail;
|
||||||
RVA bbiJump = bbi->jump;
|
RVA bbiJump = bbi->jump;
|
||||||
|
|
||||||
|
@ -14,96 +14,97 @@
|
|||||||
Basic familiarity with graph algorithms is recommended.
|
Basic familiarity with graph algorithms is recommended.
|
||||||
|
|
||||||
# Terms used:
|
# Terms used:
|
||||||
- **Vertex**, **node**, **block** - read description of graph for definition. Within this text
|
- **Vertex**, **node**, **block** - see the definition of graph. Within this text
|
||||||
vertex and node are used interchangeably with block due to code being written for visualizing basic
|
vertex/node/block are used interchangeably due to the code being purposed for visualizing basic
|
||||||
block control flow graph.
|
block control flow graph.
|
||||||
- **edge** - read description of graph for definition for precise definition.
|
- **edge** - see the definition of graph.
|
||||||
- **DAG** - directed acyclic graph, graph using directed edges which doesn't have cycles. DAG may
|
- **DAG** - directed acyclic graph, a graph using directed edges which doesn't have cycles. A DAG
|
||||||
contain loops if following them would require going in both directions of edges. Example 1->2 1->3
|
may contain loops if following them would require going in both directions of edges. Example 1->2
|
||||||
3->2 is a DAG, 2->1 1->3 3->2 isn't a DAG.
|
1->3 3->2 is a DAG, 2->1 1->3 3->2 isn't a DAG.
|
||||||
- **DFS** - depth first search, a graph traversal algorithm
|
- **DFS** - depth first search, a graph traversal algorithm
|
||||||
- **toposort** - topological sorting, process of ordering a DAG vertices that all edges go from
|
- **toposort** - topological sorting, the process of ordering a DAG vertices that results in all
|
||||||
vertices earlier in the toposort order to vertices later in toposort order. There are multiple
|
edges going from vertices earlier in the toposort order to vertices later in toposort order. There
|
||||||
algorithms for implementing toposort operation. Single DAG can have multiple valid topological
|
are multiple algorithms implementing toposort. A single DAG can have multiple valid topological
|
||||||
orderings, a toposort algorithm can be designed to prioritize a specific one from all valid toposort
|
orderings, a toposort algorithm can be designed to prioritize a specific one from all valid toposort
|
||||||
orders. Example: for graph 1->4, 2->1, 2->3, 3->4 valid topological orders are [2,1,3,4] and
|
orders. Example: for graph 1->4, 2->1, 2->3, 3->4 valid topological orders are [2,1,3,4] and
|
||||||
[2,3,1,4].
|
[2,3,1,4].
|
||||||
|
|
||||||
# High level structure of the algorithm
|
# High level algorithm structure
|
||||||
1. select subset of edges that form a DAG (remove cycles)
|
1. Select a subset of edges that form a DAG (remove cycles)
|
||||||
2. toposort the DAG
|
2. Toposort the DAG
|
||||||
3. choose a subset of edges that form a tree and assign layers
|
3. Choose a subset of edges that form a tree and assign layers
|
||||||
4. assign node positions within grid using tree structure, child subtrees are placed side by side
|
4. Assign node positions within grid using tree structure, child subtrees are placed side by side
|
||||||
with parent on top
|
with parent on top
|
||||||
5. perform edge routing
|
5. Perform edge routing
|
||||||
6. calculate column and row pixel positions based on node sizes and amount edges between the rows
|
6. Calculate column and row pixel positions based on node sizes and amount edges between the rows
|
||||||
7. [optional] layout compacting
|
7. [optional] Layout compacting
|
||||||
|
|
||||||
|
|
||||||
Contrary to many other layered graph drawing algorithm this implementation doesn't perform node
|
Contrary to many other layered graph-drawing algorithms this implementation doesn't perform node
|
||||||
reordering to minimize edge crossing. This simplifies implementation, and preserves original control
|
reordering to minimize edge crossing. This simplifies the implementation, and preserves the original
|
||||||
flow structure for conditional jumps ( true jump on one side, false jump on other). Due to most of
|
control-flow structure for conditional jumps ( true jump on one side, false jump on other). Due to
|
||||||
control flow being result of structured programming constructs like if/then/else and loops,
|
most of the control flow resulting from structured programming constructs like if/then/else and
|
||||||
resulting layout is usually readable without node reordering within layers.
|
loops, the resulting layout is usually readable without node reordering within layers.
|
||||||
|
|
||||||
|
|
||||||
# Description of grid.
|
# Grid
|
||||||
To simplify the layout algorithm initial steps assume that all nodes have the same size and edges
|
To simplify the layout algorithm, its initial steps assume that all nodes have the same size and
|
||||||
are zero width. After placing the nodes and routing the edges it is known which nodes are in in
|
that edges are zero-width. After nodes placement and edges rounting, the row/column of nodes is
|
||||||
which row and column, how many edges are between each pair of rows. Using this information positions
|
known as well as the amount of edges between each pair of rows. Using this information, positions
|
||||||
are converted from the grid cells to pixel coordinates. Routing 0 width edges between rows can also
|
are converted from grid cells to pixel coordinates. Routing zero-width edges between rows can also
|
||||||
be interpreted as every second row and column being reserved for edges. The row numbers in code are
|
be interpreted as every second row and column being reserved for edges. The row numbers in code are
|
||||||
using first interpretation. To allow better centering of nodes one above other each node is 2
|
using the first interpretation. To allow better centering of nodes one above other, each node is 2
|
||||||
columns wide and 1 row high.
|
columns wide and 1 row high.
|
||||||
|
|
||||||
\image html graph_grid.svg
|
\image html graph_grid.svg
|
||||||
|
|
||||||
# 1-2 Cycle removal and toposort
|
# 1-2 Cycle removal and toposort
|
||||||
|
|
||||||
Cycle removal and toposort are done at the same time during single DFS traversal. In case entrypoint
|
Cycle removal and toposort are done in a single DFS traversal. In case the entrypoint
|
||||||
is part of a loop DFS started from entrypoint. This ensures that entrypoint is at the top of
|
is part of a loop, the DFS starts from the entrypoint. This ensures that the entrypoint is at the
|
||||||
resulting layout if possible. Resulting toposort order is used in many of the following layout steps
|
top of resulting layout, if possible. The resulting toposort order is used in many of the following
|
||||||
that require calculating some property of a vertex based on child property or the other way around.
|
layout steps that require calculating some property of a vertex based on a child property or the
|
||||||
Using toposort order such operations can be implemented iteration through array in either forward or
|
other way around. Using toposort order, such operations can be implemented by array iteration in
|
||||||
reverse direction. To prevent running out of stack memory when processing large graphs DFS is
|
either forward/backward direction. To prevent running out of stack memory when processing large
|
||||||
implemented non-recursively.
|
graphs, DFS is implemented non-recursively.
|
||||||
|
|
||||||
# Row assignment
|
# Row assignment
|
||||||
|
|
||||||
Rows are assigned in toposort order from top to bottom, with nodes row being max(predecessor.row)+1.
|
Rows are assigned in toposort order from top to bottom, with nodes row being max(predecessor.row)+1.
|
||||||
This ensures that loop edges are only ones going from deeper levels to previous layers.
|
This ensures that loop back-edges are the only edges going from lower to higher layers.
|
||||||
|
|
||||||
To further simply node placement a subset of edges is selected which forms a tree. This turns DAG
|
To further simply node placement, a subset of edges is selected which forms a tree. This turns a DAG
|
||||||
drawing problem into a tree drawing problem. For each node in level n following nodes which have
|
drawing problem into a tree drawing problem. For each node in level n the following nodes with
|
||||||
level exactly n+1 are greedily assigned as child nodes in tree. If a node already has parent
|
level exactly n+1 are greedily assigned as child nodes in the tree. If a node already has a parent
|
||||||
assigned then corresponding edge is not part of tree.
|
assigned then the corresponding edge is not part of the tree.
|
||||||
|
|
||||||
# Node position assignment
|
# Node placement
|
||||||
|
|
||||||
Since the graph has been reduced to a tree, node placement is more or less putting subtrees side by
|
Since the graph has been reduced to a tree, node placement is more or less putting subtrees side by
|
||||||
side with parent on top. There is some room for interpretation what exactly side by side means and
|
side with parent on top. There is some room for interpretation as to what exactly 'side by side'
|
||||||
where exactly on top is. Drawing the graph either too dense or too big may make it less readable so
|
means and where exactly 'on top' is: drawing the graph either too dense or too sparse may make it
|
||||||
there are configuration options which allow choosing these things resulting in more or less dense
|
less readable, so there are configuration options which allow choosing these things resulting in
|
||||||
layout.
|
more or less dense layout.
|
||||||
|
|
||||||
Once the subtrees are placed side by side. Parent node can be placed either in the middle of
|
Once the subtrees are placed side by side, the parent node can be placed either in the middle of
|
||||||
horizontal bounds or in the middle of direct children. First option results in narrower layout and
|
the horizontal bounds or in the middle of its direct children. The first option results in narrower
|
||||||
more vertical columns. Second option results in nodes being more spread out which may help seeing
|
layout and more vertical columns, while the second option results in more spread out layout which
|
||||||
where each edge goes.
|
may help seeing where each edge goes.
|
||||||
|
|
||||||
In more compact mode two subtrees are placed side by side taking into account their shape. In wider
|
In compact mode two subtrees are placed side by side accounting for their shape. In wider
|
||||||
mode bounding box of shorter subtree is used instead of exact shape. This gives slightly sparse
|
mode the bounding box of the shorter subtree is used instead of its exact shape. This gives slightly
|
||||||
layout without it being too wide.
|
sparser layout without being too wide.
|
||||||
|
|
||||||
\image html graph_parent_placement.svg
|
\image html graph_parent_placement.svg
|
||||||
|
|
||||||
# Edge routing
|
# Edge routing
|
||||||
Edge routing can be split into: main column selection, rough routing, segment offset calculation.
|
Edge routing can be split into: main column selection, rough routing, and segment offset
|
||||||
|
calculation.
|
||||||
|
|
||||||
Transition from source to target row is done using single vertical segment. This is called main
|
Transition from source to target row is done using a single vertical segment. This segment is called
|
||||||
column.
|
the 'main column'.
|
||||||
|
|
||||||
A sweep line is used for computing main columns: Blocks and edges are processed as events top to
|
Main columns are computed using a sweep line: blocks and edges are processed as events top to
|
||||||
bottom based off their row (max(start row, end row) for edges). Blocked columns are tracked in a
|
bottom based off their row (max(start row, end row) for edges). Blocked columns are tracked in a
|
||||||
tree structure which allows searching nearest column with at least last N rows empty. The column
|
tree structure which allows searching nearest column with at least last N rows empty. The column
|
||||||
of the starting block is favored for the main column, otherwise the target block's column is chosen
|
of the starting block is favored for the main column, otherwise the target block's column is chosen
|
||||||
@ -114,10 +115,9 @@ true or false branch. In case of upward edges it is allowed to choose a column o
|
|||||||
is slightly further than nearest empty to reduce the chance of producing tilted figure 8 shaped
|
is slightly further than nearest empty to reduce the chance of producing tilted figure 8 shaped
|
||||||
crossing between two blocks.
|
crossing between two blocks.
|
||||||
|
|
||||||
Rough routing creates the path of edge using up to 5 segments using grid coordinates.
|
Due to nodes being placed in a grid, horizontal segments of edges can't intersect with any nodes.
|
||||||
Due to nodes being placed in a grid. Horizontal segments of edges can't intersect with any nodes.
|
|
||||||
The path for edges is chosen so that it consists of at most 5 segments, typically resulting in
|
The path for edges is chosen so that it consists of at most 5 segments, typically resulting in
|
||||||
sideways U shape or square Z shape.
|
sideways U shape or square Z shape:
|
||||||
- short vertical segment from node to horizontal line
|
- short vertical segment from node to horizontal line
|
||||||
- move to empty column
|
- move to empty column
|
||||||
- vertical segment between starting row and end row
|
- vertical segment between starting row and end row
|
||||||
@ -134,45 +134,49 @@ ensures that two segments don't overlap. Segment offsets within each column are
|
|||||||
with some heuristics for assignment order to reduce amount of edge crossings and result in more
|
with some heuristics for assignment order to reduce amount of edge crossings and result in more
|
||||||
visually pleasing output for a typical CFG graph. Each segment gets assigned an offset that is
|
visually pleasing output for a typical CFG graph. Each segment gets assigned an offset that is
|
||||||
maximum of previously assigned offsets overlapping with current segment + segment spacing.
|
maximum of previously assigned offsets overlapping with current segment + segment spacing.
|
||||||
Assignment order is chosen based on:
|
|
||||||
* direction of previous and last segment - helps reducing crossings and place the segments between
|
Assignment order is based on:
|
||||||
|
- direction of previous and last segment - helps reducing crossings and place the segments between
|
||||||
nodes
|
nodes
|
||||||
* segment length - reduces crossing when segment endpoints have the same structure as valid
|
- segment length - reduces crossing when segment endpoints have the same structure as valid
|
||||||
parentheses expression
|
parentheses expression
|
||||||
* edge length - establishes some kind of order when single node is connected to many edges,
|
- edge length - establishes some kind of order when single node is connected to many edges,
|
||||||
typically a block with switch statement or block after switch statement.
|
typically a block with switch statement or block after switch statement.
|
||||||
|
|
||||||
# Layout compacting
|
# Layout compacting
|
||||||
|
|
||||||
Doing the layout within a grid causes minimal spacing to be limited by widest and tallest block
|
Doing the layout on a grid limits the minimal spacing to the widest block within a column and
|
||||||
within each column and row. One common case is block with function entrypoint being wider due to
|
tallest block within a row. One common case is a function-entry block being wider due to the
|
||||||
function name causing wide horizontal space between branching blocks. Another case is rows in two
|
function name, causing wide horizontal space between branching blocks. Another case is rows in two
|
||||||
parallel columns being aligned.
|
parallel columns being aligned.
|
||||||
|
|
||||||
\image html layout_compacting.svg
|
\image html layout_compacting.svg
|
||||||
|
|
||||||
Both problems are mitigated by squishing graph. Compressing in each of the two direction is done
|
Both problems are mitigated by squishing the graph. Compressing in each of the two direction is done
|
||||||
separately. The process is defined as liner program. Each variable represents a position of edge
|
separately. The process is defined as liner program. Each variable represents a position of edge
|
||||||
segment or node in the direction being optimized.
|
segment or node in the direction being optimized.
|
||||||
|
|
||||||
Following constraints are used
|
The following constraints are used:
|
||||||
- Keep the order with nearest segments.
|
- Keep the order with nearest segments.
|
||||||
- If the node has two outgoing edges, one to the node on left side and other to the right, keep them
|
- If a node has two outgoing edges, one to the left and one to the right, keep them
|
||||||
on the corresponding side of node's center.
|
on the corresponding side of the node's center.
|
||||||
- For all edges keep the node which is above above. This helps when vertical block spacing is set
|
|
||||||
bigger than double edge spacing and edge shadows relationship between two blocks.
|
|
||||||
- Equality constraint to keep relative position between nodes and and segments directly connected to
|
- Equality constraint to keep relative position between nodes and and segments directly connected to
|
||||||
them.
|
them.
|
||||||
- Equality constraint to keep the node centered when control flow merges
|
- For all blocks connected by forward edge, keep the vertical distance at least as big as configured
|
||||||
In the vertical direction objective function minimizes y positions of nodes and lengths of vertical
|
block vertical spacing. This helps when vertical block-spacing is set bigger than double edge
|
||||||
segments. In the horizontal direction objective function minimizes lengths of horizontal segments.
|
spacing and an edge shadows relationship between two blocks.
|
||||||
|
- Equality constraint to keep a node centered when control flow merges.
|
||||||
|
|
||||||
In the resulting linear program all constraints beside x_i >= 0 consist of exactly two variables:
|
In the vertical direction the objective function minimizes y positions of nodes and lengths of
|
||||||
|
vertical segments. In the horizontal direction the objective function minimizes the lengths of
|
||||||
|
horizontal segments.
|
||||||
|
|
||||||
|
In the resulting linear program all constraints besides x_i >= 0 consist of exactly two variables:
|
||||||
either x_i - x_j <= c_k or x_i = x_j + c_k.
|
either x_i - x_j <= c_k or x_i = x_j + c_k.
|
||||||
|
|
||||||
Since it isn't necessary get perfect solution and to avoid worst case performance current
|
Since a perfect solution isn't necessary and to avoid worst case performance, the current
|
||||||
implementation isn't using a general purpose linear programming solver. Each variable is changed
|
implementation isn't using a general purpose linear solver. Instead, each variable is modified
|
||||||
until constraint is reached and afterwards variables are grouped and changed together.
|
until a constraint is satisfied and afterwards variables are grouped and modified together.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ void GraphvizLayout::CalculateLayout(std::unordered_map<ut64, GraphBlock> &block
|
|||||||
|
|
||||||
std::unordered_map<ut64, Agnode_t *> nodes;
|
std::unordered_map<ut64, Agnode_t *> nodes;
|
||||||
for (const auto &block : blocks) {
|
for (const auto &block : blocks) {
|
||||||
nodes[block.first] = agnode(g, nullptr, TRUE);
|
nodes[block.first] = agnode(g, nullptr, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> strc;
|
std::vector<std::string> strc;
|
||||||
@ -143,7 +143,7 @@ void GraphvizLayout::CalculateLayout(std::unordered_map<ut64, GraphBlock> &block
|
|||||||
if (v == nodes.end()) {
|
if (v == nodes.end()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto e = agedge(g, u, v->second, nullptr, TRUE);
|
auto e = agedge(g, u, v->second, nullptr, true);
|
||||||
edges[{ blockIt.first, edge.target }] = e;
|
edges[{ blockIt.first, edge.target }] = e;
|
||||||
if (loopEdges.find({ blockIt.first, edge.target }) != loopEdges.end()) {
|
if (loopEdges.find({ blockIt.first, edge.target }) != loopEdges.end()) {
|
||||||
agxset(e, constraintAttr, STR("0"));
|
agxset(e, constraintAttr, STR("0"));
|
||||||
@ -193,11 +193,11 @@ void GraphvizLayout::CalculateLayout(std::unordered_map<ut64, GraphBlock> &block
|
|||||||
if (it != edges.end()) {
|
if (it != edges.end()) {
|
||||||
auto e = it->second;
|
auto e = it->second;
|
||||||
if (auto spl = ED_spl(e)) {
|
if (auto spl = ED_spl(e)) {
|
||||||
for (int i = 0; i < 1 && i < spl->size; i++) {
|
for (size_t i = 0; i < 1 && i < spl->size; i++) {
|
||||||
auto bz = spl->list[i];
|
auto bz = spl->list[i];
|
||||||
edge.polyline.clear();
|
edge.polyline.clear();
|
||||||
edge.polyline.reserve(bz.size + 1);
|
edge.polyline.reserve(bz.size + 1);
|
||||||
for (int j = 0; j < bz.size; j++) {
|
for (size_t j = 0; j < bz.size; j++) {
|
||||||
edge.polyline.push_back(QPointF(bz.list[j].x, bz.list[j].y));
|
edge.polyline.push_back(QPointF(bz.list[j].x, bz.list[j].y));
|
||||||
}
|
}
|
||||||
QPointF last(0, 0);
|
QPointF last(0, 0);
|
||||||
|
@ -9,13 +9,6 @@
|
|||||||
|
|
||||||
#define DEBUGGED_PID (-1)
|
#define DEBUGGED_PID (-1)
|
||||||
|
|
||||||
enum ColumnIndex {
|
|
||||||
COLUMN_PID = 0,
|
|
||||||
COLUMN_UID,
|
|
||||||
COLUMN_STATUS,
|
|
||||||
COLUMN_PATH,
|
|
||||||
};
|
|
||||||
|
|
||||||
ProcessesWidget::ProcessesWidget(MainWindow *main)
|
ProcessesWidget::ProcessesWidget(MainWindow *main)
|
||||||
: CutterDockWidget(main), ui(new Ui::ProcessesWidget)
|
: CutterDockWidget(main), ui(new Ui::ProcessesWidget)
|
||||||
{
|
{
|
||||||
@ -23,10 +16,14 @@ ProcessesWidget::ProcessesWidget(MainWindow *main)
|
|||||||
|
|
||||||
// Setup processes model
|
// Setup processes model
|
||||||
modelProcesses = new QStandardItemModel(1, 4, this);
|
modelProcesses = new QStandardItemModel(1, 4, this);
|
||||||
modelProcesses->setHorizontalHeaderItem(COLUMN_PID, new QStandardItem(tr("PID")));
|
modelProcesses->setHorizontalHeaderItem(ProcessesWidget::COLUMN_PID,
|
||||||
modelProcesses->setHorizontalHeaderItem(COLUMN_UID, new QStandardItem(tr("UID")));
|
new QStandardItem(tr("PID")));
|
||||||
modelProcesses->setHorizontalHeaderItem(COLUMN_STATUS, new QStandardItem(tr("Status")));
|
modelProcesses->setHorizontalHeaderItem(ProcessesWidget::COLUMN_UID,
|
||||||
modelProcesses->setHorizontalHeaderItem(COLUMN_PATH, new QStandardItem(tr("Path")));
|
new QStandardItem(tr("UID")));
|
||||||
|
modelProcesses->setHorizontalHeaderItem(ProcessesWidget::COLUMN_STATUS,
|
||||||
|
new QStandardItem(tr("Status")));
|
||||||
|
modelProcesses->setHorizontalHeaderItem(ProcessesWidget::COLUMN_PATH,
|
||||||
|
new QStandardItem(tr("Path")));
|
||||||
ui->viewProcesses->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
ui->viewProcesses->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||||
ui->viewProcesses->verticalHeader()->setVisible(false);
|
ui->viewProcesses->verticalHeader()->setVisible(false);
|
||||||
ui->viewProcesses->setFont(Config()->getFont());
|
ui->viewProcesses->setFont(Config()->getFont());
|
||||||
@ -129,10 +126,10 @@ void ProcessesWidget::setProcessesGrid()
|
|||||||
rowStatus->setFont(font);
|
rowStatus->setFont(font);
|
||||||
rowPath->setFont(font);
|
rowPath->setFont(font);
|
||||||
|
|
||||||
modelProcesses->setItem(i, COLUMN_PID, rowPid);
|
modelProcesses->setItem(i, ProcessesWidget::COLUMN_PID, rowPid);
|
||||||
modelProcesses->setItem(i, COLUMN_UID, rowUid);
|
modelProcesses->setItem(i, ProcessesWidget::COLUMN_UID, rowUid);
|
||||||
modelProcesses->setItem(i, COLUMN_STATUS, rowStatus);
|
modelProcesses->setItem(i, ProcessesWidget::COLUMN_STATUS, rowStatus);
|
||||||
modelProcesses->setItem(i, COLUMN_PATH, rowPath);
|
modelProcesses->setItem(i, ProcessesWidget::COLUMN_PATH, rowPath);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,7 +152,7 @@ void ProcessesWidget::onActivated(const QModelIndex &index)
|
|||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int pid = modelFilter->data(index.sibling(index.row(), COLUMN_PID)).toInt();
|
int pid = modelFilter->data(index.sibling(index.row(), ProcessesWidget::COLUMN_PID)).toInt();
|
||||||
// Verify that the selected pid is still in the processes list since dp= will
|
// Verify that the selected pid is still in the processes list since dp= will
|
||||||
// attach to any given id. If it isn't found simply update the UI.
|
// attach to any given id. If it isn't found simply update the UI.
|
||||||
for (const auto &value : Core()->getAllProcesses()) {
|
for (const auto &value : Core()->getAllProcesses()) {
|
||||||
@ -185,7 +182,7 @@ ProcessesFilterModel::ProcessesFilterModel(QObject *parent) : QSortFilterProxyMo
|
|||||||
bool ProcessesFilterModel::filterAcceptsRow(int row, const QModelIndex &parent) const
|
bool ProcessesFilterModel::filterAcceptsRow(int row, const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
// All columns are checked for a match
|
// All columns are checked for a match
|
||||||
for (int i = COLUMN_PID; i <= COLUMN_PATH; ++i) {
|
for (int i = ProcessesWidget::COLUMN_PID; i <= ProcessesWidget::COLUMN_PATH; ++i) {
|
||||||
QModelIndex index = sourceModel()->index(row, i, parent);
|
QModelIndex index = sourceModel()->index(row, i, parent);
|
||||||
if (qhelpers::filterStringContains(sourceModel()->data(index).toString(), this)) {
|
if (qhelpers::filterStringContains(sourceModel()->data(index).toString(), this)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -31,6 +31,13 @@ class ProcessesWidget : public CutterDockWidget
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum ColumnIndex {
|
||||||
|
COLUMN_PID = 0,
|
||||||
|
COLUMN_UID,
|
||||||
|
COLUMN_STATUS,
|
||||||
|
COLUMN_PATH,
|
||||||
|
};
|
||||||
|
|
||||||
explicit ProcessesWidget(MainWindow *main);
|
explicit ProcessesWidget(MainWindow *main);
|
||||||
~ProcessesWidget();
|
~ProcessesWidget();
|
||||||
|
|
||||||
|
@ -9,21 +9,17 @@
|
|||||||
|
|
||||||
#define DEBUGGED_PID (-1)
|
#define DEBUGGED_PID (-1)
|
||||||
|
|
||||||
enum ColumnIndex {
|
|
||||||
COLUMN_PID = 0,
|
|
||||||
COLUMN_STATUS,
|
|
||||||
COLUMN_PATH,
|
|
||||||
};
|
|
||||||
|
|
||||||
ThreadsWidget::ThreadsWidget(MainWindow *main) : CutterDockWidget(main), ui(new Ui::ThreadsWidget)
|
ThreadsWidget::ThreadsWidget(MainWindow *main) : CutterDockWidget(main), ui(new Ui::ThreadsWidget)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
// Setup threads model
|
// Setup threads model
|
||||||
modelThreads = new QStandardItemModel(1, 3, this);
|
modelThreads = new QStandardItemModel(1, 3, this);
|
||||||
modelThreads->setHorizontalHeaderItem(COLUMN_PID, new QStandardItem(tr("PID")));
|
modelThreads->setHorizontalHeaderItem(ThreadsWidget::COLUMN_PID, new QStandardItem(tr("PID")));
|
||||||
modelThreads->setHorizontalHeaderItem(COLUMN_STATUS, new QStandardItem(tr("Status")));
|
modelThreads->setHorizontalHeaderItem(ThreadsWidget::COLUMN_STATUS,
|
||||||
modelThreads->setHorizontalHeaderItem(COLUMN_PATH, new QStandardItem(tr("Path")));
|
new QStandardItem(tr("Status")));
|
||||||
|
modelThreads->setHorizontalHeaderItem(ThreadsWidget::COLUMN_PATH,
|
||||||
|
new QStandardItem(tr("Path")));
|
||||||
ui->viewThreads->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
ui->viewThreads->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||||
ui->viewThreads->verticalHeader()->setVisible(false);
|
ui->viewThreads->verticalHeader()->setVisible(false);
|
||||||
ui->viewThreads->setFont(Config()->getFont());
|
ui->viewThreads->setFont(Config()->getFont());
|
||||||
@ -120,9 +116,9 @@ void ThreadsWidget::setThreadsGrid()
|
|||||||
rowStatus->setFont(font);
|
rowStatus->setFont(font);
|
||||||
QStandardItem *rowPath = new QStandardItem(path);
|
QStandardItem *rowPath = new QStandardItem(path);
|
||||||
rowPath->setFont(font);
|
rowPath->setFont(font);
|
||||||
modelThreads->setItem(i, COLUMN_PID, rowPid);
|
modelThreads->setItem(i, ThreadsWidget::COLUMN_PID, rowPid);
|
||||||
modelThreads->setItem(i, COLUMN_STATUS, rowStatus);
|
modelThreads->setItem(i, ThreadsWidget::COLUMN_STATUS, rowStatus);
|
||||||
modelThreads->setItem(i, COLUMN_PATH, rowPath);
|
modelThreads->setItem(i, ThreadsWidget::COLUMN_PATH, rowPath);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +142,7 @@ void ThreadsWidget::onActivated(const QModelIndex &index)
|
|||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int tid = modelFilter->data(index.sibling(index.row(), COLUMN_PID)).toInt();
|
int tid = modelFilter->data(index.sibling(index.row(), ThreadsWidget::COLUMN_PID)).toInt();
|
||||||
|
|
||||||
// Verify that the selected tid is still in the threads list since dpt= will
|
// Verify that the selected tid is still in the threads list since dpt= will
|
||||||
// attach to any given id. If it isn't found simply update the UI.
|
// attach to any given id. If it isn't found simply update the UI.
|
||||||
@ -169,7 +165,7 @@ ThreadsFilterModel::ThreadsFilterModel(QObject *parent) : QSortFilterProxyModel(
|
|||||||
bool ThreadsFilterModel::filterAcceptsRow(int row, const QModelIndex &parent) const
|
bool ThreadsFilterModel::filterAcceptsRow(int row, const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
// All columns are checked for a match
|
// All columns are checked for a match
|
||||||
for (int i = COLUMN_PID; i <= COLUMN_PATH; ++i) {
|
for (int i = ThreadsWidget::COLUMN_PID; i <= ThreadsWidget::COLUMN_PATH; ++i) {
|
||||||
QModelIndex index = sourceModel()->index(row, i, parent);
|
QModelIndex index = sourceModel()->index(row, i, parent);
|
||||||
if (qhelpers::filterStringContains(sourceModel()->data(index).toString(), this)) {
|
if (qhelpers::filterStringContains(sourceModel()->data(index).toString(), this)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -31,6 +31,12 @@ class ThreadsWidget : public CutterDockWidget
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum ColumnIndex {
|
||||||
|
COLUMN_PID = 0,
|
||||||
|
COLUMN_STATUS,
|
||||||
|
COLUMN_PATH,
|
||||||
|
};
|
||||||
|
|
||||||
explicit ThreadsWidget(MainWindow *main);
|
explicit ThreadsWidget(MainWindow *main);
|
||||||
~ThreadsWidget();
|
~ThreadsWidget();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user