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
rm -rf $RPM_BUILD_ROOT
%make_install
%files
%license add-license-file-here
%doc add-docs-here
%changelog
* Mon Sep 13 2021 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.8.1, 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.8.1, the output looks like this:
./.appveyor.yml: *No copyright* UNKNOWN
./.build_number: *No copyright* UNKNOWN
./.codecov.yml: *No copyright* UNKNOWN
./.editorconfig: *No copyright* UNKNOWN
./.travis.yml: *No copyright* UNKNOWN
./CMakeLists.txt: UNKNOWN
./Dockerfile: *No copyright* UNKNOWN
./LICENSE.txt: *No copyright* Apache License 2.0
./README.md: *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/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/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
./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/scripts/extract_syscall.py: *No copyright* UNKNOWN
./src/scripts/run_linux_test.sh: *No copyright* UNKNOWN
./src/scripts/triton.in: *No copyright* UNKNOWN
./src/scripts/tritonAttach.in: *No copyright* UNKNOWN
./src/testers/CMakeLists.txt: *No copyright* UNKNOWN
./src/tracer/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/pin/blacklist.py: *No copyright* UNKNOWN
./src/examples/pin/callback_concrete_needs.py: *No copyright* UNKNOWN
./src/examples/pin/callback_image.py: *No copyright* UNKNOWN
./src/examples/pin/callback_routine.py: *No copyright* UNKNOWN
./src/examples/pin/callback_signals.py: *No copyright* UNKNOWN
./src/examples/pin/callback_syscall.py: *No copyright* UNKNOWN
./src/examples/pin/count_inst.py: *No copyright* UNKNOWN
./src/examples/pin/crackme_hash_collision.py: *No copyright* UNKNOWN
./src/examples/pin/inject_model_with_snapshot.py: *No copyright* UNKNOWN
./src/examples/pin/ir.py: *No copyright* UNKNOWN
./src/examples/pin/looking_for_stack_base_and_main_addr.py: *No copyright* UNKNOWN
./src/examples/pin/path_constraints.py: *No copyright* UNKNOWN
./src/examples/pin/runtime_memory_tainting.py: *No copyright* UNKNOWN
./src/examples/pin/runtime_register_modification.py: *No copyright* UNKNOWN
./src/examples/pin/strlen.py: *No copyright* UNKNOWN
./src/examples/pin/sym_only_on_tainted.py: *No copyright* UNKNOWN
./src/examples/pin/symbolize_input_file.py: *No copyright* UNKNOWN
./src/examples/pin/trace_inst.py: *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/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/synthetizing_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: 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: 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.py: *No copyright* UNKNOWN
./src/testers/arm32/unicorn_test_arm32_interworking_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_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/pin/check_semantics.py: *No copyright* UNKNOWN
./src/testers/pin/qemu-test-x86_64.py: *No copyright* UNKNOWN
./src/testers/pin/unsuported_semantics.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_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_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/tracer/pin/CMakeLists.txt: *No copyright* UNKNOWN
./src/tracer/pin/api.cpp: Apache License 2.0
./src/tracer/pin/api.hpp: Apache License 2.0
./src/tracer/pin/bindings.cpp: Apache License 2.0
./src/tracer/pin/bindings.hpp: Apache License 2.0
./src/tracer/pin/callbacks.cpp: Apache License 2.0
./src/tracer/pin/context.cpp: Apache License 2.0
./src/tracer/pin/context.hpp: Apache License 2.0
./src/tracer/pin/init.cpp: Apache License 2.0
./src/tracer/pin/main.cpp: Apache License 2.0
./src/tracer/pin/snapshot.cpp: Apache License 2.0
./src/tracer/pin/snapshot.hpp: Apache License 2.0
./src/tracer/pin/trigger.cpp: Apache License 2.0
./src/tracer/pin/trigger.hpp: Apache License 2.0
./src/tracer/pin/utils.cpp: Apache License 2.0
./src/tracer/pin/utils.hpp: Apache License 2.0
./src/examples/python/samples/sample_1: *No copyright* UNKNOWN
./src/examples/python/samples/sample_1.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/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/tritonToZ3Ast.cpp: Apache License 2.0
./src/libtriton/ast/z3/z3ToTritonAst.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/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/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/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/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/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/syscalls.hpp: Apache License 2.0
./src/libtriton/includes/triton/taintEngine.hpp: Apache License 2.0
./src/libtriton/includes/triton/tritonToZ3Ast.hpp: Apache License 2.0
./src/libtriton/includes/triton/tritonTypes.hpp: Apache License 2.0
./src/libtriton/includes/triton/unix.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/z3ToTritonAst.hpp: Apache License 2.0
./src/libtriton/os/unix/syscallNumberToString.cpp: 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/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/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/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/initSymbolicNamespace.cpp: Apache License 2.0
./src/libtriton/bindings/python/namespaces/initSyscallNamespace.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/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.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 maintains a
list of licenses.
The “Short Name” column is what goes into the License:
field of an RPM spec
file. For the triton package, ASL 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 Source0 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, then, 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.8.1
Release: %autorelease
Summary: Dynamic binary analysis framework
License: ASL 2.0
URL: https://triton.quarkslab.com/
Source0: 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
rm -rf $RPM_BUILD_ROOT
%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 72 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.
Our %install
section starts with the line rm -rf $RPM_BUILD_ROOT
. That
line was inserted by rpmdev-newspec, which aims to be compatible with all
Linux distributions that use RPM. Fedora, however, does not want that line
in spec files; see
Tags and Section
in the packaging guidelines. Remove it now.
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.8.1
Release: %autorelease
Summary: Dynamic binary analysis framework
License: ASL 2.0
URL: https://triton.quarkslab.com/
Source0: 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.8.1.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.8.1-1.fc35.src.rpm
You may have to replace fc35
in that filename. Make it match the name of
file 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 boost problems. Now what?
Apply upstream patches
Let’s look at the sources on github to see if we are facing a known issue. In the list of recent commits, we see this one from 10 June 2021: “Fix #1016: Issue with boost 1.76.0 and bigint”. That sounds promising! Clicking through to the commit shows a handful of changes that we should apply to the sources to 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 Patch0
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 0be77d3dc69b964c697a9ce0a1f9f13ad1388323
%global date 20210910
%global forgeurl https://github.com/JonathanSalwan/Triton
Name: triton
Version: 0.8.1
%forgemeta
Release: %autorelease
Summary: Dynamic binary analysis framework
License: ASL 2.0
URL: https://triton.quarkslab.com/
Source0: %{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
%forgesetup
%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}/
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. 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 '/add_library.Triton/aset_target_properties(\${PROJECT_LIBTRITON} 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
Regenerate the source RPM and re-run the mock build. It fails, complaining
about not finding the library file. Wait, it 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
a line that reads
install (TARGETS ${PROJECT_LIBTRITON} DESTINATION lib)
There’s the problem. We need that “lib” to be “lib64” on 64-bit systems. Add
this to the %prep
section:
# Fix the install location on 64-bit systems
if [ "%{_lib}" != "lib" ]; then
sed -i 's/\(DESTINATION \)lib/\1%{_lib}/' src/libtriton/CMakeLists.txt
fi
Regenerate the source RPM and re-run the mock build.
Python interface
The build fails again. Now the complaint is:
RPM build errors:
Installed (but unpackaged) file(s) found:
/usr/lib/python3.10/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 __future__ import print_function; from distutils.sysconfig import get_python_lib; print(get_python_lib())" OUTPUT_VARIABLE PYTHON_SITE_PACKAGES OUTPUT_STRIP_TRAILING_WHITESPACE)
There’s the problem:
get_python_lib
called with no arguments yields the general library installation directory.
We have to call it with a true argument to get the platform-dependent library
installation directory. We should report this bug upstream, like
this. Until it is
fixed, 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 and install the python library in the right place
sed -e '/add_library.Triton/aset_target_properties(\${PROJECT_LIBTRITON} PROPERTIES SOVERSION 0 VERSION %{version})' \
-e 's/get_python_lib()/get_python_lib(1)/' \
-i src/libtriton/CMakeLists.txt
# Fix the install location on 64-bit systems
if [ "%{_lib}" != "lib" ]; then
sed -i 's/\(DESTINATION \)lib/\1%{_lib}/' src/libtriton/CMakeLists.txt
fi
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:
-DZ3_INTERFACE -Wall -Wextra -Wno-unused-parameter -Wno-unknown-pragmas -O3 -fno-stack-protector -fomit-frame-pointer -fno-strict-aliasing -fPIC -std=c++11 -fPIC -Wimplicit-fallthrough=0 -Wno-error=cast-function-type -DNDEBUG -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
. Fedora has also elected to build with the stack protector, so unless
there is some reason why triton won’t work with it on, we also do not want
-fno-stack-protector
. The choice of omitting the frame pointer or not is
also made by Fedora, so we should also remove -fomit-frame-ponter
. 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.
# Install the python library in the right place
sed -e 's/-O3/-O2/g' \
-e 's/ -fno-stack-protector -fomit-frame-pointer//' \
-e '/add_library.Triton/aset_target_properties(\${PROJECT_LIBTRITON} PROPERTIES SOVERSION 0 VERSION %{version})' \
-e 's/get_python_lib()/get_python_lib(1)/' \
-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.8.1-1.20210910git0be77d3.fc36.x86_64.rpm
-rw-r--r-- 1 root root 54066864 Sep 14 11:23 /usr/lib64/python3.10/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.8.1-1.20210910git0be77d3.fc36.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.3.4)(64bit)
libc.so.6(GLIBC_2.32)(64bit)
libc.so.6(GLIBC_2.4)(64bit)
libcapstone.so.4()(64bit)
libgcc_s.so.1()(64bit)
libgcc_s.so.1(GCC_3.0)(64bit)
libgcc_s.so.1(GCC_3.3.1)(64bit)
libpython3.10.so.1.0()(64bit)
libstdc++.so.6()(64bit)
libstdc++.so.6(CXXABI_1.3)(64bit)
libstdc++.so.6(CXXABI_1.3.5)(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.20)(64bit)
libstdc++.so.6(GLIBCXX_3.4.21)(64bit)
libstdc++.so.6(GLIBCXX_3.4.29)(64bit)
libstdc++.so.6(GLIBCXX_3.4.9)(64bit)
libz3.so.4.8()(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.10/site-packages/triton.so
and
/usr/lib64/libtriton.so.0.8.1
are the same.
$ rpm -q --provides -p /var/lib/mock/fedora-rawhide-x86_64/result/triton-0.8.1-1.20210910git0be77d3.fc36.x86_64.rpm
libtriton.so.0()(64bit)
triton = 0.8.1-1.20210910git0be77d3.fc36
triton(x86-64) = 0.8.1-1.20210910git0be77d3.fc36
That seems reasonable. How about the python3 subpackage?
$ rpm -q --provides -p /var/lib/mock/fedora-rawhide-x86_64/result/python3-triton-0.8.1-1.20210910git0be77d3.fc36.x86_64.rpm
libtriton.so.0()(64bit)
python-triton = 0.8.1-1.20210910git0be77d3.fc36
python3-triton = 0.8.1-1.20210910git0be77d3.fc36
python3-triton(x86-64) = 0.8.1-1.20210910git0be77d3.fc36
python3.10-triton = 0.8.1-1.20210910git0be77d3.fc36
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.8.1-1.20210910git0be77d3.fc36.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.3.4)(64bit)
libc.so.6(GLIBC_2.32)(64bit)
libc.so.6(GLIBC_2.4)(64bit)
libcapstone.so.4()(64bit)
libgcc_s.so.1()(64bit)
libgcc_s.so.1(GCC_3.0)(64bit)
libgcc_s.so.1(GCC_3.3.1)(64bit)
libpython3.10.so.1.0()(64bit)
libstdc++.so.6()(64bit)
libstdc++.so.6(CXXABI_1.3)(64bit)
libstdc++.so.6(CXXABI_1.3.5)(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.20)(64bit)
libstdc++.so.6(GLIBCXX_3.4.21)(64bit)
libstdc++.so.6(GLIBCXX_3.4.29)(64bit)
libstdc++.so.6(GLIBCXX_3.4.9)(64bit)
libz3.so.4.8()(64bit)
python(abi) = 3.10
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.8.1-1.20210910git0be77d3.fc36
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 0be77d3dc69b964c697a9ce0a1f9f13ad1388323
%global date 20210910
%global forgeurl https://github.com/JonathanSalwan/Triton
Name: triton
Version: 0.8.1
%forgemeta
Release: %autorelease
Summary: Dynamic binary analysis framework
License: ASL 2.0
URL: https://triton.quarkslab.com/
Source0: %{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.
# Install the python library in the right place
sed -e 's/-O3/-O2/g' \
-e 's/ -fno-stack-protector -fomit-frame-pointer//' \
-e 's/get_python_lib()/get_python_lib(1)/' \
-i src/libtriton/CMakeLists.txt
# Fix the install location on 64-bit systems
if [ "%{_lib}" != "lib" ]; then
sed -i 's/\(DESTINATION \)lib/\1%{_lib}/' src/libtriton/CMakeLists.txt
fi
%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
%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
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.8.1-1.20210910git0be77d3.fc36.x86_64.rpm
/usr/lib/.build-id
/usr/lib/.build-id/c8
/usr/lib/.build-id/c8/4d4d21a8a1340fa22ad9e55c0b20d31e6af970
/usr/lib64/libtriton.so.0
/usr/lib64/libtriton.so.0.8.1
/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.8.1-1.20210910git0be77d3.fc36.x86_64.rpm
/usr/lib/.build-id
/usr/lib/.build-id/63
/usr/lib/.build-id/63/91bd11555af935b4d71b20d7e3923c57e6272a
/usr/lib64/python3.10/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.8.1-1.20210910git0be77d3.fc36.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/api.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/astPythonRepresentation.hpp
/usr/include/triton/astRepresentation.hpp
/usr/include/triton/astRepresentationInterface.hpp
/usr/include/triton/astSmtRepresentation.hpp
/usr/include/triton/bitsVector.hpp
/usr/include/triton/callbacks.hpp
/usr/include/triton/callbacksEnums.hpp
/usr/include/triton/comparableFunctor.hpp
/usr/include/triton/config.hpp
/usr/include/triton/config.hpp.in
/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/memoryAccess.hpp
/usr/include/triton/modes.hpp
/usr/include/triton/modesEnums.hpp
/usr/include/triton/operandWrapper.hpp
/usr/include/triton/pathConstraint.hpp
/usr/include/triton/pathManager.hpp
/usr/include/triton/py3c_compat.h
/usr/include/triton/pythonBindings.hpp
/usr/include/triton/pythonObjects.hpp
/usr/include/triton/pythonUtils.hpp
/usr/include/triton/pythonXFunctions.hpp
/usr/include/triton/register.hpp
/usr/include/triton/semanticsInterface.hpp
/usr/include/triton/shortcutRegister.hpp
/usr/include/triton/solverEngine.hpp
/usr/include/triton/solverEnums.hpp
/usr/include/triton/solverInterface.hpp
/usr/include/triton/solverModel.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/syscalls.hpp
/usr/include/triton/taintEngine.hpp
/usr/include/triton/tritonToZ3Ast.hpp
/usr/include/triton/tritonTypes.hpp
/usr/include/triton/unix.hpp
/usr/include/triton/version.hpp
/usr/include/triton/version.hpp.in
/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/z3ToTritonAst.hpp
/usr/lib64/libtriton.so
That mostly looks okay, except I see two files with names ending in .in
in
there. Such files usually contain templates that are filled in by the build
system. We do, in fact, see the same filenames without the .in
extension in
the list, so we probably do not want the templated versions. Taking a diff of
the files in question confirms this: the .in
files are meant to be
instantiated by cmake. Let’s remove them in the %install
section:
# Remove unwanted files
rm %{buildroot}%{_includedir}/triton/*.in
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 0be77d3dc69b964c697a9ce0a1f9f13ad1388323
%global date 20210910
%global forgeurl https://github.com/JonathanSalwan/Triton
# Install documentation in the main doc directory
%global _docdir_fmt %{name}
Name: triton
Version: 0.8.1
%forgemeta
Release: %autorelease
Summary: Dynamic binary analysis framework
License: ASL 2.0
URL: https://triton.quarkslab.com/
Source0: %{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.
# Install the python library in the right place
sed -e 's/-O3/-O2/g' \
-e 's/ -fno-stack-protector -fomit-frame-pointer//' \
-e 's/get_python_lib()/get_python_lib(1)/' \
-i src/libtriton/CMakeLists.txt
# Fix the install location on 64-bit systems
if [ "%{_lib}" != "lib" ]; then
sed -i 's/\(DESTINATION \)lib/\1%{_lib}/' src/libtriton/CMakeLists.txt
fi
%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
# Remove unwanted files
rm %{buildroot}%{_includedir}/triton/*.in
# 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 -
%install
%cmake_install
# Remove unwanted files
rm %{buildroot}%{_includedir}/triton/*.in
# 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
%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 subpackages 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 has issues. The lief package is not available from Fedora at all. 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 unicorn is fixed and 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 and a unicorn bug:
# (https://bugzilla.redhat.com/show_bug.cgi?id=2004320
#
# 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 0be77d3dc69b964c697a9ce0a1f9f13ad1388323
%global date 20210910
%global forgeurl https://github.com/JonathanSalwan/Triton
# Install documentation in the main doc directory
%global _docdir_fmt %{name}
Name: libtriton
Version: 0.8.1
%forgemeta
Release: %autorelease
Summary: Dynamic binary analysis framework
License: ASL 2.0
URL: https://triton.quarkslab.com/
Source0: %{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.
# Install the python library in the right place
sed -e 's/-O3/-O2/g' \
-e 's/ -fno-stack-protector -fomit-frame-pointer//' \
-e 's/get_python_lib()/get_python_lib(1)/' \
-i src/libtriton/CMakeLists.txt
# Fix the install location on 64-bit systems
if [ "%{_lib}" != "lib" ]; then
sed -i 's/\(DESTINATION \)lib/\1%{_lib}/' src/libtriton/CMakeLists.txt
fi
%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
# Remove unwanted files
rm %{buildroot}%{_includedir}/triton/*.in
# 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 and a unicorn bug:
# (https://bugzilla.redhat.com/show_bug.cgi?id=2004320
#
# 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
%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 python3-enchant
Afterwards, we enter a mock shell and run rpmlint:
mock --enable-network -r fedora-rawhide-x86_64 --shell
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.1.0
configuration:
/usr/lib/python3.10/site-packages/rpmlint/configdefaults.toml
/etc/xdg/rpmlint/fedora.toml
/etc/xdg/rpmlint/licenses.toml
/etc/xdg/rpmlint/scoring.toml
/etc/xdg/rpmlint/users-groups.toml
/etc/xdg/rpmlint/warn-on-functions.toml
checks: 31, packages: 4
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyBool_Type (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so _Py_TrueStruct (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so _Py_NoneStruct (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyType_Type (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyType_Type (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyByteArray_Type (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyExc_RuntimeError (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so _Py_NotImplementedStruct (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyExc_TypeError (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyMethod_Type (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so _Py_FalseStruct (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyTuple_SetItem (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyByteArray_Size (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyLong_AsVoidPtr (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyObject_GetAttrString(/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyType_IsSubtype (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so _PyLong_New (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyDict_SetItem (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyModule_AddObject (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyTuple_New (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyDict_SetItemString (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyUnicode_FromFormat (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyBytes_FromStringAndSize (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyArg_ParseTupleAndKeywords (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyList_Size (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so _PyObject_New (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyErr_Format (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyObject_CallObject (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so _Py_Dealloc (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyByteArray_AsString (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so Py_BuildValue (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyList_New (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyImport_ImportModule(/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyUnicode_FromString (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyBytes_Size (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyList_GetItem (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyLong_FromVoidPtr (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyDict_New (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyArg_ParseTuple (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyUnicode_AsUTF8 (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyObject_SetAttrString(/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyBytes_AsString (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyType_Ready (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyLong_FromLong (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyObject_IsTrue (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyModule_Create2 (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyObject_GenericGetAttr (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyList_SetItem (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyObject_CallFunctionObjArgs (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyCallable_Check (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so Py_Initialize (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyErr_Print (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyDict_Clear (/usr/lib64/python3.10/site-packages/triton.so)
python3-triton.x86_64: W: undefined-non-weak-symbol /usr/lib64/python3.10/site-packages/triton.so PyBool_FromLong (/usr/lib64/python3.10/site-packages/triton.so)
libtriton.x86_64: E: shlib-policy-name-error 0
python3-triton.x86_64: W: no-documentation
libtriton-devel.x86_64: W: missing-dependency-on libtriton*/libtriton-libs/liblibtriton* = 0.8.1
libtriton.x86_64: E: invalid-ldconfig-symlink /usr/lib64/libtriton.so.0.8.1 libtriton.so.0.8.1
libtriton-doc.noarch: W: files-duplicate /usr/share/doc/libtriton/html/search/typedefs_0.js /usr/share/doc/libtriton/html/search/all_0.js
libtriton-doc.noarch: W: files-duplicate /usr/share/doc/libtriton/html/search/variables_f.js /usr/share/doc/libtriton/html/search/all_10.js
libtriton-doc.noarch: W: files-duplicate /usr/share/doc/libtriton/html/search/variables_17.js /usr/share/doc/libtriton/html/search/all_18.js
libtriton-doc.noarch: W: files-duplicate /usr/share/doc/libtriton/html/search/functions_17.js /usr/share/doc/libtriton/html/search/all_1a.js
================ 4 packages and 0 specfiles checked; 2 errors, 60 warnings, 2 badness; has taken 2.5 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 shlib-policy-name-error,
missing-dependency-on, and invalid-ldconfig-symlink warnings are bogus; I get
them on every package with a shared library, so it must be an rpmlint bug.
Let’s ignore them for now. 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.