]> git.ipfire.org Git - thirdparty/systemd.git/blob - docs/HACKING.md
mkosi: Changes to allow booting with sanitizers in mkosi
[thirdparty/systemd.git] / docs / HACKING.md
1 ---
2 title: Hacking on systemd
3 category: Contributing
4 layout: default
5 SPDX-License-Identifier: LGPL-2.1-or-later
6 ---
7
8 # Hacking on systemd
9
10 We welcome all contributions to systemd. If you notice a bug or a missing
11 feature, please feel invited to fix it, and submit your work as a
12 [GitHub Pull Request (PR)](https://github.com/systemd/systemd/pull/new).
13
14 Please make sure to follow our [Coding Style](CODING_STYLE.md) when submitting
15 patches. Also have a look at our [Contribution Guidelines](CONTRIBUTING.md).
16
17 When adding new functionality, tests should be added. For shared functionality
18 (in `src/basic/` and `src/shared/`) unit tests should be sufficient. The general
19 policy is to keep tests in matching files underneath `src/test/`,
20 e.g. `src/test/test-path-util.c` contains tests for any functions in
21 `src/basic/path-util.c`. If adding a new source file, consider adding a matching
22 test executable. For features at a higher level, tests in `src/test/` are very
23 strongly recommended. If that is not possible, integration tests in `test/` are
24 encouraged.
25
26 Please also have a look at our list of [code quality tools](CODE_QUALITY.md) we
27 have setup for systemd, to ensure our codebase stays in good shape.
28
29 Please always test your work before submitting a PR. For many of the components
30 of systemd testing is straightforward as you can simply compile systemd and
31 run the relevant tool from the build directory.
32
33 For some components (most importantly, systemd/PID 1 itself) this is not
34 possible, however. In order to simplify testing for cases like this we provide
35 a set of `mkosi` build files directly in the source tree.
36 [mkosi](https://github.com/systemd/mkosi) is a tool for building clean OS images
37 from an upstream distribution in combination with a fresh build of the project
38 in the local working directory. To make use of this, please install the
39 `mkosi` package (if not packaged for your distro, it can be downloaded from
40 the [GitHub repository](https://github.com/systemd/mkosi). `mkosi` will build an
41 image for the host distro by default. It is sufficient to type `mkosi` in the
42 systemd project directory to generate a disk image `image.raw` you can boot either
43 in `systemd-nspawn` or in an UEFI-capable VM:
44
45 ```sh
46 $ mkosi boot
47 ```
48
49 or:
50
51 ```sh
52 $ mkosi qemu
53 ```
54
55 Every time you rerun the `mkosi` command a fresh image is built, incorporating
56 all current changes you made to the project tree. To save time when rebuilding,
57 you can use mkosi's incremental mode (`-i`). This instructs mkosi to build a set
58 of cache images that make future builds a lot faster. Note that the `-i` flag
59 both instructs mkosi to build cached images if they don't exist yet and to use
60 cached images if they already exist so make sure to always specify `-i` if you
61 want mkosi to use the cached images.
62
63 If you're going to build mkosi images that use the same distribution and release
64 that you're currently using, you can speed up the initial mkosi run by having it
65 reuse the host's package cache. To do this, create a mkosi override file in
66 mkosi.default.d/ (e.g 20-local.conf) and add the following contents:
67
68 ```
69 [Packages]
70 Cache=<full-path-to-package-manager-cache> # (e.g. /var/cache/dnf)
71 ```
72
73 If you want to do a local build without mkosi, most distributions also provide
74 very simple and convenient ways to install all development packages necessary
75 to build systemd:
76
77 ```sh
78 # Fedora
79 $ sudo dnf builddep systemd
80 # Debian/Ubuntu
81 $ sudo apt-get build-dep systemd
82 # Arch
83 $ sudo pacman install asp
84 $ asp checkout systemd
85 $ cd systemd/trunk
86 $ makepkg -seoc
87 ```
88
89 Putting this all together, here's a series of commands for preparing a patch
90 for systemd:
91
92 ```sh
93 # Install build dependencies (see above)
94 # Install a recent version of mkosi (either via your distro's package manager if
95 # available there or from the github repository otherwise)
96 $ git clone https://github.com/systemd/systemd.git
97 $ cd systemd
98 $ git checkout -b <BRANCH> # where BRANCH is the name of the branch
99 $ vim src/core/main.c # or wherever you'd like to make your changes
100 $ meson build # configure the build
101 $ ninja -C build # build it locally, see if everything compiles fine
102 $ meson test -C build # run some simple regression tests
103 $ sudo mkosi # build a test image
104 $ sudo mkosi boot # boot up the test image
105 $ git add -p # interactively put together your patch
106 $ git commit # commit it
107 $ git push -u <REMOTE> # where REMOTE is your "fork" on GitHub
108 ```
109
110 And after that, head over to your repo on GitHub and click "Compare & pull request"
111
112 Happy hacking!
113
114 ## Templating engines in .in files
115
116 Some source files are generated during build. We use two templating engines:
117 * meson's `configure_file()` directive uses syntax with `@VARIABLE@`.
118
119 See the
120 [Meson docs for `configure_file()`](https://mesonbuild.com/Reference-manual.html#configure_file)
121 for details.
122
123 {% raw %}
124 * most files are rendered using jinja2, with `{{VARIABLE}}` and `{% if … %}`,
125 `{% elif … %}`, `{% else … %}`, `{% endif … %}` blocks. `{# … #}` is a
126 jinja2 comment, i.e. that block will not be visible in the rendered
127 output. `{% raw %} … `{% endraw %}`{{ '{' }}{{ '% endraw %' }}}` creates a block
128 where jinja2 syntax is not interpreted.
129
130 See the
131 [Jinja Template Designer Documentation](https://jinja2docs.readthedocs.io/en/stable/templates.html#synopsis)
132 for details.
133
134 Please note that files for both template engines use the `.in` extension.
135
136 ## Developer and release modes
137
138 In the default meson configuration (`-Dmode=developer`), certain checks are
139 enabled that are suitable when hacking on systemd (such as internal
140 documentation consistency checks). Those are not useful when compiling for
141 distribution and can be disabled by setting `-Dmode=release`.
142
143 ## Sanitizers in mkosi
144
145 See [Testing systemd using sanitizers](TESTING_WITH_SANITIZERS.md) for more information
146 on how to build with sanitizers enabled in mkosi.
147
148 ## Fuzzers
149
150 systemd includes fuzzers in `src/fuzz/` that use libFuzzer and are automatically
151 run by [OSS-Fuzz](https://github.com/google/oss-fuzz) with sanitizers.
152 To add a fuzz target, create a new `src/fuzz/fuzz-foo.c` file with a `LLVMFuzzerTestOneInput`
153 function and add it to the list in `src/fuzz/meson.build`.
154
155 Whenever possible, a seed corpus and a dictionary should also be added with new
156 fuzz targets. The dictionary should be named `src/fuzz/fuzz-foo.dict` and the seed
157 corpus should be built and exported as `$OUT/fuzz-foo_seed_corpus.zip` in
158 `tools/oss-fuzz.sh`.
159
160 The fuzzers can be built locally if you have libFuzzer installed by running
161 `tools/oss-fuzz.sh`. You should also confirm that the fuzzers can be built and
162 run using
163 [the OSS-Fuzz toolchain](https://google.github.io/oss-fuzz/advanced-topics/reproducing/#building-using-docker):
164
165 ```
166 path_to_systemd=...
167
168 git clone --depth=1 https://github.com/google/oss-fuzz
169 cd oss-fuzz
170
171 for sanitizer in address undefined memory; do
172 for engine in libfuzzer afl honggfuzz; do
173 ./infra/helper.py build_fuzzers --sanitizer "$sanitizer" --engine "$engine" \
174 --clean systemd "$path_to_systemd"
175
176 ./infra/helper.py check_build --sanitizer "$sanitizer" --engine "$engine" \
177 -e ALLOWED_BROKEN_TARGETS_PERCENTAGE=0 systemd
178 done
179 done
180
181 ./infra/helper.py build_fuzzers --clean --architecture i386 systemd "$path_to_systemd"
182 ./infra/helper.py check_build --architecture i386 -e ALLOWED_BROKEN_TARGETS_PERCENTAGE=0 systemd
183
184 ./infra/helper.py build_fuzzers --clean --sanitizer coverage systemd "$path_to_systemd"
185 ./infra/helper.py coverage --no-corpus-download systemd
186 ```
187
188 If you find a bug that impacts the security of systemd, please follow the
189 guidance in [CONTRIBUTING.md](CONTRIBUTING.md) on how to report a security vulnerability.
190
191 For more details on building fuzzers and integrating with OSS-Fuzz, visit:
192
193 - [Setting up a new project - OSS-Fuzz](https://google.github.io/oss-fuzz/getting-started/new-project-guide/)
194 - [Tutorials - OSS-Fuzz](https://google.github.io/oss-fuzz/reference/useful-links/#tutorials)
195
196 ## mkosi + clangd
197
198 [clangd](https://clangd.llvm.org/) is a language server that provides code completion, diagnostics and more
199 right in your editor of choice (with the right plugin installed). When using mkosi, we can run clangd in the
200 mkosi build container to avoid needing to build systemd on the host machine just to make clangd work. To
201 achieve this, create a script with the following contents in systemd's project directory on the host:
202
203 ```sh
204 #!/usr/bin/env sh
205 tee mkosi-clangd.build > /dev/null << EOF
206 #!/usr/bin/env sh
207 exec clangd \\
208 --compile-commands-dir=/root/build \\
209 --path-mappings=\\
210 "\\
211 $(pwd)=/root/src,\\
212 $(pwd)/mkosi.builddir=/root/build,\\
213 $(pwd)/mkosi.includedir=/usr/include,\\
214 $(pwd)/mkosi.installdir=/root/dest\\
215 " \\
216 --header-insertion=never
217 EOF
218 chmod +x mkosi-clangd.build
219 exec sudo mkosi --source-file-transfer=mount --incremental --skip-final-phase --build-script mkosi-clangd.build build
220 ```
221
222 Next, mark the script as executable and point your editor plugin to use this script to start clangd. For
223 vscode's clangd extension, this is done via setting the `clangd.path` option to the path of the
224 mkosi-clangd.sh script.
225
226 To be able to navigate to include files of systemd's dependencies, we need to make the /usr/include folder of
227 the build image available on the host. mkosi supports this by setting the `IncludeDirectory` option in
228 mkosi's config. The easiest way to set the option is to create a file 20-local.conf in mkosi.default.d/ and
229 add the following contents:
230
231 ```
232 [Packages]
233 IncludeDirectory=mkosi.includedir
234 ```
235
236 This will make the contents of /usr/include available in mkosi.includedir in the systemd project directory.
237 We already configured clangd to map any paths in /usr/include in the build image to mkosi.includedir/ on the
238 host in the mkosi-clangd.sh script.
239
240 We also need to make sure clangd is installed in the build image. To have mkosi install clangd in the build
241 image, edit the 20-local.conf file we created earlier and add the following contents under the `[Packages]`
242 section:
243
244 ```
245 BuildPackages=<clangd-package>
246 ```
247
248 Note that the exact package containing clangd will differ depending on the distribution used. Some
249 distributions have a separate clangd package, others put the clangd binary in a clang-tools-extra package and
250 some bundle clangd in the clang package.
251
252 Because mkosi needs to run as root, we also need to make sure we can enter the root password when the editor
253 plugin tries to run the mkosi-clangd.sh script. To be able to enter the root password in non-interactive
254 scripts, we use an askpass provider. This is a program that sudo will launch if it detects it's being
255 executed from a non-interactive shell so that the root password can still be entered. There are multiple
256 implementations such as gnome askpass and KDE askpass. Install one of the askpass packages your distro
257 provides and set the `SUDO_ASKPASS` environment variable to the path of the askpass binary you want to use.
258 If configured correctly, a window will appear when your editor plugin tries to run the mkosi-clangd.sh script
259 allowing you to enter the root password.
260
261 Due to a bug in btrfs, it's currently impossible to mount two mkosi btrfs images at the same time. Because of
262 this, trying to do a regular build while the clangd image is running will fail. To circumvent this, use ext4
263 instead of btrfs for the images by adding the following contents to 20-local.conf:
264
265 ```
266 [Output]
267 Format=gpt_ext4
268 ```
269
270 Finally, to ensure clangd starts up quickly in the editor, run an incremental build with mkosi to make sure
271 the cached images are initialized (`mkosi -i`).
272
273 Now, your editor will start clangd in the mkosi build image and all of clangd's features will work as
274 expected.
275
276 ## Debugging systemd with mkosi + vscode
277
278 To simplify debugging systemd when testing changes using mkosi, we're going to show how to attach
279 [VSCode](https://code.visualstudio.com/)'s debugger to an instance of systemd running in a mkosi image
280 (either using QEMU or systemd-nspawn).
281
282 To allow VSCode's debugger to attach to systemd running in a mkosi image, we have to make sure it can access
283 the container/virtual machine spawned by mkosi where systemd is running. mkosi makes this possible via a
284 handy SSH option that makes the generated image accessible via SSH when booted. The easiest way to set the
285 option is to create a file 20-local.conf in mkosi.default.d/ and add the following contents:
286
287 ```
288 [Host]
289 Ssh=yes
290 ```
291
292 Next, make sure systemd-networkd is running on the host system so that it can configure the network interface
293 connecting the host system to the container/VM spawned by mkosi. Once systemd-networkd is running, you should
294 be able to connect to a running mkosi image by executing `mkosi ssh` in the systemd repo directory.
295
296 Now we need to configure VSCode. First, make sure the C/C++ extension is installed. If you're already using
297 a different extension for code completion and other IDE features for C in VSCode, make sure to disable the
298 corresponding parts of the C/C++ extension in your VSCode user settings by adding the following entries:
299
300 ```json
301 "C_Cpp.formatting": "Disabled",
302 "C_Cpp.intelliSenseEngine": "Disabled",
303 "C_Cpp.enhancedColorization": "Disabled",
304 "C_Cpp.suggestSnippets": false,
305 ```
306
307 With the extension set up, we can create the launch.json file in the .vscode/ directory to tell the VSCode
308 debugger how to attach to the systemd instance running in our mkosi container/VM. Create the file and add the
309 following contents:
310
311 ```json
312 {
313 "version": "0.2.0",
314 "configurations": [
315 {
316 "type": "cppdbg",
317 "program": "/usr/lib/systemd/systemd",
318 "processId": "${command:pickRemoteProcess}",
319 "request": "attach",
320 "name": "systemd",
321 "pipeTransport": {
322 "pipeProgram": "mkosi",
323 "pipeArgs": [
324 "-C",
325 "/path/to/systemd/repo/directory/on/host/system/",
326 "ssh"
327 ],
328 "debuggerPath": "/usr/bin/gdb"
329 },
330 "MIMode": "gdb",
331 "sourceFileMap": {
332 "/root/build/../src": {
333 "editorPath": "${workspaceFolder}",
334 "useForBreakpoints": false
335 },
336 "/root/build/*": {
337 "editorPath": "${workspaceFolder}/mkosi.builddir",
338 "useForBreakpoints": false
339 }
340 }
341 }
342 ]
343 }
344 ```
345
346 Now that the debugger knows how to connect to our process in the container/VM and we've set up the necessary
347 source mappings, go to the "Run and Debug" window and run the "systemd" debug configuration. If everything
348 goes well, the debugger should now be attached to the systemd instance running in the container/VM. You can
349 attach breakpoints from the editor and enjoy all the other features of VSCode's debugger.
350
351 To debug systemd components other than PID 1, set "program" to the full path of the component you want to
352 debug and set "processId" to "${command:pickProcess}". Now, when starting the debugger, VSCode will ask you
353 the PID of the process you want to debug. Run `systemctl show --property MainPID --value <component>` in the
354 container to figure out the PID and enter it when asked and VSCode will attach to that process instead.
355
356 ## Debugging systemd-boot
357
358 During boot, systemd-boot and the stub loader will output a message like `systemd-boot@0x0A,0x0B`,
359 providing the location of the text and data sections. These location can then be used to attach
360 to a QEMU session (provided it was run with `-s`) with these gdb commands:
361
362 ```
363 (gdb) file build/src/boot/efi/systemd-bootx64.efi
364 (gdb) add-symbol-file build/src/boot/efi/systemd_boot.so 0x0A -s .data 0x0B
365 (gdb) set architecture i386:x86-64
366 (gdb) target remote :1234
367 ```
368
369 This process can be automated by using the `debug-sd-boot.sh` script in the tools folder. If run
370 without arguments it will provide usage information.
371
372 If the debugger is too slow to attach to examine an early boot code passage, we can uncomment the
373 call to `debug_break()` inside of `efi_main()`. As soon as the debugger has control we can then run
374 `set variable wait = 0` or `return` to continue. Once the debugger has attached, setting breakpoints
375 will work like usual.
376
377 To debug systemd-boot in an IDE such as VSCode we can use a launch configuration like this:
378 ```json
379 {
380 "name": "systemd-boot",
381 "type": "cppdbg",
382 "request": "launch",
383 "program": "${workspaceFolder}/build/src/boot/efi/systemd-bootx64.efi",
384 "cwd": "${workspaceFolder}",
385 "MIMode": "gdb",
386 "miDebuggerServerAddress": ":1234",
387 "setupCommands": [
388 { "text": "shell mkfifo /tmp/sdboot.{in,out}" },
389 { "text": "shell qemu-system-x86_64 [...] -s -serial pipe:/tmp/sdboot" },
390 { "text": "shell ${workspaceFolder}/tools/debug-sd-boot.sh ${workspaceFolder}/build/src/boot/efi/systemd-bootx64.efi /tmp/sdboot.out systemd-boot.gdb" },
391 { "text": "source /tmp/systemd-boot.gdb" },
392 ]
393 }
394 ```