mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Allow loading Z3 dynamically at runtime.
This commit is contained in:
parent
fe79a27a0a
commit
7308abc084
@ -466,7 +466,7 @@ jobs:
|
||||
<<: *build_ubuntu2004
|
||||
environment:
|
||||
MAKEFLAGS: -j 10
|
||||
CMAKE_OPTIONS: -DCMAKE_BUILD_TYPE=Release -DSOLC_LINK_STATIC=1 -DUSE_CVC4=OFF -DUSE_Z3=OFF
|
||||
CMAKE_OPTIONS: -DCMAKE_BUILD_TYPE=Release -DUSE_Z3_DLOPEN=ON -DUSE_CVC4=OFF -DSOLC_STATIC_STDLIBS=ON
|
||||
steps:
|
||||
- checkout
|
||||
- run: *run_build
|
||||
|
@ -22,6 +22,7 @@ if (IS_BIG_ENDIAN)
|
||||
endif()
|
||||
|
||||
option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF)
|
||||
option(SOLC_STATIC_STDLIBS "Link solc against static versions of libgcc and libstdc++ on supported platforms" OFF)
|
||||
|
||||
# Setup cccache.
|
||||
include(EthCcache)
|
||||
@ -51,8 +52,27 @@ configure_file("${CMAKE_SOURCE_DIR}/cmake/templates/license.h.in" include/licens
|
||||
include(EthOptions)
|
||||
configure_project(TESTS)
|
||||
|
||||
find_package(Z3 4.6.0)
|
||||
if (${Z3_FOUND})
|
||||
find_package(Z3 4.8.0)
|
||||
if(${USE_Z3_DLOPEN})
|
||||
add_definitions(-DHAVE_Z3)
|
||||
add_definitions(-DHAVE_Z3_DLOPEN)
|
||||
find_package(Python3 COMPONENTS Interpreter)
|
||||
if(${Z3_FOUND})
|
||||
get_target_property(Z3_HEADER_HINTS z3::libz3 INTERFACE_INCLUDE_DIRECTORIES)
|
||||
endif()
|
||||
find_path(Z3_HEADER_PATH z3.h HINTS ${Z3_HEADER_HINTS})
|
||||
if(Z3_HEADER_PATH)
|
||||
set(Z3_FOUND TRUE)
|
||||
else()
|
||||
message(SEND_ERROR "Dynamic loading of Z3 requires Z3 headers to be present at build time.")
|
||||
endif()
|
||||
if(NOT ${Python3_FOUND})
|
||||
message(SEND_ERROR "Dynamic loading of Z3 requires Python 3 to be present at build time.")
|
||||
endif()
|
||||
if(${SOLC_LINK_STATIC})
|
||||
message(SEND_ERROR "solc cannot be linked statically when dynamically loading Z3.")
|
||||
endif()
|
||||
elseif (${Z3_FOUND})
|
||||
add_definitions(-DHAVE_Z3)
|
||||
message("Z3 SMT solver found. This enables optional SMT checking with Z3.")
|
||||
endif()
|
||||
|
@ -7,6 +7,7 @@ Language Features:
|
||||
* Wasm backend: Add ``i32.select`` and ``i64.select`` instructions.
|
||||
|
||||
Compiler Features:
|
||||
* Build System: Optionally support dynamic loading of Z3 and use that mechanism for Linux release builds.
|
||||
* Code Generator: Avoid memory allocation for default value if it is not used.
|
||||
* SMTChecker: Report struct values in counterexamples from CHC engine.
|
||||
* SMTChecker: Support early returns in the CHC engine.
|
||||
|
@ -209,6 +209,9 @@ endif()
|
||||
|
||||
# SMT Solvers integration
|
||||
option(USE_Z3 "Allow compiling with Z3 SMT solver integration" ON)
|
||||
if(UNIX AND NOT APPLE)
|
||||
option(USE_Z3_DLOPEN "Dynamically load the Z3 SMT solver instead of linking against it." OFF)
|
||||
endif()
|
||||
option(USE_CVC4 "Allow compiling with CVC4 SMT solver integration" ON)
|
||||
|
||||
if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
|
||||
|
@ -312,7 +312,7 @@ The following are dependencies for all builds of Solidity:
|
||||
+-----------------------------------+-------------------------------------------------------+
|
||||
| `Git`_ | Command-line tool for retrieving source code. |
|
||||
+-----------------------------------+-------------------------------------------------------+
|
||||
| `z3`_ (version 4.6+, Optional) | For use with SMT checker. |
|
||||
| `z3`_ (version 4.8+, Optional) | For use with SMT checker. |
|
||||
+-----------------------------------+-------------------------------------------------------+
|
||||
| `cvc4`_ (Optional) | For use with SMT checker. |
|
||||
+-----------------------------------+-------------------------------------------------------+
|
||||
|
@ -24,10 +24,25 @@ else()
|
||||
set(cvc4_SRCS)
|
||||
endif()
|
||||
|
||||
if (${USE_Z3_DLOPEN})
|
||||
file(GLOB Z3_HEADERS ${Z3_HEADER_PATH}/z3*.h)
|
||||
set(Z3_WRAPPER ${CMAKE_CURRENT_BINARY_DIR}/z3wrapper.cpp)
|
||||
add_custom_command(
|
||||
OUTPUT ${Z3_WRAPPER}
|
||||
COMMAND ${Python3_EXECUTABLE} genz3wrapper.py ${Z3_HEADERS} > ${Z3_WRAPPER}
|
||||
DEPENDS ${Z3_HEADERS} genz3wrapper.py
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
set(z3_SRCS ${z3_SRCS} ${Z3_WRAPPER} Z3Loader.cpp Z3Loader.h)
|
||||
endif()
|
||||
|
||||
add_library(smtutil ${sources} ${z3_SRCS} ${cvc4_SRCS})
|
||||
target_link_libraries(smtutil PUBLIC solutil Boost::boost)
|
||||
|
||||
if (${Z3_FOUND})
|
||||
if (${USE_Z3_DLOPEN})
|
||||
target_include_directories(smtutil PUBLIC ${Z3_HEADER_PATH})
|
||||
target_link_libraries(smtutil PUBLIC ${CMAKE_DL_LIBS})
|
||||
elseif (${Z3_FOUND})
|
||||
target_link_libraries(smtutil PUBLIC z3::libz3)
|
||||
endif()
|
||||
|
||||
|
@ -42,7 +42,7 @@ SMTPortfolio::SMTPortfolio(
|
||||
{
|
||||
m_solvers.emplace_back(make_unique<SMTLib2Interface>(move(_smtlib2Responses), move(_smtCallback), m_queryTimeout));
|
||||
#ifdef HAVE_Z3
|
||||
if (_enabledSolvers.z3)
|
||||
if (_enabledSolvers.z3 && Z3Interface::available())
|
||||
m_solvers.emplace_back(make_unique<Z3Interface>(m_queryTimeout));
|
||||
#endif
|
||||
#ifdef HAVE_CVC4
|
||||
|
@ -21,12 +21,23 @@
|
||||
#include <libsolutil/CommonData.h>
|
||||
#include <libsolutil/CommonIO.h>
|
||||
|
||||
#include <z3_api.h>
|
||||
#ifdef HAVE_Z3_DLOPEN
|
||||
#include <libsmtutil/Z3Loader.h>
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::smtutil;
|
||||
using namespace solidity::util;
|
||||
|
||||
bool Z3Interface::available()
|
||||
{
|
||||
#ifdef HAVE_Z3_DLOPEN
|
||||
return Z3Loader::get().available();
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
Z3Interface::Z3Interface(std::optional<unsigned> _queryTimeout):
|
||||
SolverInterface(_queryTimeout),
|
||||
m_solver(m_context)
|
||||
|
@ -30,6 +30,8 @@ class Z3Interface: public SolverInterface, public boost::noncopyable
|
||||
public:
|
||||
Z3Interface(std::optional<unsigned> _queryTimeout = {});
|
||||
|
||||
static bool available();
|
||||
|
||||
void reset() override;
|
||||
|
||||
void push() override;
|
||||
|
70
libsmtutil/Z3Loader.cpp
Normal file
70
libsmtutil/Z3Loader.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
#include <libsmtutil/Z3Loader.h>
|
||||
#include <libsmtutil/Exceptions.h>
|
||||
#include <z3.h>
|
||||
#include <z3_version.h>
|
||||
#include <vector>
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
#include <dlfcn.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::smtutil;
|
||||
|
||||
Z3Loader const& Z3Loader::get()
|
||||
{
|
||||
static Z3Loader z3;
|
||||
return z3;
|
||||
}
|
||||
|
||||
void* Z3Loader::loadSymbol(char const* _name) const
|
||||
{
|
||||
smtAssert(m_handle, "Attempted to use dynamically loaded Z3, even though it is not available.");
|
||||
void* sym = dlsym(m_handle, _name);
|
||||
smtAssert(sym, string("Symbol \"") + _name + "\" not found in libz3.so");
|
||||
return sym;
|
||||
}
|
||||
|
||||
bool Z3Loader::available() const
|
||||
{
|
||||
if (m_handle == nullptr)
|
||||
return false;
|
||||
unsigned major = 0;
|
||||
unsigned minor = 0;
|
||||
unsigned build = 0;
|
||||
unsigned rev = 0;
|
||||
Z3_get_version(&major, &minor, &build, &rev);
|
||||
return major == Z3_MAJOR_VERSION && minor == Z3_MINOR_VERSION;
|
||||
}
|
||||
|
||||
Z3Loader::Z3Loader()
|
||||
{
|
||||
string libname{"libz3.so." + to_string(Z3_MAJOR_VERSION) + "." + to_string(Z3_MINOR_VERSION)};
|
||||
m_handle = dlmopen(LM_ID_NEWLM, libname.c_str(), RTLD_NOW);
|
||||
}
|
||||
|
||||
Z3Loader::~Z3Loader()
|
||||
{
|
||||
if (m_handle)
|
||||
dlclose(m_handle);
|
||||
}
|
38
libsmtutil/Z3Loader.h
Normal file
38
libsmtutil/Z3Loader.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace solidity::smtutil
|
||||
{
|
||||
|
||||
class Z3Loader
|
||||
{
|
||||
public:
|
||||
Z3Loader(Z3Loader const&) = delete;
|
||||
Z3Loader& operator=(Z3Loader const&) = delete;
|
||||
static Z3Loader const& get();
|
||||
void* loadSymbol(char const* _name) const;
|
||||
bool available() const;
|
||||
private:
|
||||
Z3Loader();
|
||||
~Z3Loader();
|
||||
void* m_handle = nullptr;
|
||||
};
|
||||
|
||||
}
|
99
libsmtutil/genz3wrapper.py
Executable file
99
libsmtutil/genz3wrapper.py
Executable file
@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env python3
|
||||
# ------------------------------------------------------------------------------
|
||||
# This file is part of solidity.
|
||||
#
|
||||
# solidity is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# solidity is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with solidity. If not, see <http://www.gnu.org/licenses/>
|
||||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# Script that generates a dlsym-wrapper for Z3 from the header files.
|
||||
# Expects all Z3 headers as arguments and outputs the wrapper code to stdout.
|
||||
|
||||
import sys
|
||||
import re
|
||||
|
||||
# Patterns to match Z3 API entry point definitions.
|
||||
def_pat = re.compile(" *def_API(.*)")
|
||||
extradef_pat = re.compile(" *extra_API(.*)")
|
||||
# Pattern to extract name and arguments from the above.
|
||||
def_args_pat = re.compile("\('([^']*)'[^\(\)]*\((.*)\)\s*\)")
|
||||
# Pattern to extract a list of arguments from the above.
|
||||
arg_list_pat = re.compile("[^\(]*\([^\)]*\)[, ]*")
|
||||
|
||||
def generateEntryPoint(line, args):
|
||||
m = def_args_pat.match(args)
|
||||
if not m:
|
||||
raise Exception('Could not parse entry point definition: ' + line)
|
||||
name = m.group(1)
|
||||
num_args = len(arg_list_pat.findall(m.group(2)))
|
||||
arglist = ', '.join(f"_{i}" for i in range(num_args))
|
||||
paramlist = ', '.join(f"ArgType<&{name}, {i}> _{i}" for i in range(num_args))
|
||||
print(f'ResultType<&{name}> Z3_API {name}({paramlist})')
|
||||
print('{')
|
||||
print(f'\tstatic auto sym = reinterpret_cast<decltype(&{name})>(Z3Loader::get().loadSymbol(\"{name}\"));')
|
||||
print(f'\treturn sym({arglist});')
|
||||
print('}')
|
||||
|
||||
|
||||
print(r"""// This file is auto-generated from genz3wrapper.py
|
||||
#include <libsmtutil/Z3Loader.h>
|
||||
#include <tuple>
|
||||
#include <z3.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
struct FunctionTrait;
|
||||
|
||||
template<typename R, typename... Args>
|
||||
struct FunctionTrait<R(*)(Args...)>
|
||||
{
|
||||
using ResultType = R;
|
||||
template<unsigned N>
|
||||
using ArgType = std::tuple_element_t<N, std::tuple<Args...>>;
|
||||
};
|
||||
|
||||
template<auto F>
|
||||
using ResultType = typename FunctionTrait<decltype(F)>::ResultType;
|
||||
|
||||
template<auto F, unsigned N>
|
||||
using ArgType = typename FunctionTrait<decltype(F)>::template ArgType<N>;
|
||||
|
||||
}
|
||||
|
||||
using namespace solidity;
|
||||
using namespace solidity::smtutil;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
void Z3_API Z3_set_error_handler(Z3_context c, Z3_error_handler h)
|
||||
{
|
||||
static auto sym = reinterpret_cast<decltype(&Z3_set_error_handler)>(Z3Loader::get().loadSymbol("Z3_set_error_handler"));
|
||||
sym(c, h);
|
||||
}
|
||||
""")
|
||||
|
||||
for header in sys.argv[1:]:
|
||||
with open(header, 'r') as f:
|
||||
for line in f:
|
||||
line = line.strip('\r\n\t ')
|
||||
m = def_pat.match(line)
|
||||
if m:
|
||||
generateEntryPoint(line, m.group(1).strip('\r\n\t '))
|
||||
m = extradef_pat.match(line)
|
||||
if m:
|
||||
generateEntryPoint(line, m.group(1).strip('\r\n\t '))
|
||||
|
||||
print('}')
|
@ -23,6 +23,10 @@
|
||||
|
||||
#include <libsmtutil/SMTPortfolio.h>
|
||||
|
||||
#ifdef HAVE_Z3_DLOPEN
|
||||
#include <z3_version.h>
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
@ -83,7 +87,10 @@ void BMC::analyze(SourceUnit const& _source, map<ASTNode const*, set<Verificatio
|
||||
m_outerErrorReporter.warning(
|
||||
8084_error,
|
||||
SourceLocation(),
|
||||
"BMC analysis was not possible since no integrated SMT solver (Z3 or CVC4) was found."
|
||||
"BMC analysis was not possible since no SMT solver (Z3 or CVC4) was found."
|
||||
#ifdef HAVE_Z3_DLOPEN
|
||||
" Install libz3.so." + to_string(Z3_MAJOR_VERSION) + "." + to_string(Z3_MINOR_VERSION) + " to enable Z3."
|
||||
#endif
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,10 @@
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
#ifdef HAVE_Z3_DLOPEN
|
||||
#include <z3_version.h>
|
||||
#endif
|
||||
|
||||
#include <queue>
|
||||
|
||||
using namespace std;
|
||||
@ -58,7 +62,9 @@ CHC::CHC(
|
||||
m_queryTimeout(_timeout)
|
||||
{
|
||||
bool usesZ3 = _enabledSolvers.z3;
|
||||
#ifndef HAVE_Z3
|
||||
#ifdef HAVE_Z3
|
||||
usesZ3 = usesZ3 && Z3Interface::available();
|
||||
#else
|
||||
usesZ3 = false;
|
||||
#endif
|
||||
if (!usesZ3)
|
||||
@ -88,16 +94,19 @@ void CHC::analyze(SourceUnit const& _source)
|
||||
}
|
||||
|
||||
bool ranSolver = true;
|
||||
#ifndef HAVE_Z3
|
||||
ranSolver = dynamic_cast<CHCSmtLib2Interface const*>(m_interface.get())->unhandledQueries().empty();
|
||||
#endif
|
||||
if (auto const* smtLibInterface = dynamic_cast<CHCSmtLib2Interface const*>(m_interface.get()))
|
||||
ranSolver = smtLibInterface->unhandledQueries().empty();
|
||||
if (!ranSolver && !m_noSolverWarning)
|
||||
{
|
||||
m_noSolverWarning = true;
|
||||
m_outerErrorReporter.warning(
|
||||
3996_error,
|
||||
SourceLocation(),
|
||||
#ifdef HAVE_Z3_DLOPEN
|
||||
"CHC analysis was not possible since libz3.so." + to_string(Z3_MAJOR_VERSION) + "." + to_string(Z3_MINOR_VERSION) + " was not found."
|
||||
#else
|
||||
"CHC analysis was not possible since no integrated z3 SMT solver was found."
|
||||
#endif
|
||||
);
|
||||
}
|
||||
else
|
||||
@ -762,7 +771,7 @@ void CHC::resetSourceAnalysis()
|
||||
|
||||
bool usesZ3 = false;
|
||||
#ifdef HAVE_Z3
|
||||
usesZ3 = m_enabledSolvers.z3;
|
||||
usesZ3 = m_enabledSolvers.z3 && Z3Interface::available();
|
||||
if (usesZ3)
|
||||
{
|
||||
/// z3::fixedpoint does not have a reset mechanism, so we need to create another.
|
||||
|
@ -17,6 +17,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
#include <libsolidity/formal/ModelChecker.h>
|
||||
#ifdef HAVE_Z3
|
||||
#include <libsmtutil/Z3Interface.h>
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
@ -63,7 +66,7 @@ solidity::smtutil::SMTSolverChoice ModelChecker::availableSolvers()
|
||||
{
|
||||
smtutil::SMTSolverChoice available = smtutil::SMTSolverChoice::None();
|
||||
#ifdef HAVE_Z3
|
||||
available.z3 = true;
|
||||
available.z3 = solidity::smtutil::Z3Interface::available();
|
||||
#endif
|
||||
#ifdef HAVE_CVC4
|
||||
available.cvc4 = true;
|
||||
|
@ -20,4 +20,9 @@ if(SOLC_LINK_STATIC AND UNIX AND NOT APPLE)
|
||||
LINK_SEARCH_START_STATIC ON
|
||||
LINK_SEARCH_END_STATIC ON
|
||||
)
|
||||
elseif(SOLC_STATIC_STDLIBS AND UNIX AND NOT APPLE)
|
||||
set_target_properties(
|
||||
solc PROPERTIES
|
||||
LINK_FLAGS "-static-libgcc -static-libstdc++"
|
||||
)
|
||||
endif()
|
Loading…
Reference in New Issue
Block a user