Contents
- Make a skeleton spec file
- Determine the version
- Use autochangelog
- Write a summary
- Determine the license
- The URL field
- The Source field
- BuildRequires
- Description
- Prep
- Build
- Install
- Files
- First test build
- Apply upstream patches
- Devel subpackage
- Library soname
- Python interface
- Build flags
- Requires and Provides
- File cleanup
- Build documentation
- Run tests
- Reconsider the name
- Run rpmlint
- Conclusion
Make a skeleton spec file
We are going to create a package for triton. You don’t have to understand what triton does. The point is to show the steps needed to create an RPM package for a C++ project. In this case, it is actually a C++ project with a Python interface, which makes it a bit more interesting.
First, change to the directory where you want the RPM spec file to be created.
cd ~/rpmbuild/SPECS
One of the packages we had you install in setup is rpmdevtools, which contains several useful utilities for working with RPM packages. One of those tools is rpmdev-newspec, which creates a skeleton spec file for you. It has a number of templates which it uses to create the spec files; look in /etc/rpmdevtools to see the available templates. We will use the minimal template.
rpmdev-newspec triton
Now there is a file named triton.spec in your current directory. Let’s see what is inside.
Name: triton
Version:
Release: 1%{?dist}
Summary:
License:
URL:
Source0:
BuildRequires:
Requires:
%description
%prep
%autosetup
%build
%configure
%make_build
%install
%make_install
%files
%license add-license-file-here
%doc add-docs-here
%changelog
* Wed Sep 24 2025 Jerry James
-
Determine the version
Let’s start filling in the spec file. First, what version are we packaging?
At the time of this writing, the latest released version is 0.9, so add that
to the Version: line. While most software, including triton, now carries
version numbers of the form number.number[.number], that style is not
universal. If you are making a package for an upstream that has an unusual
version numbering style, see
the versioning guidelines
for help on determining what to put in the Version: field.
Use autochangelog
Autochangelog is a recent addition to Fedora. It lets you avoid dealing
directly with the Release: field and the %changelog section. Change the
Release: field to read %autorelease. At the end of the spec file, replace
the lines after %changelog with %autochangelog.
Write a summary
Next we need a summary. What does this package do? The github page says:
Triton is a Dynamic Binary Analysis (DBA) framework.
It is considered bad form to include the name of the package on the Summary:
line, so we’ll shorten that to “Dynamic binary analysis framework”. Notice
that there is no period in the summary. It is not a complete sentence, but
rather a short descriptive phrase.
Determine the license
If you haven’t yet downloaded the tarball for the version you are packaging, do so now. Unpack the sources, and run licensecheck on the source files, as described here. In the case of triton 0.9, the output looks like this:
./.appveyor.yml: *No copyright* UNKNOWN
./.build_number: *No copyright* UNKNOWN
./.codecov.yml: *No copyright* UNKNOWN
./.editorconfig: *No copyright* UNKNOWN
./CMakeLists.txt: UNKNOWN
./Dockerfile: *No copyright* UNKNOWN
./LICENSE.txt: *No copyright* Apache License 2.0
./README.md: *No copyright* UNKNOWN
./CMakeModules/FindBITWUZLA.cmake: *No copyright* UNKNOWN
./CMakeModules/FindCAPSTONE.cmake: *No copyright* UNKNOWN
./CMakeModules/FindZ3.cmake: *No copyright* UNKNOWN
./CMakeModules/LibFindMacros.cmake: *No copyright* UNKNOWN
./doc/CMakeLists.txt: UNKNOWN
./doc/Doxyfile.in: *No copyright* UNKNOWN
./doc/DoxygenLayout.xml: *No copyright* UNKNOWN
./doc/customdoxygen.css: *No copyright* UNKNOWN
./doc/extract_doc.py: *No copyright* UNKNOWN
./doc/footer.html: *No copyright* UNKNOWN
./doc/header.html: *No copyright* UNKNOWN
./publications/BAR2020-qsynth-robin-david.pdf: UNKNOWN
./publications/BHUSA2021-David-Greybox-Program-Synthesis.pdf: UNKNOWN
./publications/CESAR2021_robin-david-paper.pdf: *No copyright* UNKNOWN
./publications/CESAR2021_robin-david-slide.pdf: UNKNOWN
./publications/CSAW2016-SOS-Virtual-Machine-Deobfuscation-RThomas_JSalwan.pdf: UNKNOWN
./publications/DIMVA2018-deobfuscation-salwan-bardin-potet.pdf: UNKNOWN
./publications/DIMVA2018-slide-deobfuscation-salwan-bardin-potet.pdf: *No copyright* UNKNOWN
./publications/ISPOPEN2020-slide-sydr-vishnyakov.pdf: UNKNOWN
./publications/ISPOPEN2021-security-predicates-vishnyakov.pdf: UNKNOWN
./publications/ISPOPEN2021-slide-security-predicates-vishnyakov.pdf: UNKNOWN
./publications/ISPRAS2020-sydr.pdf: *No copyright* UNKNOWN
./publications/IVMEM2021-slide-symbolic-pointers-kuts.pdf: UNKNOWN
./publications/IVMEM2021-symbolic-pointers-kuts.pdf: UNKNOWN
./publications/MISC-82_French_Paper_How_Triton_may_help_to_analyse_obfuscated_binaries_RThomas_JSalwan.pdf: *No copyright* UNKNOWN
./publications/SSTIC2015_English_slide_detailed_version_Triton_Concolic_Execution_FrameWork_FSaudel_JSalwan.pdf: UNKNOWN
./publications/SSTIC2015_French_Paper_Triton_Framework_dexecution_Concolique_FSaudel_JSalwan.pdf: UNKNOWN
./publications/SSTIC2015_French_slide_light_version_Triton_Concolic_Execution_FrameWork_FSaudel_JSalwan.pdf: UNKNOWN
./publications/SSTIC2017-French-Article-desobfuscation_binaire_reconstruction_de_fonctions_virtualisees-salwan_potet_bardin.pdf: UNKNOWN
./publications/SSTIC2017_Deobfuscation_of_VM_based_software_protection.pdf: *No copyright* UNKNOWN
./publications/SecurityDay2015_dynamic_symbolic_execution_Jonathan_Salwan.pdf: UNKNOWN
./publications/StHack2015_Dynamic_Behavior_Analysis_using_Binary_Instrumentation_Jonathan_Salwan.pdf: UNKNOWN
./publications/StHack2016_Dynamic_Binary_Analysis_and_Obfuscated_Codes_RThomas_JSalwan.pdf: UNKNOWN
./src/CMakeLists.txt: *No copyright* UNKNOWN
./.github/workflows/bitwuzla.yml: *No copyright* UNKNOWN
./.github/workflows/codecov.yml: *No copyright* UNKNOWN
./.github/workflows/docker.yml: *No copyright* UNKNOWN
./.github/workflows/linux.yml: *No copyright* UNKNOWN
./.github/workflows/llvm.yml: *No copyright* UNKNOWN
./.github/workflows/osx.yml: *No copyright* UNKNOWN
./doc/autocomplete/example.py: *No copyright* UNKNOWN
./doc/autocomplete/function.py: *No copyright* UNKNOWN
./doc/autocomplete/generate_autocomplete.py: *No copyright* UNKNOWN
./doc/figures/figure.1.1.png: UNKNOWN
./doc/figures/figure.1.10.png: UNKNOWN
./doc/figures/figure.1.11.png: UNKNOWN
./doc/figures/figure.1.19.png: UNKNOWN
./doc/figures/figure.1.20.png: UNKNOWN
./doc/figures/figure.1.4.png: UNKNOWN
./doc/figures/figure.1.5.png: UNKNOWN
./doc/figures/figure.1.6.png: UNKNOWN
./doc/figures/figure.1.7.png: UNKNOWN
./doc/figures/figure.1.8.png: UNKNOWN
./doc/figures/figure.1.9.png: UNKNOWN
./src/examples/CMakeLists.txt: *No copyright* UNKNOWN
./src/libtriton/CMakeLists.txt: *No copyright* UNKNOWN
./src/libtriton/Config.cmake.in: *No copyright* UNKNOWN
./src/scripts/gen_oracle_table.py: Apache License 2.0
./src/stubs/triton-stubs.c: *No copyright* UNKNOWN
./src/testers/CMakeLists.txt: *No copyright* UNKNOWN
./src/examples/cpp/CMakeLists.txt: *No copyright* UNKNOWN
./src/examples/cpp/Makefile: *No copyright* UNKNOWN
./src/examples/cpp/constraint.cpp: *No copyright* UNKNOWN
./src/examples/cpp/ctest_api.cpp: *No copyright* UNKNOWN
./src/examples/cpp/info_reg.cpp: *No copyright* UNKNOWN
./src/examples/cpp/ir.cpp: *No copyright* UNKNOWN
./src/examples/cpp/simplification.cpp: *No copyright* UNKNOWN
./src/examples/cpp/taint_reg.cpp: *No copyright* UNKNOWN
./src/examples/python/backward_slicing.py: *No copyright* UNKNOWN
./src/examples/python/code_coverage_crackme_xor.py: *No copyright* UNKNOWN
./src/examples/python/constraints.py: *No copyright* UNKNOWN
./src/examples/python/disass.py: *No copyright* UNKNOWN
./src/examples/python/forward_tainting.py: *No copyright* UNKNOWN
./src/examples/python/hooking_libc.py: *No copyright* UNKNOWN
./src/examples/python/ir.py: *No copyright* UNKNOWN
./src/examples/python/multi_threading.py: *No copyright* UNKNOWN
./src/examples/python/proving_opaque_predicates.py: *No copyright* UNKNOWN
./src/examples/python/simplification.py: *No copyright* UNKNOWN
./src/examples/python/small_x86-64_symbolic_emulator.py: *No copyright* UNKNOWN
./src/examples/python/symbolic_emulation_1.py: *No copyright* UNKNOWN
./src/examples/python/symbolic_emulation_2.py: *No copyright* UNKNOWN
./src/examples/python/symbolic_emulation_crackme_xor.py: *No copyright* UNKNOWN
./src/examples/python/symbolic_pointers_reasoning.py: *No copyright* UNKNOWN
./src/examples/python/synthesizing_obfuscated_code.py: *No copyright* UNKNOWN
./src/examples/python/synthesizing_obfuscated_expressions.py: *No copyright* UNKNOWN
./src/libtriton/api/api.cpp: Apache License 2.0
./src/libtriton/arch/architecture.cpp: Apache License 2.0
./src/libtriton/arch/bitsVector.cpp: Apache License 2.0
./src/libtriton/arch/immediate.cpp: Apache License 2.0
./src/libtriton/arch/instruction.cpp: Apache License 2.0
./src/libtriton/arch/irBuilder.cpp: Apache License 2.0
./src/libtriton/arch/memoryAccess.cpp: Apache License 2.0
./src/libtriton/arch/operandWrapper.cpp: Apache License 2.0
./src/libtriton/arch/register.cpp: Apache License 2.0
./src/libtriton/ast/ast.cpp: Apache License 2.0
./src/libtriton/ast/astContext.cpp: Apache License 2.0
./src/libtriton/callbacks/callbacks.cpp: Apache License 2.0
./src/libtriton/modes/modes.cpp: Apache License 2.0
./src/libtriton/utils/coreUtils.cpp: Apache License 2.0
./src/samples/32bits/crackme_xor: *No copyright* UNKNOWN
./src/samples/32bits/crackme_xor.c: *No copyright* UNKNOWN
./src/samples/aarch64/crackme_hash: UNKNOWN
./src/samples/aarch64/crackme_hash.c: *No copyright* UNKNOWN
./src/samples/aarch64/crackme_xor: UNKNOWN
./src/samples/aarch64/crackme_xor.c: *No copyright* UNKNOWN
./src/samples/code_coverage/test_atoi: *No copyright* UNKNOWN
./src/samples/code_coverage/test_atoi.c: *No copyright* UNKNOWN
./src/samples/crackmes/crackme_hash: *No copyright* UNKNOWN
./src/samples/crackmes/crackme_hash.c: *No copyright* UNKNOWN
./src/samples/crackmes/crackme_regex_fsm: *No copyright* UNKNOWN
./src/samples/crackmes/crackme_regex_fsm.c: *No copyright* UNKNOWN
./src/samples/crackmes/crackme_regex_fsm_obfuscated: *No copyright* UNKNOWN
./src/samples/crackmes/crackme_regex_fsm_obfuscated.c: *No copyright* UNKNOWN
./src/samples/crackmes/crackme_sample: *No copyright* UNKNOWN
./src/samples/crackmes/crackme_sample.c: *No copyright* UNKNOWN
./src/samples/crackmes/crackme_xor: *No copyright* UNKNOWN
./src/samples/crackmes/crackme_xor.c: *No copyright* UNKNOWN
./src/samples/ir_test_suite/ir: UNKNOWN
./src/samples/ir_test_suite/ir.c: *No copyright* UNKNOWN
./src/samples/ir_test_suite/qemu-test-x86_64: UNKNOWN
./src/samples/others/read_from_file: UNKNOWN
./src/samples/others/read_from_file.c: *No copyright* UNKNOWN
./src/samples/others/sage_example: *No copyright* UNKNOWN
./src/samples/others/sage_example.c: *No copyright* UNKNOWN
./src/samples/others/signals: *No copyright* UNKNOWN
./src/samples/others/signals.c: *No copyright* UNKNOWN
./src/samples/others/strlen: *No copyright* UNKNOWN
./src/samples/others/strlen.c: *No copyright* UNKNOWN
./src/samples/others/thread: *No copyright* UNKNOWN
./src/samples/others/thread.c: *No copyright* UNKNOWN
./src/samples/smt/af.smt2: *No copyright* UNKNOWN
./src/samples/smt/cmp.smt2: *No copyright* UNKNOWN
./src/samples/smt/firstCharTest.smt2: *No copyright* UNKNOWN
./src/samples/smt/jbe.smt2: *No copyright* UNKNOWN
./src/samples/smt/jle.smt2: *No copyright* UNKNOWN
./src/samples/smt/jnbe.smt2: *No copyright* UNKNOWN
./src/samples/smt/jnle.smt2: *No copyright* UNKNOWN
./src/samples/smt/of.smt2: *No copyright* UNKNOWN
./src/samples/smt/pf.smt2: *No copyright* UNKNOWN
./src/samples/smt/sf.smt2: *No copyright* UNKNOWN
./src/samples/vulns/formatString: *No copyright* UNKNOWN
./src/samples/vulns/formatString.c: *No copyright* UNKNOWN
./src/samples/vulns/testSuite: *No copyright* UNKNOWN
./src/samples/vulns/testSuite.c: *No copyright* UNKNOWN
./src/testers/aarch64/unicorn_test_aarch64.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_branch_arm_1.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_branch_arm_2.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_branch_pc_arm_1.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_branch_pc_arm_2.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_branch_thumb_1.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_branch_thumb_2.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_data_arm.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_data_thumb.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_interworking_arm_1.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_interworking_arm_2.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_interworking_thumb.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_it_thumb.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_loadstore_arm_1.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_loadstore_arm_2.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_loadstore_arm_3.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_loadstore_arm_4.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_loadstore_arm_5.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_loadstore_thumb_1.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_loadstore_thumb_2.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_loadstore_thumb_3.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_loadstore_thumb_5.py: *No copyright* UNKNOWN
./src/testers/unittests/test_arch.py: *No copyright* UNKNOWN
./src/testers/unittests/test_ast_conversion.py: *No copyright* UNKNOWN
./src/testers/unittests/test_ast_deep.py: *No copyright* UNKNOWN
./src/testers/unittests/test_ast_duplication.py: *No copyright* UNKNOWN
./src/testers/unittests/test_ast_eval.py: *No copyright* UNKNOWN
./src/testers/unittests/test_ast_reference.py: *No copyright* UNKNOWN
./src/testers/unittests/test_ast_representation.py: *No copyright* UNKNOWN
./src/testers/unittests/test_ast_simplification.py: *No copyright* UNKNOWN
./src/testers/unittests/test_ast_utils.py: *No copyright* UNKNOWN
./src/testers/unittests/test_bitvector.py: *No copyright* UNKNOWN
./src/testers/unittests/test_callback.py: *No copyright* UNKNOWN
./src/testers/unittests/test_concrete_value.py: *No copyright* UNKNOWN
./src/testers/unittests/test_disass.py: *No copyright* UNKNOWN
./src/testers/unittests/test_doc.py: *No copyright* UNKNOWN
./src/testers/unittests/test_examples.py: *No copyright* UNKNOWN
./src/testers/unittests/test_flags.py: *No copyright* UNKNOWN
./src/testers/unittests/test_github_issues.py: *No copyright* UNKNOWN
./src/testers/unittests/test_im_callback.py: *No copyright* UNKNOWN
./src/testers/unittests/test_immediate.py: *No copyright* UNKNOWN
./src/testers/unittests/test_immutable_registers.py: *No copyright* UNKNOWN
./src/testers/unittests/test_instruction.py: *No copyright* UNKNOWN
./src/testers/unittests/test_memory.py: *No copyright* UNKNOWN
./src/testers/unittests/test_only_symbolized_mode.py: *No copyright* UNKNOWN
./src/testers/unittests/test_only_tainted_mode.py: *No copyright* UNKNOWN
./src/testers/unittests/test_path_constraint.py: *No copyright* UNKNOWN
./src/testers/unittests/test_registers.py: *No copyright* UNKNOWN
./src/testers/unittests/test_semantics.py: *No copyright* UNKNOWN
./src/testers/unittests/test_simulation.py: *No copyright* UNKNOWN
./src/testers/unittests/test_solver.py: *No copyright* UNKNOWN
./src/testers/unittests/test_symbolic.py: *No copyright* UNKNOWN
./src/testers/unittests/test_symbolic_expression.py: *No copyright* UNKNOWN
./src/testers/unittests/test_symbolic_optimizations.py: *No copyright* UNKNOWN
./src/testers/unittests/test_symbolic_variable.py: *No copyright* UNKNOWN
./src/testers/unittests/test_synthesizer.py: *No copyright* UNKNOWN
./src/testers/unittests/test_taint.py: *No copyright* UNKNOWN
./src/testers/unittests/test_undefined_registers.py: *No copyright* UNKNOWN
./src/testers/unittests/utils.py: *No copyright* UNKNOWN
./src/testers/x86/unicorn_test_x86.py: *No copyright* UNKNOWN
./src/examples/python/samples/sample_1: *No copyright* UNKNOWN
./src/examples/python/samples/sample_1.c: *No copyright* UNKNOWN
./src/examples/python/samples/sample_2: *No copyright* UNKNOWN
./src/examples/python/samples/sample_2.c: *No copyright* UNKNOWN
./src/libtriton/arch/arm/armOperandProperties.cpp: Apache License 2.0
./src/libtriton/arch/x86/x8664Cpu.cpp: Apache License 2.0
./src/libtriton/arch/x86/x86Cpu.cpp: Apache License 2.0
./src/libtriton/arch/x86/x86Semantics.cpp: Apache License 2.0
./src/libtriton/arch/x86/x86Specifications.cpp: Apache License 2.0
./src/libtriton/ast/bitwuzla/tritonToBitwuzla.cpp: Apache License 2.0
./src/libtriton/ast/llvm/llvmToTriton.cpp: Apache License 2.0
./src/libtriton/ast/llvm/tritonToLLVM.cpp: Apache License 2.0
./src/libtriton/ast/representations/astPythonRepresentation.cpp: Apache License 2.0
./src/libtriton/ast/representations/astRepresentation.cpp: Apache License 2.0
./src/libtriton/ast/representations/astSmtRepresentation.cpp: Apache License 2.0
./src/libtriton/ast/z3/tritonToZ3.cpp: Apache License 2.0
./src/libtriton/ast/z3/z3ToTriton.cpp: Apache License 2.0
./src/libtriton/bindings/python/init.cpp: Apache License 2.0
./src/libtriton/bindings/python/pyXFunctions.cpp: Apache License 2.0
./src/libtriton/bindings/python/utils.cpp: Apache License 2.0
./src/libtriton/engines/lifters/liftingToLLVM.cpp: Apache License 2.0
./src/libtriton/engines/lifters/liftingToPython.cpp: Apache License 2.0
./src/libtriton/engines/lifters/liftingToSMT.cpp: Apache License 2.0
./src/libtriton/engines/solver/solverEngine.cpp: Apache License 2.0
./src/libtriton/engines/solver/solverModel.cpp: Apache License 2.0
./src/libtriton/engines/symbolic/pathConstraint.cpp: Apache License 2.0
./src/libtriton/engines/symbolic/pathManager.cpp: Apache License 2.0
./src/libtriton/engines/symbolic/symbolicEngine.cpp: Apache License 2.0
./src/libtriton/engines/symbolic/symbolicExpression.cpp: Apache License 2.0
./src/libtriton/engines/symbolic/symbolicSimplification.cpp: Apache License 2.0
./src/libtriton/engines/symbolic/symbolicVariable.cpp: Apache License 2.0
./src/libtriton/engines/synthesis/oracleTable.cpp: Apache License 2.0
./src/libtriton/engines/synthesis/synthesisResult.cpp: Apache License 2.0
./src/libtriton/engines/synthesis/synthesizer.cpp: Apache License 2.0
./src/libtriton/engines/taint/taintEngine.cpp: Apache License 2.0
./src/libtriton/includes/triton/aarch64.spec: *No copyright* UNKNOWN
./src/libtriton/includes/triton/aarch64Cpu.hpp: Apache License 2.0
./src/libtriton/includes/triton/aarch64Semantics.hpp: Apache License 2.0
./src/libtriton/includes/triton/aarch64Specifications.hpp: Apache License 2.0
./src/libtriton/includes/triton/api.hpp: Apache License 2.0
./src/libtriton/includes/triton/archEnums.hpp: Apache License 2.0
./src/libtriton/includes/triton/architecture.hpp: Apache License 2.0
./src/libtriton/includes/triton/arm32.spec: *No copyright* UNKNOWN
./src/libtriton/includes/triton/arm32Cpu.hpp: Apache License 2.0
./src/libtriton/includes/triton/arm32Semantics.hpp: Apache License 2.0
./src/libtriton/includes/triton/arm32Specifications.hpp: Apache License 2.0
./src/libtriton/includes/triton/armOperandProperties.hpp: Apache License 2.0
./src/libtriton/includes/triton/ast.hpp: Apache License 2.0
./src/libtriton/includes/triton/astContext.hpp: Apache License 2.0
./src/libtriton/includes/triton/astEnums.hpp: Apache License 2.0
./src/libtriton/includes/triton/astPythonRepresentation.hpp: Apache License 2.0
./src/libtriton/includes/triton/astRepresentation.hpp: Apache License 2.0
./src/libtriton/includes/triton/astRepresentationInterface.hpp: Apache License 2.0
./src/libtriton/includes/triton/astSmtRepresentation.hpp: Apache License 2.0
./src/libtriton/includes/triton/bitsVector.hpp: Apache License 2.0
./src/libtriton/includes/triton/bitwuzlaSolver.hpp: Apache License 2.0
./src/libtriton/includes/triton/callbacks.hpp: Apache License 2.0
./src/libtriton/includes/triton/callbacksEnums.hpp: Apache License 2.0
./src/libtriton/includes/triton/comparableFunctor.hpp: Apache License 2.0
./src/libtriton/includes/triton/config.hpp.in: Apache License 2.0
./src/libtriton/includes/triton/coreUtils.hpp: Apache License 2.0
./src/libtriton/includes/triton/cpuInterface.hpp: Apache License 2.0
./src/libtriton/includes/triton/cpuSize.hpp: Apache License 2.0
./src/libtriton/includes/triton/dllexport.hpp: Apache License 2.0
./src/libtriton/includes/triton/exceptions.hpp: Apache License 2.0
./src/libtriton/includes/triton/externalLibs.hpp: Apache License 2.0
./src/libtriton/includes/triton/immediate.hpp: Apache License 2.0
./src/libtriton/includes/triton/instruction.hpp: Apache License 2.0
./src/libtriton/includes/triton/irBuilder.hpp: Apache License 2.0
./src/libtriton/includes/triton/liftingEngine.hpp: Apache License 2.0
./src/libtriton/includes/triton/liftingToLLVM.hpp: Apache License 2.0
./src/libtriton/includes/triton/liftingToPython.hpp: Apache License 2.0
./src/libtriton/includes/triton/liftingToSMT.hpp: Apache License 2.0
./src/libtriton/includes/triton/llvmToTriton.hpp: Apache License 2.0
./src/libtriton/includes/triton/memoryAccess.hpp: Apache License 2.0
./src/libtriton/includes/triton/modes.hpp: Apache License 2.0
./src/libtriton/includes/triton/modesEnums.hpp: Apache License 2.0
./src/libtriton/includes/triton/operandWrapper.hpp: Apache License 2.0
./src/libtriton/includes/triton/oracleEntry.hpp: Apache License 2.0
./src/libtriton/includes/triton/pathConstraint.hpp: Apache License 2.0
./src/libtriton/includes/triton/pathManager.hpp: Apache License 2.0
./src/libtriton/includes/triton/py3c_compat.h: MIT License
./src/libtriton/includes/triton/pythonBindings.hpp: Apache License 2.0
./src/libtriton/includes/triton/pythonObjects.hpp: Apache License 2.0
./src/libtriton/includes/triton/pythonUtils.hpp: Apache License 2.0
./src/libtriton/includes/triton/pythonXFunctions.hpp: Apache License 2.0
./src/libtriton/includes/triton/register.hpp: Apache License 2.0
./src/libtriton/includes/triton/semanticsInterface.hpp: Apache License 2.0
./src/libtriton/includes/triton/shortcutRegister.hpp: Apache License 2.0
./src/libtriton/includes/triton/solverEngine.hpp: Apache License 2.0
./src/libtriton/includes/triton/solverEnums.hpp: Apache License 2.0
./src/libtriton/includes/triton/solverInterface.hpp: Apache License 2.0
./src/libtriton/includes/triton/solverModel.hpp: Apache License 2.0
./src/libtriton/includes/triton/symbolicEngine.hpp: Apache License 2.0
./src/libtriton/includes/triton/symbolicEnums.hpp: Apache License 2.0
./src/libtriton/includes/triton/symbolicExpression.hpp: Apache License 2.0
./src/libtriton/includes/triton/symbolicSimplification.hpp: Apache License 2.0
./src/libtriton/includes/triton/symbolicVariable.hpp: Apache License 2.0
./src/libtriton/includes/triton/synthesisResult.hpp: Apache License 2.0
./src/libtriton/includes/triton/synthesizer.hpp: Apache License 2.0
./src/libtriton/includes/triton/taintEngine.hpp: Apache License 2.0
./src/libtriton/includes/triton/tritonToBitwuzla.hpp: Apache License 2.0
./src/libtriton/includes/triton/tritonToLLVM.hpp: Apache License 2.0
./src/libtriton/includes/triton/tritonToZ3.hpp: Apache License 2.0
./src/libtriton/includes/triton/tritonTypes.hpp: Apache License 2.0
./src/libtriton/includes/triton/version.hpp.in: Apache License 2.0
./src/libtriton/includes/triton/x86.spec: *No copyright* UNKNOWN
./src/libtriton/includes/triton/x8664Cpu.hpp: Apache License 2.0
./src/libtriton/includes/triton/x86Cpu.hpp: Apache License 2.0
./src/libtriton/includes/triton/x86Semantics.hpp: Apache License 2.0
./src/libtriton/includes/triton/x86Specifications.hpp: Apache License 2.0
./src/libtriton/includes/triton/z3Solver.hpp: Apache License 2.0
./src/libtriton/includes/triton/z3ToTriton.hpp: Apache License 2.0
./src/testers/arm32/crypto_test/crypto_test-nothumb-O0-run.py: *No copyright* UNKNOWN
./src/testers/arm32/crypto_test/crypto_test-nothumb-O1-run.py: *No copyright* UNKNOWN
./src/testers/arm32/crypto_test/crypto_test-nothumb-O2-run.py: *No copyright* UNKNOWN
./src/testers/arm32/crypto_test/crypto_test-nothumb-O3-run.py: *No copyright* UNKNOWN
./src/testers/arm32/crypto_test/crypto_test-nothumb-Os-run.py: *No copyright* UNKNOWN
./src/testers/arm32/crypto_test/crypto_test-nothumb-Oz-run.py: *No copyright* UNKNOWN
./src/testers/arm32/crypto_test/crypto_test-thumb-O0-run.py: *No copyright* UNKNOWN
./src/testers/arm32/crypto_test/crypto_test-thumb-O1-run.py: *No copyright* UNKNOWN
./src/testers/arm32/crypto_test/crypto_test-thumb-O2-run.py: *No copyright* UNKNOWN
./src/testers/arm32/crypto_test/crypto_test-thumb-O3-run.py: *No copyright* UNKNOWN
./src/testers/arm32/crypto_test/crypto_test-thumb-Os-run.py: *No copyright* UNKNOWN
./src/testers/arm32/crypto_test/crypto_test-thumb-Oz-run.py: *No copyright* UNKNOWN
./src/testers/unittests/misc/defcamp-2015-r100.bin: *No copyright* UNKNOWN
./src/testers/unittests/misc/emu_1.dump: UNKNOWN
./src/testers/unittests/misc/ir-test-suite.bin: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/Nuit_du_Hack_CTF_Quals_2016_Matriochka_Step_3/solve.py: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/Nuit_du_Hack_CTF_Quals_2016_Matriochka_Step_3/stage3.bin: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/alexctf-2017-re2-cpp-is-awesome/re2: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/alexctf-2017-re2-cpp-is-awesome/solve.py: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/alexctf-2017-re3-catalyst-system/catalyst: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/alexctf-2017-re3-catalyst-system/catalyst.patched_without_sleep: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/alexctf-2017-re3-catalyst-system/solve.py: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/defcamp-2015-r100/r100.bin: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/defcamp-2015-r100/solve.py: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/defcon-2016-baby-re/baby-re: UNKNOWN
./src/examples/python/ctf-writeups/defcon-2016-baby-re/baby-re.dump: UNKNOWN
./src/examples/python/ctf-writeups/defcon-2016-baby-re/gdb-peda-fulldump.patch: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/defcon-2016-baby-re/solve.py: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/ekopartyctf2016_rev250/FUck_binary: UNKNOWN
./src/examples/python/ctf-writeups/ekopartyctf2016_rev250/libget_flag.so: UNKNOWN
./src/examples/python/ctf-writeups/ekopartyctf2016_rev250/solve.py: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/google2016-unbreakable/solve.py: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/google2016-unbreakable/unbreakable-enterprise-product-activation: UNKNOWN
./src/examples/python/ctf-writeups/hackcon-2016-angry-reverser/solve.py: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/hackcon-2016-angry-reverser/yolomolo: UNKNOWN
./src/examples/python/ctf-writeups/hackover-ctf-2015-r150/rvs.bin: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/hackover-ctf-2015-r150/solve.py: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/mma-2015-howtouse/howtouse.dll: UNKNOWN
./src/examples/python/ctf-writeups/mma-2015-howtouse/solve.py: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/securityfest-2016-fairlight/fairlight: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/securityfest-2016-fairlight/solve.py: *No copyright* UNKNOWN
./src/libtriton/arch/arm/aarch64/aarch64Cpu.cpp: Apache License 2.0
./src/libtriton/arch/arm/aarch64/aarch64Semantics.cpp: Apache License 2.0
./src/libtriton/arch/arm/aarch64/aarch64Specifications.cpp: Apache License 2.0
./src/libtriton/arch/arm/arm32/arm32Cpu.cpp: Apache License 2.0
./src/libtriton/arch/arm/arm32/arm32Semantics.cpp: Apache License 2.0
./src/libtriton/arch/arm/arm32/arm32Specifications.cpp: Apache License 2.0
./src/libtriton/bindings/python/modules/tritonCallbacks.cpp: Apache License 2.0
./src/libtriton/bindings/python/namespaces/initArchNamespace.cpp: Apache License 2.0
./src/libtriton/bindings/python/namespaces/initAstNodeNamespace.cpp: Apache License 2.0
./src/libtriton/bindings/python/namespaces/initAstRepresentationNamespace.cpp: Apache License 2.0
./src/libtriton/bindings/python/namespaces/initCallbackNamespace.cpp: Apache License 2.0
./src/libtriton/bindings/python/namespaces/initConditionsNamespace.cpp: Apache License 2.0
./src/libtriton/bindings/python/namespaces/initCpuSizeNamespace.cpp: Apache License 2.0
./src/libtriton/bindings/python/namespaces/initExtendNamespace.cpp: Apache License 2.0
./src/libtriton/bindings/python/namespaces/initModeNamespace.cpp: Apache License 2.0
./src/libtriton/bindings/python/namespaces/initOpcodesNamespace.cpp: Apache License 2.0
./src/libtriton/bindings/python/namespaces/initOperandNamespace.cpp: Apache License 2.0
./src/libtriton/bindings/python/namespaces/initPrefixesNamespace.cpp: Apache License 2.0
./src/libtriton/bindings/python/namespaces/initRegNamespace.cpp: Apache License 2.0
./src/libtriton/bindings/python/namespaces/initShiftsNamespace.cpp: Apache License 2.0
./src/libtriton/bindings/python/namespaces/initSolverNamespace.cpp: Apache License 2.0
./src/libtriton/bindings/python/namespaces/initSolverStateNamespace.cpp: Apache License 2.0
./src/libtriton/bindings/python/namespaces/initSymbolicNamespace.cpp: Apache License 2.0
./src/libtriton/bindings/python/namespaces/initVersionNamespace.cpp: Apache License 2.0
./src/libtriton/bindings/python/objects/pyAstContext.cpp: Apache License 2.0
./src/libtriton/bindings/python/objects/pyAstNode.cpp: Apache License 2.0
./src/libtriton/bindings/python/objects/pyBitsVector.cpp: Apache License 2.0
./src/libtriton/bindings/python/objects/pyImmediate.cpp: Apache License 2.0
./src/libtriton/bindings/python/objects/pyInstruction.cpp: Apache License 2.0
./src/libtriton/bindings/python/objects/pyMemoryAccess.cpp: Apache License 2.0
./src/libtriton/bindings/python/objects/pyPathConstraint.cpp: Apache License 2.0
./src/libtriton/bindings/python/objects/pyRegister.cpp: Apache License 2.0
./src/libtriton/bindings/python/objects/pySolverModel.cpp: Apache License 2.0
./src/libtriton/bindings/python/objects/pySymbolicExpression.cpp: Apache License 2.0
./src/libtriton/bindings/python/objects/pySymbolicVariable.cpp: Apache License 2.0
./src/libtriton/bindings/python/objects/pyTritonContext.cpp: Apache License 2.0
./src/libtriton/engines/solver/bitwuzla/bitwuzlaSolver.cpp: Apache License 2.0
./src/libtriton/engines/solver/z3/z3Solver.cpp: Apache License 2.0
./src/testers/arm32/crypto_test/bin/crypto_test-nothumb-O0.bin: UNKNOWN
./src/testers/arm32/crypto_test/bin/crypto_test-nothumb-O1.bin: UNKNOWN
./src/testers/arm32/crypto_test/bin/crypto_test-nothumb-O2.bin: UNKNOWN
./src/testers/arm32/crypto_test/bin/crypto_test-nothumb-O3.bin: UNKNOWN
./src/testers/arm32/crypto_test/bin/crypto_test-nothumb-Os.bin: UNKNOWN
./src/testers/arm32/crypto_test/bin/crypto_test-nothumb-Oz.bin: UNKNOWN
./src/testers/arm32/crypto_test/bin/crypto_test-thumb-O0.bin: UNKNOWN
./src/testers/arm32/crypto_test/bin/crypto_test-thumb-O1.bin: UNKNOWN
./src/testers/arm32/crypto_test/bin/crypto_test-thumb-O2.bin: UNKNOWN
./src/testers/arm32/crypto_test/bin/crypto_test-thumb-O3.bin: UNKNOWN
./src/testers/arm32/crypto_test/bin/crypto_test-thumb-Os.bin: UNKNOWN
./src/testers/arm32/crypto_test/bin/crypto_test-thumb-Oz.bin: UNKNOWN
./src/testers/unittests/misc/qemu/ir-test-suite-qemu-light.bin: UNKNOWN
./src/testers/unittests/misc/qemu/ir-test-suite-qemu.bin: UNKNOWN
./src/testers/unittests/misc/qemu/test-i386-muldiv.h: *No copyright* UNKNOWN
./src/testers/unittests/misc/qemu/test-i386-shift.h: *No copyright* UNKNOWN
./src/testers/unittests/misc/qemu/test-i386.c: GNU General Public License v2.0 or later
./src/testers/unittests/misc/qemu/test-i386.h: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/custom-crackmes/aarch64-hash/crackme_hash: UNKNOWN
./src/examples/python/ctf-writeups/custom-crackmes/aarch64-hash/crackme_hash.c: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/custom-crackmes/aarch64-hash/solve.py: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/custom-crackmes/arm32-hash/crackme_hash-arm: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/custom-crackmes/arm32-hash/crackme_hash-thumb: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/custom-crackmes/arm32-hash/crackme_hash.c: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/custom-crackmes/arm32-hash/solve-arm.py: *No copyright* UNKNOWN
./src/examples/python/ctf-writeups/custom-crackmes/arm32-hash/solve-thumb.py: *No copyright* UNKNOWN
Many files are identified as Apache License 2.0. The rest are either
UNKNOWN or are testing files. There is a file named LICENSE.txt, which
contains the text of the Apache License, version 2.0, further confirmation
that licensecheck correctly identified the license. If source code files are
identified as UNKNOWN, it is a good idea to look at them to see if a license
is mentioned. In the case of triton, all such files appear to be
infrastructure, testing, or example files, so we conclude that the license is
indeed Apache License 2.0.
Now we have to translate that into the name used by Fedora packages in the
License: field. The Fedora project uses
SPDX License names. For the triton package,
Apache-2.0 is the correct value.
The URL field
Next, put the project URL into the URL: field of the spec file. This should
be some sort of home page for the project. In the triton case, the value of
the field is https://triton.quarkslab.com/.
If the project is on github, the project URL may be the main repository page
for the project, which will be a URL of the form
https://github.com/owner/project. In that case, check whether either of the
pages https://owner.github.io/project or https://owner.github.io/ exist
and contain useful content. Use one of them as the URL if so.
The Source field
The Source fields in the spec file identify those files that must be present in the build root in order to build the software. See the Source URL guidelines for help determining the appropriate URL for this field.
The name of the package should be replaced with %{name} in this URL, and the
version number should be replaced with %{version}. The intent is to avoid
changing the URL field every time a new version is released.
For the triton package, we will use this as the Source URL:
https://github.com/JonathanSalwan/Triton/archive/v%{version}/%{name}-%{version}.tar.gz
BuildRequires
Each BuildRequires tag in a spec file identifies some package that must be
installed prior to building the target package. Let’s examine the triton
package.
At the top level of the source directory, we see a file named
CMakeLists.txt. That tells us that cmake is used to build this project, so
we add BuildRequires: cmake to the spec file.
Looking into the src subdirectory, we see that this project contains files
with a .cpp suffix; i.e., they are C++ source files. We need a C++ compiler
to build, so add BuildRequires: gcc-c++ to the spec file.
Now let’s look through CMakeLists.txt to see what else we might need. We
see that this package contains python bindings, so add
BuildRequires: python3-devel to the spec file.
Further down in CMakeLists.txt, we see find_package(Z3 REQUIRED). Fedora
has a z3 package, so add BuildRequires: z3-devel to the spec file, right?
Almost. The use of find_package means that cmake is going to use pkgconfig
to find what it needs. According to the
packaging guidelines,
if the z3-devel package provides a capability of the form pkgconfig(x), then
our BuildRequires should refer to that capability instead. Let’s check:
$ dnf repoquery --provides z3-devel
cmake(Z3) = 4.8.12.0
cmake(z3) = 4.8.12.0
pkgconfig(z3) = 4.8.12.0
z3-devel = 4.8.12-1.fc34
z3-devel(x86-32) = 4.8.12-1.fc34
z3-devel(x86-64) = 4.8.12-1.fc34
Indeed it does, so we should add BuildRequires: pkgconfig(z3) to the spec
file instead. In this case, it would also be acceptable to add
BuildRequires: cmake(z3).
Further down, we see this: find_package(CAPSTONE REQUIRED). Since the
capstone package has a pkgconfig capability, add
BuildRequires: pkgconfig(capstone) to the spec file.
Finally, we see find_package(Boost 1.55.0 REQUIRED). Checking with
repoquery shows that this package has no pkgconfig or cmake capabilities, so
we add BuildRequires: boost-devel to the spec file. The spec file should
now look like this:
Name: triton
Version: 0.9
Release: %autorelease
Summary: Dynamic binary analysis framework
License: Apache-2.0
URL: https://triton.quarkslab.com/
Source: https://github.com/JonathanSalwan/Triton/archive/v%{version}/%{name}-%{version}.tar.gz
BuildRequires: boost-devel
BuildRequires: cmake
BuildRequires: cmake(z3)
BuildRequires: gcc-c++
BuildRequires: pkgconfig(capstone)
BuildRequires: python3-devel
%description
%prep
%autosetup
%build
%configure
%make_build
%install
%make_install
%files
%license add-license-file-here
%doc add-docs-here
%changelog
%autochangelog
Description
The %description section is typically filled in with text from a README file. Failing that, do your best to write a paragraph describing what this package does. You want a person who has never heard of the software before to be able to get some idea of the purpose of the package from reading the description. As noted in the packaging guidelines, keep lines to 80 characters or less. Longer lines can be displayed in strange ways in the various graphical software managers. I usually limit description lines to 78 characters to be on the safe side.
Prep
The %prep section is where we do anything that must be done prior to
building the software. Typically, the first step is to unpack a tarball or
zip file. The %autosetup macro is often sufficient. It knows how to unpack
a variety of files and compression types. This macro assumes that, after
unpacking, it will find the source files in a directory named
%{name}-%{version}. If that is not the case, we need to tell it so. In the
triton case, the name of the directory is Triton-0.8.1. That starts with an
upper case T, where the package name starts with a lower case t, so they
do not match. Change the %autosetup macro to read:
%autosetup -n Triton-%{version}
Build
The %build section contains instructions on how to build the softare. The
%configure and %make_build macros placed there by rpmdev-newspec are
intended for use with software that supplies an autoconf-generated configure
script. In the case of triton, we want to build with cmake instead. Replace
%configure with %cmake ., which invokes cmake with Fedora-specific flags.
Replace %make_build with %cmake_build.
Install
The %install section installs the software into a directory identified by
the macro %buildroot. RPM uses the files placed there to build the final
binary package.
Now we just have %make_install in the %install section. That macro runs
make install with some flags that are commonly used to get the installed
files to go into %buildroot. Like %configure and %make_build, it is
intended for projects that use autoconf. For a cmake project like triton, we
need to replace it with %cmake_install.
Files
The %files section lists all of the files that go into the binary RPM. We
don’t have to guess; we’ll do a test build and install to help us figure this
out. However, there are two things we can do right now. Since we see a file
named LICENSE.txt in the source directory, change the %license line to
refer to it. Also, there is a README.md file, so change the %doc line to
refer to it. The spec file should now look something like this:
Name: triton
Version: 0.9
Release: %autorelease
Summary: Dynamic binary analysis framework
License: Apache-2.0
URL: https://triton.quarkslab.com/
Source: https://github.com/JonathanSalwan/Triton/archive/v%{version}/%{name}-%{version}.tar.gz
BuildRequires: boost-devel
BuildRequires: cmake
BuildRequires: cmake(z3)
BuildRequires: gcc-c++
BuildRequires: pkgconfig(capstone)
BuildRequires: python3-devel
%description
Triton is a dynamic binary analysis (DBA) framework. It provides internal
components like a Dynamic Symbolic Execution (DSE) engine, a dynamic taint
engine, AST representations of the x86, x86-64, ARM32 and AArch64 Instruction
Set Architectures (ISA), SMT simplification passes, an SMT solver interface,
and Python bindings. Based on these components, one can build program
analysis tools, automate reverse engineering and perform software
verification.
%prep
%autosetup -n Triton-%{version}
%build
%cmake .
%cmake_build
%install
%cmake_install
%files
%license LICENSE.txt
%doc README.md
%changelog
%autochangelog
First test build
Make sure that your spec file is in ~/rpmbuild/SPECS, and that
triton-0.9.tar.gz is in ~/rpmbuild/SOURCES. While in the
~/rpmbuild/SPECS directory, run rpmbuild -bs triton.spec. That command
generates a source rpm in ~/rpmbuild/SRPMS.
Next, we will attempt to build the package with mock. Refer to building with mock for details. Briefly, run this command:
mock -r fedora-rawhide-x86_64 --rebuild ~/rpmbuild/SRPMS/triton-0.9-1.fc42.src.rpm
You may have to replace fc42 in that filename. Make it match the name of the
file in your ~/rpmbuild/SRPMS directory.
The test build fails, complaining that it cannot find the z3 library. What’s
up with that? We included BuildRequires: cmake(z3) in our spec file! A
close look at the build log reveals the problem:
Z3_INCLUDE_DIR=Z3_INCLUDE_DIR-NOTFOUND
Z3_INCLUDE_DIRS=
This project looks for the z3 headers in a different place than where Fedora
installs them. If we look at the z3-devel package, we see that the headers
are installed in /usr/include/z3. Change the %cmake line in the spec file
to read:
%cmake -DZ3_INCLUDE_DIRS=%{_includedir}/z3 -DZ3_LIBRARIES=%{_libdir}/libz3.so .
Regenerate the source rpm and run the mock build again. It fails with compiler complaints about capstone problems. Now what?
Apply upstream patches
Let’s look at the sources on github to see if we are facing a known issue. If
we got to the file with the first error,
src/libtriton/arch/x86/x86Specifications.cpp, we see a
recent commit
with the message Coding style. That doesn’t sound very promising. If we
click through to look at it, however, we see that it is touching the very lines
we just had errors for. Maybe this will fix our issue.
We now have a choice to make. If we think that applying this one commit is
all we need to do, we should make a patch out of it, name it Patch in the
spec file, and move on. On the other hand, if the patch does not apply
cleanly to the version we want to build, or if we see lots of bug-fixing
commits after the most recent release, we may be better building a git
snapshot.
After looking through the git history, I conclude that for triton we would be
better off with a git snapshot until the next version is released.
Fortunately, Fedora provides a set of %forge macros that make this easy.
Change the spec file to look like this:
# There have been lots of bug fixes since the last release. Build from git.
%global commit 34e1de35869eb3d7e571f21e9c2561dff126e040
%global date 20250824
%global forgeurl https://github.com/JonathanSalwan/Triton
Name: triton
Version: 0.9
%forgemeta
Release: %autorelease
Summary: Dynamic binary analysis framework
License: Apache-2.0
URL: https://triton.quarkslab.com/
Source: %{forgesource}
BuildRequires: boost-devel
BuildRequires: cmake
BuildRequires: cmake(z3)
BuildRequires: gcc-c++
BuildRequires: pkgconfig(capstone)
BuildRequires: python3-devel
%description
Triton is a dynamic binary analysis (DBA) framework. It provides internal
components like a Dynamic Symbolic Execution (DSE) engine, a dynamic taint
engine, AST representations of the x86, x86-64, ARM32 and AArch64 Instruction
Set Architectures (ISA), SMT simplification passes, an SMT solver interface,
and Python bindings. Based on these components, one can build program
analysis tools, automate reverse engineering and perform software
verification.
%prep
%forgeautosetup
%build
%cmake -DZ3_INCLUDE_DIRS=%{_includedir}/z3 -DZ3_LIBRARIES=%{_libdir}/libz3.so .
%cmake_build
%install
%cmake_install
%files
%license LICENSE.txt
%doc README.md
%changelog
%autochangelog
The new source tarball can be downloaded by running spectool -g triton.spec.
Move it to the ~/rpmbuild/SOURCES directory, then generate a new source RPM
and build it with mock.
Devel subpackage
The second test build ended with an error indicating that a bunch of files
were installed into %buildroot without being mentioned in %files. Great!
We can now figure out what the content of %files should be. But wait.
Files were installed into /usr/include. That means that we need a
devel package.
Add this after %description:
%package devel
Summary: Header files and library links for libtriton
Requires: %{name}%{?_isa} = %{version}-%{release}
%description devel
Header files and library links for building applications that use
libtriton.
Down at the bottom, change the %files section to include files for both the
main package and the devel subpackage like so:
%files
%license LICENSE.txt
%doc README.md
%{_libdir}/libtriton.so
%files devel
%{_includedir}/%{name}/
%{_libdir}/cmake/%{name}/
There, now we’re done!
Library soname
Not so fast. According to the packaging guidelines section on devel packages,
we should have a versioned library file in the main package, and an
unversioned symbolic link in the devel subpackage. But all we have is an
unversioned library file. What’s up with that? The problem is that the
triton package does not set an
soname
on the library. Whether we should “fix” this or not is debatable; see the
relevant section of the packaging guidelines.
Many packagers will set an soname of the form “0.0.0” if upstream does not set
one. That way, if the upstream ever gets around to setting an soname, it will
presumably be higher. We can do that by patching
src/libtriton/CMakeLists.txt. For single-line edits like this, I often use
sed to make the change. Add this to the end of %prep:
# Set an soname
sed '/set_target_properties/s/PROPERTIES/& SOVERSION 0 VERSION %{version}/' \
-i src/libtriton/CMakeLists.txt
Then change the %files sections to read:
%files
%license LICENSE.txt
%doc README.md
%{_libdir}/libtriton.so.0*
%files devel
%{_includedir}/%{name}/
%{_libdir}/libtriton.so
%{_libdir}/cmake/%{name}/
Regenerate the source RPM and re-run the mock build. It fails, complaining
about a cmake file that got installed into /usr/lib instead of /usr/lib64!
This is a common occurrence with cmake projects when upstream develops on a
Debian-based system. If we look in src/libtriton/CMakeLists.txt, we see
this:
# Install tritonTargets.cmake
install(
EXPORT tritonTargets
DESTINATION lib/cmake/triton
NAMESPACE triton::
)
There’s the problem. We need that “lib” to be “lib64” on 64-bit systems.
Change the sed invocation in %prep to this:
# Set an soname
# Fix the install location on 64-bit systems
sed -e '/set_target_properties/s/PROPERTIES/& SOVERSION 0 VERSION %{version}/' \
-e 's/\(DESTINATION \)lib/\1${CMAKE_INSTALL_LIBDIR}/' \
-i src/libtriton/CMakeLists.txt
Regenerate the source RPM and re-run the mock build.
Python interface
The build fails again. Now the complaint is:
Checking for unpackaged file(s): /usr/lib/rpm/check-files /builddir/build/BUILD/triton-0.9-build/BUILDROOT
error: Installed (but unpackaged) file(s) found:
/usr/lib/python3.14/site-packages/triton.so
There are two problems here. First, we need to account for the python
interface in files. Second, that file isn’t in the right place. It is in
/usr/lib, not /usr/lib64 where architecture-specific files are supposed to
go. Let’s fix the second problem first.
Looking in src/libtriton/CMakeLists.txt again, we see this:
execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "from sys import version_info; print(f'lib/python{version_info[0]}.{version_info[1]}/site-packages')" OUTPUT_VARIABLE PYTHON_SITE_PACKAGES OUTPUT_STRIP_TRAILING_WHITESPACE)
There’s the problem: another explicit lib directory. Let’s work around it by
modifying src/libtriton/CMakeLists.txt again. Change the %prep section to
look like this:
%prep
%forgesetup
# Set an soname
# Fix the install location on 64-bit systems
sed -e '/set_target_properties/s/PROPERTIES/& SOVERSION 0 VERSION %{version}/' \
-e 's,lib/,%{_lib}/,' \
-i src/libtriton/CMakeLists.txt
Now for the issue that we don’t account for the python interface. We need a
new subpackage for the python parts. Add this just above %prep:
%package -n python3-%{name}
Summary: Python 3 interface to triton
Requires: %{name}%{?_isa} = %{version}-%{release}
%description -n python3-%{name}
Python 3 interface to triton.
and add a new %files section:
%files -n python3-%{name}
%{python3_sitearch}/%{name}.so
Now it builds successfully. We’re done!
Build flags
Just kidding! Let’s look at that build log again. In particular, look at the build flags used for C++ source files. After the Fedora flags, you’ll see some flags inserted by triton upstream:
-DNDEBUG -O3 -std=gnu++17 -fPIC
We don’t necessarily want all of upstream’s flags. In particular, Fedora
chooses its own optimization flag (currently -O2), so we do not want the
-O3. The other flags should be fine.
Let’s change our sed invocation in %prep to read as follows:
# Do not override Fedora flags.
# Set an soname on the library.
# Fix the install location on 64-bit systems
sed -e 's/-O3/-O2/g' \
-e '/set_target_properties/s/PROPERTIES/& SOVERSION 0 VERSION %{version}/' \
-e 's,lib/,%{_lib}/,' \
-i src/libtriton/CMakeLists.txt
Build again. The build is successful, but when we look in the results directory, we should notice something odd: there is a debuginfo package for the main triton package, but there isn’t one for the python3-triton package. That package contains a shared object; shouldn’t debuginfo be generated for it? Let’s see what is in that package:
$ rpm -qlpv /var/lib/mock/fedora-rawhide-x86_64/result/python3-triton-0.9-1.20250824git34e1de3.fc44.x86_64.rpm
-rw-r--r-- 1 root root 72868800 Sep 23 18:00 /usr/lib64/python3.14/site-packages/triton.so
The debuginfo generator looks for ELF files with the executable bit set. This
file does not have executable bits set. Add this to the %install section:
# Add missing executable bits
chmod 0755 %{buildroot}%{python3_sitearch}/%{name}.so
Run the build again and a python3-triton-debuginfo package is generated.
Requires and Provides
Now that we have reasonable looking packages, lets make sure that the final Requires and Provides look right.
$ rpm -q --requires -p /var/lib/mock/fedora-rawhide-x86_64/result/triton-0.9-1.20250824git34e1de3.fc44.x86_64.rpm
libc.so.6()(64bit)
libc.so.6(GLIBC_2.14)(64bit)
libc.so.6(GLIBC_2.2.5)(64bit)
libc.so.6(GLIBC_2.32)(64bit)
libc.so.6(GLIBC_2.4)(64bit)
libc.so.6(GLIBC_ABI_DT_RELR)(64bit)
libcapstone.so.5()(64bit)
libgcc_s.so.1()(64bit)
libgcc_s.so.1(GCC_3.0)(64bit)
libgcc_s.so.1(GCC_3.3.1)(64bit)
libpython3.14.so.1.0()(64bit)
libstdc++.so.6()(64bit)
libstdc++.so.6(CXXABI_1.3)(64bit)
libstdc++.so.6(CXXABI_1.3.15)(64bit)
libstdc++.so.6(CXXABI_1.3.5)(64bit)
libstdc++.so.6(CXXABI_1.3.9)(64bit)
libstdc++.so.6(GLIBCXX_3.4)(64bit)
libstdc++.so.6(GLIBCXX_3.4.11)(64bit)
libstdc++.so.6(GLIBCXX_3.4.14)(64bit)
libstdc++.so.6(GLIBCXX_3.4.15)(64bit)
libstdc++.so.6(GLIBCXX_3.4.18)(64bit)
libstdc++.so.6(GLIBCXX_3.4.19)(64bit)
libstdc++.so.6(GLIBCXX_3.4.20)(64bit)
libstdc++.so.6(GLIBCXX_3.4.21)(64bit)
libstdc++.so.6(GLIBCXX_3.4.26)(64bit)
libstdc++.so.6(GLIBCXX_3.4.29)(64bit)
libstdc++.so.6(GLIBCXX_3.4.30)(64bit)
libstdc++.so.6(GLIBCXX_3.4.32)(64bit)
libstdc++.so.6(GLIBCXX_3.4.9)(64bit)
libz3.so.4.15()(64bit)
rpmlib(CompressedFileNames) <= 3.0.4-1
rpmlib(FileDigests) <= 4.6.0-1
rpmlib(PayloadFilesHavePrefix) <= 4.0-1
rpmlib(PayloadIsZstd) <= 5.4.18-1
rtld(GNU_HASH)
I see dependencies on the capstone and z3 libraries, which is good. The main
library also depends on the python library, though. That seems odd. A little
checking shows the reason: the files
/usr/lib64/python3.14/site-packages/triton.so and
/usr/lib64/libtriton.so.0.9 are the same.
$ rpm -q --provides -p /var/lib/mock/fedora-rawhide-x86_64/result/triton-0.9-1.20250824git34e1de3.fc44.x86_64.rpm
libtriton.so.0()(64bit)
triton = 0.9-1.20250824git34e1de3.fc44
triton(x86-64) = 0.9-1.20250824git34e1de3.fc44
That seems reasonable. How about the python3 subpackage?
$ rpm -q --provides -p /var/lib/mock/fedora-rawhide-x86_64/result/python3-triton-0.9-1.20250824git34e1de3.fc44.x86_64.rpm
libtriton.so.0()(64bit)
python-triton = 0.9-1.20250824git34e1de3.fc44
python3-triton = 0.9-1.20250824git34e1de3.fc44
python3-triton(x86-64) = 0.9-1.20250824git34e1de3.fc44
python3.14-triton = 0.9-1.20250824git34e1de3.fc44
Notice that both packages Provide libtriton.so.0()(64bit). That’s because
they are the same file, as we noted above, so both have an soname.
$ rpm -q --requires -p /var/lib/mock/fedora-rawhide-x86_64/result/python3-triton-0.9-1.20250824git34e1de3.fc44.x86_64.rpm
libc.so.6()(64bit)
libc.so.6(GLIBC_2.14)(64bit)
libc.so.6(GLIBC_2.2.5)(64bit)
libc.so.6(GLIBC_2.32)(64bit)
libc.so.6(GLIBC_2.4)(64bit)
libc.so.6(GLIBC_ABI_DT_RELR)(64bit)
libcapstone.so.5()(64bit)
libgcc_s.so.1()(64bit)
libgcc_s.so.1(GCC_3.0)(64bit)
libgcc_s.so.1(GCC_3.3.1)(64bit)
libpython3.14.so.1.0()(64bit)
libstdc++.so.6()(64bit)
libstdc++.so.6(CXXABI_1.3)(64bit)
libstdc++.so.6(CXXABI_1.3.15)(64bit)
libstdc++.so.6(CXXABI_1.3.5)(64bit)
libstdc++.so.6(CXXABI_1.3.9)(64bit)
libstdc++.so.6(GLIBCXX_3.4)(64bit)
libstdc++.so.6(GLIBCXX_3.4.11)(64bit)
libstdc++.so.6(GLIBCXX_3.4.14)(64bit)
libstdc++.so.6(GLIBCXX_3.4.15)(64bit)
libstdc++.so.6(GLIBCXX_3.4.18)(64bit)
libstdc++.so.6(GLIBCXX_3.4.19)(64bit)
libstdc++.so.6(GLIBCXX_3.4.20)(64bit)
libstdc++.so.6(GLIBCXX_3.4.21)(64bit)
libstdc++.so.6(GLIBCXX_3.4.26)(64bit)
libstdc++.so.6(GLIBCXX_3.4.29)(64bit)
libstdc++.so.6(GLIBCXX_3.4.30)(64bit)
libstdc++.so.6(GLIBCXX_3.4.32)(64bit)
libstdc++.so.6(GLIBCXX_3.4.9)(64bit)
libz3.so.4.15()(64bit)
python(abi) = 3.14
rpmlib(CompressedFileNames) <= 3.0.4-1
rpmlib(FileDigests) <= 4.6.0-1
rpmlib(PayloadFilesHavePrefix) <= 4.0-1
rpmlib(PayloadIsZstd) <= 5.4.18-1
rtld(GNU_HASH)
triton(x86-64) = 0.9-1.20250824git34e1de3.fc44
I don’t see the z3 python interface listed here; i.e., python3-z3. Is it needed? Digging through the source code shows that it is, indeed.
So we have two problems: we’ve got duplicate libraries, and the python
interface lacks a Requires. The second problem is easy to fix. Just add this
under %package -n python3-%{name}:
Requires: python3-z3
The first problem requires some thought. What do we want the final installed
objects to look like? Ideally, the library in /usr/lib64 would have an
soname, and no dependency on Python. It would be a library that C++
applications can link to. The library in
/usr/lib64/python3.10/site-packages would not have an soname, would contain
only the Python interface, and would be linked against the library in
/usr/lib64. However, upstream has not given us that option. That means we
have to choose. Do we want to hack upstream’s build scripts to get what we
really want, or should we settle for what we can get from upstream’s build
scripts?
There is actually a third option: let upstream build the way it wants to build, then relink the libraries the way we want. Take a look at this version of the spec file:
# There have been lots of bug fixes since the last release. Build from git.
%global commit 34e1de35869eb3d7e571f21e9c2561dff126e040
%global date 20250824
%global forgeurl https://github.com/JonathanSalwan/Triton
Name: triton
Version: 0.9
%forgemeta
Release: %autorelease
Summary: Dynamic binary analysis framework
License: Apache-2.0
URL: https://triton.quarkslab.com/
Source: %{forgesource}
BuildRequires: boost-devel
BuildRequires: cmake
BuildRequires: cmake(z3)
BuildRequires: gcc-c++
BuildRequires: pkgconfig(capstone)
BuildRequires: python3-devel
%description
Triton is a dynamic binary analysis (DBA) framework. It provides internal
components like a Dynamic Symbolic Execution (DSE) engine, a dynamic taint
engine, AST representations of the x86, x86-64, ARM32 and AArch64 Instruction
Set Architectures (ISA), SMT simplification passes, an SMT solver interface,
and Python bindings. Based on these components, one can build program
analysis tools, automate reverse engineering and perform software
verification.
%package devel
Summary: Header files and library links for libtriton
Requires: %{name}%{?_isa} = %{version}-%{release}
%description devel
Header files and library links for building applications that use
libtriton.
%package -n python3-%{name}
Summary: Python 3 interface to triton
Requires: %{name}%{?_isa} = %{version}-%{release}
Requires: python3-z3
%description -n python3-%{name}
Python 3 interface to triton.
%prep
%forgesetup
# Do not override Fedora flags.
# Fix the install location on 64-bit systems
sed -e 's/-O3/-O2/g' \
-e 's,lib/,%{_lib}/,' \
-i src/libtriton/CMakeLists.txt
%build
%cmake -DZ3_INCLUDE_DIRS=%{_includedir}/z3 -DZ3_LIBRARIES=%{_libdir}/libz3.so .
%cmake_build
cd %{_vpath_builddir}/src/libtriton
# Relink the main library without the python bindings.
g++ %{build_cxxflags} %{build_ldflags} -shared -Wl,-soname,libtriton.so.0 \
$(find CMakeFiles -path '*/bindings/python' -prune -o -name \*.o -print) \
-o libtriton.so -lz3 -lcapstone
# Relink the python library and link it to the main library
g++ %{build_cxxflags} %{build_ldflags} -shared -o triton.so \
$(find CMakeFiles/triton.dir/bindings/python -name \*.o) \
-L. -ltriton
cd -
%install
%cmake_install
# Add ldconfig links
cd %{buildroot}%{_libdir}
mv libtriton.so libtriton.so.%{version}
ln -s libtriton.so.%{version} libtriton.so.0
ln -s libtriton.so.0 libtriton.so
cd -
# Add missing executable bits
chmod 0755 %{buildroot}%{python3_sitearch}/%{name}.so
%files
%license LICENSE.txt
%doc README.md
%{_libdir}/libtriton.so.0*
%files devel
%{_includedir}/%{name}/
%{_libdir}/libtriton.so
%{_libdir}/cmake/%{name}/
%files -n python3-%{name}
%{python3_sitearch}/%{name}.so
%changelog
%autochangelog
Note that we no longer convince cmake to add an soname to the library in
%prep. We relink the shared object anyway, so we just add an soname at that
time. Notice also that we do not link the python interface to libpython
like triton upstream does. See this bug
for a discussion of the issues involved.
Rebuild with that spec file and notice that the Requires and Provides now show that we are not shipping the same library file in two packages.
File cleanup
Now that we have the libraries linked the way we want, and the Requires and Provides look sane, let’s examine the contents of each package to see if they look okay.
$ rpm -qlp /var/lib/mock/fedora-rawhide-x86_64/result/triton-0.9-1.20250824git34e1de3.fc44.x86_64.rpm
/usr/lib/.build-id
/usr/lib/.build-id/45
/usr/lib/.build-id/45/bd3348773011b611d326ff13dbe8014558ad37
/usr/lib64/libtriton.so.0
/usr/lib64/libtriton.so.0.9
/usr/share/doc/triton
/usr/share/doc/triton/README.md
/usr/share/licenses/triton
/usr/share/licenses/triton/LICENSE.txt
That looks okay. How about the python interface?
$ rpm -qlp /var/lib/mock/fedora-rawhide-x86_64/result/python3-triton-0.9-1.20250824git34e1de3.fc44.x86_64.rpm
/usr/lib/.build-id
/usr/lib/.build-id/6b
/usr/lib/.build-id/6b/6530c61667e35314a01535eb68f19459024d83
/usr/lib64/python3.14/site-packages/triton.so
That looks okay, too. How about the devel subpackage?
$ rpm -qlp /var/lib/mock/fedora-rawhide-x86_64/result/triton-devel-0.9-1.20250824git34e1de3.fc44.x86_64.rpm
/usr/include/triton
/usr/include/triton/aarch64.spec
/usr/include/triton/aarch64Cpu.hpp
/usr/include/triton/aarch64Semantics.hpp
/usr/include/triton/aarch64Specifications.hpp
/usr/include/triton/archEnums.hpp
/usr/include/triton/architecture.hpp
/usr/include/triton/arm32.spec
/usr/include/triton/arm32Cpu.hpp
/usr/include/triton/arm32Semantics.hpp
/usr/include/triton/arm32Specifications.hpp
/usr/include/triton/armOperandProperties.hpp
/usr/include/triton/ast.hpp
/usr/include/triton/astContext.hpp
/usr/include/triton/astEnums.hpp
/usr/include/triton/astPcodeRepresentation.hpp
/usr/include/triton/astPythonRepresentation.hpp
/usr/include/triton/astRepresentation.hpp
/usr/include/triton/astRepresentationInterface.hpp
/usr/include/triton/astSmtRepresentation.hpp
/usr/include/triton/basicBlock.hpp
/usr/include/triton/bitsVector.hpp
/usr/include/triton/bitwuzlaSolver.hpp
/usr/include/triton/callbacks.hpp
/usr/include/triton/callbacksEnums.hpp
/usr/include/triton/comparableFunctor.hpp
/usr/include/triton/config.hpp
/usr/include/triton/context.hpp
/usr/include/triton/coreUtils.hpp
/usr/include/triton/cpuInterface.hpp
/usr/include/triton/cpuSize.hpp
/usr/include/triton/dllexport.hpp
/usr/include/triton/exceptions.hpp
/usr/include/triton/externalLibs.hpp
/usr/include/triton/immediate.hpp
/usr/include/triton/instruction.hpp
/usr/include/triton/irBuilder.hpp
/usr/include/triton/liftingEngine.hpp
/usr/include/triton/liftingToDot.hpp
/usr/include/triton/liftingToLLVM.hpp
/usr/include/triton/liftingToPython.hpp
/usr/include/triton/liftingToSMT.hpp
/usr/include/triton/llvmToTriton.hpp
/usr/include/triton/memoryAccess.hpp
/usr/include/triton/modes.hpp
/usr/include/triton/modesEnums.hpp
/usr/include/triton/operandWrapper.hpp
/usr/include/triton/oracleEntry.hpp
/usr/include/triton/pathConstraint.hpp
/usr/include/triton/pathManager.hpp
/usr/include/triton/register.hpp
/usr/include/triton/riscv32.spec
/usr/include/triton/riscv32Cpu.hpp
/usr/include/triton/riscv64.spec
/usr/include/triton/riscv64Cpu.hpp
/usr/include/triton/riscvSemantics.hpp
/usr/include/triton/riscvSpecifications.hpp
/usr/include/triton/semanticsInterface.hpp
/usr/include/triton/shortcutRegister.hpp
/usr/include/triton/softfloat.hpp
/usr/include/triton/solverEngine.hpp
/usr/include/triton/solverEnums.hpp
/usr/include/triton/solverInterface.hpp
/usr/include/triton/solverModel.hpp
/usr/include/triton/stubs.hpp
/usr/include/triton/symbolicEngine.hpp
/usr/include/triton/symbolicEnums.hpp
/usr/include/triton/symbolicExpression.hpp
/usr/include/triton/symbolicSimplification.hpp
/usr/include/triton/symbolicVariable.hpp
/usr/include/triton/synthesisResult.hpp
/usr/include/triton/synthesizer.hpp
/usr/include/triton/taintEngine.hpp
/usr/include/triton/tritonToBitwuzla.hpp
/usr/include/triton/tritonToLLVM.hpp
/usr/include/triton/tritonToZ3.hpp
/usr/include/triton/tritonTypes.hpp
/usr/include/triton/uintwide_t.h
/usr/include/triton/version.hpp
/usr/include/triton/x86.spec
/usr/include/triton/x8664Cpu.hpp
/usr/include/triton/x86Cpu.hpp
/usr/include/triton/x86Semantics.hpp
/usr/include/triton/x86Specifications.hpp
/usr/include/triton/z3Solver.hpp
/usr/include/triton/z3ToTriton.hpp
/usr/lib64/cmake/triton
/usr/lib64/cmake/triton/tritonConfig.cmake
/usr/lib64/cmake/triton/tritonConfigVersion.cmake
/usr/lib64/cmake/triton/tritonTargets.cmake
/usr/lib64/libtriton.so
That looks okay, too. Great!
Build documentation
The source directory has doc and publication subdirectories. We haven’t
done anything with them. Let’s try building the documentation. What do we
need? The doc subdirectory contains a file named Doxyfile.in, so we need
doxygen. Add BuildRequires: doxygen to the other BuildRequires. In the
%build section, add %make_build -C %{_vpath_builddir} doc below the
%cmake_build line. With luck, that will build the documentation for us.
(We’ll check in a moment.) But what are we going to do with the documentation
once it is built?
Let’s add another subpackage, specifically to hold the documentation. Here’s
a little trick: if you add %global _docdir_fmt %{name} to your spec file,
then all of the documentation in subpackages will be installed to the
documentation directory of the parent package, perhaps making it a bit easier
to find. With that trick, our spec file now looks like this:
# There have been lots of bug fixes since the last release. Build from git.
%global commit 34e1de35869eb3d7e571f21e9c2561dff126e040
%global date 20250824
%global forgeurl https://github.com/JonathanSalwan/Triton
# Install documentation in the main doc directory
%global _docdir_fmt %{name}
Name: triton
Version: 0.9
%forgemeta
Release: %autorelease
Summary: Dynamic binary analysis framework
License: Apache-2.0
URL: https://triton.quarkslab.com/
Source: %{forgesource}
BuildRequires: boost-devel
BuildRequires: cmake
BuildRequires: cmake(z3)
BuildRequires: doxygen
BuildRequires: gcc-c++
BuildRequires: pkgconfig(capstone)
BuildRequires: python3-devel
%description
Triton is a dynamic binary analysis (DBA) framework. It provides internal
components like a Dynamic Symbolic Execution (DSE) engine, a dynamic taint
engine, AST representations of the x86, x86-64, ARM32 and AArch64 Instruction
Set Architectures (ISA), SMT simplification passes, an SMT solver interface,
and Python bindings. Based on these components, one can build program
analysis tools, automate reverse engineering and perform software
verification.
%package devel
Summary: Header files and library links for triton
Requires: %{name}%{?_isa} = %{version}-%{release}
%description devel
Header files and library links for building applications that use
libtriton.
%package doc
Summary: API documentation for libtriton
BuildArch: noarch
%description doc
API documentation for libtriton.
%package -n python3-%{name}
Summary: Python 3 interface to triton
Requires: %{name}%{?_isa} = %{version}-%{release}
Requires: python3-z3
%description -n python3-%{name}
Python 3 interface to triton.
%prep
%forgesetup
# Do not override Fedora flags.
# Fix the install location on 64-bit systems
sed -e 's/-O3/-O2/g' \
-e 's,lib/,%{_lib}/,' \
-i src/libtriton/CMakeLists.txt
%build
%cmake -DZ3_INCLUDE_DIRS=%{_includedir}/z3 -DZ3_LIBRARIES=%{_libdir}/libz3.so .
%cmake_build
%make_build -C %{_vpath_builddir} doc
cd %{_vpath_builddir}/src/libtriton
# Rebuild the main library without the python bindings.
g++ %{build_cxxflags} %{build_ldflags} -shared -Wl,-soname,libtriton.so.0 \
$(find CMakeFiles -path '*/bindings/python' -prune -o -name \*.o -print) \
-o libtriton.so -lz3 -lcapstone
# Rebuild the python library linked to the main library
g++ %{build_cxxflags} %{build_ldflags} -shared -o triton.so \
$(find CMakeFiles/triton.dir/bindings/python -name \*.o) \
-L. -ltriton
cd -
%install
%cmake_install
# Add ldconfig links
cd %{buildroot}%{_libdir}
mv libtriton.so libtriton.so.%{version}
ln -s libtriton.so.%{version} libtriton.so.0
ln -s libtriton.so.0 libtriton.so
cd -
# Add missing executable bits
chmod 0755 %{buildroot}%{python3_sitearch}/%{name}.so
%files
%license LICENSE.txt
%doc README.md
%{_libdir}/libtriton.so.0*
%files devel
%{_includedir}/%{name}/
%{_libdir}/libtriton.so
%{_libdir}/cmake/%{name}/
%files doc
%license LICENSE.txt
%doc publications %{_vpath_builddir}/doc/doc/html
%files -n python3-%{name}
%{python3_sitearch}/%{name}.so
%changelog
%autochangelog
Notice that we added BuildArch: noarch to the doc subpackage. There are no
architecture-specific files in that subpackage. Hopefully, exactly the same
documentation will be built on all architectures. (That usually, but not
always, works out.) Also notice that the doc subpackage does not have any
Requires. You don’t need to have the main package installed to read the
documentation. Finally, notice that we added the license file to the doc
subpackage. The license file must be installed when any triton package is
installed, and the doc subpackage does not depend on any of the others.
Run tests
We’re getting close now! Upstream has provided us with some tests. We should
run them to make sure we haven’t somehow broken the whole thing. With a cmake
project, the %ctest macro usually does what you want. Let’s add this to our
spec file:
%check
%ctest
Run that again and … wait a minute! All of the tests failed! What gives?
There are three problems. First, all of the tests report:
ModuleNotFoundError: No module named 'triton'
We can fix that by setting PYTHONPATH to point to the python module in the
buildroot. Add this between %check and %ctest:
export PYTHONPATH=%{buildroot}%{python3_sitearch}
Second, that module is linked with a library that only exists in the build root, so we have to tell the dynamic linker how to find it. Add this line just after the previous one:
export LD_LIBRARY_PATH=%{buildroot}%{_libdir}
Third, recall that we determined that the python interface needs python3-z3.
Well, we didn’t list that as a BuildRequires, did we? Add BuildRequires:
python3-z3 and try building again.
The tests still fail, now complaining about missing unicorn and lief modules. The python3-unicorn package is available from Fedora, but the lief package is not. At this point, we have to reluctantly decide that running upstream’s tests isn’t going to work. Instead we will leave a comment behind explaining what to do if lief is packaged for Fedora, and use a simple “can the module be imported?” test instead. That looks like this:
%check
# This does not work due to missing lief package
#
# export PYTHONPATH=%%{buildroot}%%{python3_sitearch}
# export LD_LIBRARY_PATH=%%{buildroot}%%{_libdir}
# %%ctest
#
# For now, just check that we can import the module.
export LD_LIBRARY_PATH=%{buildroot}%{_libdir}
%py3_check_import %{name}
Reconsider the name
At this point, we should think about the name of the package again. We used the project name, triton, but is that the best choice? Since the main package contains only a library, libtriton, maybe libtriton would be a better choice. This could be argued either way, but if we were to decide to go with libtriton, the final spec file would look like this:
# There have been lots of bug fixes since the last release. Build from git.
%global commit 34e1de35869eb3d7e571f21e9c2561dff126e040
%global date 20250824
%global forgeurl https://github.com/JonathanSalwan/Triton
# Install documentation in the main doc directory
%global _docdir_fmt %{name}
Name: libtriton
Version: 0.9
%forgemeta
Release: %autorelease
Summary: Dynamic binary analysis framework
License: Apache-2.0
URL: https://triton.quarkslab.com/
Source: %{forgesource}
BuildRequires: boost-devel
BuildRequires: cmake
BuildRequires: cmake(z3)
BuildRequires: doxygen
BuildRequires: gcc-c++
BuildRequires: pkgconfig(capstone)
BuildRequires: python3-devel
BuildRequires: python3-z3
%description
Triton is a dynamic binary analysis (DBA) framework. It provides internal
components like a Dynamic Symbolic Execution (DSE) engine, a dynamic taint
engine, AST representations of the x86, x86-64, ARM32 and AArch64 Instruction
Set Architectures (ISA), SMT simplification passes, an SMT solver interface,
and Python bindings. Based on these components, one can build program
analysis tools, automate reverse engineering and perform software
verification.
%package devel
Summary: Header files and library links for triton
Requires: %{name}%{?_isa} = %{version}-%{release}
%description devel
Header files and library links for building applications that use
libtriton.
%package doc
Summary: API documentation for libtriton
BuildArch: noarch
%description doc
API documentation for libtriton.
%package -n python3-triton
Summary: Python 3 interface to triton
Requires: %{name}%{?_isa} = %{version}-%{release}
Requires: python3-z3
%description -n python3-triton
Python 3 interface to triton.
%prep
%forgesetup
# Do not override Fedora flags.
# Fix the install location on 64-bit systems
sed -e 's/-O3/-O2/g' \
-e 's,lib/,%{_lib}/,' \
-i src/libtriton/CMakeLists.txt
%build
%cmake -DZ3_INCLUDE_DIRS=%{_includedir}/z3 -DZ3_LIBRARIES=%{_libdir}/libz3.so .
%cmake_build
%make_build -C %{_vpath_builddir} doc
cd %{_vpath_builddir}/src/libtriton
# Rebuild the main library without the python bindings.
g++ %{build_cxxflags} %{build_ldflags} -shared -Wl,-soname,libtriton.so.0 \
$(find CMakeFiles -path '*/bindings/python' -prune -o -name \*.o -print) \
-o libtriton.so -lz3 -lcapstone
# Rebuild the python library linked to the main library
g++ %{build_cxxflags} %{build_ldflags} -shared -o triton.so \
$(find CMakeFiles/triton.dir/bindings/python -name \*.o) \
-L. -ltriton
cd -
%install
%cmake_install
# Add ldconfig links
cd %{buildroot}%{_libdir}
mv %{name}.so %{name}.so.%{version}
ln -s %{name}.so.%{version} %{name}.so.0
ln -s %{name}.so.0 %{name}.so
cd -
# Add missing executable bits
chmod 0755 %{buildroot}%{python3_sitearch}/triton.so
%check
# This does not work due to missing lief package
#
# export PYTHONPATH=%%{buildroot}%%{python3_sitearch}
# export LD_LIBRARY_PATH=%%{buildroot}%%{_libdir}
# %%ctest
#
# For now, just check that we can import the module.
export LD_LIBRARY_PATH=%{buildroot}%{_libdir}
%py3_check_import triton
%files
%license LICENSE.txt
%doc README.md
%{_libdir}/libtriton.so.0*
%files devel
%{_includedir}/triton/
%{_libdir}/libtriton.so
%{_libdir}/cmake/triton/
%files doc
%license LICENSE.txt
%doc publications %{_vpath_builddir}/doc/doc/html
%files -n python3-triton
%{python3_sitearch}/triton.so
%changelog
%autochangelog
Run rpmlint
We finally have a viable spec file! Let’s run an automated tool over it that can detect many common packaging problems.
mock -r fedora-rawhide-x86_64 --install /var/lib/mock/fedora-rawhide-x86_64/result/*.{noarch,x86_64}.rpm rpmlint pam
Afterwards, we enter a mock shell and run rpmlint:
mock -r fedora-rawhide-x86_64 --shell --enable-network
The --enable-network argument allows rpmlint to access the network in order
to do some checks, such as URL validity. In the shell, run this command:
# rpmlint -i libtriton libtriton-devel libtriton-doc python3-triton
================================================ rpmlint session starts ===============================================
rpmlint: 2.7.0
configuration:
/usr/lib/python3.14/site-packages/rpmlint/configdefaults.toml
/etc/xdg/rpmlint/fedora-spdx-licenses.toml
/etc/xdg/rpmlint/fedora.toml
/etc/xdg/rpmlint/scoring.toml
/etc/xdg/rpmlint/users-groups.toml
/etc/xdg/rpmlint/warn-on-functions.toml
checks: 32, packages: 4
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyBool_Type (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so _Py_TrueStruct (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so _Py_NoneStruct (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyType_Type (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyByteArray_Type (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyExc_RuntimeError (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so _Py_NotImplementedStruct (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyExc_TypeError (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyMethod_Type (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so _Py_FalseStruct (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyTuple_SetItem (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyByteArray_Size (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyLong_AsVoidPtr (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyObject_GetAttrString (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyType_IsSubtype (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so _PyLong_New (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyDict_SetItem (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyModule_AddObject (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyTuple_New (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyDict_SetItemString(/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyUnicode_FromFormat(/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyBytes_FromStringAndSize (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyArg_ParseTupleAndKeywords (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyList_Size (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so _PyObject_New (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyErr_Format (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyObject_CallObject (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so _Py_Dealloc (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyByteArray_AsString(/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so Py_BuildValue (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyList_New (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyImport_ImportModule(/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyUnicode_FromString(/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyBytes_Size (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyList_GetItem (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyLong_FromVoidPtr (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyDict_New (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyArg_ParseTuple (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyUnicode_AsUTF8 (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyObject_SetAttrString (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyBytes_AsString (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyType_Ready (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyLong_FromLong (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyObject_IsTrue (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyModule_Create2 (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyObject_GenericGetAttr (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyList_SetItem (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyObject_CallFunctionObjArgs (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyCallable_Check (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so Py_Initialize (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyErr_Print (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyDict_Clear (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.14/site-packages/triton.so PyBool_FromLong (/usr/lib64/python3.14/site-packages/triton.so)
python3-triton.x86_64: W: no-documentation
libtriton-doc.noarch: W: files-duplicate /usr/share/doc/libtriton/html/search/pages_0.js /usr/share/doc/libtriton/html/search/all_0.js
libtriton-doc.noarch: W: files-duplicate /usr/share/doc/libtriton/html/search/typedefs_0.js /usr/share/doc/libtriton/html/search/all_1.js
libtriton-doc.noarch: W: files-duplicate /usr/share/doc/libtriton/html/search/variables_f.js /usr/share/doc/libtriton/html/search/all_11.js
libtriton-doc.noarch: W: files-duplicate /usr/share/doc/libtriton/html/search/variables_17.js /usr/share/doc/libtriton/html/search/all_19.js
libtriton-doc.noarch: W: files-duplicate /usr/share/doc/libtriton/html/search/functions_16.js /usr/share/doc/libtriton/html/search/all_1b.js
libtriton-doc.noarch: W: files-duplicate /usr/share/doc/libtriton/html/search/variables_9.js /usr/share/doc/libtriton/html/search/all_b.js
========== 4 packages and 0 specfiles checked; 0 errors, 60 warnings, 26 filtered, 0 badness; has taken 0.7 s =========
Oh my, that’s a lot of output! Is something wrong? The
undefined-non-weak-symbol warnings are because we deliberately did not link
the Python module with libpython. Just verify that each symbol starts with
Py or _Py and we can move on. The python3-triton package indeed has no
documentation; we put that into the doc subpackage, so that’s okay. The
files-duplicate warnings are all due to actions by doxygen that we don’t want
to countermand. Everything looks fine.
Conclusion
I hope you learned something from this case study. Please send suggestions for improvements to Jerry James. If you want to submit the libtriton package to Fedora, please feel free. Just make sure nobody else beat you to it.