diff --git a/repos/os/include/util/formatted_output.h b/repos/os/include/util/formatted_output.h
new file mode 100644
index 0000000000..dc4aa7651c
--- /dev/null
+++ b/repos/os/include/util/formatted_output.h
@@ -0,0 +1,122 @@
+/*
+ * \brief Utilities for formatted text output
+ * \author Norman Feske
+ * \date 2022-03-14
+ */
+
+/*
+ * Copyright (C) 2022 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _INCLUDE__OS__UTIL__FORMATTED_OUTPUT_H_
+#define _INCLUDE__OS__UTIL__FORMATTED_OUTPUT_H_
+
+#include
+
+namespace Genode {
+
+ template struct Truncated;
+ template struct Repeated;
+ template struct Left_aligned;
+ template struct Right_aligned;
+
+ /**
+ * Return the number of characters needed when rendering 'args' as text
+ */
+ template
+ static unsigned printed_length(ARGS &&... args)
+ {
+ struct _Output : Output
+ {
+ unsigned count = 0;
+ void out_char(char) override { count++; }
+ } _output;
+
+ print(_output, args...);
+ return _output.count;
+ }
+}
+
+
+template
+struct Genode::Truncated
+{
+ unsigned const _limit;
+ T const &_arg;
+
+ Truncated(unsigned limit, T const &arg) : _limit(limit), _arg(arg) { }
+
+ void print(Output &out) const
+ {
+ struct _Output : Output
+ {
+ Output &out;
+ unsigned const limit;
+ unsigned count = 0;
+
+ void out_char(char c) override
+ {
+ if (count++ < limit)
+ out.out_char(c);
+ }
+
+ _Output(Output &out, unsigned limit) : out(out), limit(limit) { }
+
+ } _output(out, _limit);
+
+ Genode::print(_output, _arg);
+ }
+};
+
+
+template
+struct Genode::Repeated
+{
+ unsigned const _n;
+ T const &_arg;
+
+ Repeated(unsigned n, T const &arg) : _n(n), _arg(arg) { }
+
+ void print(Output &out) const
+ {
+ for (unsigned i = 0; i < _n; i++)
+ Genode::print(out, _arg);
+ }
+};
+
+
+template
+struct Genode::Left_aligned
+{
+ unsigned const _n;
+ T const &_arg;
+
+ Left_aligned(unsigned n, T const &arg) : _n(n), _arg(arg) { }
+
+ void print(Output &out) const
+ {
+ unsigned const len = min(printed_length(_arg), _n);
+ Genode::print(out, Truncated(len, _arg), Repeated(_n - len, Char(' ')));
+ }
+};
+
+
+template
+struct Genode::Right_aligned
+{
+ unsigned const _n;
+ T const &_arg;
+
+ Right_aligned(unsigned n, T const &arg) : _n(n), _arg(arg) { }
+
+ void print(Output &out) const
+ {
+ unsigned const len = min(printed_length(_arg), _n);
+ Genode::print(out, Repeated(_n - len, Char(' ')), Truncated(len, _arg));
+ }
+};
+
+#endif /* _INCLUDE__OS__UTIL__FORMATTED_OUTPUT_H_ */