1{ stdenv
2, fetchFromGitHub
3, fetchpatch
4, fetchzip
5, lib
6, callPackage
7, openssl
8, cmake
9, autoconf
10, automake
11, libtool
12, pkg-config
13, bison
14, flex
15, groff
16, perl
17, python3
18, time
19, upx
20, ncurses
21, libffi
22, libxml2
23, zlib
24, withPEPatterns ? false
25}:
26
27let
28 capstone = fetchFromGitHub {
29 owner = "avast-tl";
30 repo = "capstone";
31 rev = "27c713fe4f6eaf9721785932d850b6291a6073fe";
32 sha256 = "105z1g9q7s6n15qpln9vzhlij7vj6cyc5dqdr05n7wzjvlagwgxc";
33 };
34 elfio = fetchFromGitHub {
35 owner = "avast-tl";
36 repo = "elfio";
37 rev = "998374baace397ea98f3b1d768e81c978b4fba41";
38 sha256 = "09n34rdp0wpm8zy30zx40wkkc4gbv2k3cv181y6c1260rllwk5d1";
39 };
40 keystone = fetchFromGitHub { # only for tests
41 owner = "keystone-engine";
42 repo = "keystone";
43 rev = "d7ba8e378e5284e6384fc9ecd660ed5f6532e922";
44 sha256 = "1yzw3v8xvxh1rysh97y0i8y9svzbglx2zbsqjhrfx18vngh0x58f";
45 };
46 libdwarf = fetchFromGitHub {
47 owner = "avast-tl";
48 repo = "libdwarf";
49 rev = "85465d5e235cc2d2f90d04016d6aca1a452d0e73";
50 sha256 = "11y62r65py8yp57i57a4cymxispimn62by9z4j2g19hngrpsgbki";
51 };
52 llvm = fetchFromGitHub {
53 owner = "avast-tl";
54 repo = "llvm";
55 rev = "725d0cee133c6ab9b95c493f05de3b08016f5c3c";
56 sha256 = "0dzvafmn4qs62w1y9vh0a11clpj6q3hb41aym4izpcyybjndf9bq";
57 };
58 pelib = fetchFromGitHub {
59 owner = "avast-tl";
60 repo = "pelib";
61 rev = "a7004b2e80e4f6dc984f78b821e7b585a586050d";
62 sha256 = "0nyrb3g749lxgcymz1j584xbb1x6rvy1mc700lyn0brznvqsm81n";
63 };
64 rapidjson = fetchFromGitHub {
65 owner = "Tencent";
66 repo = "rapidjson";
67 rev = "v1.1.0";
68 sha256 = "1jixgb8w97l9gdh3inihz7avz7i770gy2j2irvvlyrq3wi41f5ab";
69 };
70 yaracpp = callPackage ./yaracpp.nix {}; # is its own package because it needs a patch
71 yaramod = fetchFromGitHub {
72 owner = "avast-tl";
73 repo = "yaramod";
74 rev = "v2.2.2";
75 sha256 = "0cq9h4h686q9ybamisbl797g6xjy211s3cq83nixkwkigmz48ccp";
76 };
77 jsoncpp = fetchFromGitHub {
78 owner = "open-source-parsers";
79 repo = "jsoncpp";
80 rev = "1.8.4";
81 sha256 = "1z0gj7a6jypkijmpknis04qybs1hkd04d1arr3gy89lnxmp6qzlm";
82 };
83 googletest = fetchFromGitHub { # only for tests
84 owner = "google";
85 repo = "googletest";
86 rev = "83fa0cb17dad47a1d905526dcdddb5b96ed189d2";
87 sha256 = "1c2r0p9v7vz2vasy8bknfb448l6wsvzw35s8hmc5z013z5502mpk";
88 };
89 tinyxml2 = fetchFromGitHub {
90 owner = "leethomason";
91 repo = "tinyxml2";
92 rev = "cc1745b552dd12bb1297a99f82044f83b06729e0";
93 sha256 = "015g8520a0c55gwmv7pfdsgfz2rpdmh3d1nq5n9bd65n35492s3q";
94 };
95
96 retdec-support = let
97 version = "2018-02-08"; # make sure to adjust both hashes (once with withPEPatterns=true and once withPEPatterns=false)
98 in fetchzip {
99 url = "https://github.com/avast-tl/retdec-support/releases/download/${version}/retdec-support_${version}.tar.xz";
100 sha256 = if withPEPatterns then "148i8flbyj1y4kfdyzsz7jsj38k4h97npjxj18h6v4wksd4m4jm7"
101 else "0ixv9qyqq40pzyqy6v9jf5rxrvivjb0z0zn260nbmb9gk765bacy";
102 stripRoot = false;
103 # Removing PE signatures reduces this from 3.8GB -> 642MB (uncompressed)
104 postFetch = lib.optionalString (!withPEPatterns) ''
105 rm -r "$out/generic/yara_patterns/static-code/pe"
106 '';
107 } // {
108 inherit version; # necessary to check the version against the expected version
109 };
110
111 # patch CMakeLists.txt for a dependency and compare the versions to the ones expected by upstream
112 # this has to be applied for every dependency (which it is in postPatch)
113 patchDep = dep: ''
114 # check if our version of dep is the same version that upstream expects
115 echo "Checking version of ${dep.dep_name}"
116 expected_rev="$( sed -n -e 's|.*URL https://github.com/.*/archive/\(.*\)\.zip.*|\1|p' "deps/${dep.dep_name}/CMakeLists.txt" )"
117 if [ "$expected_rev" != '${dep.rev}' ]; then
118 echo "The ${dep.dep_name} dependency has the wrong version: ${dep.rev} while $expected_rev is expected."
119 exit 1
120 fi
121
122 # patch the CMakeLists.txt file to use our local copy of the dependency instead of fetching it at build time
123 sed -i -e 's|URL .*|URL ${dep}|' "deps/${dep.dep_name}/CMakeLists.txt"
124 '';
125
126in stdenv.mkDerivation rec {
127 pname = "retdec";
128
129 # If you update this you will also need to adjust the versions of the updated dependencies. You can do this by first just updating retdec
130 # itself and trying to build it. The build should fail and tell you which dependencies you have to upgrade to which versions.
131 # I've notified upstream about this problem here:
132 # https://github.com/avast-tl/retdec/issues/412
133 # gcc is pinned to gcc8 in all-packages.nix. That should probably be re-evaluated on update.
134 version = "3.2";
135
136 src = fetchFromGitHub {
137 owner = "avast-tl";
138 repo = pname;
139 rev = "refs/tags/v${version}";
140 sha256 = "0chky656lsddn20bnm3pmz6ix20y4a0y8swwr42hrhi01vkhmzrp";
141 };
142
143 nativeBuildInputs = [
144 cmake
145 autoconf
146 automake
147 libtool
148 pkg-config
149 bison
150 flex
151 groff
152 perl
153 python3
154 ];
155
156 buildInputs = [
157 openssl
158 ncurses
159 libffi
160 libxml2
161 zlib
162 ];
163
164 cmakeFlags = [
165 "-DRETDEC_TESTS=ON" # build tests
166 ];
167
168 # all dependencies that are normally fetched during build time (the subdirectories of `deps`)
169 # all of these need to be fetched through nix and the CMakeLists files need to be patched not to fetch them themselves
170 external_deps = [
171 (capstone // { dep_name = "capstone"; })
172 (elfio // { dep_name = "elfio"; })
173 (googletest // { dep_name = "googletest"; })
174 (jsoncpp // { dep_name = "jsoncpp"; })
175 (keystone // { dep_name = "keystone"; })
176 (libdwarf // { dep_name = "libdwarf"; })
177 (llvm // { dep_name = "llvm"; })
178 (pelib // { dep_name = "pelib"; })
179 (rapidjson // { dep_name = "rapidjson"; })
180 (tinyxml2 // { dep_name = "tinyxml2"; })
181 (yaracpp // { dep_name = "yaracpp"; })
182 (yaramod // { dep_name = "yaramod"; })
183 ];
184
185 # Use newer yaramod to fix w/bison 3.2+
186 patches = [
187 # 2.1.2 -> 2.2.1
188 (fetchpatch {
189 url = "https://github.com/avast-tl/retdec/commit/c9d23da1c6e23c149ed684c6becd3f3828fb4a55.patch";
190 sha256 = "0hdq634f72fihdy10nx2ajbps561w03dfdsy5r35afv9fapla6mv";
191 })
192 # 2.2.1 -> 2.2.2
193 (fetchpatch {
194 url = "https://github.com/avast-tl/retdec/commit/fb85f00754b5d13b781385651db557741679721e.patch";
195 sha256 = "0a8mwmwb39pr5ag3q11nv81ncdk51shndqrkm92shqrmdq14va52";
196 })
197 ];
198
199 postPatch = (lib.concatMapStrings patchDep external_deps) + ''
200 # install retdec-support
201 echo "Checking version of retdec-support"
202 expected_version="$( sed -n -e "s|^version = '\(.*\)'$|\1|p" 'cmake/install-share.py' )"
203 if [ "$expected_version" != '${retdec-support.version}' ]; then
204 echo "The retdec-support dependency has the wrong version: ${retdec-support.version} while $expected_version is expected."
205 exit 1
206 fi
207 mkdir -p "$out/share/retdec"
208 cp -r ${retdec-support} "$out/share/retdec/support" # write permission needed during install
209 chmod -R u+w "$out/share/retdec/support"
210 # python file originally responsible for fetching the retdec-support archive to $out/share/retdec
211 # that is not necessary anymore, so empty the file
212 echo > cmake/install-share.py
213
214 # call correct `time` and `upx` programs
215 substituteInPlace scripts/retdec-config.py --replace /usr/bin/time ${time}/bin/time
216 substituteInPlace scripts/retdec-unpacker.py --replace "'upx'" "'${upx}/bin/upx'"
217 '';
218
219 doInstallCheck = true;
220 installCheckPhase = ''
221 ${python3.interpreter} "$out/bin/retdec-tests-runner.py"
222
223 rm -rf $out/bin/__pycache__
224 '';
225
226 meta = with lib; {
227 description = "A retargetable machine-code decompiler based on LLVM";
228 homepage = "https://retdec.com";
229 license = licenses.mit;
230 maintainers = with maintainers; [ dtzWill timokau ];
231 platforms = ["x86_64-linux" "i686-linux"];
232 };
233}