CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
193 lines
5.0 KiB
C++
193 lines
5.0 KiB
C++
// Copyright (c) Advanced Micro Devices, Inc.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// Catch regressions where bundled sqlite3_* symbols leak out of libprofiler-hub.so.
|
|
//
|
|
// SQLite is compiled with -fvisibility=hidden and the shared library uses
|
|
// --exclude-libs on ELF so we do not step on another sqlite3 in the same process
|
|
// (TheRock has one). We parse nm/dumpbin output on the built artifact, not at
|
|
// runtime. PROFILER_HUB_SHARED_LIB comes from CMake. Skip if the host has no
|
|
// suitable tool.
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#if !defined(_WIN32)
|
|
# include <sys/wait.h>
|
|
#endif
|
|
|
|
#include <array>
|
|
#include <cstdio>
|
|
#include <memory>
|
|
#include <regex>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace
|
|
{
|
|
|
|
// Set by CMake (absolute path to libprofiler-hub.so).
|
|
constexpr const char* k_shared_library = PROFILER_HUB_SHARED_LIB;
|
|
|
|
#if defined(_WIN32)
|
|
# define PHUB_POPEN _popen
|
|
# define PHUB_PCLOSE _pclose
|
|
#else
|
|
# define PHUB_POPEN popen
|
|
# define PHUB_PCLOSE pclose
|
|
#endif
|
|
|
|
struct file_op
|
|
{
|
|
void operator()(FILE* pipe) const noexcept
|
|
{
|
|
if(pipe != nullptr)
|
|
{
|
|
PHUB_PCLOSE(pipe);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Run command, collect stdout. Returns the child exit status via `status`
|
|
// (-1 if the shell could not run it). False only when the pipe could not open.
|
|
bool
|
|
run_capture(const std::string& command, std::string& output, int& status)
|
|
{
|
|
output.clear();
|
|
status = -1;
|
|
std::unique_ptr<FILE, file_op> pipe(PHUB_POPEN(command.c_str(), "r"));
|
|
if(!pipe)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
std::array<char, 4096> buffer{};
|
|
while(std::fgets(buffer.data(), static_cast<int>(buffer.size()), pipe.get()) !=
|
|
nullptr)
|
|
{
|
|
output += buffer.data();
|
|
}
|
|
|
|
status = PHUB_PCLOSE(pipe.release());
|
|
return true;
|
|
}
|
|
|
|
// Convenience overload: success means the command ran and exited zero.
|
|
bool
|
|
run_capture(const std::string& command, std::string& output)
|
|
{
|
|
int status = -1;
|
|
if(!run_capture(command, output, status))
|
|
{
|
|
return false;
|
|
}
|
|
#if defined(_WIN32)
|
|
// _pclose gives the child exit code directly (-1 on error).
|
|
return status == 0;
|
|
#else
|
|
return status != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0;
|
|
#endif
|
|
}
|
|
|
|
struct symbol_holder
|
|
{
|
|
std::string command;
|
|
std::regex pattern;
|
|
};
|
|
|
|
// A probe only needs to prove the tool exists and runs. Some tools (notably
|
|
// dumpbin /?) print their banner to stdout but exit non-zero, so presence is
|
|
// decided by captured output, not by the exit status.
|
|
bool
|
|
tool_available(const std::string& probe)
|
|
{
|
|
std::string out;
|
|
int status = -1;
|
|
return run_capture(probe, out, status) && !out.empty();
|
|
}
|
|
|
|
// First nm/llvm-nm/dumpbin that works on this platform, or false.
|
|
bool
|
|
pick_tool(const std::string& library, symbol_holder& out)
|
|
{
|
|
const std::string quoted = "\"" + library + "\"";
|
|
|
|
#if defined(_WIN32)
|
|
// dumpbin export table: ordinal, hint, RVA, name.
|
|
if(tool_available("dumpbin /? 2>NUL"))
|
|
{
|
|
out = {
|
|
"dumpbin /nologo /exports " + quoted + " 2>NUL",
|
|
std::regex{
|
|
R"(^\s*[0-9]+\s+[0-9A-Fa-f]+\s+[0-9A-Fa-f]+\s+(sqlite3_[A-Za-z0-9_]+))" }
|
|
};
|
|
return true;
|
|
}
|
|
if(tool_available("llvm-nm --version 2>NUL"))
|
|
{
|
|
out = { "llvm-nm --defined-only " + quoted + " 2>NUL",
|
|
std::regex{ R"(\b[A-Za-z] (sqlite3_[A-Za-z0-9_]+)$)" } };
|
|
return true;
|
|
}
|
|
#else
|
|
for(const char* candidate : { "nm", "llvm-nm" })
|
|
{
|
|
if(tool_available(std::string{ candidate } + " --version 2>/dev/null"))
|
|
{
|
|
out = {
|
|
std::string{ candidate } + " -D --defined-only " + quoted +
|
|
" 2>/dev/null",
|
|
std::regex{
|
|
R"(^[0-9a-fA-F]+ [A-Za-z] (sqlite3_[A-Za-z0-9_]+(?:@{1,2}[A-Za-z0-9_.-]+)?)$)" }
|
|
};
|
|
return true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
TEST(sqlite3_symbol_seal, shared_library_exports_no_sqlite3_symbols)
|
|
{
|
|
symbol_holder lister;
|
|
if(!pick_tool(k_shared_library, lister))
|
|
{
|
|
GTEST_SKIP() << "need nm, llvm-nm, or dumpbin to inspect " << k_shared_library;
|
|
}
|
|
|
|
std::string output;
|
|
ASSERT_TRUE(run_capture(lister.command, output))
|
|
<< "failed to inspect exported symbols of " << k_shared_library;
|
|
|
|
std::vector<std::string> leaked;
|
|
std::istringstream stream(output);
|
|
std::string line;
|
|
while(std::getline(stream, line))
|
|
{
|
|
if(!line.empty() && line.back() == '\r')
|
|
{
|
|
line.pop_back();
|
|
}
|
|
|
|
std::smatch match;
|
|
if(std::regex_search(line, match, lister.pattern))
|
|
{
|
|
leaked.push_back(match[1].str());
|
|
}
|
|
}
|
|
|
|
std::string detail;
|
|
for(const auto& symbol : leaked)
|
|
{
|
|
detail += "\n " + symbol;
|
|
}
|
|
|
|
EXPECT_TRUE(leaked.empty())
|
|
<< k_shared_library << " exports bundled sqlite3 symbol(s):" << detail
|
|
<< " (expected none, heck -fvisibility=hidden on profiler-hub-sqlite3-static "
|
|
"and --exclude-libs on the profiler-hub shared link)";
|
|
}
|
|
|
|
} // namespace
|