treewide: setup libelf build

Change-Id: I42ead2eac3be455015a302f947dfcc311057e46e
Reviewed-on: https://review.monogon.dev/c/monogon/+/4097
Tested-by: Jenkins CI
Reviewed-by: Jan Schär <jan@monogon.tech>
diff --git a/MODULE.bazel b/MODULE.bazel
index 3bbc839..515b5c9 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -57,6 +57,7 @@
 
 bazel_dep(name = "rules_rust_protobuf", version = RULES_RUST_VERSION)
 bazel_dep(name = "rules_rust_mdbook", version = RULES_RUST_VERSION)
+bazel_dep(name = "zstd", version = "1.5.7")
 bazel_dep(name = "gperf", version = "3.1")
 bazel_dep(name = "zlib", version = "1.3.1.bcr.6")
 bazel_dep(name = "boringssl", version = "0.20250514.0")
diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock
index b2aefad..fa0aa11 100644
--- a/MODULE.bazel.lock
+++ b/MODULE.bazel.lock
@@ -236,7 +236,9 @@
     "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.6/MODULE.bazel": "e937cf0a3772f93ad91f3c7af4f330b76a878bbfee06527ca1a9673b790eb896",
     "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.6/source.json": "5f397158198f338129c865a4c3ae21bc5626a9664b3c3b40fa3b3c2ec1ff83bf",
     "https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198",
-    "https://bcr.bazel.build/modules/zlib/1.3/MODULE.bazel": "6a9c02f19a24dcedb05572b2381446e27c272cd383aed11d41d99da9e3167a72"
+    "https://bcr.bazel.build/modules/zlib/1.3/MODULE.bazel": "6a9c02f19a24dcedb05572b2381446e27c272cd383aed11d41d99da9e3167a72",
+    "https://bcr.bazel.build/modules/zstd/1.5.7/MODULE.bazel": "f5780cdbd6f4c5bb985a20f839844316fe48fb5e463056f372dbc37cfabdf450",
+    "https://bcr.bazel.build/modules/zstd/1.5.7/source.json": "f72c48184b6528ffc908a5a2bcbf3070c6684f3db03da2182c8ca999ae5f5cfd"
   },
   "selectedYankedVersions": {},
   "moduleExtensions": {
diff --git a/build/bazel/third_party.MODULE.bazel b/build/bazel/third_party.MODULE.bazel
index 9bd49fb..76d7c69 100644
--- a/build/bazel/third_party.MODULE.bazel
+++ b/build/bazel/third_party.MODULE.bazel
@@ -287,3 +287,16 @@
     strip_prefix = "swtpm-" + SWTPM_VERSION,
     urls = ["https://github.com/stefanberger/swtpm/archive/%s.tar.gz" % SWTPM_VERSION],
 )
+
+ELFUTILS_VERSION = "0.192.1"
+
+http_archive(
+    name = "libelf",
+    build_file = "//third_party/libelf:libelf.bzl",
+    integrity = "sha256-o1l1WlQgDg2fq0vxadWvfSF30SsySHTGwy6qxcznkpU=",
+    strip_prefix = "libelf-%s" % ELFUTILS_VERSION,
+    urls = [
+        # TODO(tim): Migrate this to a proper bzlmod module with the official upstream.
+        "https://github.com/arachsys/libelf/archive/refs/tags/v%s.tar.gz" % ELFUTILS_VERSION,
+    ],
+)
diff --git a/third_party/BUILD.bazel b/third_party/BUILD.bazel
index 80b8cfb..7a1c6c1 100644
--- a/third_party/BUILD.bazel
+++ b/third_party/BUILD.bazel
@@ -24,3 +24,9 @@
     actual = "@zlib",
     visibility = ["//visibility:public"],
 )
+
+alias(
+    name = "zstd",
+    actual = "@zstd//:zstd",
+    visibility = ["//visibility:public"],
+)
diff --git a/third_party/libelf/BUILD.bazel b/third_party/libelf/BUILD.bazel
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/libelf/BUILD.bazel
diff --git a/third_party/libelf/libelf.bzl b/third_party/libelf/libelf.bzl
new file mode 100644
index 0000000..179630f
--- /dev/null
+++ b/third_party/libelf/libelf.bzl
@@ -0,0 +1,27 @@
+load("@rules_cc//cc:defs.bzl", "cc_library")
+
+filegroup(
+    name = "all",
+    srcs = glob(["**/*"]),
+    visibility = ["//visibility:public"],
+)
+
+cc_library(
+    name = "elf",
+    srcs = glob(["src/*.c", "src/*.h"]),
+    hdrs = glob(["include/*.h"]),
+    textual_hdrs = glob(["src/*.c"]),
+    copts = [
+        "-I{path}/include".format(path = package_relative_label(":all").workspace_root),
+        "-I{path}/src".format(path = package_relative_label(":all").workspace_root),
+    ],
+    local_defines = [
+        "HAVE_CONFIG_H",
+    ],
+    linkstatic = True,
+    deps = [
+        "@zlib//:zlib",
+        "@zstd//:zstd",
+    ],
+    visibility = ["//visibility:public"],
+)