Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.1/patches/2225599/?format=api
{ "id": 2225599, "url": "http://patchwork.ozlabs.org/api/1.1/patches/2225599/?format=api", "web_url": "http://patchwork.ozlabs.org/project/glibc/patch/20260421090020.59726-2-l.stelmach@samsung.com/", "project": { "id": 41, "url": "http://patchwork.ozlabs.org/api/1.1/projects/41/?format=api", "name": "GNU C Library", "link_name": "glibc", "list_id": "libc-alpha.sourceware.org", "list_email": "libc-alpha@sourceware.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20260421090020.59726-2-l.stelmach@samsung.com>", "date": "2026-04-21T09:00:18", "name": "[RFC,1/3] dlconf: Add support for config file for ld.so", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "2320bc7b71417b813c03b4c820da8316dc196f93", "submitter": { "id": 65337, "url": "http://patchwork.ozlabs.org/api/1.1/people/65337/?format=api", "name": "Łukasz Stelmach", "email": "l.stelmach@samsung.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/glibc/patch/20260421090020.59726-2-l.stelmach@samsung.com/mbox/", "series": [ { "id": 500763, "url": "http://patchwork.ozlabs.org/api/1.1/series/500763/?format=api", "web_url": "http://patchwork.ozlabs.org/project/glibc/list/?series=500763", "date": "2026-04-21T09:00:19", "name": "Use multiple ld.so caches to separate execution environments", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/500763/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2225599/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2225599/checks/", "tags": {}, "headers": { "Return-Path": "<libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "libc-alpha@sourceware.org" ], "Delivered-To": [ "patchwork-incoming@legolas.ozlabs.org", "libc-alpha@sourceware.org" ], "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=samsung.com header.i=@samsung.com header.a=rsa-sha256\n header.s=mail20170921 header.b=HkoLsBnL;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org\n (client-ip=2620:52:6:3111::32; helo=vm01.sourceware.org;\n envelope-from=libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org;\n receiver=patchwork.ozlabs.org)", "sourceware.org;\n\tdkim=pass (1024-bit key,\n unprotected) header.d=samsung.com header.i=@samsung.com header.a=rsa-sha256\n header.s=mail20170921 header.b=HkoLsBnL", "sourceware.org;\n dmarc=pass (p=none dis=none) header.from=samsung.com", "sourceware.org; spf=pass smtp.mailfrom=samsung.com", "server2.sourceware.org;\n arc=none smtp.remote-ip=210.118.77.11" ], "Received": [ "from vm01.sourceware.org (vm01.sourceware.org\n [IPv6:2620:52:6:3111::32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g0GbN6chBz1yGt\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 21 Apr 2026 19:02:56 +1000 (AEST)", "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id D3D2A4BA23F3\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 21 Apr 2026 09:02:54 +0000 (GMT)", "from mailout1.w1.samsung.com (mailout1.w1.samsung.com\n [210.118.77.11])\n by sourceware.org (Postfix) with ESMTPS id DDE074BA23C0\n for <libc-alpha@sourceware.org>; Tue, 21 Apr 2026 09:00:42 +0000 (GMT)", "from eucas1p1.samsung.com (unknown [182.198.249.206])\n by mailout1.w1.samsung.com (KnoxPortal) with ESMTP id\n 20260421090041euoutp01eb7f6e056606edc38c405bd2b991ad01~oU6nY-byP1120211202euoutp01k\n for <libc-alpha@sourceware.org>; Tue, 21 Apr 2026 09:00:41 +0000 (GMT)", "from eusmtip2.samsung.com (unknown [203.254.199.222]) by\n eucas1p1.samsung.com (KnoxPortal) with ESMTPA id\n 20260421090041eucas1p12a3965fc64bade9ff8df4e87c9f1499b~oU6m4c5-s2618026180eucas1p1X;\n Tue, 21 Apr 2026 09:00:41 +0000 (GMT)", "from localhost (unknown [106.120.51.111]) by eusmtip2.samsung.com\n (KnoxPortal) with ESMTPA id\n 20260421090041eusmtip23d08b156ae45c5ed45d4945b996f520c~oU6mvFb8f2178621786eusmtip2K;\n Tue, 21 Apr 2026 09:00:41 +0000 (GMT)" ], "DKIM-Filter": [ "OpenDKIM Filter v2.11.0 sourceware.org D3D2A4BA23F3", "OpenDKIM Filter v2.11.0 sourceware.org DDE074BA23C0", "OpenDKIM Filter v2.11.0 mailout1.w1.samsung.com\n 20260421090041euoutp01eb7f6e056606edc38c405bd2b991ad01~oU6nY-byP1120211202euoutp01k" ], "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org DDE074BA23C0", "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org DDE074BA23C0", "ARC-Seal": "i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1776762043; cv=none;\n b=YCUBfkD9TiMjGDfP61GLh/SZNCv3RVsjbmBwSUijK860r31MDflrj3qloJqThGYSa8PwyVGHCkGVyTSFP99nPQ8Uxg7B5lovaomXTG3U46VcV1hf89ju3Htwm4UFpV/9futEQEUZj+KKUZ3cMeFnN1xYt3kcHTDUx4FvHxe8o3s=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1776762043; c=relaxed/simple;\n bh=BNebEfQu/zDST7xyLWKGcfz7p9FzUBnFJUbCsw5/hqY=;\n h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version;\n b=H4aeCBqMwrbetSLBMRP76cbG2vL+t9EKIal4EQvH4MyHwW2GLl1qllkzZPsaFGtU183sQZt0AE95O16zcufFkytI+Q9lzuhDCIdtykkPk+QSgQ4hF0KPxydpIM/CL6vUOLBiLCieEBuyIS1K5cJ+MzJN4igdKRtWhaq9dlrDqm0=", "ARC-Authentication-Results": "i=1; server2.sourceware.org", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com;\n s=mail20170921; t=1776762041;\n bh=3IGfEMCwGUYWs76U8i25ouLKJ9GOkh1TEMgHBPFgFTM=;\n h=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n b=HkoLsBnL9AoCqRsgiKVJ4R1J4cyFS6WzFWHGr+yDkixYSlZz3Yu1vOzg/ArSv2IDA\n ilJxJCeIGqSNV5wMq558itQpw/ylPZw2FgpUC3voQN9vSIItKvvmDTolxpOpJDdu57\n iQv6wQBdt4QO524qr7t0ksQK0enOpGOSokveWPC0=", "From": "=?utf-8?q?=C5=81ukasz_Stelmach?= <l.stelmach@samsung.com>", "To": "libc-alpha@sourceware.org", "Cc": "j.kryszyn@samsung.com, m.szyprowski@samsung.com, k.lewandowsk@samsung.com,\n cw00.choi@samsung.com, dongkyun.s@samsung.com, sungguk.na@samsung.com,\n Mateusz Moscicki <m.moscicki2@samsung.com>,\n =?utf-8?q?=C5=81ukasz_Stelmach?= <l.stelmach@samsung.com>", "Subject": "[RFC 1/3] dlconf: Add support for config file for ld.so", "Date": "Tue, 21 Apr 2026 11:00:18 +0200", "Message-ID": "<20260421090020.59726-2-l.stelmach@samsung.com>", "X-Mailer": "git-send-email 2.47.3", "In-Reply-To": "<20260421090020.59726-1-l.stelmach@samsung.com>", "MIME-Version": "1.0", "Organization": "Samsung R&D Institute Poland", "Content-Transfer-Encoding": "8bit", "X-CMS-MailID": "20260421090041eucas1p12a3965fc64bade9ff8df4e87c9f1499b", "X-Msg-Generator": "CA", "Content-Type": "text/plain; charset=\"utf-8\"", "X-RootMTR": "20260421090041eucas1p12a3965fc64bade9ff8df4e87c9f1499b", "X-EPHeader": "CA", "X-CMS-RootMailID": "20260421090041eucas1p12a3965fc64bade9ff8df4e87c9f1499b", "References": "<20260421090020.59726-1-l.stelmach@samsung.com>\n <CGME20260421090041eucas1p12a3965fc64bade9ff8df4e87c9f1499b@eucas1p1.samsung.com>", "X-BeenThere": "libc-alpha@sourceware.org", "X-Mailman-Version": "2.1.30", "Precedence": "list", "List-Id": "Libc-alpha mailing list <libc-alpha.sourceware.org>", "List-Unsubscribe": "<https://sourceware.org/mailman/options/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=unsubscribe>", "List-Archive": "<https://sourceware.org/pipermail/libc-alpha/>", "List-Post": "<mailto:libc-alpha@sourceware.org>", "List-Help": "<mailto:libc-alpha-request@sourceware.org?subject=help>", "List-Subscribe": "<https://sourceware.org/mailman/listinfo/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=subscribe>", "Errors-To": "libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org" }, "content": "From: Mateusz Moscicki <m.moscicki2@samsung.com>\n\nConfiguration allows to specify which ld.so.cache file to use when\nlooking for dependencies for a library or binary from a specific path\n(note that setting LD_LIBRARY_PATH or RUNPATH will result in the\nfollowing configuration not being used).\n\nThe configuration is stored in:\n\n /etc/ldconfig.conf\n /etc/ldconfig.conf.d/*\n\nand has a format similar to INI:\n\n [additional_cache]\n cache=/etc/ld-additional.so.cache\n\n [somelib_paths]\n path=/usr/lib/some1/\n\n [somelib_paths:ext]\n path=/usr/lib/some2/\n\n [somelib]\n cache=:additional_cache\n path=:somelib_paths\n isolation=yes\n dlopen_isolation=yes\n dlopen_path=/usr/lib/some1\n dlopen_path=/usr/lib/some2\n\nThe names of the sections are not relevant.\n\n path= - path from which objects are loaded. The name of other section\n can be used after ':'.\n\n cache= - location of the ld.so.cache file, which will be used for\n objects loaded from the path=. The name of the other section can be\n used after ':'.\n\n isolation= - if \"yes\" then the default /etc/ld.so.cache will not be\n used, as well as the default paths (/lib, /usr/lib) to search for\n dependencies. If \"no\" then the default cache and paths will be\n searched if the specified ld.so.cache does not contain dependencies.\n\n dlopen_isolation= - if \"yes\" then object can only dlopen libraries from\n paths specified by \"dlopen_path\"\n\n dlopen_path= - the path from which libraries can be loaded by dlopen()\n when \"dlopen_isolation\" is set\n\n [<section>:ext] - expands the content of the <section>\n\nBoth \"path\", \"cache\", \"dlopen_path\" can occur multiple times.\n\nConfiguration from ldconfig.conf.d/ directory has higher priority than\nldconfig.conf file.\nConfiguration from ldconfig.conf.d/001-ldconfig.conf has higher priority than\nldconfig.conf.d/002-ldconfig.conf\n\nPreparing custom ld.so.cache (current version of the script is prepared\nfor aarch64):\n1. Prepare a file with a list of libraries to be included in the\n resulting ld.so.cache file. For example:\n\n # cat list.txt\n /usr/lib/libc.so.6\n /usr/lib/some1/libsome1.so\n /usr/lib/some2/libsome2.so\n\n2. Run ld_so_cache_maker.py which is located in packaging/ subdirectory:\n\n python3 ld_so_cache_maker.py list.txt ld-additional.so.cache\n\nInstead of searching for configuration files each time and parsing them, a binary\nfile is used in which the configuration is stored as a tree. This makes\nthe search time much shorter.\n\nThe ldconfig.dat path is:\n\n /run/ldconfig.dat\n\nThe file is generated from the configuration files in:\n\n /etc/ldconfig.conf\n /etc/ldconfig.conf.d/*\n\nby:\n\n ld.so --build-ldconfig-dat\n\nThe content of the ldconfig.dat can be displayed by:\n\n ld.so --print-ldconfig-dat\n\nThis is an example output:\n\n root:/> ld.so --print-ldconfig-dat\n /\n | isolation: 0\n | dlopen_isolation: 0\n `-- usr/\n | isolation: 0\n | dlopen_isolation: 0\n `-- lib/\n | isolation: 0\n | dlopen_isolation: 0\n |-- some1/\n | isolation: 1\n | dlopen_isolation: 1\n | caches:\n | `-- /etc/ld-additional.so.cache\n | dlopen_paths:\n | |-- /usr/lib/some1\n | `-- /usr/lib/some2\n `-- some2/\n isolation: 1\n dlopen_isolation: 1\n caches:\n `-- /etc/ld-additional.so.cache\n dlopen_paths:\n |-- /usr/lib/some1\n `-- /usr/lib/some2\n root:~>\n\nSigned-off-by: Mateusz Mościcki <m.moscicki2@samsung.com>\nSigned-off-by: Łukasz Stelmach <l.stelmach@samsung.com>\n---\n config.h.in | 9 +\n config.make.in | 4 +\n configure | 58 +\n configure.ac | 28 +\n elf/Makefile | 9 +\n elf/dl-cache.c | 192 +-\n elf/dl-load.c | 47 +-\n elf/dl-main.h | 3 +\n elf/dl-open.c | 12 +\n elf/dl-usage.c | 9 +-\n elf/dlconf-getexecutable.c | 20 +\n elf/dlconf-print.c | 265 ++\n elf/dlconf.c | 2441 +++++++++++++++++\n elf/dlconf.h | 175 ++\n elf/rtld.c | 37 +\n scripts/ld_so_cache_maker.py | 76 +\n .../unix/sysv/linux/dlconf-getexecutable.c | 61 +\n 17 files changed, 3372 insertions(+), 74 deletions(-)\n create mode 100644 elf/dlconf-getexecutable.c\n create mode 100644 elf/dlconf-print.c\n create mode 100644 elf/dlconf.c\n create mode 100644 elf/dlconf.h\n create mode 100644 scripts/ld_so_cache_maker.py\n create mode 100644 sysdeps/unix/sysv/linux/dlconf-getexecutable.c", "diff": "diff --git a/config.h.in b/config.h.in\nindex f495f11244..0c82d6dfb6 100644\n--- a/config.h.in\n+++ b/config.h.in\n@@ -100,6 +100,15 @@\n include/libc-symbols.h that avoid PLT slots in the shared objects. */\n #undef\tNO_RTLD_HIDDEN\n \n+/* Define this to enable dynamic loader configuration support. */\n+#undef DLCONF\n+\n+/* Define this to specify ldconfig.dat file path. */\n+#undef DLCONF_DAT_PATH\n+\n+/* Define the dlconf configuration path. */\n+#undef DLCONF_CONF_PATH\n+\n /* Define this to disable lazy relocations in DSOs. */\n #undef\tBIND_NOW\n \ndiff --git a/config.make.in b/config.make.in\nindex 36096881b7..48aa44b522 100644\n--- a/config.make.in\n+++ b/config.make.in\n@@ -27,6 +27,10 @@ multidir= @libc_cv_multidir@\n # Should we use and build ldconfig?\n use-ldconfig = @use_ldconfig@\n \n+dlconf = @dlconf@\n+dlconf_ldconfig_dat_path = @dlconf_ldconfig_dat_path@\n+dlconf_conf_path = @dlconf_conf_path@\n+\n # Maybe the `ldd' script must be rewritten.\n ldd-rewrite-script = @ldd_rewrite_script@\n \ndiff --git a/configure b/configure\nindex 1d543548cd..f47efd505a 100755\n--- a/configure\n+++ b/configure\n@@ -697,6 +697,7 @@ memory_tagging\n enable_werror\n force_install\n bindnow\n+dlconf\n hardcoded_path_in_tests\n enable_timezone_tools\n man_pages_version\n@@ -791,6 +792,9 @@ enable_default_pie\n enable_timezone_tools\n enable_hardcoded_path_in_tests\n enable_hidden_plt\n+enable_dlconf\n+with_dlconf_ldconfig_dat_path\n+with_dlconf_conf_path\n enable_bind_now\n enable_stack_protector\n enable_static_nss\n@@ -1460,6 +1464,7 @@ Optional Features:\n hardcode newly built glibc path in tests\n [default=no]\n --disable-hidden-plt do not hide internal function calls to avoid PLT\n+ --enable-dlconf dynamic loader configuration [default=no]\n --enable-bind-now disable lazy relocations in DSOs\n --enable-stack-protector=[yes|no|all|strong]\n Use -fstack-protector[-all|-strong] to detect glibc\n@@ -1512,6 +1517,12 @@ Optional Packages:\n specify an integer to scale the timeout\n --with-man-pages=VERSION\n tie manual to a specific man-pages version\n+ --with-dlconf-ldconfig-dat-path=PATH\n+ Path to the ldconfig.dat file (default:\n+ /run/ldconfig.dat)\n+ --with-dlconf-conf-path=PATH\n+ Path to the ldconfig.conf file (default:\n+ /etc/ldconfig.conf)\n --with-cpu=CPU select code for CPU variant\n \n Some influential environment variables:\n@@ -4559,6 +4570,53 @@ if test \"x$hidden\" = xno; then\n \n fi\n \n+# Check whether --enable-dlconf was given.\n+if test ${enable_dlconf+y}\n+then :\n+ enableval=$enable_dlconf; dlconf=$enableval\n+else case e in #(\n+ e) dlconf=no ;;\n+esac\n+fi\n+\n+\n+if test \"x$dlconf\" = xyes; then\n+ printf \"%s\\n\" \"#define DLCONF 1\" >>confdefs.h\n+\n+fi\n+\n+\n+# Check whether --with-dlconf-ldconfig-dat-path was given.\n+if test ${with_dlconf_ldconfig_dat_path+y}\n+then :\n+ withval=$with_dlconf_ldconfig_dat_path; dlconf_ldconfig_dat_path=$withval\n+else case e in #(\n+ e) dlconf_ldconfig_dat_path=\"/run/ldconfig.dat\" ;;\n+esac\n+fi\n+\n+if test \"x$dlconf_ldconfig_dat_path\" != \"x\"; then\n+\n+printf \"%s\\n\" \"#define DLCONF_DAT_PATH \\\"$dlconf_ldconfig_dat_path\\\"\" >>confdefs.h\n+\n+fi\n+\n+\n+# Check whether --with-dlconf-conf-path was given.\n+if test ${with_dlconf_conf_path+y}\n+then :\n+ withval=$with_dlconf_conf_path; dlconf_conf_path=$withval\n+else case e in #(\n+ e) dlconf_conf_path=\"/etc/ldconfig.conf\" ;;\n+esac\n+fi\n+\n+if test \"x$dlconf_conf_path\" != \"x\"; then\n+\n+printf \"%s\\n\" \"#define DLCONF_CONF_PATH \\\"$dlconf_conf_path\\\"\" >>confdefs.h\n+\n+fi\n+\n # Check whether --enable-bind-now was given.\n if test ${enable_bind_now+y}\n then :\ndiff --git a/configure.ac b/configure.ac\nindex 9cbc0bf68f..0663b6b9d4 100644\n--- a/configure.ac\n+++ b/configure.ac\n@@ -236,6 +236,34 @@ if test \"x$hidden\" = xno; then\n AC_DEFINE(NO_HIDDEN)\n fi\n \n+AC_ARG_ENABLE([dlconf],\n+\t AS_HELP_STRING([--enable-dlconf],\n+\t\t\t [dynamic loader configuration @<:@default=no@:>@]),\n+\t [dlconf=$enableval],\n+\t [dlconf=no])\n+AC_SUBST(dlconf)\n+if test \"x$dlconf\" = xyes; then\n+ AC_DEFINE(DLCONF)\n+fi\n+\n+AC_ARG_WITH([dlconf-ldconfig-dat-path],\n+ [AS_HELP_STRING([--with-dlconf-ldconfig-dat-path=PATH], [Path to the ldconfig.dat file (default: /run/ldconfig.dat)])],\n+ [dlconf-ldconfig-dat-path=$withval],\n+ [dlconf-ldconfig-dat-path=\"/run/ldconfig.dat\"])\n+\n+if test \"x$with_dlconf_ldconfig_dat_path\" != \"x\"; then\n+ AC_DEFINE_UNQUOTED([DLCONF_DAT_PATH], [\"$with_dlconf_ldconfig_dat_path\"], [Path to the ldconfig.dat file (default: /run/ldconfig.dat)])\n+fi\n+\n+AC_ARG_WITH([dlconf-conf-path],\n+ [AS_HELP_STRING([--with-dlconf-conf-path=PATH], [Path to the ldconfig.conf file (default: /etc/ldconfig.conf)])],\n+ [dlconf-conf-path=$withval],\n+ [dlconf-conf-path=\"/etc/ldconfig.conf\"])\n+\n+if test \"x$with_dlconf_conf_path\" != \"x\"; then\n+ AC_DEFINE_UNQUOTED([DLCONF_DAT_PATH], [\"$with_dlconf_conf_path\"], [Path to the ldconfig.conf file (default: /etc/ldconfig.conf)])\n+fi\n+\n AC_ARG_ENABLE([bind-now],\n \t AS_HELP_STRING([--enable-bind-now],\n \t\t\t [disable lazy relocations in DSOs]),\ndiff --git a/elf/Makefile b/elf/Makefile\nindex a3475f3fb5..a86775202e 100644\n--- a/elf/Makefile\n+++ b/elf/Makefile\n@@ -88,6 +88,11 @@ dl-routines = \\\n \n ifeq (yes,$(use-ldconfig))\n dl-routines += dl-cache\n+ifeq (yes, $(dlconf))\n+dl-routines += dlconf\n+dl-routines += dlconf-print\n+dl-routines += dlconf-getexecutable\n+endif\n endif\n \n ifeq (yesyes,$(build-shared)$(run-built-tests))\n@@ -1468,6 +1473,10 @@ CFLAGS-ldconfig.c += $(SYSCONF-FLAGS) -D'LIBDIR=\"$(libdir)\"' \\\n \t\t -D'SLIBDIR=\"$(slibdir)\"'\n libof-ldconfig = ldconfig\n CFLAGS-dl-cache.c += $(SYSCONF-FLAGS)\n+ifeq (yes, $(dlconf))\n+CFLAGS-dlconf.c += $(SYSCONF-FLAGS)\n+CFLAGS-dlconf-print.c += $(SYSCONF-FLAGS)\n+endif\n CFLAGS-cache.c += $(SYSCONF-FLAGS)\n CFLAGS-rtld.c += $(SYSCONF-FLAGS)\n CFLAGS-dl-usage.c += $(SYSCONF-FLAGS) \\\ndiff --git a/elf/dl-cache.c b/elf/dl-cache.c\nindex 7c7dc58745..c290659614 100644\n--- a/elf/dl-cache.c\n+++ b/elf/dl-cache.c\n@@ -27,41 +27,59 @@\n #include <dl-hwcaps.h>\n #include <dl-isa-level.h>\n \n-/* This is the starting address and the size of the mmap()ed file. */\n-static struct cache_file *cache;\n-static struct cache_file_new *cache_new;\n-static size_t cachesize;\n-\n+#ifdef DLCONF\n+#include \"dlconf.h\"\n+\n+#else\n+/* Common cache structure used in both standard and DLCONF modes.\n+ In standard mode, we use a single static instance.\n+ In DLCONF mode, we use a linked list of caches. */\n+struct caches\n+{\n+ char *cache_name;\n+ struct cache_file *cache;\n+ struct cache_file_new *cache_new;\n+ size_t cachesize;\n+ struct caches *next;\n #ifdef SHARED\n /* This is used to cache the priorities of glibc-hwcaps\n subdirectories. The elements of _dl_cache_priorities correspond to\n the strings in the cache_extension_tag_glibc_hwcaps section. */\n-static uint32_t *glibc_hwcaps_priorities;\n-static uint32_t glibc_hwcaps_priorities_length;\n-static uint32_t glibc_hwcaps_priorities_allocated;\n+ uint32_t *glibc_hwcaps_priorities;\n+ uint32_t glibc_hwcaps_priorities_length;\n+ uint32_t glibc_hwcaps_priorities_allocated;\n+\n+ /* True if the full malloc was used to allocated the array. */\n+ bool glibc_hwcaps_priorities_malloced;\n+#endif /* SHARED */\n+};\n \n-/* True if the full malloc was used to allocated the array. */\n-static bool glibc_hwcaps_priorities_malloced;\n+/* In standard mode, we have a single global cache instance. */\n+static struct caches global_cache;\n+#endif /* DLCONF */\n \n+#ifdef SHARED\n /* Deallocate the glibc_hwcaps_priorities array. */\n static void\n-glibc_hwcaps_priorities_free (void)\n+glibc_hwcaps_priorities_free (struct caches *cache)\n {\n /* When the minimal malloc is in use, free does not do anything,\n so it does not make sense to call it. */\n- if (glibc_hwcaps_priorities_malloced)\n- free (glibc_hwcaps_priorities);\n- glibc_hwcaps_priorities = NULL;\n- glibc_hwcaps_priorities_allocated = 0;\n+ if (cache->glibc_hwcaps_priorities_malloced)\n+ free (cache->glibc_hwcaps_priorities);\n+ cache->glibc_hwcaps_priorities = NULL;\n+ cache->glibc_hwcaps_priorities_allocated = 0;\n }\n \n /* Ordered comparison of a hwcaps string from the cache on the left\n (identified by its string table index) and a _dl_hwcaps_priorities\n element on the right. */\n static int\n-glibc_hwcaps_compare (uint32_t left_index, struct dl_hwcaps_priority *right)\n+glibc_hwcaps_compare (uint32_t left_index, struct dl_hwcaps_priority *right,\n+\t\t struct caches *cache)\n {\n- const char *left_name = (const char *) cache + left_index;\n+ struct cache_file *cache_file = cache->cache;\n+ const char *left_name = (const char *) cache_file + left_index;\n uint32_t left_name_length = strlen (left_name);\n uint32_t to_compare;\n if (left_name_length < right->name_length)\n@@ -82,17 +100,21 @@ glibc_hwcaps_compare (uint32_t left_index, struct dl_hwcaps_priority *right)\n /* Initialize the glibc_hwcaps_priorities array and its length,\n glibc_hwcaps_priorities_length. */\n static void\n-glibc_hwcaps_priorities_init (void)\n+glibc_hwcaps_priorities_init (struct caches *cache)\n {\n+ struct cache_file *cache_file = cache->cache;\n+ struct cache_file_new *cache_new = cache->cache_new;\n+ size_t cachesize = cache->cachesize;\n+\n struct cache_extension_all_loaded ext;\n- if (!cache_extension_load (cache_new, cache, cachesize, &ext))\n+ if (!cache_extension_load (cache_new, cache_file, cachesize, &ext))\n return;\n \n uint32_t length = (ext.sections[cache_extension_tag_glibc_hwcaps].size\n \t\t / sizeof (uint32_t));\n- if (length > glibc_hwcaps_priorities_allocated)\n+ if (length > cache->glibc_hwcaps_priorities_allocated)\n {\n- glibc_hwcaps_priorities_free ();\n+ glibc_hwcaps_priorities_free (cache);\n \n uint32_t *new_allocation = malloc (length * sizeof (uint32_t));\n if (new_allocation == NULL)\n@@ -100,9 +122,9 @@ glibc_hwcaps_priorities_init (void)\n \t errors. */\n \treturn;\n \n- glibc_hwcaps_priorities = new_allocation;\n- glibc_hwcaps_priorities_allocated = length;\n- glibc_hwcaps_priorities_malloced = __rtld_malloc_is_complete ();\n+ cache->glibc_hwcaps_priorities = new_allocation;\n+ cache->glibc_hwcaps_priorities_allocated = length;\n+ cache->glibc_hwcaps_priorities_malloced = __rtld_malloc_is_complete ();\n }\n \n /* Compute the priorities for the subdirectories by merging the\n@@ -111,13 +133,13 @@ glibc_hwcaps_priorities_init (void)\n const uint32_t *left_end = left + length;\n struct dl_hwcaps_priority *right = _dl_hwcaps_priorities;\n struct dl_hwcaps_priority *right_end = right + _dl_hwcaps_priorities_length;\n- uint32_t *result = glibc_hwcaps_priorities;\n+ uint32_t *result = cache->glibc_hwcaps_priorities;\n \n while (left < left_end && right < right_end)\n {\n if (*left < cachesize)\n \t{\n-\t int cmp = glibc_hwcaps_compare (*left, right);\n+\t int cmp = glibc_hwcaps_compare (*left, right, cache);\n \t if (cmp == 0)\n \t {\n \t *result = right->priority;\n@@ -147,24 +169,24 @@ glibc_hwcaps_priorities_init (void)\n ++left;\n }\n \n- glibc_hwcaps_priorities_length = length;\n+ cache->glibc_hwcaps_priorities_length = length;\n }\n \n /* Return the priority of the cache_extension_tag_glibc_hwcaps section\n entry at INDEX. Zero means do not use. Otherwise, lower values\n indicate greater preference. */\n static uint32_t\n-glibc_hwcaps_priority (uint32_t index)\n+glibc_hwcaps_priority (uint32_t index, struct caches *cache)\n {\n /* This does not need to repeated initialization attempts because\n this function is only called if there is glibc-hwcaps data in the\n cache, so the first call initializes the glibc_hwcaps_priorities\n array. */\n- if (glibc_hwcaps_priorities_length == 0)\n- glibc_hwcaps_priorities_init ();\n+ if (cache->glibc_hwcaps_priorities_length == 0)\n+ glibc_hwcaps_priorities_init (cache);\n \n- if (index < glibc_hwcaps_priorities_length)\n- return glibc_hwcaps_priorities[index];\n+ if (index < cache->glibc_hwcaps_priorities_length)\n+ return cache->glibc_hwcaps_priorities[index];\n else\n return 0;\n }\n@@ -195,7 +217,7 @@ _dl_cache_file_entry (const struct file_entry *libs, size_t entry_size,\n static const char *\n search_cache (const char *string_table, uint32_t string_table_size,\n \t struct file_entry *libs, uint32_t nlibs, uint32_t entry_size,\n-\t const char *name)\n+\t const char *name, struct caches *cache)\n {\n int left = 0;\n int right = nlibs - 1;\n@@ -293,7 +315,7 @@ search_cache (const char *string_table, uint32_t string_table_size,\n \t\t if (named_hwcap)\n \t\t\t{\n \t\t\t uint32_t entry_priority\n-\t\t\t = glibc_hwcaps_priority (libnew->hwcap);\n+\t\t\t = glibc_hwcaps_priority (libnew->hwcap, cache);\n \t\t\t if (entry_priority == 0)\n \t\t\t /* Not usable at all. Skip. */\n \t\t\t continue;\n@@ -382,17 +404,29 @@ _dl_cache_libcmp (const char *p1, const char *p2)\n may be unmapped at any time by a completing recursive dlopen and\n this function must take care that it does not return references to\n any data in the mapping. */\n+#ifdef DLCONF\n+char *\n+dlconf_load_cache_lookup_from (const char *name, const char *cache_path)\n+#else\n char *\n _dl_load_cache_lookup (const char *name)\n+#endif /* DLCONF */\n {\n+#ifdef DLCONF\n+ struct caches *cache = dlconf_find_cache(cache_path);\n+#else\n+ struct caches *cache = &global_cache;\n+ const char *cache_path = LD_SO_CACHE;\n+#endif /* DLCONF */\n+\n /* Print a message if the loading of libs is traced. */\n if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))\n- _dl_debug_printf (\" search cache=%s\\n\", LD_SO_CACHE);\n+ _dl_debug_printf (\" search cache=%s\\n\", cache_path);\n \n- if (cache == NULL)\n+ if (cache->cache == NULL)\n {\n /* Read the contents of the file. */\n- void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &cachesize,\n+ void *file = _dl_sysdep_read_whole_file (cache_path, &cache->cachesize,\n \t\t\t\t\t PROT_READ);\n \n /* We can handle three different cache file formats here:\n@@ -400,84 +434,86 @@ _dl_load_cache_lookup (const char *name)\n \t - the old libc5/glibc2.0/2.1 format\n \t - the old format with the new format in it\n \t The following checks if the cache contains any of these formats. */\n- if (file != MAP_FAILED && cachesize > sizeof *cache_new\n+ if (file != MAP_FAILED && cache->cachesize > sizeof *(cache->cache_new)\n \t && memcmp (file, CACHEMAGIC_VERSION_NEW,\n \t\t sizeof CACHEMAGIC_VERSION_NEW - 1) == 0\n \t /* Check for corruption, avoiding overflow. */\n-\t && ((cachesize - sizeof *cache_new) / sizeof (struct file_entry_new)\n+\t && ((cache->cachesize - sizeof *(cache->cache_new))\n+\t / sizeof (struct file_entry_new)\n \t >= ((struct cache_file_new *) file)->nlibs))\n \t{\n \t if (! cache_file_new_matches_endian (file))\n \t {\n-\t __munmap (file, cachesize);\n+\t __munmap (file, cache->cachesize);\n \t file = (void *) -1;\n \t }\n-\t cache_new = file;\n-\t cache = file;\n+\t cache->cache_new = file;\n+\t cache->cache = file;\n \t}\n- else if (file != MAP_FAILED && cachesize > sizeof *cache\n+ else if (file != MAP_FAILED && cache->cachesize > sizeof *(cache->cache)\n \t && memcmp (file, CACHEMAGIC, sizeof CACHEMAGIC - 1) == 0\n \t /* Check for corruption, avoiding overflow. */\n-\t && ((cachesize - sizeof *cache) / sizeof (struct file_entry)\n+\t && ((cache->cachesize - sizeof *(cache->cache))\n+\t\t / sizeof (struct file_entry)\n \t\t >= ((struct cache_file *) file)->nlibs))\n \t{\n \t size_t offset;\n \t /* Looks ok. */\n-\t cache = file;\n+\t cache->cache = file;\n \n \t /* Check for new version. */\n \t offset = ALIGN_CACHE (sizeof (struct cache_file)\n-\t\t\t\t+ cache->nlibs * sizeof (struct file_entry));\n+\t\t\t\t+ cache->cache->nlibs * sizeof (struct file_entry));\n \n-\t cache_new = (struct cache_file_new *) ((void *) cache + offset);\n-\t if (cachesize < (offset + sizeof (struct cache_file_new))\n-\t || memcmp (cache_new->magic, CACHEMAGIC_VERSION_NEW,\n+\t cache->cache_new = (struct cache_file_new *) ((void *) cache->cache + offset);\n+\t if (cache->cachesize < (offset + sizeof (struct cache_file_new))\n+\t || memcmp (cache->cache_new->magic, CACHEMAGIC_VERSION_NEW,\n \t\t\t sizeof CACHEMAGIC_VERSION_NEW - 1) != 0)\n-\t cache_new = (void *) -1;\n+\t cache->cache_new = (void *) -1;\n \t else\n \t {\n-\t if (! cache_file_new_matches_endian (cache_new))\n+\t if (! cache_file_new_matches_endian (cache->cache_new))\n \t\t{\n \t\t /* The old-format part of the cache is bogus as well\n \t\t if the endianness does not match. (But it is\n \t\t unclear how the new header can be located if the\n \t\t endianness does not match.) */\n-\t\t cache = (void *) -1;\n-\t\t cache_new = (void *) -1;\n-\t\t __munmap (file, cachesize);\n+\t\t cache->cache = (void *) -1;\n+\t\t cache->cache_new = (void *) -1;\n+\t\t __munmap (file, cache->cachesize);\n \t\t}\n \t }\n \t}\n else\n \t{\n \t if (file != MAP_FAILED)\n-\t __munmap (file, cachesize);\n-\t cache = (void *) -1;\n+\t __munmap (file, cache->cachesize);\n+\t cache->cache = (void *) -1;\n \t}\n \n- assert (cache != NULL);\n+ assert (cache->cache != NULL);\n }\n \n- if (cache == (void *) -1)\n+ if (cache->cache == (void *) -1)\n /* Previously looked for the cache file and didn't find it. */\n return NULL;\n \n const char *best;\n- if (cache_new != (void *) -1)\n+ if (cache->cache_new != (void *) -1)\n {\n- const char *string_table = (const char *) cache_new;\n- best = search_cache (string_table, cachesize,\n-\t\t\t &cache_new->libs[0].entry, cache_new->nlibs,\n-\t\t\t sizeof (cache_new->libs[0]), name);\n+ const char *string_table = (const char *) cache->cache_new;\n+ best = search_cache (string_table, cache->cachesize,\n+\t\t\t &cache->cache_new->libs[0].entry, cache->cache_new->nlibs,\n+\t\t\t sizeof (cache->cache_new->libs[0]), name, cache);\n }\n else\n {\n- const char *string_table = (const char *) &cache->libs[cache->nlibs];\n+ const char *string_table = (const char *) &cache->cache->libs[cache->cache->nlibs];\n uint32_t string_table_size\n-\t= (const char *) cache + cachesize - string_table;\n+\t= (const char *) cache->cache + cache->cachesize - string_table;\n best = search_cache (string_table, string_table_size,\n-\t\t\t &cache->libs[0], cache->nlibs,\n-\t\t\t sizeof (cache->libs[0]), name);\n+\t\t\t &cache->cache->libs[0], cache->cache->nlibs,\n+\t\t\t sizeof (cache->cache->libs[0]), name, cache);\n }\n \n /* Print our result if wanted. */\n@@ -499,6 +535,14 @@ _dl_load_cache_lookup (const char *name)\n return __strdup (temp);\n }\n \n+#ifdef DLCONF\n+char *\n+_dl_load_cache_lookup (const char *name)\n+{\n+ return dlconf_load_cache_lookup_from(name, LD_SO_CACHE);\n+}\n+#endif /* DLCONF */\n+\n #ifndef MAP_COPY\n /* If the system does not support MAP_COPY we cannot leave the file open\n all the time since this would create problems when the file is replaced.\n@@ -507,14 +551,18 @@ _dl_load_cache_lookup (const char *name)\n void\n _dl_unload_cache (void)\n {\n- if (cache != NULL && cache != (struct cache_file *) -1)\n+#ifdef DLCONF\n+ dlconf_unload_cache();\n+#else\n+ if (global_cache.cache != NULL && global_cache.cache != (struct cache_file *) -1)\n {\n- __munmap (cache, cachesize);\n- cache = NULL;\n+ __munmap (global_cache.cache, global_cache.cachesize);\n+ global_cache.cache = NULL;\n }\n #ifdef SHARED\n /* This marks the glibc_hwcaps_priorities array as out-of-date. */\n- glibc_hwcaps_priorities_length = 0;\n+ global_cache.glibc_hwcaps_priorities_length = 0;\n #endif\n+#endif /* DLCONF */\n }\n-#endif\n+#endif\n\\ No newline at end of file\ndiff --git a/elf/dl-load.c b/elf/dl-load.c\nindex 8a89b71016..997e1f0d1d 100644\n--- a/elf/dl-load.c\n+++ b/elf/dl-load.c\n@@ -76,6 +76,11 @@ struct filebuf\n #include <not-cancel.h>\n \n #include <endian.h>\n+\n+#ifdef DLCONF\n+#include \"dlconf.h\"\n+#endif /* DLCONF */\n+\n #if BYTE_ORDER == BIG_ENDIAN\n # define byteorder ELFDATA2MSB\n #elif BYTE_ORDER == LITTLE_ENDIAN\n@@ -1911,6 +1916,33 @@ _dl_map_object (struct link_map *loader, const char *name,\n assert (nsid >= 0);\n assert (nsid < GL(dl_nns));\n \n+#ifdef DLCONF\n+ bool try_default = true;\n+ char full_path_buff[PATH_MAX];\n+ const char *full_path = NULL;\n+\n+ if (loader != NULL)\n+ {\n+ /* In case the object is a main program, we need to get an\n+ absolute path to the binary file. glibc doesn't know the path,\n+ it only have access to argv[0], which might be something like\n+ \"./some_bin\". */\n+ if (loader->l_type == lt_executable\n+\t && DSO_FILENAME (loader->l_name)[0] != '/')\n+\t{\n+\t full_path = dlconf_get_executable_path (full_path_buff,\n+\t\t\t\t\t\t sizeof (full_path_buff));\n+\t}\n+ if (full_path == NULL)\n+\t{\n+\t full_path = DSO_FILENAME (loader->l_name);\n+\t}\n+ }\n+\n+ char *cached = dlconf_get_cached_path (full_path, name, &try_default);\n+#endif /* DLCONF */\n+\n+\n /* Look for this name among those already loaded. */\n for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next)\n {\n@@ -2054,7 +2086,17 @@ _dl_map_object (struct link_map *loader, const char *name,\n \t{\n \t /* Check the list of libraries in the file /etc/ld.so.cache,\n \t for compatibility with Linux's ldconfig program. */\n-\t char *cached = _dl_load_cache_lookup (name);\n+#ifdef DLCONF\n+ if (__glibc_likely(cached == NULL))\n+ {\n+ if (__glibc_likely(try_default))\n+ {\n+ cached = _dl_load_cache_lookup (name);\n+ }\n+ }\n+#else\n+ char *cached = _dl_load_cache_lookup (name);\n+#endif /* DLCONF */\n \n \t if (cached != NULL)\n \t {\n@@ -2106,6 +2148,9 @@ _dl_map_object (struct link_map *loader, const char *name,\n \n /* Finally, try the default path. */\n if (fd == -1\n+#ifdef DLCONF\n+ && try_default\n+#endif /* DLCONF */\n \t && ((l = loader ?: GL(dl_ns)[nsid]._ns_loaded) == NULL\n \t || __glibc_likely (!(l->l_flags_1 & DF_1_NODEFLIB)))\n \t && __rtld_search_dirs.dirs != (void *) -1)\ndiff --git a/elf/dl-main.h b/elf/dl-main.h\nindex 36816f6320..656a32cc06 100644\n--- a/elf/dl-main.h\n+++ b/elf/dl-main.h\n@@ -64,6 +64,9 @@ enum rtld_mode\n {\n rtld_mode_normal, rtld_mode_list, rtld_mode_verify, rtld_mode_trace,\n rtld_mode_list_tunables, rtld_mode_list_diagnostics, rtld_mode_help,\n+#ifdef DLCONF\n+ rtld_mode_build_dlconf_dat, rtld_mode_print_dlconf_dat\n+#endif\n };\n \n /* Aggregated state information extracted from environment variables\ndiff --git a/elf/dl-open.c b/elf/dl-open.c\nindex c378da16c0..70d190a246 100644\n--- a/elf/dl-open.c\n+++ b/elf/dl-open.c\n@@ -41,6 +41,10 @@\n #include <dl-dst.h>\n #include <dl-prop.h>\n \n+#ifdef DLCONF\n+#include \"dlconf.h\"\n+#endif /* DLCONF */\n+\n \n /* We must be careful not to leave us in an inconsistent state. Thus we\n catch any error and re-raise it after cleaning up. */\n@@ -578,6 +582,14 @@ dl_open_worker_begin (void *a)\n args->map = new = _dl_map_object (call_map, file, lt_loaded, 0,\n \t\t\t\t mode | __RTLD_CALLMAP, args->nsid);\n \n+#ifdef DLCONF\n+ const void *caller_dlopen = args->caller_dlopen;\n+ if (!dlconf_allowed_dlopen(new->l_name, caller_dlopen))\n+ {\n+ _dl_signal_error (EINVAL, file, NULL, N_ (\"prohibited loading path\"));\n+ }\n+#endif /* DLCONF */\n+\n /* If the pointer returned is NULL this means the RTLD_NOLOAD flag is\n set and the object is not already loaded. */\n if (new == NULL)\ndiff --git a/elf/dl-usage.c b/elf/dl-usage.c\nindex 5baac4ba8e..28aac90c77 100644\n--- a/elf/dl-usage.c\n+++ b/elf/dl-usage.c\n@@ -198,7 +198,14 @@ setting environment variables (which would be inherited by subprocesses).\\n\\\n --preload LIST preload objects named in LIST\\n\\\n --argv0 STRING set argv[0] to STRING before running\\n\\\n --list-tunables list all tunables with minimum and maximum values\\n\\\n- --list-diagnostics list diagnostics information\\n\\\n+ --list-diagnostics list diagnostics information\\n\"\n+#ifdef DLCONF\n+\"\\\n+ --build-ldconfig-dat build ldconfig.dat file\\n\\\n+ --print-ldconfig-dat print ldconfig.dat file content\\n\\\n+\"\n+#endif\n+ \"\\\n --help display this help and exit\\n\\\n --version output version information and exit\\n\\\n \\n\\\ndiff --git a/elf/dlconf-getexecutable.c b/elf/dlconf-getexecutable.c\nnew file mode 100644\nindex 0000000000..818ba516d0\n--- /dev/null\n+++ b/elf/dlconf-getexecutable.c\n@@ -0,0 +1,20 @@\n+/* Copyright (C) 2026 Samsung Electronics Co., Ltd.\n+\n+ This file is part of the GNU C Library.\n+\n+ The GNU C Library is free software; you can redistribute it and/or\n+ modify it under the terms of the GNU Lesser General Public\n+ License as published by the Free Software Foundation; either\n+ version 2.1 of the License, or (at your option) any later version.\n+\n+ The GNU C Library is distributed in the hope that it will be useful,\n+ but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+ Lesser General Public License for more details.\n+\n+ You should have received a copy of the GNU Lesser General Public\n+ License along with the GNU C Library; if not, see\n+ <https://www.gnu.org/licenses/>. */\n+char *dlconf_get_executable_path(char *buff, size_t bufflen) {\n+ return NULL;\n+}\ndiff --git a/elf/dlconf-print.c b/elf/dlconf-print.c\nnew file mode 100644\nindex 0000000000..ba009e8ef6\n--- /dev/null\n+++ b/elf/dlconf-print.c\n@@ -0,0 +1,265 @@\n+/* Copyright (C) 2026 Samsung Electronics Co., Ltd.\n+\n+ This file is part of the GNU C Library.\n+\n+ The GNU C Library is free software; you can redistribute it and/or\n+ modify it under the terms of the GNU Lesser General Public\n+ License as published by the Free Software Foundation; either\n+ version 2.1 of the License, or (at your option) any later version.\n+\n+ The GNU C Library is distributed in the hope that it will be useful,\n+ but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+ Lesser General Public License for more details.\n+\n+ You should have received a copy of the GNU Lesser General Public\n+ License along with the GNU C Library; if not, see\n+ <https://www.gnu.org/licenses/>. */\n+#include <ldsodefs.h>\n+#include <linux/limits.h>\n+#include <stdlib.h>\n+#include <string.h>\n+#include <sys/types.h>\n+#include <sys/mman.h>\n+#include <sys/stat.h>\n+#include <errno.h>\n+#include <fcntl.h>\n+#include \"dlconf.h\"\n+\n+struct conf_dat_entry_with_children {\n+ struct conf_dat_entry *conf_dat_entry;\n+ char *name;\n+ int32_t children_count;\n+ struct conf_dat_entry_with_children **children;\n+};\n+\n+\n+struct conf_dat_entry_with_children *_read_conf_dat_entries (void *file, int32_t start)\n+{\n+ int32_t *nodes_count;\n+ int32_t *name_offset;\n+ int32_t next_node_start;\n+ struct conf_dat_entry *centry = dlconf_read_one_conf_dat_entry(file, start);\n+\n+ if (centry == NULL)\n+ {\n+ return NULL;\n+ }\n+ struct conf_dat_entry_with_children *centry_wc = malloc(sizeof(*centry_wc));\n+\n+ if (centry_wc == NULL)\n+ {\n+ goto error;\n+ }\n+\n+ nodes_count = (int32_t*)(file + start + DLCONF_CHILD_COUNT_OFFSET);\n+\n+ centry_wc->conf_dat_entry = centry;\n+ centry_wc->children = malloc(sizeof(struct conf_data_entry_with_children*)*(*nodes_count));\n+ if (centry_wc->children == NULL)\n+ {\n+ free(centry_wc);\n+ goto error;\n+ }\n+\n+ centry_wc->children_count = *nodes_count;\n+ name_offset = (int32_t*)(file + start + DLCONF_NAME_START_OFFSET);\n+ centry_wc->name = (char*)(file + *name_offset);\n+ next_node_start = start + DLCONF_NODE_SIZE_IN_BYTES;\n+\n+ for (int32_t i = 0; i < *nodes_count; i++)\n+ {\n+ struct conf_dat_entry_with_children *subnode = _read_conf_dat_entries (file, next_node_start);\n+ centry_wc->children[i] = subnode;\n+ next_node_start += *(int32_t *) (file + next_node_start) * DLCONF_NODE_SIZE_IN_BYTES;\n+ }\n+\n+ return centry_wc;\n+error:\n+ dlconf_free_string_list(centry->caches, false);\n+ dlconf_free_string_list(centry->dlopen_paths, false);\n+ free(centry);\n+ return NULL;\n+}\n+\n+struct conf_dat_entry_with_children *read_conf_dat_entries (void *file)\n+{\n+ struct conf_dat_entry_with_children *node = _read_conf_dat_entries(file, DLCONF_DAT_HEADER_SIZE);\n+\n+ return node;\n+}\n+\n+static void _print_list(const struct string_list *list,\n+ const char *list_name,\n+ const char *prefix,\n+ const char *connector1,\n+ const char *connector2)\n+{\n+ if (list != NULL)\n+ {\n+ _dl_printf(\"%s%s%s%s:\\n\", prefix, connector1, connector2, list_name);\n+ while (list != NULL)\n+ {\n+ bool is_last = list->next == NULL;\n+ const char *mark = is_last ? \"`--\" : \"|--\";\n+ _dl_printf(\"%s%s%s %s %s\\n\", prefix, connector1, connector2, mark, list->string);\n+ list = list->next;\n+ }\n+ }\n+}\n+\n+static bool file_exists(const char *path, bool *file_exists, bool *is_directory)\n+{\n+ *is_directory = false;\n+ *file_exists = false;\n+ struct stat statbuf;\n+\n+ int res = stat(path, &statbuf);\n+\n+ if (res != 0)\n+ {\n+ return errno == ENOENT || errno == ENOTDIR || errno == ENOENT;\n+ }\n+\n+ *file_exists = true;\n+\n+ if (S_ISDIR(statbuf.st_mode))\n+ {\n+ *is_directory = true;\n+ }\n+ return true;\n+}\n+\n+static int _print_conf_dat_tree (const struct conf_dat_entry_with_children *tree, const char *prefix, const char *base_path)\n+{\n+ char full_path[PATH_MAX+1];\n+ int base_path_len = strlen(base_path);\n+ int prefix_len;\n+\n+ strncpy(full_path, base_path, sizeof(full_path) - 1);\n+ if (base_path_len > 0 && base_path[base_path_len - 1] != '/')\n+ {\n+ full_path[base_path_len++] = '/';\n+ }\n+\n+ prefix_len = strlen(prefix);\n+\n+ for (int32_t i = 0; i < tree->children_count; i++)\n+ {\n+ bool is_last = i == tree->children_count - 1;\n+ const char *conn = is_last?\"`-- \" : \"|-- \";\n+ char new_prefix[100];\n+ const char *conn2, *conn3, *addon;\n+ bool f_exists, is_directory;\n+ struct string_list *list;\n+\n+ strncpy(&full_path[base_path_len], tree->children[i]->name, PATH_MAX - base_path_len - 3);\n+ full_path[sizeof(full_path)-1] = '\\0';\n+\n+ strncpy(new_prefix, prefix, sizeof(new_prefix)-1);\n+ conn2 = is_last ? \" \" : \"| \";\n+\n+ strncpy(&new_prefix[prefix_len], conn2, sizeof(new_prefix) - 1 - strlen(new_prefix));\n+\n+ conn3 = tree->children[i]->children_count > 0 ? \"| \" : \" \";\n+\n+ new_prefix[prefix_len + strlen(conn2)] = '\\0';\n+ addon = \"\";\n+ if (file_exists(full_path, &f_exists, &is_directory))\n+ {\n+ if (is_directory)\n+ addon = \"/\";\n+ else if (!f_exists)\n+ addon = \" (not exist)\";\n+ }\n+\n+ _dl_printf(\"%s%s%s%s\\n\", prefix, conn, tree->children[i]->name, addon);\n+ _dl_printf(\"%s%s%sisolation: %d\\n\", prefix, conn2, conn3, tree->children[i]->conf_dat_entry->isolation);\n+ _dl_printf(\"%s%s%sdlopen_isolation: %d\\n\", prefix, conn2, conn3, tree->children[i]->conf_dat_entry->dlopen_isolation);\n+ list = tree->children[i]->conf_dat_entry->caches;\n+ _print_list(list, \"caches\", prefix, conn2, conn3);\n+\n+ list = tree->children[i]->conf_dat_entry->dlopen_paths;\n+ _print_list(list, \"dlopen_paths\", prefix, conn2, conn3);\n+ _print_conf_dat_tree(tree->children[i], new_prefix, full_path);\n+ }\n+\n+ return DLCONF_ERR_OK;\n+}\n+\n+\n+static int print_conf_dat_tree (const struct conf_dat_entry_with_children *centry)\n+{\n+ _dl_printf(\"/\\n\");\n+ _dl_printf(\"| isolation: %d\\n\", centry->conf_dat_entry->isolation);\n+ _dl_printf(\"| dlopen_isolation: %d\\n\", centry->conf_dat_entry->dlopen_isolation);\n+ _print_list(centry->conf_dat_entry->caches, \"caches\", \"| \", \"\", \"\");\n+ _print_list(centry->conf_dat_entry->dlopen_paths, \"dlopen_paths\", \"| \", \"\", \"\");\n+\n+ return _print_conf_dat_tree(centry, \"\", \"/\");\n+}\n+\n+static void free_conf_dat_tree (struct conf_dat_entry_with_children *base_node)\n+{\n+ if (base_node == NULL)\n+ {\n+ return;\n+ }\n+\n+ for (int32_t i = 0; i < base_node->children_count; i++)\n+ {\n+ free_conf_dat_tree(base_node->children[i]);\n+ }\n+\n+ dlconf_free_string_list(base_node->conf_dat_entry->dlopen_paths, false);\n+ dlconf_free_string_list(base_node->conf_dat_entry->caches, false);\n+ free(base_node->conf_dat_entry);\n+ free(base_node->children);\n+ free(base_node);\n+}\n+\n+/*\n+ * Public functions\n+ */\n+\n+void dlconf_print_conf_dat (void)\n+{\n+ struct stat statbuf;\n+ struct conf_dat_entry_with_children *root_node;\n+ int fd = open(DLCONF_DAT_PATH, O_RDONLY);\n+ void *file;\n+\n+ if (fd < 0)\n+ {\n+ return;\n+ }\n+\n+ if (fstat(fd, &statbuf) == -1)\n+ {\n+ close(fd);\n+ return;\n+ }\n+\n+ file = mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);\n+\n+ if (file == MAP_FAILED)\n+ {\n+ close(fd);\n+ return;\n+ }\n+\n+ root_node = read_conf_dat_entries(file);\n+\n+ if (root_node == NULL)\n+ {\n+ goto exit;\n+ }\n+ print_conf_dat_tree(root_node);\n+ free_conf_dat_tree(root_node);\n+ dlconf_free_g_lists();\n+\n+exit:\n+ munmap(file, statbuf.st_size);\n+ close(fd);\n+\n+}\ndiff --git a/elf/dlconf.c b/elf/dlconf.c\nnew file mode 100644\nindex 0000000000..c02d1ec1be\n--- /dev/null\n+++ b/elf/dlconf.c\n@@ -0,0 +1,2441 @@\n+/* Copyright (C) 2026 Samsung Electronics Co., Ltd.\n+\n+ This file is part of the GNU C Library.\n+\n+ The GNU C Library is free software; you can redistribute it and/or\n+ modify it under the terms of the GNU Lesser General Public\n+ License as published by the Free Software Foundation; either\n+ version 2.1 of the License, or (at your option) any later version.\n+\n+ The GNU C Library is distributed in the hope that it will be useful,\n+ but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+ Lesser General Public License for more details.\n+\n+ You should have received a copy of the GNU Lesser General Public\n+ License along with the GNU C Library; if not, see\n+ <https://www.gnu.org/licenses/>. */\n+#include <assert.h>\n+#include <stdlib.h>\n+#include <stdbool.h>\n+#include <search.h>\n+#include <string.h>\n+#include <sys/mman.h>\n+#include <dirent.h>\n+#include <ldsodefs.h>\n+#include <sys/types.h>\n+#include <sys/stat.h>\n+#include <fcntl.h>\n+#include <stddef.h>\n+#include <linux/limits.h>\n+\n+#include \"dlconf.h\"\n+\n+#define write_or_return(fd, data, size) \\\n+ { \\\n+ if (_dl_write(fd, data, size) < 0) \\\n+ return DLCONF_ERR_IO; \\\n+ }\n+\n+#define SECTIONS_CAP 10\n+#define MAX_PATHS_LIMIT 1000\n+\n+/*\n+ * The struct contains data read from ldconfig.conf files.\n+ * Base on the data in this structure will be created ldconfig.dat file.\n+ * We cannot build ldconfig.dat directly from ldconfig.conf, because we need to\n+ * know the lengths of all strings and the paths must be sorted accordingly.\n+ */\n+struct cache_info {\n+ char *path;\n+ struct string_list *dlopen_paths;\n+ struct string_list *caches;\n+ bool isolation;\n+ bool dlopen_isolation;\n+ struct cache_info *next;\n+};\n+\n+/*\n+ * This structure contains data from cache_info stored as a TRIE.\n+ */\n+struct node {\n+ char *name;\n+ int32_t dlopen_paths_idx;\n+ int32_t caches_idx;\n+ int32_t isolation;\n+ int32_t dlopen_isolation;\n+ struct node **children;\n+ int32_t children_count;\n+};\n+\n+static void *conf_data = NULL;\n+static size_t conf_data_size = 0;\n+struct hsearch_data path_htab;\n+\n+struct string_list_array {\n+ struct string_list **elements;\n+ int32_t count;\n+};\n+\n+static struct string_list_array g_caches = { NULL, 0 };\n+static struct string_list_array g_dlopen_paths = { NULL, 0 };\n+\n+static struct caches *cache_list = NULL;\n+\n+static void *dlconf_realloc(void *ptr, size_t new_size, size_t old_size)\n+{\n+ void *old_ptr;\n+\n+ if (ptr == NULL)\n+ {\n+ ptr = malloc (new_size);\n+ return ptr;\n+ }\n+\n+ old_ptr = ptr;\n+ ptr = malloc(new_size);\n+\n+ if (ptr == NULL)\n+ {\n+ free (old_ptr);\n+ return NULL;\n+ }\n+\n+ memcpy(ptr, old_ptr, old_size);\n+ free(old_ptr);\n+ return ptr;\n+}\n+\n+struct section_string_list {\n+ char *string;\n+ struct section *section;\n+ struct section_string_list *next;\n+};\n+\n+enum s_type {\n+ ST_NORMAL = 0,\n+ ST_EXTEND\n+};\n+\n+struct section {\n+ char *name;\n+ enum s_type section_type;\n+ struct section_string_list *paths;\n+ struct section_string_list *caches;\n+ struct section_string_list *dlopen_paths;\n+ bool isolation;\n+ bool dlopen_isolation;\n+ bool processing; /* Field for cycle detection */\n+ bool removed; /* Field for \"fast\" remove section from array */\n+};\n+\n+struct sections {\n+ struct section **sections;\n+ size_t capacity;\n+ size_t count;\n+};\n+\n+static void free_section(struct section *section)\n+{\n+ if (section == NULL)\n+ {\n+ return;\n+ }\n+\n+ free(section->name);\n+ free(section);\n+}\n+\n+static void free_sections(struct sections *sections)\n+{\n+ for (size_t i = 0; i < sections->count; i++)\n+ {\n+ free_section(sections->sections[i]);\n+ }\n+\n+ free(sections->sections);\n+ sections->sections = NULL;\n+ sections->count = 0;\n+ sections->capacity = 0;\n+}\n+\n+void free_section_string_list (struct section_string_list *slist, bool free_string);\n+static struct string_list* copy_string_list(const struct string_list *list);\n+static struct string_list* convert_sec_str_list_to_str_list(struct section_string_list *list)\n+{\n+ struct string_list *new_list = NULL;\n+ struct string_list *prev = NULL;\n+ struct string_list *first_element = NULL;\n+\n+ while (list != NULL)\n+ {\n+ new_list = malloc(sizeof(*new_list));\n+\n+ if (new_list == NULL)\n+ {\n+ _dl_error_printf(\"Memory allocation error\\n\");\n+ goto error;\n+ }\n+\n+ new_list->next = NULL;\n+ new_list->string = strdup(list->string);\n+\n+ if (new_list->string == NULL)\n+ {\n+ _dl_error_printf(\"Memory allocation error\\n\");\n+ goto error;\n+ }\n+\n+ if (prev != NULL)\n+ {\n+ prev->next = new_list;\n+ }\n+\n+ prev = new_list;\n+\n+ if (first_element == NULL)\n+ {\n+ first_element = new_list;\n+ }\n+ list = list->next;\n+ }\n+ return first_element;\n+error:\n+ dlconf_free_string_list(new_list, true);\n+ dlconf_free_string_list (first_element, true);\n+ return NULL;\n+}\n+\n+static int add_to_sorted_list(struct string_list **list, char *element)\n+{\n+ struct string_list *entry = *list;\n+ struct string_list *previous = NULL;\n+\n+ while (entry != NULL)\n+ {\n+ if (strcmp (element, entry->string) < 0)\n+\t{\n+\t break;\n+\t}\n+\n+ previous = entry;\n+ entry = entry->next;\n+ }\n+\n+ struct string_list *new_entry = malloc (sizeof (*new_entry));\n+\n+ if (new_entry == NULL)\n+ {\n+ _dl_error_printf (\"Memory allocation error\\n\");\n+ return DLCONF_ERR_NOMEM;\n+ }\n+\n+ new_entry->string = element;\n+ new_entry->next = entry;\n+\n+ if (previous == NULL)\n+ {\n+ *list = new_entry;\n+ }\n+ else\n+ {\n+ previous->next = new_entry;\n+ }\n+\n+ return DLCONF_ERR_OK;\n+}\n+\n+static void free_centry(struct conf_dat_entry *centry)\n+{\n+ dlconf_free_string_list(centry->caches, false);\n+ dlconf_free_string_list(centry->dlopen_paths, false);\n+ free(centry);\n+}\n+\n+static bool list_eq(const struct string_list *list_a, const struct string_list *list_b) {\n+ while (list_a != NULL && list_b != NULL)\n+ {\n+ if (strcmp(list_a->string, list_b->string) != 0)\n+ {\n+ return false;\n+ }\n+\n+ list_a = list_a->next;\n+ list_b = list_b->next;\n+ }\n+ return list_a == NULL && list_b == NULL;\n+}\n+\n+/*\n+ * The funciton expect \"g_dlopen_paths\" or \"g_caches\"\" as \"g_list\" (global\n+ * list) and a new string list as \"list\" argument. If g_list does ot contain\n+ * \"list\", it will be added.\n+ * The position of the \"list\" in \"g_list\" is returned as the result.\n+ */\n+static int add_to_g_list(struct string_list_array *g_list, const struct string_list *list)\n+{\n+ for (size_t i = 0; i < g_list->count; i++)\n+ {\n+ if (list_eq(list, g_list->elements[i]))\n+ return i;\n+ }\n+\n+ g_list->elements = dlconf_realloc(g_list->elements,\n+ sizeof(*g_list->elements)*(g_list->count+1),\n+ sizeof(*g_list->elements)*(g_list->count));\n+\n+ if (g_list->elements == NULL)\n+ {\n+ _dl_error_printf (\"Memory allocation error\\n\");\n+ return DLCONF_ERR_NOMEM;\n+ }\n+\n+ g_list->elements[g_list->count] = copy_string_list(list);\n+ g_list->count++;\n+\n+ return g_list->count-1;\n+}\n+\n+static struct string_list *get_file_list(const char *path)\n+{\n+ size_t path_len;\n+ struct string_list *files_list = NULL;\n+ struct dirent *entry;\n+ DIR *dir = opendir(path);\n+\n+ if (dir == NULL)\n+ return NULL;\n+\n+ path_len = strlen(path);\n+\n+ while ((entry = readdir (dir)) != NULL)\n+ {\n+ size_t full_path_len;\n+ char *full_path;\n+\n+ if (entry->d_type != DT_REG)\n+\t continue;\n+\n+ full_path_len = path_len + strlen (entry->d_name) + 1;\n+ full_path = malloc (full_path_len + 1);\n+\n+ if (full_path == NULL)\n+\t{\n+\t dlconf_free_string_list (files_list, true);\n+\t files_list = NULL;\n+\t goto exit;\n+\t}\n+\n+ strncpy (full_path, path, full_path_len);\n+ full_path[path_len] = '/';\n+ strncpy (&full_path[path_len + 1], entry->d_name,\n+\t full_path_len - path_len - 1);\n+ full_path[full_path_len] = '\\0';\n+\n+ if (add_to_sorted_list (&files_list, full_path) != DLCONF_ERR_OK)\n+\t{\n+\t dlconf_free_string_list (files_list, true);\n+\t files_list = NULL;\n+\t free (full_path);\n+\t goto exit;\n+\t}\n+ }\n+exit:\n+ closedir(dir);\n+ return files_list;\n+}\n+\n+static void print_cinfo (struct cache_info *cinfo)\n+{\n+ _dl_debug_printf(\"\\ndynamic loader configuration:\\n\");\n+\n+ while (cinfo != NULL)\n+ {\n+ struct string_list *cache;\n+ _dl_debug_printf(\"record:\\n\");\n+ _dl_debug_printf(\" path: %s\\n\", cinfo->path);\n+\n+ cache = cinfo->caches;\n+\n+ while (cache != NULL)\n+ {\n+ _dl_debug_printf(\" cache: %s\\n\", cache->string);\n+ cache = cache->next;\n+ }\n+\n+ _dl_debug_printf(\" isolation: %d\\n\", cinfo->isolation);\n+ _dl_debug_printf(\" dlopen_isolation: %d\\n\", cinfo->dlopen_isolation);\n+ cinfo = cinfo->next;\n+ }\n+\n+ _dl_debug_printf(\"\\n\");\n+}\n+\n+static void free_cache_info (struct cache_info *cinfo)\n+{\n+ if (__glibc_unlikely(cinfo == NULL))\n+ return;\n+\n+ free(cinfo->path);\n+ dlconf_free_string_list(cinfo->caches, true);\n+ free_cache_info(cinfo->next);\n+ free(cinfo);\n+}\n+\n+static int get_line (const char *content, size_t content_len, char *buff, int buff_len)\n+{\n+ static size_t c_pos = 0;\n+ int line_len = 0;\n+ int eof = 0;\n+\n+ if (__glibc_unlikely(content == NULL || buff == NULL || buff_len <= 0))\n+ {\n+ _dl_error_printf(\"Invalid arguments\\n\");\n+ return DLCONF_ERR_INVAL;\n+ }\n+\n+ /* Ignore leading space characters */\n+ while (c_pos < content_len && content[c_pos] == ' ')\n+ c_pos++;\n+\n+ while (line_len < buff_len - 1)\n+ {\n+ if (__glibc_unlikely(c_pos >= content_len))\n+ {\n+ c_pos = 0;\n+ eof = 1;\n+ break;\n+ }\n+ if (__glibc_unlikely(content[c_pos] == '\\n'))\n+ {\n+ c_pos++;\n+ break;\n+ }\n+ buff[line_len] = content[c_pos];\n+ line_len++;\n+ c_pos++;\n+ }\n+\n+ /* Ignore trailing space characters */\n+ while (line_len > 0 && buff[line_len-1] == ' ')\n+ {\n+ line_len--;\n+ }\n+\n+ buff[line_len] = '\\0';\n+ return (eof == 1) ? DLCONF_ERR_IO : line_len;\n+}\n+\n+/*\n+ * This funciton adds a new section_string_list to the beginning of the list.\n+ * So after addition is complete, the list should be reversed. */\n+static int section_string_list_add(char *str, struct section_string_list **list)\n+{\n+ struct section_string_list *str_list;\n+\n+ if (str == NULL || list == NULL)\n+ {\n+ _dl_error_printf(\"Invalid arguments\\n\");\n+ return DLCONF_ERR_INVAL;\n+ }\n+\n+ str_list = malloc(sizeof(*str_list));\n+\n+ if (str_list == NULL)\n+ {\n+ _dl_error_printf(\"Memory allocation error\\n\");\n+ return DLCONF_ERR_NOMEM;\n+ }\n+\n+ str_list->string = strdup(str);\n+\n+ if (str_list->string == NULL)\n+ {\n+ free(str_list);\n+ _dl_error_printf(\"Memory allocation error\\n\");\n+ return DLCONF_ERR_NOMEM;\n+ }\n+\n+ str_list->section = NULL;\n+ str_list->next = NULL;\n+\n+ if (*list == NULL)\n+ {\n+ *list = str_list;\n+ }\n+ else\n+ {\n+ str_list->next = *list;\n+ *list = str_list;\n+ }\n+\n+ return DLCONF_ERR_OK;\n+}\n+\n+static int section_string_list_reverse(struct section_string_list **list)\n+{\n+ struct section_string_list *prev = NULL;\n+ struct section_string_list *current = *list;\n+ struct section_string_list *next = NULL;\n+\n+ if (list == NULL)\n+ {\n+ _dl_error_printf(\"Invalid arguments\\n\");\n+ return DLCONF_ERR_INVAL;\n+ }\n+\n+ while (current != NULL)\n+ {\n+ next = current->next;\n+ current->next = prev;\n+ prev = current;\n+ current = next;\n+ }\n+\n+ *list = prev;\n+ return DLCONF_ERR_OK;\n+}\n+\n+static struct string_list* copy_string_list(const struct string_list *list)\n+{\n+ struct string_list *new = NULL;\n+ struct string_list *prev = NULL;\n+\n+ if (list == NULL)\n+ return NULL;\n+\n+ while (list != NULL)\n+ {\n+ struct string_list *cur = malloc(sizeof(*cur));\n+ if (cur == NULL)\n+ {\n+ dlconf_free_string_list(new, true);\n+ return NULL;\n+ }\n+\n+ cur->string = strdup(list->string);\n+ cur->next = NULL;\n+\n+ if (cur->string == NULL)\n+ {\n+ dlconf_free_string_list(new, true);\n+ free(cur);\n+ return NULL;\n+ }\n+\n+ if (prev == NULL)\n+ new = cur;\n+ else\n+ prev->next = cur;\n+\n+ prev = cur;\n+ list = list->next;\n+ }\n+\n+ return new;\n+}\n+\n+static int get_section_name_and_type(char **raw_name, char **section_name, enum s_type *type)\n+{\n+ char *addon;\n+ int result = DLCONF_ERR_OK;\n+\n+ if (raw_name == NULL || section_name == NULL || type == NULL)\n+ {\n+ _dl_error_printf(\"Invalid arguments\\n\");\n+ return DLCONF_ERR_INVAL;\n+ }\n+\n+ *type = ST_NORMAL;\n+ addon = strchr(*raw_name, ':');\n+\n+ if (addon == NULL)\n+ {\n+ *section_name = *raw_name;\n+ *raw_name = NULL;\n+ }\n+ else\n+ {\n+ char *sname = strndup(*raw_name, addon - *raw_name);\n+\n+ if (sname == NULL)\n+ {\n+ result = DLCONF_ERR_NOMEM;\n+ _dl_error_printf(\"Memory allocation error\\n\");\n+ goto exit;\n+ }\n+\n+ if (strcmp(addon+1, \"ext\") == 0)\n+ *type = ST_EXTEND;\n+\n+ *section_name = sname;\n+ free(*raw_name);\n+ *raw_name = NULL;\n+ }\n+\n+exit:\n+ if (result != DLCONF_ERR_OK)\n+ *section_name = NULL;\n+\n+ return result;\n+}\n+\n+static int add_new_section(struct sections *sections,\n+ char **section_name,\n+ struct section_string_list *paths,\n+ struct section_string_list *caches,\n+ struct section_string_list *dlopen_paths,\n+ bool isolation,\n+ bool dlopen_isolation)\n+{\n+ char *new_section_name = NULL;\n+ enum s_type section_type;\n+ int result = DLCONF_ERR_OK;\n+ struct section *new_section = NULL;\n+\n+ if ((result = get_section_name_and_type(section_name,\n+ &new_section_name,\n+ §ion_type)) != DLCONF_ERR_OK)\n+ {\n+ goto exit;\n+ }\n+\n+ for (size_t i = 0; i < sections->count; i++)\n+ {\n+ if (sections->sections[i]->section_type == ST_NORMAL &&\n+ sections->sections[i]->section_type == section_type &&\n+ strcmp(sections->sections[i]->name, new_section_name) == 0)\n+ {\n+ /* New section with the same name and type. Ignore it. */\n+ _dl_error_printf(\"Duplicated section: %s.\\n\", new_section_name);\n+ result = DLCONF_ERR_OK;\n+ goto exit;\n+ }\n+ }\n+\n+ new_section = malloc(sizeof(*new_section));\n+\n+ if (new_section == NULL)\n+ {\n+ _dl_error_printf(\"Memory allocation error\\n\");\n+ result = DLCONF_ERR_NOMEM;\n+ goto exit;\n+ }\n+\n+ new_section->name = new_section_name;\n+ new_section->section_type = section_type;\n+ new_section->paths = paths;\n+ new_section->caches = caches;\n+ new_section->dlopen_paths = dlopen_paths;\n+ new_section->isolation = isolation;\n+ new_section->dlopen_isolation = dlopen_isolation;\n+ new_section->processing = false;\n+ new_section->removed = false;\n+\n+ if (sections->count >= sections->capacity)\n+ {\n+ sections->sections = dlconf_realloc(sections->sections,\n+ sizeof(*sections->sections)*(sections->capacity + SECTIONS_CAP),\n+ sizeof(*sections->sections)*sections->capacity);\n+ if (sections->sections == NULL)\n+ {\n+ result = DLCONF_ERR_NOMEM;\n+ _dl_error_printf(\"Memory allocation error\\n\");\n+ goto exit;\n+ }\n+ sections->capacity += SECTIONS_CAP;\n+ }\n+ sections->sections[sections->count++] = new_section;\n+\n+exit:\n+ if (result != DLCONF_ERR_OK)\n+ {\n+ if (new_section == NULL)\n+\t{\n+\t /* If `new_section` is NULL, then perhaps is alredy allocated memory\n+\t for `new_section_name`*/\n+\t free (new_section_name);\n+\t}\n+ else\n+\t{\n+ /* If not, the `new_section_name` will be freed with `new_section` */\n+\t free_section (new_section);\n+\t}\n+ }\n+ return result;\n+}\n+\n+static int append_to_the_end(struct section_string_list **base, struct section_string_list *addon)\n+{\n+ if (base == NULL)\n+ {\n+ _dl_error_printf(\"Invalid arguments\\n\");\n+ return DLCONF_ERR_INVAL;\n+ }\n+\n+ if (*base == NULL)\n+ {\n+ *base = addon;\n+ return DLCONF_ERR_OK;\n+ }\n+\n+ struct section_string_list *last = *base;\n+\n+ while (last->next != NULL)\n+ last = last->next;\n+\n+ last->next = addon;\n+ return DLCONF_ERR_OK;\n+}\n+\n+static int merge_sections(struct section *base, struct section *extensions)\n+{\n+ int result;\n+ /*\n+ * Settings from extension are added before settings that were already.\n+ */\n+ if ((result = append_to_the_end(&extensions->caches, base->caches)) != DLCONF_ERR_OK ||\n+ (result = append_to_the_end(&extensions->paths, base->paths)) != DLCONF_ERR_OK ||\n+ (result = append_to_the_end(&extensions->dlopen_paths, base->dlopen_paths)) != DLCONF_ERR_OK)\n+ {\n+ return result;\n+ }\n+\n+ base->caches = extensions->caches;\n+ extensions->caches = NULL;\n+ base->paths = extensions->paths;\n+ extensions->paths = NULL;\n+ base->dlopen_paths = extensions->dlopen_paths;\n+ extensions->dlopen_paths = NULL;\n+\n+ return DLCONF_ERR_OK;\n+}\n+\n+static int apply_extension(struct section *section_a, struct section *section_b)\n+{\n+ int result;\n+ struct section *a, *b;\n+\n+ /* Now we check which sections is BASE and which is the extension.\n+ In case both are extensions, the order doesn't matter. */\n+ if (section_a->section_type == ST_EXTEND)\n+ {\n+ a = section_b;\n+ b = section_a;\n+ }\n+ else\n+ {\n+ a = section_a;\n+ b = section_b;\n+ }\n+\n+ if ((result = merge_sections (a, b)) != DLCONF_ERR_OK)\n+ {\n+ return result;\n+ }\n+\n+ b->removed = true;\n+\n+ return DLCONF_ERR_OK;\n+}\n+\n+struct section *\n+find_section (const char *name, struct sections *sections, size_t *idx)\n+{\n+ size_t bottom = 0;\n+ size_t upper = sections->count - 1;\n+ ssize_t found_idx = -1;\n+\n+ while (bottom <= upper)\n+ {\n+ size_t mid = (bottom + upper) / 2;\n+ struct section *current = sections->sections[mid];\n+ int res = strcmp (name, current->name);\n+\n+ if (res == 0)\n+ {\n+ /* Found matching name - check if it's removed */\n+ if (!current->removed)\n+ {\n+ found_idx = mid;\n+ goto success;\n+ }\n+\n+ /* Search for nearest non-removed section with same name\n+ Check left side first */\n+ for (ssize_t i = mid - 1; i >= 0; i--)\n+ {\n+ if (strcmp (name, sections->sections[i]->name) != 0)\n+ {\n+ break;\n+ }\n+ if (!sections->sections[i]->removed)\n+ {\n+ /* We want most left element, so save current index and keep\n+ looking */\n+ found_idx = i;\n+ }\n+ }\n+\n+ if (found_idx > -1)\n+ goto success;\n+ /* Then check right side */\n+ for (size_t i = mid + 1; i < sections->count; i++)\n+ {\n+ if (strcmp (name, sections->sections[i]->name) != 0)\n+ break;\n+\n+ if (!sections->sections[i]->removed)\n+ {\n+ found_idx = i;\n+ goto success;\n+ }\n+ }\n+ return NULL;\n+ }\n+ else if (res < 0)\n+ {\n+ upper = mid - 1;\n+ }\n+ else\n+ {\n+ bottom = mid + 1;\n+ }\n+ }\n+\n+ return NULL;\n+success:\n+ if (idx != NULL)\n+ *idx = found_idx;\n+\n+ return sections->sections[found_idx];\n+}\n+\n+static int apply_extensions(struct sections *sections)\n+{\n+ if (sections == NULL)\n+ {\n+ _dl_error_printf(\"Invalid arguments\\n\");\n+ return DLCONF_ERR_INVAL;\n+ }\n+ /* Go through all sections and look for section extension like:\n+\n+ [section:ext]\n+ */\n+ for (size_t i = 0; i < sections->count; i++)\n+ {\n+ struct section *cur_section = sections->sections[i];\n+\n+ if (cur_section->removed)\n+ {\n+ continue;\n+ }\n+\n+ for (size_t n = i + 1; n < sections->count; n++)\n+ {\n+ struct section *next_section = sections->sections[n];\n+ /* Ignore removed sections */\n+ if (next_section->removed)\n+ continue;\n+\n+ if (strcmp (cur_section->name, next_section->name) == 0)\n+ {\n+ int result;\n+ if ((result = apply_extension (cur_section, next_section)) != DLCONF_ERR_OK)\n+ return result;\n+ }\n+ else\n+ {\n+ /* Sections are sorted, so if the name is not the same, we can\n+ break inner loop */\n+ break;\n+ }\n+ }\n+ }\n+ return 0;\n+}\n+\n+static void apply_links_string_list(struct section_string_list *list, struct sections *sections)\n+{\n+ while (list != NULL)\n+ {\n+ if (list->section != NULL)\n+ continue; /* already processed */\n+\n+ /* If the string contains a link to another section, here we look for it\n+ and save a pointer */\n+ if (list->string != NULL && list->string[0] == ':')\n+ {\n+ list->section = find_section(&list->string[1], sections, NULL);\n+ }\n+ list = list->next;\n+ }\n+ return;\n+}\n+\n+enum string_list_type {\n+ SLT_PATH = 0,\n+ SLT_CACHE,\n+ SLT_DLOPEN_PATH\n+};\n+\n+static int replace_section_string_list(struct section_string_list *list,\n+ struct section_string_list *new_list)\n+{\n+ struct section_string_list *tmp_list;\n+ struct section_string_list *cur_next_list;\n+\n+ if (list == NULL || new_list == NULL)\n+ {\n+ _dl_error_printf(\"Invalid arguments\\n\");\n+ return DLCONF_ERR_INVAL;\n+ }\n+ /* Free our \":some_section\" string */\n+ free(list->string);\n+ /* and assign the string from the first new_list item */\n+ list->string = new_list->string;\n+ new_list->string = NULL;\n+\n+ /* Remove link to the section, because it is not needed when we copy its\n+ contents */\n+ list->section = NULL;\n+\n+ /* Save the rest of the list */\n+ cur_next_list = list->next;\n+\n+ /* Insert new_list as the next current item, or more precisely, the next\n+ element in the \"new_list\" , because the string from the first element of\n+ the \"new_list\" we copied earlier. We do this because we don't have a\n+ pointer to the predecessor of the \"list\", and we can't do something like\n+\n+ list->prev->next = new_list\n+\n+ so we do:\n+\n+ list->string = new_list->string\n+ list->next = new_list->next\n+ */\n+ list->next = new_list->next;\n+ new_list->next = NULL;\n+\n+ /* Looking for the end of the list */\n+ tmp_list = list;\n+ while (tmp_list->next != NULL)\n+ tmp_list = tmp_list->next;\n+\n+ /* Attach the original rest of the list at the end */\n+ tmp_list->next = cur_next_list;\n+ return DLCONF_ERR_OK;\n+}\n+\n+static int replace_link_with_content(struct section_string_list *list, enum string_list_type type);\n+\n+static int get_linked_data(struct section *section,\n+ enum string_list_type type,\n+ struct section_string_list **out_list)\n+{\n+ int result = DLCONF_ERR_OK;\n+ struct section_string_list *list = NULL;\n+ struct section_string_list *last_element = NULL;\n+\n+ if (section == NULL || out_list == NULL)\n+ {\n+ _dl_error_printf(\"Invalid arguments\\n\");\n+ return DLCONF_ERR_INVAL;\n+ }\n+\n+ if (section->processing)\n+ {\n+ _dl_error_printf(\"Cycle detected in section %s\\n\", section->name);\n+ return DLCONF_ERR_CYCLE;\n+ }\n+\n+ section->processing = true;\n+\n+ switch (type) {\n+ case SLT_PATH:\n+ list = section->paths;\n+ break;\n+ case SLT_CACHE:\n+ list = section->caches;\n+ break;\n+ case SLT_DLOPEN_PATH:\n+ list = section->dlopen_paths;\n+ break;\n+ }\n+\n+ while (list != NULL)\n+ {\n+ struct section_string_list *new_list = NULL;\n+\n+ if (list->section == NULL)\n+ {\n+ new_list = malloc(sizeof(*new_list));\n+\n+ if (new_list == NULL)\n+ {\n+ result = DLCONF_ERR_NOMEM;\n+ goto exit;\n+ }\n+\n+ new_list->next = NULL;\n+ new_list->section = NULL;\n+ new_list->string = strdup(list->string);\n+\n+ if (new_list->string == NULL)\n+ {\n+ result = DLCONF_ERR_NOMEM;\n+ _dl_error_printf(\"Memory allocation error\\n\");\n+ free(new_list);\n+ free_section_string_list(*out_list, true);\n+ goto exit;\n+ }\n+\n+ if (*out_list == NULL)\n+ {\n+ *out_list = new_list;\n+ last_element = new_list;\n+ }\n+ else\n+ {\n+\t /* We want to append new item to the *out_list,\n+\t so we have to find the last element */\n+\t if (last_element == NULL)\n+ {\n+ last_element = *out_list;\n+\n+ while (last_element->next != NULL)\n+ {\n+ last_element = last_element->next;\n+ }\n+ }\n+ last_element->next = new_list;\n+ last_element = new_list;\n+ }\n+ }\n+ else\n+ {\n+ /* Replace the string like \":other_section\" in current \"list\"\n+ The list can looks like:\n+\n+ /some/path1\n+ :other_section <-- here we are\n+ /some/path2\n+ */\n+ if ((result = replace_link_with_content(list, type)) != DLCONF_ERR_OK)\n+ {\n+ goto exit;\n+ }\n+ /* Now the list looks like this:\n+ /some/path1\n+ /some/other_section/path1 <-- here we are\n+ /some/other_section/path2\n+ /some/path2\n+ We want to copy the current item, so we skip taking list->next item. */\n+ continue;\n+ }\n+ list = list->next;\n+ }\n+\n+exit:\n+ section->processing = false;\n+ return result;\n+}\n+\n+static int replace_link_with_content(struct section_string_list *list, enum string_list_type type)\n+{\n+ int result = DLCONF_ERR_OK;\n+ struct section_string_list *new_list = NULL;\n+\n+ if (list == NULL || list->section == NULL)\n+ return result;\n+\n+ /* Get the content of the linked section, and insert it into the \"list\" */\n+ if ((result = get_linked_data(list->section, type, &new_list)) == DLCONF_ERR_OK)\n+ result = replace_section_string_list(list, new_list);\n+\n+ if (new_list != NULL)\n+ free(new_list);\n+\n+ return result;\n+}\n+\n+\n+static int collect_linked_data(struct section_string_list *list,\n+ enum string_list_type type)\n+{\n+ int result = DLCONF_ERR_OK;\n+ /* Go through the list and deal with entries like:\n+\n+ cache=:other_section\n+ */\n+ while (list != NULL)\n+ {\n+ /* At this point, we have pointers to sections, thanks to\n+ apply_links_string_list(), so thats way we check if the list item is\n+ \"normal\" path or link to other section */\n+ if (list->section != NULL)\n+ {\n+ if ((result = replace_link_with_content(list, type)) != DLCONF_ERR_OK)\n+ return result;\n+ }\n+ list = list->next;\n+ }\n+ return DLCONF_ERR_OK;\n+}\n+\n+static int apply_links (struct sections *sections)\n+{\n+ if (sections == NULL)\n+ {\n+ _dl_error_printf(\"Invalid arguments\\n\");\n+ return DLCONF_ERR_INVAL;\n+ }\n+ int result = DLCONF_ERR_OK;\n+ /* Replacing links:\n+\n+ cache=:other_section\n+\n+ is done in two steps:\n+ 1 - sections referenced by the link are searched for and the pointers to them are saved in the list\n+ 2 - recursively retrieve paths from linked sections\n+\n+\n+ First stage - link sections in section_string_list */\n+\n+ for (size_t i = 0; i < sections->count; i++)\n+ {\n+ if (sections->sections[i]->removed)\n+ continue;\n+\n+ struct section *cur_section = sections->sections[i];\n+ apply_links_string_list(cur_section->paths, sections);\n+ apply_links_string_list(cur_section->caches, sections);\n+ apply_links_string_list(cur_section->dlopen_paths, sections);\n+ }\n+\n+ /* Second stage - collect caches, paths, and dlopen_paths */\n+ for (size_t i = 0; i < sections->count; i++)\n+ {\n+ if (sections->sections[i]->removed)\n+ continue;\n+ struct section *cur_section = sections->sections[i];\n+ if ((result = collect_linked_data(cur_section->paths, SLT_PATH)) != DLCONF_ERR_OK)\n+ return result;\n+\n+ if ((result = collect_linked_data(cur_section->caches, SLT_CACHE)) != DLCONF_ERR_OK)\n+ return result;\n+\n+ if ((result = collect_linked_data(cur_section->dlopen_paths, SLT_DLOPEN_PATH)) != DLCONF_ERR_OK)\n+ return result;\n+ }\n+\n+ return result;\n+}\n+\n+static int add_new_cinfo(struct cache_info **cinfo,\n+ struct section_string_list *paths,\n+ struct section_string_list *caches,\n+ struct section_string_list *dlopen_paths,\n+ bool isolation,\n+ bool dlopen_isolation)\n+{\n+ struct section_string_list *l;\n+ const struct section_string_list *cur_path = paths;\n+\n+ if (cinfo == NULL)\n+ return DLCONF_ERR_INVAL;\n+\n+ /* If more paths than one were defined in the section, we need to create more\n+ objects because one cache_info object can store only one path (for later\n+ optimizations). */\n+ while (cur_path != NULL)\n+ {\n+ int done;\n+ struct string_list *caches_new;\n+ struct string_list *dlopen_paths_new;\n+ struct cache_info *c, *prev_c;\n+ struct cache_info *cinfo_new = malloc(sizeof(*cinfo_new));\n+\n+ if (cinfo_new == NULL)\n+ {\n+ _dl_error_printf(\"Memory allocation error\\n\");\n+ return DLCONF_ERR_NOMEM;\n+ }\n+\n+ caches_new = convert_sec_str_list_to_str_list(caches);\n+ dlopen_paths_new = convert_sec_str_list_to_str_list(dlopen_paths);\n+ cinfo_new->path = strdup(cur_path->string);\n+\n+ if (cinfo_new->path == NULL)\n+ {\n+ dlconf_free_string_list(caches_new, true);\n+ dlconf_free_string_list(dlopen_paths_new, true);\n+ free(cinfo_new);\n+ return DLCONF_ERR_NOMEM;\n+ }\n+\n+ cinfo_new->caches = caches_new;\n+ cinfo_new->dlopen_paths = dlopen_paths_new;\n+ cinfo_new->isolation = isolation;\n+ cinfo_new->dlopen_isolation = dlopen_isolation;\n+ cinfo_new->next = NULL;\n+\n+ if (*cinfo == NULL)\n+ {\n+ *cinfo = cinfo_new;\n+ cur_path = cur_path->next;\n+ continue;\n+ }\n+ c = *cinfo;\n+ prev_c = NULL;\n+ done = 0;\n+\n+ /* The new object of type cache_info is inserted into the list in\n+ ascending order. */\n+ while (c != NULL)\n+ {\n+ if (strcmp(cur_path->string, c->path) < 0)\n+ {\n+ cinfo_new->next = c;\n+\n+ if (prev_c == NULL)\n+ *cinfo = cinfo_new;\n+ else\n+ prev_c->next = cinfo_new;\n+\n+ done = 1;\n+ break;\n+ }\n+ prev_c = c;\n+ c = c->next;\n+ }\n+\n+ if (done == 0)\n+ prev_c->next = cinfo_new;\n+\n+ cur_path = cur_path->next;\n+ }\n+\n+ l = paths;\n+\n+ while (l != NULL)\n+ {\n+ struct section_string_list *t = l;\n+ l = l->next;\n+ free(t);\n+ }\n+ return DLCONF_ERR_OK;\n+}\n+\n+static int convert_sections_to_cache_info(struct sections *sections, struct cache_info **cache_info)\n+{\n+ int result = DLCONF_ERR_OK;\n+ for (size_t i = 0; i < sections->count; i++)\n+ {\n+ struct section *section = sections->sections[i];\n+ if (section->removed)\n+ continue;\n+\n+ if ((result = add_new_cinfo(cache_info,\n+ section->paths,\n+ section->caches,\n+ section->dlopen_paths,\n+ section->isolation,\n+ section->dlopen_isolation)) != DLCONF_ERR_OK)\n+ {\n+ goto exit;\n+ }\n+ }\n+exit:\n+ if (result != DLCONF_ERR_OK)\n+ {\n+ free_cache_info(*cache_info);\n+ *cache_info = NULL;\n+ }\n+ return result;\n+}\n+\n+static bool path_already_defined (char *new_path, struct sections *sections)\n+{\n+ char tmp_path[PATH_MAX];\n+ ENTRY item;\n+ item.key = new_path;\n+ item.data = NULL;\n+ ENTRY *out = NULL;\n+\n+ hsearch_r(item, FIND, &out, &path_htab);\n+ if (out != NULL)\n+ return true;\n+\n+ /* Since paths don't have to end with a '/', as it's valid to point to a specific binary,\n+ we need to check whether we already have the path defined both with and without the trailing slash,\n+ to detect that '/some/a' is the same as '/some/a/'. */\n+ size_t new_path_len = strlen (new_path);\n+\n+ if (new_path[new_path_len - 1] == '/')\n+ {\n+ strncpy (tmp_path, new_path, sizeof(tmp_path) - 1);\n+ tmp_path[new_path_len - 1] = '\\0';\n+ }\n+ else\n+ {\n+ strncpy (tmp_path, new_path, sizeof(tmp_path) - 1);\n+ tmp_path[new_path_len] = '/';\n+ tmp_path[new_path_len + 1] = '\\0';\n+ }\n+\n+ item.key = tmp_path;\n+ out = NULL;\n+ hsearch_r(item, FIND, &out, &path_htab);\n+\n+ return out != NULL;\n+}\n+\n+static bool is_positive_value(const char *value, size_t value_len)\n+{\n+ return (strncmp(value, \"yes\", sizeof(\"yes\")) == 0 ||\n+ strncmp(value, \"on\", sizeof(\"on\")) == 0 ||\n+ strncmp(value, \"1\", sizeof(\"1\")) == 0);\n+}\n+\n+static int parse_paraini (const char *content, size_t content_len, struct sections *sections)\n+{\n+ char *section_name = NULL;\n+ struct section_string_list *paths = NULL;\n+ struct section_string_list *caches = NULL;\n+ struct section_string_list *dlopen_paths = NULL;\n+ bool isolation = false;\n+ bool dlopen_isolation = false;\n+\n+ char line[1024];\n+ int line_len;\n+ int find_next_section = 1;\n+ int result = DLCONF_ERR_OK;\n+\n+ if (content == NULL || sections == NULL)\n+ {\n+ _dl_error_printf (\"Invalid arguments\\n\");\n+ return DLCONF_ERR_INVAL;\n+ }\n+\n+ while ((line_len = get_line (content, content_len, line, sizeof (line)))\n+\t >= 0)\n+ {\n+ int key_len;\n+ char *sname_start, *sname_end, *key, *value, *equal_sign;\n+ /* Ignore empty lines */\n+ if (line_len <= 0)\n+\tcontinue;\n+\n+ /* Ignore commented lines */\n+ if (line[0] == '#')\n+\tcontinue;\n+\n+ sname_start = strchr (line, '[');\n+ if (sname_start != NULL)\n+\t{\n+\t sname_end = strchr (line, ']');\n+\n+\t if (sname_end == NULL)\n+\t continue; /* Invalid section tag */\n+\n+\t /* New section */\n+\t find_next_section = 0;\n+\n+\t if (paths != NULL || caches != NULL)\n+\t {\n+\t section_string_list_reverse (&paths);\n+\t section_string_list_reverse (&caches);\n+\t section_string_list_reverse (&dlopen_paths);\n+\t if ((result = add_new_section (sections, §ion_name, paths,\n+\t\t caches, dlopen_paths, isolation,\n+\t\t dlopen_isolation)) != DLCONF_ERR_OK)\n+\t\t{\n+\t\t goto exit;\n+\t\t}\n+\t section_name = NULL;\n+\t paths = NULL;\n+\t caches = NULL;\n+\t dlopen_paths = NULL;\n+\t }\n+\n+\t if (dlopen_paths != NULL)\n+\t dlopen_paths = NULL;\n+\n+\t section_name = strndup (sname_start + 1, sname_end - sname_start - 1);\n+\t if (section_name == NULL)\n+\t {\n+\t result = DLCONF_ERR_NOMEM;\n+\t _dl_error_printf (\"Memory allocation error\\n\");\n+\t goto exit;\n+\t }\n+\n+\t isolation = false;\n+\t dlopen_isolation = false;\n+\t continue;\n+\t}\n+ else if (find_next_section == 1)\n+\t{\n+\t /* We are looking for the next section. */\n+\t continue;\n+\t}\n+ /* We have a section, so we are looking for key=value. */\n+ equal_sign = strchr (line, '=');\n+\n+ if (equal_sign == NULL)\n+\t{\n+\t /* Unexpected line (empty?), let's try the next one. */\n+\t continue;\n+\t}\n+\n+ key_len = equal_sign - line - 1;\n+ key = line;\n+\n+ /* Remove spaces from the end of a key */\n+ while (key_len > 0 && key[key_len] == ' ')\n+\tkey_len--;\n+\n+ key_len++;\n+ if (key_len == 0)\n+\tcontinue;\n+\n+ key[key_len] = '\\0';\n+\n+ value = equal_sign + 1;\n+\n+ /* Remove spaces from the begin of a value */\n+ while (*value == ' ')\n+\tvalue++;\n+\n+ int value_len = line + line_len - value;\n+\n+ if (value_len == 0)\n+\tcontinue;\n+\n+ value[value_len] = '\\0';\n+\n+ if (strncmp (key, \"path\", sizeof (\"path\")) == 0)\n+\t{\n+\t struct section_string_list *path_list = paths;\n+\t int skip_this_path = 0;\n+\t while (path_list != NULL)\n+\t {\n+\t if (strcmp (path_list->string, value) == 0)\n+\t\t{\n+\t\t /* The same path is duplicated in the same section. Skip it. */\n+\t\t skip_this_path = 1;\n+\t\t break;\n+\t\t}\n+\t path_list = path_list->next;\n+\t }\n+\n+\t if (skip_this_path == 1)\n+\t continue;\n+\n+\t if (path_already_defined (value, sections))\n+\t {\n+\t /* Specified path is already defined by a higher priority config\n+\t file. */\n+\t _dl_error_printf (\"Path %s already defined. This path from %s \"\n+\t\t\t\t\"section will be ignored.\\n\",\n+\t\t\t\tvalue, section_name);\n+\t continue;\n+\t }\n+\n+\t if ((result = section_string_list_add (value, &paths))\n+\t != DLCONF_ERR_OK)\n+\t {\n+\t goto exit;\n+\t }\n+\n+\t /* Add path to the hashmap */\n+\t ENTRY item;\n+\t item.key = paths->string;\n+\t item.data = (void *) 0;\n+\t ENTRY *out = NULL;\n+\t hsearch_r (item, ENTER, &out, &path_htab);\n+\t}\n+ else if (strncmp (key, \"cache\", sizeof (\"cache\")) == 0)\n+\t{\n+\t if ((result = section_string_list_add (value, &caches)) != DLCONF_ERR_OK)\n+\t goto exit;\n+\t}\n+ else if (strncmp (key, \"dlopen_path\", sizeof (\"dlopen_path\")) == 0)\n+\t{\n+\t if ((result = section_string_list_add (value, &dlopen_paths)) != DLCONF_ERR_OK)\n+\t goto exit;\n+\t}\n+ else if (strncmp (key, \"isolation\", sizeof (\"isolation\")) == 0)\n+\t{\n+\t if (is_positive_value (value, value_len))\n+\t isolation = true;\n+\t else\n+\t isolation = false;\n+\t}\n+ else if (strncmp (key, \"dlopen_isolation\", sizeof (\"dlopen_isolation\"))\n+\t == 0)\n+\t{\n+\t if (is_positive_value (value, value_len))\n+\t dlopen_isolation = true;\n+\t else\n+\t dlopen_isolation = false;\n+\t}\n+ }\n+ if (caches != NULL || paths != NULL)\n+ {\n+ section_string_list_reverse (&paths);\n+ section_string_list_reverse (&caches);\n+ section_string_list_reverse (&dlopen_paths);\n+ result = add_new_section (sections, §ion_name, paths, caches,\n+\t\t\t\tdlopen_paths, isolation, dlopen_isolation);\n+ }\n+exit:\n+ free(section_name);\n+ if (result != DLCONF_ERR_OK)\n+ {\n+ free_section_string_list(paths, true);\n+ free_section_string_list(caches, true);\n+ free_section_string_list(dlopen_paths, true);\n+ }\n+ return result;\n+#undef PREFIX_MAX_LEN\n+#undef CACHE_STR\n+#undef PATH_STR\n+}\n+\n+static bool file_exists (const char *path);\n+\n+static int read_ld_so_config(const char *path, struct sections *sections)\n+{\n+ int result;\n+ size_t file_size = 0;\n+ char *file = (char*)_dl_sysdep_read_whole_file(path, &file_size, PROT_READ);\n+\n+ if (file == MAP_FAILED)\n+ {\n+ /* Probably a config file is missing. We can live with that. */\n+ return DLCONF_ERR_IO;\n+ }\n+\n+ result = parse_paraini(file, file_size, sections);\n+\n+ __munmap(file, file_size);\n+ return result;\n+}\n+\n+static void try_read_ld_so_config(const char *path, struct sections *sections)\n+{\n+ int result = read_ld_so_config(path, sections);\n+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_LIBS && result != DLCONF_ERR_OK))\n+ _dl_debug_printf (\"Error while reading configuration file: %s (%d)\\n\", path, result);\n+}\n+\n+static char *dlconf_find_cached_path (const char *name, const struct string_list *list)\n+{\n+ assert(name);\n+ char *cached = NULL;\n+\n+ while (list != NULL)\n+ {\n+ char *cache_path = list->string;\n+ if (cache_path != NULL)\n+ {\n+ cached = dlconf_load_cache_lookup_from(name, cache_path);\n+\t if (cached != NULL)\n+\t break;\n+\t}\n+ list = list->next;\n+ }\n+ return cached;\n+}\n+\n+static int compare_sections(const void *l, const void *r)\n+{\n+ const struct section *a = *(struct section**)l;\n+ const struct section *b = *(struct section**)r;\n+\n+ int res = strcmp(a->name, b->name);\n+\n+ if (res == 0)\n+ res = a->section_type - b->section_type;\n+\n+ return res;\n+}\n+\n+static int read_ld_so_configs(struct cache_info **cinfo)\n+{\n+ int result = DLCONF_ERR_OK;\n+\n+ struct config_path {\n+ const char *directory;\n+ const char *file;\n+ };\n+\n+ /* Configuration files have a certain priority.\n+ Those defined first have the highest priority\n+ Files in the directory are sorted in ascending order and have a higher\n+ priority than the conf file defined in structure below.\n+\n+ The motivation is so that it is possible to provide (for example by\n+ overlayfs) a configuration that will overwrite the configuration that came\n+ with the system. */\n+ struct config_path configs[] = {\n+ { DLCONF_CONF_PATH \".d/\", DLCONF_CONF_PATH },\n+ { NULL, NULL },\n+ };\n+ size_t i = 0;\n+ struct config_path *cur_config = &configs[i];\n+ struct sections sections = {\n+ .sections = NULL,\n+ .count = 0,\n+ .capacity = 0\n+ };\n+\n+ memset(&path_htab, 0, sizeof(path_htab));\n+ hcreate_r (MAX_PATHS_LIMIT, &path_htab);\n+\n+ while (cur_config->directory != NULL && cur_config->file != NULL)\n+ {\n+ struct string_list *s_list = get_file_list(cur_config->directory);\n+ struct string_list *element = s_list;\n+\n+ /* Potential errors don't pervent us from reading the next config files. */\n+ while (element != NULL)\n+ {\n+ try_read_ld_so_config(element->string, §ions);\n+ element = element->next;\n+ }\n+\n+ try_read_ld_so_config(cur_config->file, §ions);\n+ i++;\n+ cur_config = &configs[i];\n+ dlconf_free_string_list(s_list, true);\n+ }\n+\n+ hdestroy_r (&path_htab);\n+\n+ if (sections.sections == NULL)\n+ {\n+ /* No sections. We can finish the work. */\n+ return result;\n+ }\n+ /* Sort sections array so that binary search would be possible */\n+ qsort (sections.sections,\n+ sections.count,\n+ sizeof (*sections.sections),\n+ compare_sections);\n+\n+ if ((result = apply_extensions (§ions)) != DLCONF_ERR_OK\n+ || (result = apply_links (§ions)) != DLCONF_ERR_OK\n+ || (result = convert_sections_to_cache_info (§ions, cinfo)) != DLCONF_ERR_OK)\n+ {\n+ free_sections (§ions);\n+ return result;\n+ }\n+ free_sections (§ions);\n+\n+ if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))\n+ print_cinfo(*cinfo);\n+\n+ return result;\n+}\n+\n+static int add_list_to_g_list(struct string_list_array *glist,\n+ struct string_list *paths_list,\n+ int32_t *path_index)\n+{\n+ if (paths_list == NULL)\n+ {\n+ *path_index = -1;\n+ }\n+ else\n+ {\n+ int32_t idx = add_to_g_list(glist, paths_list);\n+ if (idx == -1)\n+ {\n+ _dl_error_printf(\"Memory allocation error\\n\");\n+ return DLCONF_ERR_NOMEM;\n+ }\n+ *path_index = idx;\n+ }\n+\n+ return DLCONF_ERR_OK;\n+}\n+\n+static int insert_to_trie (struct node *base_node, char *token, const struct cache_info *cinfo, char **saveptr)\n+{\n+ int result = DLCONF_ERR_OK;\n+ char *new_token;\n+ struct node *new_child;\n+ struct node **new_children;\n+\n+ for (int i = 0; i < base_node->children_count; i++)\n+ {\n+ if (strcmp(token, base_node->children[i]->name) == 0)\n+ {\n+ char *new_token = strtok_r(NULL, \"/\", saveptr);\n+ if (new_token == NULL)\n+ return DLCONF_ERR_OK;\n+\n+ return insert_to_trie(base_node->children[i], new_token, cinfo, saveptr);\n+ }\n+ }\n+\n+ new_child = malloc(sizeof(*new_child));\n+\n+ if (new_child == NULL)\n+ {\n+ _dl_error_printf(\"Memory allocation error\\n\");\n+ return DLCONF_ERR_NOMEM;\n+ }\n+\n+ new_child->children_count = 0;\n+ new_child->name = strdup(token);\n+\n+ if (new_child->name == NULL)\n+ {\n+ _dl_error_printf(\"Memory allocation error\\n\");\n+ free(new_child);\n+ return DLCONF_ERR_NOMEM;\n+ }\n+\n+ new_child->children = NULL;\n+\n+ new_children = dlconf_realloc(base_node->children,\n+ sizeof(struct node*)*(base_node->children_count + 1),\n+ sizeof(struct node*)*(base_node->children_count));\n+ if (new_children == NULL)\n+ {\n+ _dl_error_printf(\"Memory allocation error\\n\");\n+ result = DLCONF_ERR_NOMEM;\n+ goto exit;\n+ }\n+\n+ new_children[base_node->children_count] = new_child;\n+ base_node->children_count++;\n+ base_node->children = new_children;\n+\n+ new_token = strtok_r(NULL, \"/\", saveptr);\n+\n+ if (new_token != NULL)\n+ {\n+ new_child->isolation = base_node->isolation;\n+ new_child->dlopen_isolation = base_node->dlopen_isolation;\n+ new_child->caches_idx = base_node->caches_idx;\n+ new_child->dlopen_paths_idx = base_node->dlopen_paths_idx;\n+\n+ if ((result = insert_to_trie(new_child, new_token, cinfo, saveptr)) != DLCONF_ERR_OK)\n+ {\n+ goto exit;\n+ }\n+ }\n+ else\n+ {\n+ if ((result = add_list_to_g_list(&g_caches,\n+ cinfo->caches,\n+ &new_child->caches_idx)) != DLCONF_ERR_OK ||\n+ (result = add_list_to_g_list(&g_dlopen_paths,\n+ cinfo->dlopen_paths,\n+ &new_child->dlopen_paths_idx)) != DLCONF_ERR_OK)\n+ {\n+ goto exit;\n+ }\n+\n+ new_child->isolation = cinfo->isolation;\n+ new_child->dlopen_isolation = cinfo->dlopen_isolation;\n+ }\n+\n+exit:\n+ if (result != DLCONF_ERR_OK)\n+ {\n+ if (new_children != NULL)\n+ {\n+ new_children[base_node->children_count] = NULL;\n+ base_node->children_count--;\n+ }\n+ free(new_child->name);\n+ free(new_child);\n+ }\n+\n+ return result;\n+}\n+\n+void free_node(struct node *node)\n+{\n+ for (size_t i = 0; i < node->children_count; i++)\n+ free_node (node->children[i]);\n+\n+ free(node->name);\n+ free(node->children);\n+ free(node);\n+}\n+\n+static struct node *convert_to_trie (const struct cache_info *cinfo)\n+{\n+ struct node *base_node = malloc(sizeof(*base_node));\n+\n+ if (base_node == NULL)\n+ return NULL;\n+\n+ /* Create the root node */\n+ base_node->children_count = 0;\n+ base_node->children = NULL;\n+ base_node->name = strdup(\"/\");\n+\n+ if (base_node->name == NULL)\n+ goto error;\n+\n+ base_node->isolation = 0;\n+ base_node->dlopen_isolation = 0;\n+ base_node->dlopen_paths_idx = -1;\n+ base_node->caches_idx = -1;\n+\n+ while (cinfo != NULL)\n+ {\n+ char *path = strdup(cinfo->path);\n+\n+ if (path == NULL)\n+\tgoto error;\n+\n+ /* The base_node is handled differently */\n+ if (strncmp(path, \"/\", 2) == 0)\n+ {\n+ free(path);\n+ if (add_list_to_g_list(&g_caches, cinfo->caches, &base_node->caches_idx) != DLCONF_ERR_OK ||\n+ add_list_to_g_list(&g_dlopen_paths, cinfo->dlopen_paths, &base_node->dlopen_paths_idx) != DLCONF_ERR_OK)\n+ {\n+ goto error;\n+ }\n+ base_node->isolation = cinfo->isolation;\n+ base_node->dlopen_isolation = cinfo->dlopen_isolation;\n+ }\n+ else\n+ {\n+ char *saveptr;\n+ char *token = strtok_r(path, \"/\", &saveptr);\n+ if (token == NULL || insert_to_trie(base_node, token, cinfo, &saveptr) != DLCONF_ERR_OK)\n+ {\n+ free(path);\n+ goto error;\n+ }\n+ free(path);\n+ }\n+ cinfo = cinfo->next;\n+ }\n+ return base_node;\n+error:\n+ free_node(base_node);\n+ return NULL;\n+}\n+\n+static void add_to_list_at_end (struct string_list **new_list, char *token)\n+{\n+ struct string_list *n_ele = malloc(sizeof(*n_ele));\n+\n+ if (n_ele != NULL)\n+ {\n+ n_ele->string = token;\n+ n_ele->next = NULL;\n+ }\n+\n+ if (*new_list == NULL)\n+ *new_list = n_ele;\n+ else\n+ (*new_list)->next = n_ele;\n+}\n+\n+static int count_list_strlen (const struct string_list *list)\n+{\n+ int res = 1;\n+\n+ while (list != NULL)\n+ {\n+ res += strlen(list->string)+1;\n+ list = list->next;\n+ }\n+\n+ return res;\n+}\n+\n+static int count_lists_strlen (const struct string_list_array *list)\n+{\n+ int res = 0;\n+\n+ for (size_t i = 0; i < list->count; i++)\n+ res += count_list_strlen (list->elements[i]);\n+\n+ return res;\n+}\n+\n+static int count_nodes (const struct node *node)\n+{\n+ int res = 1;\n+\n+ for (int i = 0; i < node->children_count; i++)\n+ res += count_nodes (node->children[i]);\n+\n+ return res;\n+}\n+\n+static int count_nodes_names_length (const struct node *node)\n+{\n+ int res = strlen(node->name) + 1;\n+\n+ for (int i = 0; i < node->children_count; i++)\n+ res += count_nodes_names_length (node->children[i]);\n+\n+ return res;\n+}\n+\n+static int compute_glist_offset (struct string_list **list, int idx)\n+{\n+ int res = 0;\n+\n+ for (size_t i = 0; i < idx; i++)\n+ res += count_list_strlen (list[i]);\n+\n+ return res;\n+}\n+\n+static bool file_exists (const char *path)\n+{\n+ return (access(path, F_OK) == 0);\n+}\n+\n+static int save_names (const struct node *node, int fd)\n+{\n+ int result = _dl_write(fd, node->name, strlen(node->name)+1);\n+\n+ for (int32_t i = 0; i < node->children_count && result >= 0; i++)\n+ result = save_names (node->children[i], fd);\n+\n+ return result >= 0 ? 0 : DLCONF_ERR_IO;\n+}\n+\n+static int save_conf_dat_header (int fd)\n+{\n+ int8_t version = DLCONF_VERSION;\n+ char reserved[DLCONF_RESERVED_LEN];\n+\n+ write_or_return(fd, DLCONF_MAGIC, DLCONF_MAGIC_LEN);\n+ write_or_return(fd, &version, DLCONF_VERSION_LEN);\n+ write_or_return(fd, reserved, DLCONF_RESERVED_LEN);\n+\n+ return DLCONF_ERR_OK;\n+}\n+\n+static void free_g_list (struct string_list_array *g_list, bool free_strings)\n+{\n+ for (int32_t i = 0; i < g_list->count; i++)\n+ dlconf_free_string_list (g_list->elements[i], free_strings);\n+\n+ free(g_list->elements);\n+ g_list->elements = NULL;\n+ g_list->count = 0;\n+}\n+\n+static int save_list (struct string_list_array *glist, int fd)\n+{\n+ int result = 0;\n+\n+ for (size_t i = 0; i < glist->count && result >= 0; i++)\n+ {\n+ struct string_list *list = glist->elements[i];\n+ while (list != NULL)\n+\t{\n+\t result = _dl_write (fd, list->string, strlen (list->string) + 1);\n+\t if (result < 0)\n+\t goto exit;\n+\n+\t list = list->next;\n+\t}\n+ result = _dl_write (fd, \"\\0\", 1);\n+ }\n+exit:\n+ return result >= 0 ? 0 : DLCONF_ERR_IO;\n+}\n+\n+static int _save_trie (const struct node *node, int fd, int32_t *name_offset, int32_t cache_start, int32_t dlpath_start)\n+{\n+ int result = 0;\n+ int32_t all_subnodes = count_nodes(node);\n+ int32_t no_value = -1;\n+\n+ write_or_return(fd, &all_subnodes, sizeof(all_subnodes));\n+ write_or_return(fd, &node->children_count, sizeof(node->children_count));\n+ write_or_return(fd, name_offset, sizeof(*name_offset));\n+\n+ if (node->caches_idx >= 0)\n+ {\n+ int cache_offset = cache_start + compute_glist_offset(g_caches.elements, node->caches_idx);\n+ write_or_return(fd, &cache_offset, sizeof(cache_offset));\n+ }\n+ else\n+ {\n+ write_or_return(fd, &no_value, sizeof(no_value));\n+ }\n+ if (node->dlopen_paths_idx >= 0)\n+ {\n+ int dlpath_offset = dlpath_start + compute_glist_offset(g_dlopen_paths.elements, node->dlopen_paths_idx);\n+ write_or_return(fd, &dlpath_offset, sizeof(dlpath_offset));\n+ }\n+ else\n+ {\n+ write_or_return(fd, &no_value, sizeof(no_value));\n+ }\n+\n+ write_or_return(fd, &node->isolation, sizeof(node->isolation));\n+ write_or_return(fd, &node->dlopen_isolation, sizeof(node->dlopen_isolation));\n+\n+ *name_offset += strlen(node->name)+1;\n+\n+ for (int i = 0; i < node->children_count && result >= 0; i++)\n+ result = _save_trie (node->children[i], fd, name_offset, cache_start,\n+\t\t\t dlpath_start);\n+\n+ return result;\n+}\n+\n+static int save_trie (const struct node *base_node, const char *path)\n+{\n+ int result;\n+ int fd = open(DLCONF_DAT_PATH, O_CREAT | O_WRONLY, 0600);\n+\n+ if (fd < 0)\n+ {\n+ _dl_error_printf(\"File %s open error\\n\", DLCONF_DAT_PATH);\n+ return DLCONF_ERR_IO;\n+ }\n+\n+ int all_nodes = count_nodes(base_node);\n+ int name_offset = all_nodes*DLCONF_NODE_SIZE_IN_BYTES + DLCONF_DAT_HEADER_SIZE;\n+ int cache_offset = name_offset + count_nodes_names_length(base_node);\n+ int dlopen_path_offset = cache_offset + count_lists_strlen(&g_caches);\n+\n+ for (size_t i = 0; i < g_caches.count; i++)\n+ {\n+ struct string_list *l = g_caches.elements[i];\n+ while (l != 0)\n+ {\n+ l = l->next;\n+ }\n+ }\n+\n+ if (save_conf_dat_header(fd) != DLCONF_ERR_OK)\n+ {\n+ close(fd);\n+ _dl_error_printf(\"File %s save error\\n\", DLCONF_DAT_PATH);\n+ return DLCONF_ERR_IO;\n+ }\n+\n+ result = _save_trie(base_node, fd, &name_offset, cache_offset, dlopen_path_offset);\n+ if (result != DLCONF_ERR_OK)\n+ {\n+ goto exit;\n+ }\n+ if ((result = save_names(base_node, fd)) == DLCONF_ERR_OK &&\n+ (result = save_list(&g_caches, fd)) == DLCONF_ERR_OK)\n+ {\n+ result = save_list(&g_dlopen_paths, fd);\n+ }\n+exit:\n+ close(fd);\n+ return result;\n+}\n+\n+static int save_conf_dat (const struct cache_info *cache_info)\n+{\n+ int result;\n+ struct node *base_node = convert_to_trie(cache_info);\n+\n+ if (base_node == NULL)\n+ return DLCONF_ERR_NOMEM;\n+\n+ result = save_trie(base_node, DLCONF_DAT_PATH);\n+ free_node(base_node);\n+ free_g_list(&g_caches, true);\n+ free_g_list(&g_dlopen_paths, true);\n+ return result;\n+}\n+\n+static struct string_list *read_string_list (void *file, int32_t offset)\n+{\n+ struct string_list *list = NULL;\n+ struct string_list **list_tail = &list;\n+ char *str_ptr = (char*)(file+offset);\n+\n+ while (*str_ptr != '\\0')\n+ {\n+ add_to_list_at_end(list_tail, (char*)str_ptr);\n+\n+ if (*list_tail == NULL)\n+\tbreak;\n+\n+ str_ptr += strlen((*list_tail)->string)+1;\n+ list_tail = &((*list_tail)->next);\n+ }\n+\n+ return list;\n+}\n+\n+static struct conf_dat_entry *_find_conf_dat_entry (const char *token, void *file, int start, char **saveptr)\n+{\n+ int32_t *child_count = (int32_t*)(file + start + DLCONF_CHILD_COUNT_OFFSET);\n+ for (size_t i = 0; i < *child_count; i++)\n+ {\n+ int next_node_start = start + DLCONF_NODE_SIZE_IN_BYTES;\n+\n+ int *name_start = (int32_t*)(file + next_node_start + DLCONF_NAME_START_OFFSET);\n+ char *name = (char*)(file + *name_start);\n+\n+ if (strncmp(token, name, strlen(token)+1) == 0)\n+ {\n+ char *next_token = strtok_r(NULL, \"/\", saveptr);\n+ if (next_token == NULL)\n+ {\n+ return dlconf_read_one_conf_dat_entry(file, next_node_start);\n+ }\n+ else\n+ {\n+ struct conf_dat_entry* result = _find_conf_dat_entry(next_token, file, next_node_start, saveptr);\n+\n+\t if (result == NULL)\n+\t\treturn dlconf_read_one_conf_dat_entry (file, next_node_start);\n+\t else\n+\t\treturn result;\n+\t }\n+ }\n+ int32_t subtree_count = *(int32_t*)(file + next_node_start);\n+ start += subtree_count * DLCONF_NODE_SIZE_IN_BYTES;\n+ }\n+ return NULL;\n+}\n+\n+static struct conf_dat_entry *find_conf_dat_entry (const char *path, void *file)\n+{\n+ struct conf_dat_entry* result = NULL;\n+ char *npath = strdup(path);\n+ char *saveptr, *token;\n+ if (npath == NULL)\n+ return NULL;\n+\n+ token = strtok_r(npath, \"/\", &saveptr);\n+\n+ if (token != NULL)\n+ result\n+\t= _find_conf_dat_entry (token, file, DLCONF_DAT_HEADER_SIZE, &saveptr);\n+\n+ if (result == NULL)\n+ result = dlconf_read_one_conf_dat_entry (file, DLCONF_DAT_HEADER_SIZE);\n+\n+ free(npath);\n+ return result;\n+}\n+\n+static bool check_file_header(void *file)\n+{\n+ struct conf_dat_header\n+ {\n+ char magic[DLCONF_MAGIC_LEN];\n+ int8_t version;\n+ };\n+\n+ struct conf_dat_header *header = file;\n+\n+ if (strncmp(header->magic, DLCONF_MAGIC, DLCONF_MAGIC_LEN) == 0 ||\n+ header->version == DLCONF_VERSION)\n+ {\n+ return true;\n+ }\n+ return false;\n+}\n+\n+int load_conf_dat (const char *path)\n+{\n+ size_t file_size = 0;\n+ void *file = (void*)_dl_sysdep_read_whole_file(path, &file_size, PROT_READ);\n+\n+ if (file == MAP_FAILED)\n+ {\n+ _dl_error_printf(\"File %s open error\\n\", path);\n+ return DLCONF_ERR_IO;\n+ }\n+\n+ if (!check_file_header(file))\n+ {\n+ __munmap(file, file_size);\n+ _dl_error_printf(\"Invalid header\\n\");\n+ return DLCONF_ERR_INVAL;\n+ }\n+\n+ conf_data = file;\n+ conf_data_size = file_size;\n+ return 0;\n+}\n+\n+static struct conf_dat_entry *dlconf_find_proper_conf_dat_entry (const char *path)\n+{\n+ if (__glibc_unlikely(conf_data == NULL))\n+ {\n+ if (file_exists (DLCONF_DAT_PATH))\n+\tload_conf_dat (DLCONF_DAT_PATH);\n+ }\n+\n+ if (__glibc_likely(conf_data != NULL))\n+ {\n+ struct conf_dat_entry *result = find_conf_dat_entry(path, conf_data);\n+ return result;\n+ }\n+ return NULL;\n+}\n+\n+/*\n+ * Public functions\n+ */\n+\n+void dlconf_free_g_lists(void)\n+{\n+ free_g_list(&g_caches, false);\n+ free_g_list(&g_dlopen_paths, false);\n+}\n+\n+void dlconf_free_string_list (struct string_list *slist, bool free_string)\n+{\n+ struct string_list *s = slist;\n+\n+ while (s != NULL)\n+ {\n+ struct string_list *t = s;\n+ s = s->next;\n+\n+ if (free_string)\n+\tfree (t->string);\n+\n+ free(t);\n+ }\n+}\n+\n+void free_section_string_list (struct section_string_list *slist, bool free_string)\n+{\n+ if (slist == NULL)\n+ return;\n+\n+ struct section_string_list *s = slist;\n+ while (s != NULL)\n+ {\n+ struct section_string_list *t = s;\n+ s = s->next;\n+\n+ if (free_string)\n+\tfree (t->string);\n+\n+ free(t);\n+ }\n+}\n+\n+struct conf_dat_entry *dlconf_read_one_conf_dat_entry (void *file, int start)\n+{\n+ struct conf_dat_entry *node = malloc(sizeof(*node));\n+ struct conf_dat_raw_entry *rnode;\n+\n+ if (node == NULL)\n+ return NULL;\n+\n+ rnode = (struct conf_dat_raw_entry*)(file+start);\n+\n+ node->isolation = rnode->isolation;\n+ node->dlopen_isolation = rnode->dlopen_isolation;\n+ node->dlopen_paths = NULL;\n+ node->caches = NULL;\n+\n+ if (rnode->dlopen_paths_offset >= 0)\n+ node->dlopen_paths = read_string_list (file, rnode->dlopen_paths_offset);\n+\n+ if (rnode->caches_offset >= 0)\n+ node->caches = read_string_list (file, rnode->caches_offset);\n+\n+ return node;\n+}\n+\n+\n+struct caches* dlconf_find_cache (const char *cache_path)\n+{\n+ struct caches *new_cache;\n+ struct caches *c = cache_list;\n+\n+ while (c)\n+ {\n+ if (strcmp (c->cache_name, cache_path) == 0)\n+\treturn c;\n+\n+ c = c->next;\n+ }\n+\n+ new_cache = malloc(sizeof(*new_cache));\n+\n+ if (__glibc_unlikely (new_cache == NULL))\n+ return NULL;\n+\n+ new_cache->cache = NULL;\n+ new_cache->cache_new = NULL;\n+ new_cache->cachesize = 0;\n+ new_cache->next = cache_list;\n+ new_cache->cache_name = strdup(cache_path);\n+\n+ if (__glibc_unlikely(new_cache->cache_name == NULL))\n+ {\n+ free(new_cache);\n+ return NULL;\n+ }\n+ cache_list = new_cache;\n+\n+ return new_cache;\n+}\n+\n+bool dlconf_allowed_dlopen (const char *file, const void *caller_dlopen)\n+{\n+ char buff[PATH_MAX];\n+ struct string_list *s;\n+ char *caller_path;\n+ struct conf_dat_entry *centry;\n+ struct link_map *l = _dl_find_dso_for_object ((ElfW (Addr)) caller_dlopen);\n+\n+ if (l == NULL)\n+ return true;\n+\n+ if (l->l_type == lt_executable)\n+ {\n+ buff[sizeof (buff) - 1] = '\\0';\n+ caller_path = dlconf_get_executable_path (buff, sizeof (buff) - 1);\n+ }\n+ else\n+ {\n+ caller_path = l->l_name;\n+ }\n+\n+ /* If we can't find the path to the main program, let dlopen() execute after\n+ all. Blocking dlopen() is not a security mechanism, but only to help modularize\n+ the system. */\n+ if (caller_path == NULL)\n+ return true;\n+\n+ centry = dlconf_find_proper_conf_dat_entry(caller_path);\n+\n+ if (centry == NULL)\n+ return true;\n+\n+ if (!centry->dlopen_isolation)\n+ {\n+ free_centry(centry);\n+ return true;\n+ }\n+\n+ s = centry->dlopen_paths;\n+\n+ while (s != NULL)\n+ {\n+ if (strncmp(file, s->string, strlen(s->string)) == 0)\n+ {\n+ free_centry(centry);\n+ return true;\n+ }\n+ s = s->next;\n+ }\n+\n+ free_centry(centry);\n+ return false;\n+}\n+\n+char *dlconf_get_cached_path(const char *name, const char *libname, bool *try_default)\n+{\n+ struct conf_dat_entry *centry;\n+ char *cached;\n+\n+ if (try_default)\n+ *try_default = true;\n+\n+ if (name == NULL)\n+ return NULL;\n+\n+ centry = dlconf_find_proper_conf_dat_entry(name);\n+\n+ if (centry == NULL)\n+ {\n+ if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))\n+ {\n+ _dl_debug_printf(\"No entry for: %s %s\\n\", name, libname);\n+ }\n+ return NULL;\n+ }\n+\n+ if (try_default)\n+ *try_default = !centry->isolation;\n+\n+ cached = dlconf_find_cached_path(libname, centry->caches);\n+\n+ if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))\n+ {\n+ if (cached != NULL)\n+ _dl_debug_printf(\"Found entry: %s for %s %s\\n\", cached, name, libname);\n+ else\n+ _dl_debug_printf(\"Not found entry for %s %s\\n\", name, libname);\n+ }\n+\n+ free_centry(centry);\n+ return cached;\n+}\n+\n+void dlconf_unload_cache (void)\n+{\n+ struct caches *c = cache_list;\n+\n+ while (c)\n+ {\n+ struct caches *next;\n+ struct cache_file *cache = c->cache;\n+ size_t cachesize = c->cachesize;\n+\n+ if (cache != NULL && cache != (struct cache_file *) -1)\n+ {\n+ __munmap (cache, cachesize);\n+ cache = NULL;\n+ }\n+#ifdef SHARED\n+ /* This marks the glibc_hwcaps_priorities array as out-of-date. */\n+ c->glibc_hwcaps_priorities_length = 0;\n+#endif\n+ next = c->next;\n+ free(c->cache_name);\n+ free(c);\n+ c = next;\n+ }\n+\n+ cache_list = NULL;\n+ dlconf_free_g_lists();\n+\n+ if (conf_data != NULL)\n+ {\n+ __munmap(conf_data, conf_data_size);\n+ conf_data = NULL;\n+ conf_data_size = 0;\n+ }\n+}\n+\n+int dlconf_generate_conf_dat (void)\n+{\n+ struct cache_info *cache_info = NULL;\n+ int result;\n+\n+ if ((result = read_ld_so_configs(&cache_info)) == DLCONF_ERR_OK)\n+ {\n+ if (save_conf_dat(cache_info) != DLCONF_ERR_OK)\n+ {\n+ _dl_error_printf(\"Error converting configuration files\\n\");\n+ return DLCONF_ERR_IO;\n+ }\n+ free_cache_info(cache_info);\n+ }\n+\n+ return result;\n+}\ndiff --git a/elf/dlconf.h b/elf/dlconf.h\nnew file mode 100644\nindex 0000000000..00f408a839\n--- /dev/null\n+++ b/elf/dlconf.h\n@@ -0,0 +1,175 @@\n+/* Copyright (C) 2026 Samsung Electronics Co., Ltd.\n+\n+ This file is part of the GNU C Library.\n+\n+ The GNU C Library is free software; you can redistribute it and/or\n+ modify it under the terms of the GNU Lesser General Public\n+ License as published by the Free Software Foundation; either\n+ version 2.1 of the License, or (at your option) any later version.\n+\n+ The GNU C Library is distributed in the hope that it will be useful,\n+ but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+ Lesser General Public License for more details.\n+\n+ You should have received a copy of the GNU Lesser General Public\n+ License along with the GNU C Library; if not, see\n+ <https://www.gnu.org/licenses/>. */\n+#ifndef _DLCONF_H\n+#define _DLCONF_H\n+\n+#include <unistd.h>\n+#include <stdbool.h>\n+#include <sys/types.h>\n+#include <stddef.h>\n+\n+\n+#define DLCONF_ERR_OK 0 /* Success */\n+#define DLCONF_ERR_INVAL -1 /* Invalid parameters */\n+#define DLCONF_ERR_NOMEM -2 /* Memory allocation failure */\n+#define DLCONF_ERR_IO -3 /* File I/O error */\n+#define DLCONF_ERR_CYCLE -4 /* Cycle detected in configuration */\n+\n+/*\n+ * Struct that stores cache data (eg. ld.so.cache). It ensures that each cache\n+ * file is loaded at most once. The next time data from the structure will be\n+ * use, instad of reading file from disk.\n+ *\n+ * The same mechanism was there before, the current solution has added support\n+ * for linked list handling.\n+ */\n+struct caches {\n+ char *cache_name;\n+ struct cache_file *cache;\n+ struct cache_file_new *cache_new;\n+ size_t cachesize;\n+ struct caches *next;\n+ #ifdef SHARED\n+/* This is used to cache the priorities of glibc-hwcaps\n+ subdirectories. The elements of _dl_cache_priorities correspond to\n+ the strings in the cache_extension_tag_glibc_hwcaps section. */\n+ uint32_t *glibc_hwcaps_priorities;\n+ uint32_t glibc_hwcaps_priorities_length;\n+ uint32_t glibc_hwcaps_priorities_allocated;\n+\n+ /* True if the full malloc was used to allocated the array. */\n+ bool glibc_hwcaps_priorities_malloced;\n+\n+ #endif /* SHARED */\n+};\n+\n+/*\n+ * This structure represents an entry in ldconfig.dat file\n+ */\n+struct conf_dat_raw_entry {\n+ int32_t subtree_count;\n+ int32_t nodes_count;\n+ int32_t name_offset;\n+ int32_t caches_offset;\n+ int32_t dlopen_paths_offset;\n+ int32_t isolation;\n+ int32_t dlopen_isolation;\n+};\n+\n+/*\n+ * List of strings.\n+ */\n+struct string_list {\n+ char *string;\n+ struct string_list *next;\n+};\n+\n+/*\n+ * This structure represents a one entry read from ldconfig.dat file\n+ */\n+struct conf_dat_entry {\n+ bool isolation;\n+ bool dlopen_isolation;\n+\n+ struct string_list *caches;\n+ struct string_list *dlopen_paths;\n+};\n+\n+/*\n+ * Description of dlconfig.dat header\n+ */\n+#define DLCONF_MAGIC \"DLC\"\n+#define DLCONF_VERSION 1\n+#define DLCONF_MAGIC_LEN 4 /* DLC\\0 */\n+#define DLCONF_VERSION_LEN 1\n+#define DLCONF_RESERVED_LEN 3\n+#define DLCONF_DAT_HEADER_SIZE (DLCONF_MAGIC_LEN + DLCONF_VERSION_LEN + DLCONF_RESERVED_LEN)\n+\n+/*\n+ * Offsets of the data in ldconfig.dat entry\n+ */\n+#define DLCONF_SUBNODE_COUNT_OFFSET 0\n+#define DLCONF_CHILD_COUNT_OFFSET offsetof(struct conf_dat_raw_entry, nodes_count)\n+#define DLCONF_NAME_START_OFFSET offsetof(struct conf_dat_raw_entry, name_offset)\n+#define DLCONF_CACHES_START_OFFSET offsetof(struct conf_dat_raw_entry, caches_offset)\n+#define DLCONF_DLPATHS_START_OFFSET offsetof(struct conf_dat_raw_entry, dlopen_paths_offset)\n+#define DLCONF_ISO_OFFSET offsetof(struct conf_dat_raw_entry, isolation)\n+#define DLCONF_DLOPEN_ISO_OFFSET offsetof(struct conf_dat_raw_entry, dlopen_isolation)\n+#define DLCONF_NODE_FIELDS_COUNT 7\n+#define DLCONF_NODE_SIZE_IN_BYTES DLCONF_NODE_FIELDS_COUNT*sizeof(int32_t)\n+\n+/*\n+ * Free global caches paths and dlopen paths\n+ */\n+void dlconf_free_g_lists(void) attribute_hidden;\n+\n+/*\n+ * Free string list. If `free_string` argument is true, then additionally strings will be freed\n+ */\n+void dlconf_free_string_list(struct string_list *list, bool free_string) attribute_hidden;\n+\n+/*\n+ * Function read from `file` at specified offset `start` one dlconfig.dat entry\n+ */\n+struct conf_dat_entry *dlconf_read_one_conf_dat_entry(void *file, int start) attribute_hidden;\n+\n+/*\n+ * Function returns the path to the library with the given name using specified cache file.\n+ * This is wrapper for _dl_load_cache_lookup(), which allows to provide custom\n+ * ld.so.cache path.\n+ */\n+extern char *dlconf_load_cache_lookup_from (const char *name, const char *cache_path) attribute_hidden;\n+\n+/*\n+ * Returns structure which contains cache data for a cache from a specified path.\n+ */\n+extern struct caches* dlconf_find_cache (const char *cache_path) attribute_hidden;\n+\n+/*\n+ * Free caches list.\n+ */\n+extern void dlconf_unload_cache(void) attribute_hidden;\n+\n+/*\n+ * Return true or false depending on whether the library can be dlopened\n+ */\n+extern bool dlconf_allowed_dlopen (const char *file, const void *caller_dlopen) attribute_hidden;\n+\n+/*\n+ * Returns path to the library with a given libname.\n+ * name argument is a program name with a path, for which the library is loading.\n+ * try_default will be set to true or false depending on whether isolation is true or false.\n+ */\n+extern char *dlconf_get_cached_path(const char *name, const char *libname, bool *try_default) attribute_hidden;\n+\n+/*\n+ * Generate ldconfig.dat from /etc/ldconfig.conf and /etc/ldconfig.conf.d/* files\n+ */\n+extern int dlconf_generate_conf_dat (void) attribute_hidden;\n+\n+/*\n+ * Print ldconfig.conf dat content\n+ */\n+extern void dlconf_print_conf_dat (void) attribute_hidden;\n+\n+/*\n+ * Return the path to the main executable.\n+ */\n+char *dlconf_get_executable_path (char *buff, size_t bufflen) attribute_hidden;\n+\n+#endif /* _DLCONF_H */\ndiff --git a/elf/rtld.c b/elf/rtld.c\nindex bfdf632e77..f70bc7e18c 100644\n--- a/elf/rtld.c\n+++ b/elf/rtld.c\n@@ -53,6 +53,7 @@\n #include <dl-find_object.h>\n #include <dl-audit-check.h>\n #include <dl-call_tls_init_tp.h>\n+#include <dlconf.h>\n \n #include <assert.h>\n \n@@ -1491,6 +1492,22 @@ dl_main (const ElfW(Phdr) *phdr,\n \t --_dl_argc;\n \t ++_dl_argv;\n \t }\n+ #ifdef DLCONF\n+\telse if (! strcmp (_dl_argv[1], \"--build-ldconfig-dat\"))\n+\t {\n+\t state.mode = rtld_mode_build_dlconf_dat;\n+\n+\t --_dl_argc;\n+\t ++_dl_argv;\n+\t }\n+\telse if (! strcmp (_dl_argv[1], \"--print-ldconfig-dat\"))\n+\t {\n+\t state.mode = rtld_mode_print_dlconf_dat;\n+\n+\t --_dl_argc;\n+\t ++_dl_argv;\n+\t }\n+ #endif /* DLCONF */\n \telse if (strcmp (_dl_argv[1], \"--help\") == 0)\n \t {\n \t state.mode = rtld_mode_help;\n@@ -1511,6 +1528,20 @@ dl_main (const ElfW(Phdr) *phdr,\n \telse\n \t break;\n \n+#ifdef DLCONF\n+ if (__glibc_unlikely (state.mode == rtld_mode_build_dlconf_dat))\n+\t{\n+\t int res = dlconf_generate_conf_dat ();\n+\t _exit (res);\n+\t}\n+\n+ if (__glibc_unlikely (state.mode == rtld_mode_print_dlconf_dat))\n+\t{\n+\t dlconf_print_conf_dat ();\n+\t _exit (0);\n+\t}\n+#endif /* DLCONF */\n+\n if (__glibc_unlikely (state.mode == rtld_mode_list_tunables))\n \t{\n \t __tunables_print ();\n@@ -1969,6 +2000,12 @@ dl_main (const ElfW(Phdr) *phdr,\n \t\t\t state.mode == rtld_mode_trace, 0);\n rtld_timer_accum (&load_time, start);\n }\n+ #ifdef DLCONF\n+ /* The dependencies are already loaded, so we can free the cache now because\n+ we don't need it. Especially since freeing them after\n+ __rtld_malloc_init_real() will not be allowed. */\n+ dlconf_unload_cache();\n+ #endif /* DLCONF */\n \n /* Mark all objects as being in the global scope. */\n for (i = main_map->l_searchlist.r_nlist; i > 0; )\ndiff --git a/scripts/ld_so_cache_maker.py b/scripts/ld_so_cache_maker.py\nnew file mode 100644\nindex 0000000000..5d49b1bf0d\n--- /dev/null\n+++ b/scripts/ld_so_cache_maker.py\n@@ -0,0 +1,76 @@\n+#!/usr/bin/python3\n+# Copyright (C) 2026 Samsung Electronics Co., Ltd.\n+# This file is part of the GNU C Library.\n+#\n+# The GNU C Library is free software; you can redistribute it and/or\n+# modify it under the terms of the GNU Lesser General Public\n+# License as published by the Free Software Foundation; either\n+# version 2.1 of the License, or (at your option) any later version.\n+#\n+# The GNU C Library is distributed in the hope that it will be useful,\n+# but WITHOUT ANY WARRANTY; without even the implied warranty of\n+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+# Lesser General Public License for more details.\n+#\n+# You should have received a copy of the GNU Lesser General Public\n+# License along with the GNU C Library; if not, see\n+# <https://www.gnu.org/licenses/>.\n+import os\n+import sys\n+import struct\n+\n+ARCH_FLAG=0x0A03\n+\n+def read_library_list(input_file):\n+ if not os.path.exists(input_file):\n+ raise FileNotFoundError(f\"File {input_file} not exist.\")\n+\n+ with open(input_file, \"r\") as f:\n+ libraries = [line.strip() for line in f if line.strip()]\n+\n+ libraries.sort(reverse=True, key=lambda x: os.path.basename(x))\n+ return libraries\n+\n+def generate_ld_so_cache(libraries, output_file):\n+ entries = []\n+\n+ for lib in libraries:\n+\n+ lib_name = os.path.basename(lib)\n+ entries.append((lib_name, lib))\n+\n+ magic = b\"ld.so-1.7.0\"\n+ entry_count = len(entries)\n+\n+ with open(output_file, \"wb\") as f:\n+ f.write(magic + b\"\\0\" * (12 - len(magic)))\n+ f.write(struct.pack(\"I\", entry_count))\n+\n+ offsets = []\n+ data_section = b\"\"\n+\n+ for lib_name, lib_path in entries:\n+ name_offset = len(data_section)\n+ data_section += lib_name.encode(\"utf-8\") + b\"\\0\"\n+\n+ path_offset = len(data_section)\n+ data_section += lib_path.encode(\"utf-8\") + b\"\\0\"\n+\n+ offsets.append((name_offset, path_offset))\n+ for name_offset, path_offset in offsets:\n+ f.write(struct.pack(\"III\", ARCH_FLAG, name_offset, path_offset))\n+\n+ f.write(data_section)\n+\n+ print(f\"Cache {output_file} with {entry_count} entries was generated successful\")\n+\n+def main():\n+ try:\n+ libraries = read_library_list(sys.argv[1])\n+ generate_ld_so_cache(libraries, sys.argv[2])\n+ except Exception as e:\n+ print(f\"[ERROR] {e}\")\n+\n+if __name__ == \"__main__\":\n+ main()\n+\ndiff --git a/sysdeps/unix/sysv/linux/dlconf-getexecutable.c b/sysdeps/unix/sysv/linux/dlconf-getexecutable.c\nnew file mode 100644\nindex 0000000000..51f4202263\n--- /dev/null\n+++ b/sysdeps/unix/sysv/linux/dlconf-getexecutable.c\n@@ -0,0 +1,61 @@\n+/* Copyright (C) 2026 Samsung Electronics Co., Ltd.\n+\n+ This file is part of the GNU C Library.\n+\n+ The GNU C Library is free software; you can redistribute it and/or\n+ modify it under the terms of the GNU Lesser General Public\n+ License as published by the Free Software Foundation; either\n+ version 2.1 of the License, or (at your option) any later version.\n+\n+ The GNU C Library is distributed in the hope that it will be useful,\n+ but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+ Lesser General Public License for more details.\n+\n+ You should have received a copy of the GNU Lesser General Public\n+ License along with the GNU C Library; if not, see\n+ <https://www.gnu.org/licenses/>. */\n+#include \"ldsodefs.h\"\n+#include <string.h>\n+#include <linux/limits.h>\n+#include <sys/types.h>\n+#include <unistd.h>\n+\n+char *dlconf_get_executable_path(char *buff, size_t bufflen) {\n+ if (buff == NULL || bufflen == 0)\n+ {\n+ return NULL;\n+ }\n+\n+ char link_path[PATH_MAX];\n+ char pid_str_buff[12];\n+ size_t offset;\n+ ssize_t bytes;\n+\n+ pid_str_buff[sizeof(pid_str_buff) - 1] = '\\0';\n+ char *pid_str\n+ = _itoa (getpid (), pid_str_buff + sizeof (pid_str_buff) - 1, 10, 0);\n+\n+ offset = 0;\n+\n+ strncpy (link_path + offset, \"/proc/\", sizeof (link_path) - 1);\n+ offset += sizeof(\"/proc/\") - 1;\n+ strncpy(link_path + offset, pid_str, sizeof(link_path) - offset - 1);\n+ offset += strlen(pid_str);\n+ strncpy(link_path + offset, \"/exe\", sizeof(link_path) - offset - 1);\n+ offset += sizeof(\"/exe\") - 1;\n+\n+ link_path[offset] = '\\0';\n+\n+ if ((bytes = readlink (link_path, buff, bufflen - 1)) == -1)\n+ {\n+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_LIBS))\n+ {\n+ _dl_debug_printf (\"Read link of %s error\\n\", link_path);\n+ }\n+ return NULL;\n+ }\n+\n+ buff[bytes] = '\\0';\n+ return buff;\n+}\n", "prefixes": [ "RFC", "1/3" ] }