[RFC,1/3] dlconf: Add support for config file for ld.so

Message ID 20260421090020.59726-2-l.stelmach@samsung.com (mailing list archive)
State New
Headers
Series Use multiple ld.so caches to separate execution environments |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch fail Patch failed to apply to master at the time it was sent

Commit Message

Lukasz Stelmach April 21, 2026, 9 a.m. UTC
  From: Mateusz Moscicki <m.moscicki2@samsung.com>

Configuration allows to specify which ld.so.cache file to use when
looking for dependencies for a library or binary from a specific path
(note that setting LD_LIBRARY_PATH or RUNPATH will result in the
following configuration not being used).

The configuration is stored in:

    /etc/ldconfig.conf
    /etc/ldconfig.conf.d/*

and has a format similar to INI:

    [additional_cache]
    cache=/etc/ld-additional.so.cache

    [somelib_paths]
    path=/usr/lib/some1/

    [somelib_paths:ext]
    path=/usr/lib/some2/

    [somelib]
    cache=:additional_cache
    path=:somelib_paths
    isolation=yes
    dlopen_isolation=yes
    dlopen_path=/usr/lib/some1
    dlopen_path=/usr/lib/some2

The names of the sections are not relevant.

  path= - path from which objects are loaded. The name of other section
          can be used after ':'.

  cache= - location of the ld.so.cache file, which will be used for
           objects loaded from the path=. The name of the other section can be
           used after ':'.

  isolation= - if "yes" then the default /etc/ld.so.cache will not be
  used, as well as the default paths (/lib, /usr/lib) to search for
  dependencies. If "no" then the default cache and paths will be
  searched if the specified ld.so.cache does not contain dependencies.

  dlopen_isolation= - if "yes" then object can only dlopen libraries from
  paths specified by "dlopen_path"

  dlopen_path= - the path from which libraries can be loaded by dlopen()
  when "dlopen_isolation" is set

  [<section>:ext] - expands the content of the <section>

Both "path", "cache", "dlopen_path" can occur multiple times.

Configuration from ldconfig.conf.d/ directory has higher priority than
ldconfig.conf file.
Configuration from ldconfig.conf.d/001-ldconfig.conf has higher priority than
ldconfig.conf.d/002-ldconfig.conf

Preparing custom ld.so.cache (current version of the script is prepared
for aarch64):
1. Prepare a file with a list of libraries to be included in the
   resulting ld.so.cache file. For example:

       # cat list.txt
       /usr/lib/libc.so.6
       /usr/lib/some1/libsome1.so
       /usr/lib/some2/libsome2.so

2. Run ld_so_cache_maker.py which is located in packaging/ subdirectory:

       python3 ld_so_cache_maker.py list.txt ld-additional.so.cache

Instead of searching for configuration files each time and parsing them, a binary
file is used in which the configuration is stored as a tree. This makes
the search time much shorter.

The ldconfig.dat path is:

    /run/ldconfig.dat

The file is generated from the configuration files in:

    /etc/ldconfig.conf
    /etc/ldconfig.conf.d/*

by:

    ld.so --build-ldconfig-dat

The content of the ldconfig.dat can be displayed by:

    ld.so --print-ldconfig-dat

This is an example output:

    root:/> ld.so --print-ldconfig-dat
    /
    |   isolation: 0
    |   dlopen_isolation: 0
    `-- usr/
        |   isolation: 0
        |   dlopen_isolation: 0
        `-- lib/
            |   isolation: 0
            |   dlopen_isolation: 0
            |-- some1/
            |       isolation: 1
            |       dlopen_isolation: 1
            |       caches:
            |         `-- /etc/ld-additional.so.cache
            |       dlopen_paths:
            |         |-- /usr/lib/some1
            |         `-- /usr/lib/some2
            `-- some2/
                    isolation: 1
                    dlopen_isolation: 1
                    caches:
                    `-- /etc/ld-additional.so.cache
                    dlopen_paths:
                    |-- /usr/lib/some1
                    `-- /usr/lib/some2
    root:~>

Signed-off-by: Mateusz Mościcki <m.moscicki2@samsung.com>
Signed-off-by: Łukasz Stelmach <l.stelmach@samsung.com>
---
 config.h.in                                   |    9 +
 config.make.in                                |    4 +
 configure                                     |   58 +
 configure.ac                                  |   28 +
 elf/Makefile                                  |    9 +
 elf/dl-cache.c                                |  192 +-
 elf/dl-load.c                                 |   47 +-
 elf/dl-main.h                                 |    3 +
 elf/dl-open.c                                 |   12 +
 elf/dl-usage.c                                |    9 +-
 elf/dlconf-getexecutable.c                    |   20 +
 elf/dlconf-print.c                            |  265 ++
 elf/dlconf.c                                  | 2441 +++++++++++++++++
 elf/dlconf.h                                  |  175 ++
 elf/rtld.c                                    |   37 +
 scripts/ld_so_cache_maker.py                  |   76 +
 .../unix/sysv/linux/dlconf-getexecutable.c    |   61 +
 17 files changed, 3372 insertions(+), 74 deletions(-)
 create mode 100644 elf/dlconf-getexecutable.c
 create mode 100644 elf/dlconf-print.c
 create mode 100644 elf/dlconf.c
 create mode 100644 elf/dlconf.h
 create mode 100644 scripts/ld_so_cache_maker.py
 create mode 100644 sysdeps/unix/sysv/linux/dlconf-getexecutable.c
  

Comments

Adhemerval Zanella May 4, 2026, 7:32 p.m. UTC | #1
On 21/04/26 06:00, Łukasz Stelmach wrote:
> From: Mateusz Moscicki <m.moscicki2@samsung.com>
> 
> Configuration allows to specify which ld.so.cache file to use when
> looking for dependencies for a library or binary from a specific path
> (note that setting LD_LIBRARY_PATH or RUNPATH will result in the
> following configuration not being used).

Hi,

This RFC failed to apply [1]. And even after I tried to manually fix it, it
does not build cleanly with --enable-dconf (most likely due some extra
object pulling during ld.so build). Did you create the patchset against an
older glibc version?

I understand this feature make sense in an embedded or multi-stack Linux 
like Tizen, where distinct library subtrees co-exist under different directories;
and these subtrees have their own versions of common libraries.

But I have some design concerns that it would require further discussion:
 
  1. The --enable-dlconf / #ifdef DLCONF compile-time gate: we try to avoid
     configure switch that changes the dynamic linker semantics and this
     ones adds:

     a. A feature that is inaccessible to users if a distro's glibc does
        not enable it.

     b. two separate ld.so code paths to maintain, test, and audit forever.

     The glibc's precedent for optional features that affect ld.so behavior 
     (tunables, audit hooks, hwcaps) is to make them runtime-conditional, not 
     compile-time-conditional.

  2. The two-tier config format adds operational fragility: the extra dat file
     adds extra maintenance and code complexity. Compare to how ldconfig works: 
     it is explicitly integrated into the package manager and init system. This
     feature introduces a second pipeline with no such integration.

  3. No namespace awareness: The feature uses three module-level globals
     (conf_data, cache_list, g_caches/g_dlopen_paths) with no awareness of glibc's 
     link-map namespaces (dlmopen creates separate namespaces). A process using 
     multiple namespaces will have all of them share the same dlconf policy, which
     may not be the intent.

There is also some implementation issues:

  1. The hot-path cost of a failed lookup: when no configuration applies to a 
     binary (the common case for most binaries on a configured system), 
     _dl_map_new_object now does, for every library load:

     a. A lazy mmap of /run/ldconfig.dat
     b. strdup(path) + strtok_r path tokenization
     c. A trie traversal with per-node bounds unchecked pointer arithmetic
     d. Heap allocation of a conf_dat_entry + linked list nodes on a hit
     e. Free of all of the above
 
     It is a lot of heap allocations and we try to avoid it specially during
     the process startup because the initial bump allocator is very limited
     on what it can actually returns to OS.

  2. Thread safety: the lazy load of conf_data is racy:

     static struct conf_dat_entry *dlconf_find_proper_conf_dat_entry (const char *path)
     {
       if (__glibc_unlikely(conf_data == NULL))
       {
         if (file_exists (DLCONF_DAT_PATH))
	   load_conf_dat (DLCONF_DAT_PATH);
       }
       [...]
     }

     The conf_data is a global set by load_conf_dat and if two threads calls
     dlopen concurrently, both can see conf_data == NULL and both call load_conf_dat,
     double-mapping the file and leaking one copy.  The loader uses _dl_load_lock 
     in other places precisely for this reason.

  3. The dlopen_isolation check fires after the library is loaded: In elf/dl-open.c,
     the dlconf_allowed_dlopen check is called after _dl_map_new_object has already 
     mapped and linked the library.

  4. Priority ordering is backwards from Linux convention: Every other .d/ drop-in
     directory convention in Linux (systemd units, udev rules, sysctl.d, modules-load.d) 
     gives higher-numbered files higher priority.

  5. No tests: this patchset adds 3,370 lines of new C code with no new test cases.

Most implementation issues can be fixed, but 5. is really a deal breaker if you intend 
this for inclusion.


However, before sending a new version I think you should consider some alternative 
approaches:

  1. Extend the existing ld.so.cache format: the cache format already has an extension 
     section (cache_extension_tag_glibc_hwcaps is one example). Adding per-library or
     per-directory metadata (alternative cache path, isolation flag) to the existing 
     format would let ldconfig generate it in one step, with no new binary format, 
     no new operational pipeline, and no change to ld.so's startup sequence.

  2. Extend ldconfig to generate per-subtree caches: ldconfig already traverses directory
     trees and it allows it to generate subsidiary cache files (e.g., 
     /etc/ld.so.cache.d/usr-lib-foo.cache) referenced from the main cache. The main cache 
     lookup already happens exactly once per process; adding a redirect there keeps 
     the runtime path clean.

  3. Add a new ld.so option --cache=PATH:DIR,.... Instead of system-wide policy: it lets
     the launcher (a shell script, systemd unit, or container runtime) pass 
     --cache=/etc/ld-foo.so.cache:/usr/lib/foo to ld.so when executing the binary. This 
     is explicit, per-process, requires no new config file format, and composes with 
     existing namespace tools. The isolation use case then becomes the launcher's 
     responsibility, which is arguably where it belongs.

For the three options, I would go for the 1. which seems the cleanest one. And I would
not make it a configure option, instead it will be an optional ldconfg extension as we
did for the hwcap.  It will most likely require a bit of working from the ldconfig and
loader, but it would be cleaner than current proposal.


However, there is also the question whether another design can solve the both the
per-path cache selection, and load-time isolation. One option could be a supervisor
process.

A supervisor process creates a private mount namespace for the target binary and 
bind-mounts the custom cache file over the standard one before execve. The child 
process sees a different /etc/ld.so.cache without any glibc change: 

supervisor:
  1. Read config: /usr/lib/foo/* → cache=/etc/ld-foo.so.cache
  2. fork()
  child:
    3. unshare(CLONE_NEWNS)
    4. mount("--bind", "/etc/ld-foo.so.cache", "/etc/ld.so.cache", ...)
    5. execve("/usr/lib/foo/some_binary", argv, envp)

From that point on, the entire process — including all libraries transitively 
loaded — sees the custom cache, because the mount namespace is inherited. This 
covers the transitive loading case automatically: when /usr/lib/foo/libfoo.so 
later loads libbar.so, the lookup hits the custom cache.

With Linux user namespaces available (unshare -r), this works without root, which
makes it composable with unprivileged launchers. This is precisely what
bubblewrap/Flatpak does.

To emulate isolation=yes (no fallback to /lib, /usr/lib), the supervisor can do two 
things in the namespace before exec:

  a. overlay default paths with empty dirs:
   mount("tmpfs", "/lib", MS_BIND, ...)
   mount("tmpfs", "/usr/lib", MS_BIND, ...)

  b. use LD_LIBRARY_PATH to take priority:

The dlopen isolation would be more complex, since it would require the supervisor
to injects a small preload library that wraps dlopen() and enforces path policy.
But it is doable.

This can not provide two libraries from different isolated paths use two different 
cache simultaneously, but I am not sure how common and required this scenario is. 
For this case you will indeed need glibc changes.


I do not have a strong opinion which design is better for your usercase, but
I am also a bit worried of the extra ldconfig code and complexity that might
be only used in a very narrow and specific usercases (specially now with runtime
environment leaning more and more of Linux namespaces and kernel-provided isolation).


[1] https://patchwork.sourceware.org/project/glibc/list/?series=60045

> 
> The configuration is stored in:
> 
>     /etc/ldconfig.conf
>     /etc/ldconfig.conf.d/*
> 
> and has a format similar to INI:
> 
>     [additional_cache]
>     cache=/etc/ld-additional.so.cache
> 
>     [somelib_paths]
>     path=/usr/lib/some1/
> 
>     [somelib_paths:ext]
>     path=/usr/lib/some2/
> 
>     [somelib]
>     cache=:additional_cache
>     path=:somelib_paths
>     isolation=yes
>     dlopen_isolation=yes
>     dlopen_path=/usr/lib/some1
>     dlopen_path=/usr/lib/some2
> 
> The names of the sections are not relevant.
> 
>   path= - path from which objects are loaded. The name of other section
>           can be used after ':'.
> 
>   cache= - location of the ld.so.cache file, which will be used for
>            objects loaded from the path=. The name of the other section can be
>            used after ':'.
> 
>   isolation= - if "yes" then the default /etc/ld.so.cache will not be
>   used, as well as the default paths (/lib, /usr/lib) to search for
>   dependencies. If "no" then the default cache and paths will be
>   searched if the specified ld.so.cache does not contain dependencies.
> 
>   dlopen_isolation= - if "yes" then object can only dlopen libraries from
>   paths specified by "dlopen_path"
> 
>   dlopen_path= - the path from which libraries can be loaded by dlopen()
>   when "dlopen_isolation" is set
> 
>   [<section>:ext] - expands the content of the <section>
> 
> Both "path", "cache", "dlopen_path" can occur multiple times.
> 
> Configuration from ldconfig.conf.d/ directory has higher priority than
> ldconfig.conf file.
> Configuration from ldconfig.conf.d/001-ldconfig.conf has higher priority than
> ldconfig.conf.d/002-ldconfig.conf
> 
> Preparing custom ld.so.cache (current version of the script is prepared
> for aarch64):
> 1. Prepare a file with a list of libraries to be included in the
>    resulting ld.so.cache file. For example:
> 
>        # cat list.txt
>        /usr/lib/libc.so.6
>        /usr/lib/some1/libsome1.so
>        /usr/lib/some2/libsome2.so
> 
> 2. Run ld_so_cache_maker.py which is located in packaging/ subdirectory:
> 
>        python3 ld_so_cache_maker.py list.txt ld-additional.so.cache
> 
> Instead of searching for configuration files each time and parsing them, a binary
> file is used in which the configuration is stored as a tree. This makes
> the search time much shorter.
> 
> The ldconfig.dat path is:
> 
>     /run/ldconfig.dat
> 
> The file is generated from the configuration files in:
> 
>     /etc/ldconfig.conf
>     /etc/ldconfig.conf.d/*
> 
> by:
> 
>     ld.so --build-ldconfig-dat
> 
> The content of the ldconfig.dat can be displayed by:
> 
>     ld.so --print-ldconfig-dat
> 
> This is an example output:
> 
>     root:/> ld.so --print-ldconfig-dat
>     /
>     |   isolation: 0
>     |   dlopen_isolation: 0
>     `-- usr/
>         |   isolation: 0
>         |   dlopen_isolation: 0
>         `-- lib/
>             |   isolation: 0
>             |   dlopen_isolation: 0
>             |-- some1/
>             |       isolation: 1
>             |       dlopen_isolation: 1
>             |       caches:
>             |         `-- /etc/ld-additional.so.cache
>             |       dlopen_paths:
>             |         |-- /usr/lib/some1
>             |         `-- /usr/lib/some2
>             `-- some2/
>                     isolation: 1
>                     dlopen_isolation: 1
>                     caches:
>                     `-- /etc/ld-additional.so.cache
>                     dlopen_paths:
>                     |-- /usr/lib/some1
>                     `-- /usr/lib/some2
>     root:~>
> 
> Signed-off-by: Mateusz Mościcki <m.moscicki2@samsung.com>
> Signed-off-by: Łukasz Stelmach <l.stelmach@samsung.com>
  
Lukasz Stelmach May 5, 2026, 12:43 p.m. UTC | #2
It was 2026-05-04 pon 16:32, when Adhemerval Zanella Netto wrote:
> On 21/04/26 06:00, Łukasz Stelmach wrote:
>> From: Mateusz Moscicki <m.moscicki2@samsung.com>
>> 
>> Configuration allows to specify which ld.so.cache file to use when
>> looking for dependencies for a library or binary from a specific path
>> (note that setting LD_LIBRARY_PATH or RUNPATH will result in the
>> following configuration not being used).
>
> Hi,
>
> This RFC failed to apply [1]. And even after I tried to manually fix it, it
> does not build cleanly with --enable-dconf (most likely due some extra
> object pulling during ld.so build). Did you create the patchset against an
> older glibc version?

Yes, we worked on glibc 2.40. I am sorry, I forgot to mention it.

> I understand this feature make sense in an embedded or multi-stack
> Linux like Tizen, where distinct library subtrees co-exist under
> different directories; and these subtrees have their own versions of
> common libraries.

Yes, that is our approach to integration of vendor BSPs.

> But I have some design concerns that it would require further discussion:
>  
>   1. The --enable-dlconf / #ifdef DLCONF compile-time gate: we try to avoid
>      configure switch that changes the dynamic linker semantics and this
>      ones adds:
>
>      a. A feature that is inaccessible to users if a distro's glibc does
>         not enable it.
>
>      b. two separate ld.so code paths to maintain, test, and audit forever.
>
>      The glibc's precedent for optional features that affect ld.so behavior 
>      (tunables, audit hooks, hwcaps) is to make them runtime-conditional, not 
>      compile-time-conditional.

ACK.

[…]

>   2. The two-tier config format adds operational fragility: the extra dat file
>      adds extra maintenance and code complexity. Compare to how ldconfig works: 
>      it is explicitly integrated into the package manager and init system. This
>      feature introduces a second pipeline with no such integration.

The reason it looks like a separate pipeline is because we don't
want/need that level of integration. Although in Tizen we package
software in RPMs, they are not distributed. We use images and it is the
image building process where we put stuff together. That's where we come
from. Of course for the patches to be acceptable for wider audience
we'll try to come up with something more in line with traditional
setups.

>   3. No namespace awareness: The feature uses three module-level globals
>      (conf_data, cache_list, g_caches/g_dlopen_paths) with no awareness of glibc's 
>      link-map namespaces (dlmopen creates separate namespaces). A process using 
>      multiple namespaces will have all of them share the same dlconf policy, which
>      may not be the intent.

Thanks a lot for your detailed review. This is just to acknowledge, that we've
received it and we will be preparing v2 as well as we will answer your
question in details in the next message.

Kind regards,
  
Adhemerval Zanella May 5, 2026, 1:51 p.m. UTC | #3
On 05/05/26 09:43, Lukasz Stelmach wrote:
> It was 2026-05-04 pon 16:32, when Adhemerval Zanella Netto wrote:
>> On 21/04/26 06:00, Łukasz Stelmach wrote:
>>> From: Mateusz Moscicki <m.moscicki2@samsung.com>
>>>
>>> Configuration allows to specify which ld.so.cache file to use when
>>> looking for dependencies for a library or binary from a specific path
>>> (note that setting LD_LIBRARY_PATH or RUNPATH will result in the
>>> following configuration not being used).
>>
>> Hi,
>>
>> This RFC failed to apply [1]. And even after I tried to manually fix it, it
>> does not build cleanly with --enable-dconf (most likely due some extra
>> object pulling during ld.so build). Did you create the patchset against an
>> older glibc version?
> 
> Yes, we worked on glibc 2.40. I am sorry, I forgot to mention it.

Right, patches for libc-alpha are expected to be crafted against master branch.
Submission for old releases are usually for backporting and bugfixes, since
it is hard to reason for a new feature against a old release.

> 
>> I understand this feature make sense in an embedded or multi-stack
>> Linux like Tizen, where distinct library subtrees co-exist under
>> different directories; and these subtrees have their own versions of
>> common libraries.
> 
> Yes, that is our approach to integration of vendor BSPs.
> 
>> But I have some design concerns that it would require further discussion:
>>  
>>   1. The --enable-dlconf / #ifdef DLCONF compile-time gate: we try to avoid
>>      configure switch that changes the dynamic linker semantics and this
>>      ones adds:
>>
>>      a. A feature that is inaccessible to users if a distro's glibc does
>>         not enable it.
>>
>>      b. two separate ld.so code paths to maintain, test, and audit forever.
>>
>>      The glibc's precedent for optional features that affect ld.so behavior 
>>      (tunables, audit hooks, hwcaps) is to make them runtime-conditional, not 
>>      compile-time-conditional.
> 
> ACK.
> 
> […]
> 
>>   2. The two-tier config format adds operational fragility: the extra dat file
>>      adds extra maintenance and code complexity. Compare to how ldconfig works: 
>>      it is explicitly integrated into the package manager and init system. This
>>      feature introduces a second pipeline with no such integration.
> 
> The reason it looks like a separate pipeline is because we don't
> want/need that level of integration. Although in Tizen we package
> software in RPMs, they are not distributed. We use images and it is the
> image building process where we put stuff together. That's where we come
> from. Of course for the patches to be acceptable for wider audience
> we'll try to come up with something more in line with traditional
> setups.
> 
>>   3. No namespace awareness: The feature uses three module-level globals
>>      (conf_data, cache_list, g_caches/g_dlopen_paths) with no awareness of glibc's 
>>      link-map namespaces (dlmopen creates separate namespaces). A process using 
>>      multiple namespaces will have all of them share the same dlconf policy, which
>>      may not be the intent.
> 
> Thanks a lot for your detailed review. This is just to acknowledge, that we've
> received it and we will be preparing v2 as well as we will answer your
> question in details in the next message.

I would like also to take a step back and think if this proposed solution would
fit in other scenarios other than Tizen.  For instance, Flatpack and Snap rely 
on user namespace to provide similar isolation, while AppImages uses squasfs plus
LD_LIBRARY_PATH. So my question is why this new scheme is preferable over how
my exploration supervisor idea and/or how current Linux runtime are currently
doing.

I recall some project do patch glibc ld.so.cache logic to solve different 
issues [1] (the 'stat storm' that hits hard on some network files). Other
system went for a different route, like Spark with RPATH / RUNPATH. But
I read that even HPC workloads are moving to user namespace and bind mount
to avoid similar issues.

[1] https://issues.guix.gnu.org/44899#:~:text=%5BPATCH%201%2F3%5D%20gnu,mk%20(dist_patch_DATA)%3A%20Add%20it.
  

Patch

diff --git a/config.h.in b/config.h.in
index f495f11244..0c82d6dfb6 100644
--- a/config.h.in
+++ b/config.h.in
@@ -100,6 +100,15 @@ 
    include/libc-symbols.h that avoid PLT slots in the shared objects.  */
 #undef	NO_RTLD_HIDDEN
 
+/* Define this to enable dynamic loader configuration support. */
+#undef  DLCONF
+
+/* Define this to specify ldconfig.dat file path. */
+#undef  DLCONF_DAT_PATH
+
+/* Define the dlconf configuration path. */
+#undef  DLCONF_CONF_PATH
+
 /* Define this to disable lazy relocations in DSOs.  */
 #undef	BIND_NOW
 
diff --git a/config.make.in b/config.make.in
index 36096881b7..48aa44b522 100644
--- a/config.make.in
+++ b/config.make.in
@@ -27,6 +27,10 @@  multidir= @libc_cv_multidir@
 # Should we use and build ldconfig?
 use-ldconfig = @use_ldconfig@
 
+dlconf = @dlconf@
+dlconf_ldconfig_dat_path = @dlconf_ldconfig_dat_path@
+dlconf_conf_path = @dlconf_conf_path@
+
 # Maybe the `ldd' script must be rewritten.
 ldd-rewrite-script = @ldd_rewrite_script@
 
diff --git a/configure b/configure
index 1d543548cd..f47efd505a 100755
--- a/configure
+++ b/configure
@@ -697,6 +697,7 @@  memory_tagging
 enable_werror
 force_install
 bindnow
+dlconf
 hardcoded_path_in_tests
 enable_timezone_tools
 man_pages_version
@@ -791,6 +792,9 @@  enable_default_pie
 enable_timezone_tools
 enable_hardcoded_path_in_tests
 enable_hidden_plt
+enable_dlconf
+with_dlconf_ldconfig_dat_path
+with_dlconf_conf_path
 enable_bind_now
 enable_stack_protector
 enable_static_nss
@@ -1460,6 +1464,7 @@  Optional Features:
                           hardcode newly built glibc path in tests
                           [default=no]
   --disable-hidden-plt    do not hide internal function calls to avoid PLT
+  --enable-dlconf         dynamic loader configuration [default=no]
   --enable-bind-now       disable lazy relocations in DSOs
   --enable-stack-protector=[yes|no|all|strong]
                           Use -fstack-protector[-all|-strong] to detect glibc
@@ -1512,6 +1517,12 @@  Optional Packages:
                           specify an integer to scale the timeout
   --with-man-pages=VERSION
                           tie manual to a specific man-pages version
+  --with-dlconf-ldconfig-dat-path=PATH
+                          Path to the ldconfig.dat file (default:
+                          /run/ldconfig.dat)
+  --with-dlconf-conf-path=PATH
+                          Path to the ldconfig.conf file (default:
+                          /etc/ldconfig.conf)
   --with-cpu=CPU          select code for CPU variant
 
 Some influential environment variables:
@@ -4559,6 +4570,53 @@  if test "x$hidden" = xno; then
 
 fi
 
+# Check whether --enable-dlconf was given.
+if test ${enable_dlconf+y}
+then :
+  enableval=$enable_dlconf; dlconf=$enableval
+else case e in #(
+  e) dlconf=no ;;
+esac
+fi
+
+
+if test "x$dlconf" = xyes; then
+  printf "%s\n" "#define DLCONF 1" >>confdefs.h
+
+fi
+
+
+# Check whether --with-dlconf-ldconfig-dat-path was given.
+if test ${with_dlconf_ldconfig_dat_path+y}
+then :
+  withval=$with_dlconf_ldconfig_dat_path; dlconf_ldconfig_dat_path=$withval
+else case e in #(
+  e) dlconf_ldconfig_dat_path="/run/ldconfig.dat" ;;
+esac
+fi
+
+if test "x$dlconf_ldconfig_dat_path" != "x"; then
+
+printf "%s\n" "#define DLCONF_DAT_PATH \"$dlconf_ldconfig_dat_path\"" >>confdefs.h
+
+fi
+
+
+# Check whether --with-dlconf-conf-path was given.
+if test ${with_dlconf_conf_path+y}
+then :
+  withval=$with_dlconf_conf_path; dlconf_conf_path=$withval
+else case e in #(
+  e) dlconf_conf_path="/etc/ldconfig.conf" ;;
+esac
+fi
+
+if test "x$dlconf_conf_path" != "x"; then
+
+printf "%s\n" "#define DLCONF_CONF_PATH \"$dlconf_conf_path\"" >>confdefs.h
+
+fi
+
 # Check whether --enable-bind-now was given.
 if test ${enable_bind_now+y}
 then :
diff --git a/configure.ac b/configure.ac
index 9cbc0bf68f..0663b6b9d4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -236,6 +236,34 @@  if test "x$hidden" = xno; then
   AC_DEFINE(NO_HIDDEN)
 fi
 
+AC_ARG_ENABLE([dlconf],
+	      AS_HELP_STRING([--enable-dlconf],
+			     [dynamic loader configuration @<:@default=no@:>@]),
+	      [dlconf=$enableval],
+	      [dlconf=no])
+AC_SUBST(dlconf)
+if test "x$dlconf" = xyes; then
+  AC_DEFINE(DLCONF)
+fi
+
+AC_ARG_WITH([dlconf-ldconfig-dat-path],
+            [AS_HELP_STRING([--with-dlconf-ldconfig-dat-path=PATH], [Path to the ldconfig.dat file (default: /run/ldconfig.dat)])],
+            [dlconf-ldconfig-dat-path=$withval],
+            [dlconf-ldconfig-dat-path="/run/ldconfig.dat"])
+
+if test "x$with_dlconf_ldconfig_dat_path" != "x"; then
+  AC_DEFINE_UNQUOTED([DLCONF_DAT_PATH], ["$with_dlconf_ldconfig_dat_path"], [Path to the ldconfig.dat file (default: /run/ldconfig.dat)])
+fi
+
+AC_ARG_WITH([dlconf-conf-path],
+            [AS_HELP_STRING([--with-dlconf-conf-path=PATH], [Path to the ldconfig.conf file (default: /etc/ldconfig.conf)])],
+            [dlconf-conf-path=$withval],
+            [dlconf-conf-path="/etc/ldconfig.conf"])
+
+if test "x$with_dlconf_conf_path" != "x"; then
+  AC_DEFINE_UNQUOTED([DLCONF_DAT_PATH], ["$with_dlconf_conf_path"], [Path to the ldconfig.conf file (default: /etc/ldconfig.conf)])
+fi
+
 AC_ARG_ENABLE([bind-now],
 	      AS_HELP_STRING([--enable-bind-now],
 			     [disable lazy relocations in DSOs]),
diff --git a/elf/Makefile b/elf/Makefile
index a3475f3fb5..a86775202e 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -88,6 +88,11 @@  dl-routines = \
 
 ifeq (yes,$(use-ldconfig))
 dl-routines += dl-cache
+ifeq (yes, $(dlconf))
+dl-routines += dlconf
+dl-routines += dlconf-print
+dl-routines += dlconf-getexecutable
+endif
 endif
 
 ifeq (yesyes,$(build-shared)$(run-built-tests))
@@ -1468,6 +1473,10 @@  CFLAGS-ldconfig.c += $(SYSCONF-FLAGS) -D'LIBDIR="$(libdir)"' \
 		    -D'SLIBDIR="$(slibdir)"'
 libof-ldconfig = ldconfig
 CFLAGS-dl-cache.c += $(SYSCONF-FLAGS)
+ifeq (yes, $(dlconf))
+CFLAGS-dlconf.c += $(SYSCONF-FLAGS)
+CFLAGS-dlconf-print.c += $(SYSCONF-FLAGS)
+endif
 CFLAGS-cache.c += $(SYSCONF-FLAGS)
 CFLAGS-rtld.c += $(SYSCONF-FLAGS)
 CFLAGS-dl-usage.c += $(SYSCONF-FLAGS) \
diff --git a/elf/dl-cache.c b/elf/dl-cache.c
index 7c7dc58745..c290659614 100644
--- a/elf/dl-cache.c
+++ b/elf/dl-cache.c
@@ -27,41 +27,59 @@ 
 #include <dl-hwcaps.h>
 #include <dl-isa-level.h>
 
-/* This is the starting address and the size of the mmap()ed file.  */
-static struct cache_file *cache;
-static struct cache_file_new *cache_new;
-static size_t cachesize;
-
+#ifdef DLCONF
+#include "dlconf.h"
+
+#else
+/* Common cache structure used in both standard and DLCONF modes.
+   In standard mode, we use a single static instance.
+   In DLCONF mode, we use a linked list of caches.  */
+struct caches
+{
+  char *cache_name;
+  struct cache_file *cache;
+  struct cache_file_new *cache_new;
+  size_t cachesize;
+  struct caches *next;
 #ifdef SHARED
 /* This is used to cache the priorities of glibc-hwcaps
    subdirectories.  The elements of _dl_cache_priorities correspond to
    the strings in the cache_extension_tag_glibc_hwcaps section.  */
-static uint32_t *glibc_hwcaps_priorities;
-static uint32_t glibc_hwcaps_priorities_length;
-static uint32_t glibc_hwcaps_priorities_allocated;
+  uint32_t *glibc_hwcaps_priorities;
+  uint32_t glibc_hwcaps_priorities_length;
+  uint32_t glibc_hwcaps_priorities_allocated;
+
+  /* True if the full malloc was used to allocated the array.  */
+  bool glibc_hwcaps_priorities_malloced;
+#endif /* SHARED */
+};
 
-/* True if the full malloc was used to allocated the array.  */
-static bool glibc_hwcaps_priorities_malloced;
+/* In standard mode, we have a single global cache instance.  */
+static struct caches global_cache;
+#endif /* DLCONF */
 
+#ifdef SHARED
 /* Deallocate the glibc_hwcaps_priorities array.  */
 static void
-glibc_hwcaps_priorities_free (void)
+glibc_hwcaps_priorities_free (struct caches *cache)
 {
   /* When the minimal malloc is in use, free does not do anything,
      so it does not make sense to call it.  */
-  if (glibc_hwcaps_priorities_malloced)
-    free (glibc_hwcaps_priorities);
-  glibc_hwcaps_priorities = NULL;
-  glibc_hwcaps_priorities_allocated = 0;
+  if (cache->glibc_hwcaps_priorities_malloced)
+    free (cache->glibc_hwcaps_priorities);
+  cache->glibc_hwcaps_priorities = NULL;
+  cache->glibc_hwcaps_priorities_allocated = 0;
 }
 
 /* Ordered comparison of a hwcaps string from the cache on the left
    (identified by its string table index) and a _dl_hwcaps_priorities
    element on the right.  */
 static int
-glibc_hwcaps_compare (uint32_t left_index, struct dl_hwcaps_priority *right)
+glibc_hwcaps_compare (uint32_t left_index, struct dl_hwcaps_priority *right,
+		      struct caches *cache)
 {
-  const char *left_name = (const char *) cache + left_index;
+  struct cache_file *cache_file = cache->cache;
+  const char *left_name = (const char *) cache_file + left_index;
   uint32_t left_name_length = strlen (left_name);
   uint32_t to_compare;
   if (left_name_length < right->name_length)
@@ -82,17 +100,21 @@  glibc_hwcaps_compare (uint32_t left_index, struct dl_hwcaps_priority *right)
 /* Initialize the glibc_hwcaps_priorities array and its length,
    glibc_hwcaps_priorities_length.  */
 static void
-glibc_hwcaps_priorities_init (void)
+glibc_hwcaps_priorities_init (struct caches *cache)
 {
+  struct cache_file *cache_file = cache->cache;
+  struct cache_file_new *cache_new = cache->cache_new;
+  size_t cachesize = cache->cachesize;
+
   struct cache_extension_all_loaded ext;
-  if (!cache_extension_load (cache_new, cache, cachesize, &ext))
+  if (!cache_extension_load (cache_new, cache_file, cachesize, &ext))
     return;
 
   uint32_t length = (ext.sections[cache_extension_tag_glibc_hwcaps].size
 		     / sizeof (uint32_t));
-  if (length > glibc_hwcaps_priorities_allocated)
+  if (length > cache->glibc_hwcaps_priorities_allocated)
     {
-      glibc_hwcaps_priorities_free ();
+      glibc_hwcaps_priorities_free (cache);
 
       uint32_t *new_allocation = malloc (length * sizeof (uint32_t));
       if (new_allocation == NULL)
@@ -100,9 +122,9 @@  glibc_hwcaps_priorities_init (void)
 	   errors.  */
 	return;
 
-      glibc_hwcaps_priorities = new_allocation;
-      glibc_hwcaps_priorities_allocated = length;
-      glibc_hwcaps_priorities_malloced = __rtld_malloc_is_complete ();
+      cache->glibc_hwcaps_priorities = new_allocation;
+      cache->glibc_hwcaps_priorities_allocated = length;
+      cache->glibc_hwcaps_priorities_malloced = __rtld_malloc_is_complete ();
     }
 
   /* Compute the priorities for the subdirectories by merging the
@@ -111,13 +133,13 @@  glibc_hwcaps_priorities_init (void)
   const uint32_t *left_end = left + length;
   struct dl_hwcaps_priority *right = _dl_hwcaps_priorities;
   struct dl_hwcaps_priority *right_end = right + _dl_hwcaps_priorities_length;
-  uint32_t *result = glibc_hwcaps_priorities;
+  uint32_t *result = cache->glibc_hwcaps_priorities;
 
   while (left < left_end && right < right_end)
     {
       if (*left < cachesize)
 	{
-	  int cmp = glibc_hwcaps_compare (*left, right);
+	  int cmp = glibc_hwcaps_compare (*left, right, cache);
 	  if (cmp == 0)
 	    {
 	      *result = right->priority;
@@ -147,24 +169,24 @@  glibc_hwcaps_priorities_init (void)
       ++left;
     }
 
-  glibc_hwcaps_priorities_length = length;
+  cache->glibc_hwcaps_priorities_length = length;
 }
 
 /* Return the priority of the cache_extension_tag_glibc_hwcaps section
    entry at INDEX.  Zero means do not use.  Otherwise, lower values
    indicate greater preference.  */
 static uint32_t
-glibc_hwcaps_priority (uint32_t index)
+glibc_hwcaps_priority (uint32_t index, struct caches *cache)
 {
   /* This does not need to repeated initialization attempts because
      this function is only called if there is glibc-hwcaps data in the
      cache, so the first call initializes the glibc_hwcaps_priorities
      array.  */
-  if (glibc_hwcaps_priorities_length == 0)
-    glibc_hwcaps_priorities_init ();
+  if (cache->glibc_hwcaps_priorities_length == 0)
+    glibc_hwcaps_priorities_init (cache);
 
-  if (index < glibc_hwcaps_priorities_length)
-    return glibc_hwcaps_priorities[index];
+  if (index < cache->glibc_hwcaps_priorities_length)
+    return cache->glibc_hwcaps_priorities[index];
   else
     return 0;
 }
@@ -195,7 +217,7 @@  _dl_cache_file_entry (const struct file_entry *libs, size_t entry_size,
 static const char *
 search_cache (const char *string_table, uint32_t string_table_size,
 	      struct file_entry *libs, uint32_t nlibs, uint32_t entry_size,
-	      const char *name)
+	      const char *name, struct caches *cache)
 {
   int left = 0;
   int right = nlibs - 1;
@@ -293,7 +315,7 @@  search_cache (const char *string_table, uint32_t string_table_size,
 		      if (named_hwcap)
 			{
 			  uint32_t entry_priority
-			    = glibc_hwcaps_priority (libnew->hwcap);
+			    = glibc_hwcaps_priority (libnew->hwcap, cache);
 			  if (entry_priority == 0)
 			    /* Not usable at all.  Skip.  */
 			    continue;
@@ -382,17 +404,29 @@  _dl_cache_libcmp (const char *p1, const char *p2)
    may be unmapped at any time by a completing recursive dlopen and
    this function must take care that it does not return references to
    any data in the mapping.  */
+#ifdef DLCONF
+char *
+dlconf_load_cache_lookup_from (const char *name, const char *cache_path)
+#else
 char *
 _dl_load_cache_lookup (const char *name)
+#endif /* DLCONF */
 {
+#ifdef DLCONF
+  struct caches *cache = dlconf_find_cache(cache_path);
+#else
+  struct caches *cache = &global_cache;
+  const char *cache_path = LD_SO_CACHE;
+#endif /* DLCONF */
+
   /* Print a message if the loading of libs is traced.  */
   if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
-    _dl_debug_printf (" search cache=%s\n", LD_SO_CACHE);
+    _dl_debug_printf (" search cache=%s\n", cache_path);
 
-  if (cache == NULL)
+  if (cache->cache == NULL)
     {
       /* Read the contents of the file.  */
-      void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &cachesize,
+      void *file = _dl_sysdep_read_whole_file (cache_path, &cache->cachesize,
 					       PROT_READ);
 
       /* We can handle three different cache file formats here:
@@ -400,84 +434,86 @@  _dl_load_cache_lookup (const char *name)
 	 - the old libc5/glibc2.0/2.1 format
 	 - the old format with the new format in it
 	 The following checks if the cache contains any of these formats.  */
-      if (file != MAP_FAILED && cachesize > sizeof *cache_new
+      if (file != MAP_FAILED && cache->cachesize > sizeof *(cache->cache_new)
 	  && memcmp (file, CACHEMAGIC_VERSION_NEW,
 		     sizeof CACHEMAGIC_VERSION_NEW - 1) == 0
 	  /* Check for corruption, avoiding overflow.  */
-	  && ((cachesize - sizeof *cache_new) / sizeof (struct file_entry_new)
+	  && ((cache->cachesize - sizeof *(cache->cache_new))
+	      / sizeof (struct file_entry_new)
 	      >= ((struct cache_file_new *) file)->nlibs))
 	{
 	  if (! cache_file_new_matches_endian (file))
 	    {
-	      __munmap (file, cachesize);
+	      __munmap (file, cache->cachesize);
 	      file = (void *) -1;
 	    }
-	  cache_new = file;
-	  cache = file;
+	  cache->cache_new = file;
+	  cache->cache = file;
 	}
-      else if (file != MAP_FAILED && cachesize > sizeof *cache
+      else if (file != MAP_FAILED && cache->cachesize > sizeof *(cache->cache)
 	       && memcmp (file, CACHEMAGIC, sizeof CACHEMAGIC - 1) == 0
 	       /* Check for corruption, avoiding overflow.  */
-	       && ((cachesize - sizeof *cache) / sizeof (struct file_entry)
+	       && ((cache->cachesize - sizeof *(cache->cache))
+		   / sizeof (struct file_entry)
 		   >= ((struct cache_file *) file)->nlibs))
 	{
 	  size_t offset;
 	  /* Looks ok.  */
-	  cache = file;
+	  cache->cache = file;
 
 	  /* Check for new version.  */
 	  offset = ALIGN_CACHE (sizeof (struct cache_file)
-				+ cache->nlibs * sizeof (struct file_entry));
+				+ cache->cache->nlibs * sizeof (struct file_entry));
 
-	  cache_new = (struct cache_file_new *) ((void *) cache + offset);
-	  if (cachesize < (offset + sizeof (struct cache_file_new))
-	      || memcmp (cache_new->magic, CACHEMAGIC_VERSION_NEW,
+	  cache->cache_new = (struct cache_file_new *) ((void *) cache->cache + offset);
+	  if (cache->cachesize < (offset + sizeof (struct cache_file_new))
+	      || memcmp (cache->cache_new->magic, CACHEMAGIC_VERSION_NEW,
 			 sizeof CACHEMAGIC_VERSION_NEW - 1) != 0)
-	      cache_new = (void *) -1;
+	      cache->cache_new = (void *) -1;
 	  else
 	    {
-	      if (! cache_file_new_matches_endian (cache_new))
+	      if (! cache_file_new_matches_endian (cache->cache_new))
 		{
 		  /* The old-format part of the cache is bogus as well
 		     if the endianness does not match.  (But it is
 		     unclear how the new header can be located if the
 		     endianness does not match.)  */
-		  cache = (void *) -1;
-		  cache_new = (void *) -1;
-		  __munmap (file, cachesize);
+		  cache->cache = (void *) -1;
+		  cache->cache_new = (void *) -1;
+		  __munmap (file, cache->cachesize);
 		}
 	    }
 	}
       else
 	{
 	  if (file != MAP_FAILED)
-	    __munmap (file, cachesize);
-	  cache = (void *) -1;
+	    __munmap (file, cache->cachesize);
+	  cache->cache = (void *) -1;
 	}
 
-      assert (cache != NULL);
+      assert (cache->cache != NULL);
     }
 
-  if (cache == (void *) -1)
+  if (cache->cache == (void *) -1)
     /* Previously looked for the cache file and didn't find it.  */
     return NULL;
 
   const char *best;
-  if (cache_new != (void *) -1)
+  if (cache->cache_new != (void *) -1)
     {
-      const char *string_table = (const char *) cache_new;
-      best = search_cache (string_table, cachesize,
-			   &cache_new->libs[0].entry, cache_new->nlibs,
-			   sizeof (cache_new->libs[0]), name);
+      const char *string_table = (const char *) cache->cache_new;
+      best = search_cache (string_table, cache->cachesize,
+			   &cache->cache_new->libs[0].entry, cache->cache_new->nlibs,
+			   sizeof (cache->cache_new->libs[0]), name, cache);
     }
   else
     {
-      const char *string_table = (const char *) &cache->libs[cache->nlibs];
+      const char *string_table = (const char *) &cache->cache->libs[cache->cache->nlibs];
       uint32_t string_table_size
-	= (const char *) cache + cachesize - string_table;
+	= (const char *) cache->cache + cache->cachesize - string_table;
       best = search_cache (string_table, string_table_size,
-			   &cache->libs[0], cache->nlibs,
-			   sizeof (cache->libs[0]), name);
+			   &cache->cache->libs[0], cache->cache->nlibs,
+			   sizeof (cache->cache->libs[0]), name, cache);
     }
 
   /* Print our result if wanted.  */
@@ -499,6 +535,14 @@  _dl_load_cache_lookup (const char *name)
   return __strdup (temp);
 }
 
+#ifdef DLCONF
+char *
+_dl_load_cache_lookup (const char *name)
+{
+  return dlconf_load_cache_lookup_from(name, LD_SO_CACHE);
+}
+#endif /* DLCONF */
+
 #ifndef MAP_COPY
 /* If the system does not support MAP_COPY we cannot leave the file open
    all the time since this would create problems when the file is replaced.
@@ -507,14 +551,18 @@  _dl_load_cache_lookup (const char *name)
 void
 _dl_unload_cache (void)
 {
-  if (cache != NULL && cache != (struct cache_file *) -1)
+#ifdef DLCONF
+  dlconf_unload_cache();
+#else
+  if (global_cache.cache != NULL && global_cache.cache != (struct cache_file *) -1)
     {
-      __munmap (cache, cachesize);
-      cache = NULL;
+      __munmap (global_cache.cache, global_cache.cachesize);
+      global_cache.cache = NULL;
     }
 #ifdef SHARED
   /* This marks the glibc_hwcaps_priorities array as out-of-date.  */
-  glibc_hwcaps_priorities_length = 0;
+  global_cache.glibc_hwcaps_priorities_length = 0;
 #endif
+#endif /* DLCONF */
 }
-#endif
+#endif
\ No newline at end of file
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 8a89b71016..997e1f0d1d 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -76,6 +76,11 @@  struct filebuf
 #include <not-cancel.h>
 
 #include <endian.h>
+
+#ifdef DLCONF
+#include "dlconf.h"
+#endif /* DLCONF */
+
 #if BYTE_ORDER == BIG_ENDIAN
 # define byteorder ELFDATA2MSB
 #elif BYTE_ORDER == LITTLE_ENDIAN
@@ -1911,6 +1916,33 @@  _dl_map_object (struct link_map *loader, const char *name,
   assert (nsid >= 0);
   assert (nsid < GL(dl_nns));
 
+#ifdef DLCONF
+  bool try_default = true;
+  char full_path_buff[PATH_MAX];
+  const char *full_path = NULL;
+
+  if (loader != NULL)
+    {
+      /* In case the object is a main program, we need to get an
+         absolute path to the binary file. glibc doesn't know the path,
+         it only have access to argv[0], which might be something like
+         "./some_bin". */
+      if (loader->l_type == lt_executable
+	  && DSO_FILENAME (loader->l_name)[0] != '/')
+	{
+	  full_path = dlconf_get_executable_path (full_path_buff,
+						  sizeof (full_path_buff));
+	}
+      if (full_path == NULL)
+	{
+	  full_path = DSO_FILENAME (loader->l_name);
+	}
+    }
+
+  char *cached = dlconf_get_cached_path (full_path, name, &try_default);
+#endif /* DLCONF */
+
+
   /* Look for this name among those already loaded.  */
   for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next)
     {
@@ -2054,7 +2086,17 @@  _dl_map_object (struct link_map *loader, const char *name,
 	{
 	  /* Check the list of libraries in the file /etc/ld.so.cache,
 	     for compatibility with Linux's ldconfig program.  */
-	  char *cached = _dl_load_cache_lookup (name);
+#ifdef DLCONF
+          if (__glibc_likely(cached == NULL))
+            {
+              if (__glibc_likely(try_default))
+                {
+                  cached = _dl_load_cache_lookup (name);
+                }
+            }
+#else
+          char *cached = _dl_load_cache_lookup (name);
+#endif /* DLCONF */
 
 	  if (cached != NULL)
 	    {
@@ -2106,6 +2148,9 @@  _dl_map_object (struct link_map *loader, const char *name,
 
       /* Finally, try the default path.  */
       if (fd == -1
+#ifdef DLCONF
+          && try_default
+#endif /* DLCONF */
 	  && ((l = loader ?: GL(dl_ns)[nsid]._ns_loaded) == NULL
 	      || __glibc_likely (!(l->l_flags_1 & DF_1_NODEFLIB)))
 	  && __rtld_search_dirs.dirs != (void *) -1)
diff --git a/elf/dl-main.h b/elf/dl-main.h
index 36816f6320..656a32cc06 100644
--- a/elf/dl-main.h
+++ b/elf/dl-main.h
@@ -64,6 +64,9 @@  enum rtld_mode
   {
     rtld_mode_normal, rtld_mode_list, rtld_mode_verify, rtld_mode_trace,
     rtld_mode_list_tunables, rtld_mode_list_diagnostics, rtld_mode_help,
+#ifdef DLCONF
+    rtld_mode_build_dlconf_dat, rtld_mode_print_dlconf_dat
+#endif
   };
 
 /* Aggregated state information extracted from environment variables
diff --git a/elf/dl-open.c b/elf/dl-open.c
index c378da16c0..70d190a246 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -41,6 +41,10 @@ 
 #include <dl-dst.h>
 #include <dl-prop.h>
 
+#ifdef DLCONF
+#include "dlconf.h"
+#endif /* DLCONF */
+
 
 /* We must be careful not to leave us in an inconsistent state.  Thus we
    catch any error and re-raise it after cleaning up.  */
@@ -578,6 +582,14 @@  dl_open_worker_begin (void *a)
   args->map = new = _dl_map_object (call_map, file, lt_loaded, 0,
 				    mode | __RTLD_CALLMAP, args->nsid);
 
+#ifdef DLCONF
+  const void *caller_dlopen = args->caller_dlopen;
+  if (!dlconf_allowed_dlopen(new->l_name, caller_dlopen))
+    {
+      _dl_signal_error (EINVAL, file, NULL, N_ ("prohibited loading path"));
+    }
+#endif /* DLCONF */
+
   /* If the pointer returned is NULL this means the RTLD_NOLOAD flag is
      set and the object is not already loaded.  */
   if (new == NULL)
diff --git a/elf/dl-usage.c b/elf/dl-usage.c
index 5baac4ba8e..28aac90c77 100644
--- a/elf/dl-usage.c
+++ b/elf/dl-usage.c
@@ -198,7 +198,14 @@  setting environment variables (which would be inherited by subprocesses).\n\
   --preload LIST        preload objects named in LIST\n\
   --argv0 STRING        set argv[0] to STRING before running\n\
   --list-tunables       list all tunables with minimum and maximum values\n\
-  --list-diagnostics    list diagnostics information\n\
+  --list-diagnostics    list diagnostics information\n"
+#ifdef DLCONF
+"\
+  --build-ldconfig-dat  build ldconfig.dat file\n\
+  --print-ldconfig-dat  print ldconfig.dat file content\n\
+"
+#endif
+ "\
   --help                display this help and exit\n\
   --version             output version information and exit\n\
 \n\
diff --git a/elf/dlconf-getexecutable.c b/elf/dlconf-getexecutable.c
new file mode 100644
index 0000000000..818ba516d0
--- /dev/null
+++ b/elf/dlconf-getexecutable.c
@@ -0,0 +1,20 @@ 
+/* Copyright (C) 2026 Samsung Electronics Co., Ltd.
+
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+char *dlconf_get_executable_path(char *buff, size_t bufflen) {
+  return NULL;
+}
diff --git a/elf/dlconf-print.c b/elf/dlconf-print.c
new file mode 100644
index 0000000000..ba009e8ef6
--- /dev/null
+++ b/elf/dlconf-print.c
@@ -0,0 +1,265 @@ 
+/* Copyright (C) 2026 Samsung Electronics Co., Ltd.
+
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+#include <ldsodefs.h>
+#include <linux/limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include "dlconf.h"
+
+struct conf_dat_entry_with_children {
+  struct conf_dat_entry *conf_dat_entry;
+  char *name;
+  int32_t children_count;
+  struct conf_dat_entry_with_children **children;
+};
+
+
+struct conf_dat_entry_with_children *_read_conf_dat_entries (void *file, int32_t start)
+{
+  int32_t *nodes_count;
+  int32_t *name_offset;
+  int32_t next_node_start;
+  struct conf_dat_entry *centry = dlconf_read_one_conf_dat_entry(file, start);
+
+  if (centry == NULL)
+    {
+      return NULL;
+    }
+  struct conf_dat_entry_with_children *centry_wc = malloc(sizeof(*centry_wc));
+
+  if (centry_wc == NULL)
+    {
+      goto error;
+    }
+
+  nodes_count = (int32_t*)(file + start + DLCONF_CHILD_COUNT_OFFSET);
+
+  centry_wc->conf_dat_entry = centry;
+  centry_wc->children = malloc(sizeof(struct conf_data_entry_with_children*)*(*nodes_count));
+  if (centry_wc->children == NULL)
+    {
+      free(centry_wc);
+      goto error;
+    }
+
+  centry_wc->children_count = *nodes_count;
+  name_offset = (int32_t*)(file + start + DLCONF_NAME_START_OFFSET);
+  centry_wc->name = (char*)(file + *name_offset);
+  next_node_start = start + DLCONF_NODE_SIZE_IN_BYTES;
+
+  for (int32_t i = 0; i < *nodes_count; i++)
+    {
+      struct conf_dat_entry_with_children *subnode = _read_conf_dat_entries (file, next_node_start);
+      centry_wc->children[i] = subnode;
+      next_node_start += *(int32_t *) (file + next_node_start) * DLCONF_NODE_SIZE_IN_BYTES;
+    }
+
+  return centry_wc;
+error:
+  dlconf_free_string_list(centry->caches, false);
+  dlconf_free_string_list(centry->dlopen_paths, false);
+  free(centry);
+  return NULL;
+}
+
+struct conf_dat_entry_with_children *read_conf_dat_entries (void *file)
+{
+  struct conf_dat_entry_with_children *node = _read_conf_dat_entries(file, DLCONF_DAT_HEADER_SIZE);
+
+  return node;
+}
+
+static void _print_list(const struct string_list *list,
+                        const char *list_name,
+                        const char *prefix,
+                        const char *connector1,
+                        const char *connector2)
+{
+  if (list != NULL)
+    {
+      _dl_printf("%s%s%s%s:\n", prefix, connector1, connector2, list_name);
+      while (list != NULL)
+        {
+          bool is_last = list->next == NULL;
+          const char *mark = is_last ? "`--" : "|--";
+          _dl_printf("%s%s%s  %s %s\n", prefix, connector1, connector2, mark, list->string);
+          list = list->next;
+        }
+    }
+}
+
+static bool file_exists(const char *path, bool *file_exists, bool *is_directory)
+{
+  *is_directory = false;
+  *file_exists = false;
+  struct stat statbuf;
+
+  int res = stat(path, &statbuf);
+
+  if (res != 0)
+    {
+      return errno == ENOENT || errno == ENOTDIR || errno == ENOENT;
+    }
+
+  *file_exists = true;
+
+  if (S_ISDIR(statbuf.st_mode))
+    {
+      *is_directory = true;
+    }
+  return true;
+}
+
+static int _print_conf_dat_tree (const struct conf_dat_entry_with_children *tree, const char *prefix, const char *base_path)
+{
+  char full_path[PATH_MAX+1];
+  int base_path_len = strlen(base_path);
+  int prefix_len;
+
+  strncpy(full_path, base_path, sizeof(full_path) - 1);
+  if (base_path_len > 0 && base_path[base_path_len - 1] != '/')
+    {
+      full_path[base_path_len++] = '/';
+    }
+
+  prefix_len = strlen(prefix);
+
+  for (int32_t i = 0; i < tree->children_count; i++)
+    {
+      bool is_last = i == tree->children_count - 1;
+      const char *conn = is_last?"`-- " : "|-- ";
+      char new_prefix[100];
+      const char *conn2, *conn3, *addon;
+      bool f_exists, is_directory;
+      struct string_list *list;
+
+      strncpy(&full_path[base_path_len], tree->children[i]->name, PATH_MAX - base_path_len - 3);
+      full_path[sizeof(full_path)-1] = '\0';
+
+      strncpy(new_prefix, prefix, sizeof(new_prefix)-1);
+      conn2 = is_last ? "    " : "|   ";
+
+      strncpy(&new_prefix[prefix_len], conn2, sizeof(new_prefix) - 1 - strlen(new_prefix));
+
+      conn3 = tree->children[i]->children_count > 0 ? "|   " : "    ";
+
+      new_prefix[prefix_len + strlen(conn2)] = '\0';
+      addon = "";
+      if (file_exists(full_path, &f_exists, &is_directory))
+        {
+          if (is_directory)
+            addon = "/";
+          else if (!f_exists)
+            addon = " (not exist)";
+        }
+
+      _dl_printf("%s%s%s%s\n", prefix, conn, tree->children[i]->name, addon);
+      _dl_printf("%s%s%sisolation: %d\n", prefix, conn2, conn3, tree->children[i]->conf_dat_entry->isolation);
+      _dl_printf("%s%s%sdlopen_isolation: %d\n", prefix, conn2, conn3, tree->children[i]->conf_dat_entry->dlopen_isolation);
+      list = tree->children[i]->conf_dat_entry->caches;
+      _print_list(list, "caches", prefix, conn2, conn3);
+
+      list = tree->children[i]->conf_dat_entry->dlopen_paths;
+      _print_list(list, "dlopen_paths", prefix, conn2, conn3);
+      _print_conf_dat_tree(tree->children[i], new_prefix, full_path);
+    }
+
+  return DLCONF_ERR_OK;
+}
+
+
+static int print_conf_dat_tree (const struct conf_dat_entry_with_children *centry)
+{
+  _dl_printf("/\n");
+  _dl_printf("|   isolation: %d\n", centry->conf_dat_entry->isolation);
+  _dl_printf("|   dlopen_isolation: %d\n", centry->conf_dat_entry->dlopen_isolation);
+  _print_list(centry->conf_dat_entry->caches, "caches", "|   ", "", "");
+  _print_list(centry->conf_dat_entry->dlopen_paths, "dlopen_paths", "|   ", "", "");
+
+  return _print_conf_dat_tree(centry, "", "/");
+}
+
+static void free_conf_dat_tree (struct conf_dat_entry_with_children *base_node)
+{
+  if (base_node == NULL)
+    {
+      return;
+    }
+
+  for (int32_t i = 0; i < base_node->children_count; i++)
+    {
+      free_conf_dat_tree(base_node->children[i]);
+    }
+
+  dlconf_free_string_list(base_node->conf_dat_entry->dlopen_paths, false);
+  dlconf_free_string_list(base_node->conf_dat_entry->caches, false);
+  free(base_node->conf_dat_entry);
+  free(base_node->children);
+  free(base_node);
+}
+
+/*
+ * Public functions
+ */
+
+void dlconf_print_conf_dat (void)
+{
+  struct stat statbuf;
+  struct conf_dat_entry_with_children *root_node;
+  int fd = open(DLCONF_DAT_PATH, O_RDONLY);
+  void *file;
+
+  if (fd < 0)
+    {
+      return;
+    }
+
+  if (fstat(fd, &statbuf) == -1)
+    {
+      close(fd);
+      return;
+    }
+
+  file = mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
+
+  if (file == MAP_FAILED)
+    {
+      close(fd);
+      return;
+    }
+
+  root_node = read_conf_dat_entries(file);
+
+  if (root_node == NULL)
+    {
+      goto exit;
+    }
+  print_conf_dat_tree(root_node);
+  free_conf_dat_tree(root_node);
+  dlconf_free_g_lists();
+
+exit:
+  munmap(file, statbuf.st_size);
+  close(fd);
+
+}
diff --git a/elf/dlconf.c b/elf/dlconf.c
new file mode 100644
index 0000000000..c02d1ec1be
--- /dev/null
+++ b/elf/dlconf.c
@@ -0,0 +1,2441 @@ 
+/* Copyright (C) 2026 Samsung Electronics Co., Ltd.
+
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+#include <assert.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <search.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <dirent.h>
+#include <ldsodefs.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <linux/limits.h>
+
+#include "dlconf.h"
+
+#define write_or_return(fd, data, size) \
+  {                                     \
+    if (_dl_write(fd, data, size) < 0)  \
+      return DLCONF_ERR_IO;             \
+  }
+
+#define SECTIONS_CAP 10
+#define MAX_PATHS_LIMIT 1000
+
+/*
+ * The struct contains data read from ldconfig.conf files.
+ * Base on the data in this structure will be created ldconfig.dat file.
+ * We cannot build ldconfig.dat directly from ldconfig.conf, because we need to
+ * know the lengths of all strings and the paths must be sorted accordingly.
+ */
+struct cache_info {
+  char *path;
+  struct string_list *dlopen_paths;
+  struct string_list *caches;
+  bool isolation;
+  bool dlopen_isolation;
+  struct cache_info *next;
+};
+
+/*
+ * This structure contains data from cache_info stored as a TRIE.
+ */
+struct node {
+  char *name;
+  int32_t dlopen_paths_idx;
+  int32_t caches_idx;
+  int32_t isolation;
+  int32_t dlopen_isolation;
+  struct node **children;
+  int32_t children_count;
+};
+
+static void *conf_data = NULL;
+static size_t conf_data_size = 0;
+struct hsearch_data path_htab;
+
+struct string_list_array {
+  struct string_list **elements;
+  int32_t count;
+};
+
+static struct string_list_array g_caches = { NULL, 0 };
+static struct string_list_array g_dlopen_paths = { NULL, 0 };
+
+static struct caches *cache_list = NULL;
+
+static void *dlconf_realloc(void *ptr, size_t new_size, size_t old_size)
+{
+  void *old_ptr;
+
+  if (ptr == NULL)
+    {
+      ptr = malloc (new_size);
+      return ptr;
+    }
+
+  old_ptr = ptr;
+  ptr = malloc(new_size);
+
+  if (ptr == NULL)
+    {
+      free (old_ptr);
+      return NULL;
+    }
+
+  memcpy(ptr, old_ptr, old_size);
+  free(old_ptr);
+  return ptr;
+}
+
+struct section_string_list {
+  char *string;
+  struct section *section;
+  struct section_string_list *next;
+};
+
+enum s_type {
+  ST_NORMAL = 0,
+  ST_EXTEND
+};
+
+struct section {
+  char *name;
+  enum s_type section_type;
+  struct section_string_list *paths;
+  struct section_string_list *caches;
+  struct section_string_list *dlopen_paths;
+  bool isolation;
+  bool dlopen_isolation;
+  bool processing; /* Field for cycle detection */
+  bool removed; /* Field for "fast" remove section from array */
+};
+
+struct sections {
+  struct section **sections;
+  size_t capacity;
+  size_t count;
+};
+
+static void free_section(struct section *section)
+{
+  if (section == NULL)
+    {
+      return;
+    }
+
+  free(section->name);
+  free(section);
+}
+
+static void free_sections(struct sections *sections)
+{
+  for (size_t i = 0; i < sections->count; i++)
+    {
+      free_section(sections->sections[i]);
+    }
+
+  free(sections->sections);
+  sections->sections = NULL;
+  sections->count = 0;
+  sections->capacity = 0;
+}
+
+void free_section_string_list (struct section_string_list *slist, bool free_string);
+static struct string_list* copy_string_list(const struct string_list *list);
+static struct string_list* convert_sec_str_list_to_str_list(struct section_string_list *list)
+{
+  struct string_list *new_list = NULL;
+  struct string_list *prev = NULL;
+  struct string_list *first_element = NULL;
+
+  while (list != NULL)
+    {
+      new_list = malloc(sizeof(*new_list));
+
+      if (new_list == NULL)
+       {
+         _dl_error_printf("Memory allocation error\n");
+         goto error;
+       }
+
+      new_list->next = NULL;
+      new_list->string = strdup(list->string);
+
+      if (new_list->string == NULL)
+        {
+         _dl_error_printf("Memory allocation error\n");
+          goto error;
+        }
+
+      if (prev != NULL)
+        {
+          prev->next = new_list;
+        }
+
+      prev = new_list;
+
+      if (first_element == NULL)
+        {
+          first_element = new_list;
+        }
+      list = list->next;
+    }
+  return first_element;
+error:
+  dlconf_free_string_list(new_list, true);
+  dlconf_free_string_list (first_element, true);
+  return NULL;
+}
+
+static int add_to_sorted_list(struct string_list **list, char *element)
+{
+  struct string_list *entry = *list;
+  struct string_list *previous = NULL;
+
+  while (entry != NULL)
+    {
+      if (strcmp (element, entry->string) < 0)
+	{
+	  break;
+	}
+
+      previous = entry;
+      entry = entry->next;
+    }
+
+  struct string_list *new_entry = malloc (sizeof (*new_entry));
+
+  if (new_entry == NULL)
+    {
+      _dl_error_printf ("Memory allocation error\n");
+      return DLCONF_ERR_NOMEM;
+    }
+
+  new_entry->string = element;
+  new_entry->next = entry;
+
+  if (previous == NULL)
+    {
+      *list = new_entry;
+    }
+  else
+    {
+      previous->next = new_entry;
+    }
+
+  return DLCONF_ERR_OK;
+}
+
+static void free_centry(struct conf_dat_entry *centry)
+{
+  dlconf_free_string_list(centry->caches, false);
+  dlconf_free_string_list(centry->dlopen_paths, false);
+  free(centry);
+}
+
+static bool list_eq(const struct string_list *list_a, const struct string_list *list_b) {
+  while (list_a != NULL && list_b != NULL)
+    {
+      if (strcmp(list_a->string, list_b->string) != 0)
+        {
+          return false;
+        }
+
+      list_a = list_a->next;
+      list_b = list_b->next;
+    }
+  return list_a == NULL && list_b == NULL;
+}
+
+/*
+ * The funciton expect "g_dlopen_paths" or "g_caches"" as "g_list" (global
+ * list) and a new string list as "list" argument. If g_list does ot contain
+ * "list", it will be added.
+ * The position of the "list" in "g_list" is returned as the result.
+ */
+static int add_to_g_list(struct string_list_array *g_list, const struct string_list *list)
+{
+  for (size_t i = 0; i < g_list->count; i++)
+  {
+    if (list_eq(list, g_list->elements[i]))
+      return i;
+  }
+
+  g_list->elements = dlconf_realloc(g_list->elements,
+                           sizeof(*g_list->elements)*(g_list->count+1),
+                           sizeof(*g_list->elements)*(g_list->count));
+
+  if (g_list->elements == NULL)
+    {
+      _dl_error_printf ("Memory allocation error\n");
+      return DLCONF_ERR_NOMEM;
+    }
+
+  g_list->elements[g_list->count] = copy_string_list(list);
+  g_list->count++;
+
+  return g_list->count-1;
+}
+
+static struct string_list *get_file_list(const char *path)
+{
+  size_t path_len;
+  struct string_list *files_list = NULL;
+  struct dirent *entry;
+  DIR *dir = opendir(path);
+
+  if (dir == NULL)
+    return NULL;
+
+  path_len = strlen(path);
+
+  while ((entry = readdir (dir)) != NULL)
+    {
+      size_t full_path_len;
+      char *full_path;
+
+      if (entry->d_type != DT_REG)
+	  continue;
+
+      full_path_len = path_len + strlen (entry->d_name) + 1;
+      full_path = malloc (full_path_len + 1);
+
+      if (full_path == NULL)
+	{
+	  dlconf_free_string_list (files_list, true);
+	  files_list = NULL;
+	  goto exit;
+	}
+
+      strncpy (full_path, path, full_path_len);
+      full_path[path_len] = '/';
+      strncpy (&full_path[path_len + 1], entry->d_name,
+	       full_path_len - path_len - 1);
+      full_path[full_path_len] = '\0';
+
+      if (add_to_sorted_list (&files_list, full_path) != DLCONF_ERR_OK)
+	{
+	  dlconf_free_string_list (files_list, true);
+	  files_list = NULL;
+	  free (full_path);
+	  goto exit;
+	}
+    }
+exit:
+  closedir(dir);
+  return files_list;
+}
+
+static void print_cinfo (struct cache_info *cinfo)
+{
+  _dl_debug_printf("\ndynamic loader configuration:\n");
+
+  while (cinfo != NULL)
+    {
+      struct string_list *cache;
+      _dl_debug_printf("record:\n");
+      _dl_debug_printf("  path: %s\n", cinfo->path);
+
+      cache = cinfo->caches;
+
+      while (cache != NULL)
+        {
+          _dl_debug_printf("  cache: %s\n", cache->string);
+          cache = cache->next;
+        }
+
+      _dl_debug_printf("  isolation: %d\n", cinfo->isolation);
+      _dl_debug_printf("  dlopen_isolation: %d\n", cinfo->dlopen_isolation);
+      cinfo = cinfo->next;
+    }
+
+  _dl_debug_printf("\n");
+}
+
+static void free_cache_info (struct cache_info *cinfo)
+{
+  if (__glibc_unlikely(cinfo == NULL))
+      return;
+
+  free(cinfo->path);
+  dlconf_free_string_list(cinfo->caches, true);
+  free_cache_info(cinfo->next);
+  free(cinfo);
+}
+
+static int get_line (const char *content, size_t content_len, char *buff, int buff_len)
+{
+  static size_t c_pos = 0;
+  int line_len = 0;
+  int eof = 0;
+
+  if (__glibc_unlikely(content == NULL || buff == NULL || buff_len <= 0))
+    {
+      _dl_error_printf("Invalid arguments\n");
+      return DLCONF_ERR_INVAL;
+    }
+
+  /* Ignore leading space characters */
+  while (c_pos < content_len && content[c_pos] == ' ')
+      c_pos++;
+
+  while (line_len < buff_len - 1)
+    {
+      if (__glibc_unlikely(c_pos >= content_len))
+        {
+          c_pos = 0;
+          eof = 1;
+          break;
+        }
+      if (__glibc_unlikely(content[c_pos] == '\n'))
+        {
+          c_pos++;
+          break;
+        }
+      buff[line_len] = content[c_pos];
+      line_len++;
+      c_pos++;
+    }
+
+  /* Ignore trailing space characters */
+  while (line_len > 0 && buff[line_len-1] == ' ')
+    {
+      line_len--;
+    }
+
+  buff[line_len] = '\0';
+  return (eof == 1) ? DLCONF_ERR_IO : line_len;
+}
+
+/*
+ * This funciton adds a new section_string_list to the beginning of the list.
+ * So after addition is complete, the list should be reversed. */
+static int section_string_list_add(char *str, struct section_string_list **list)
+{
+  struct section_string_list *str_list;
+
+  if (str == NULL || list == NULL)
+    {
+      _dl_error_printf("Invalid arguments\n");
+      return DLCONF_ERR_INVAL;
+    }
+
+  str_list = malloc(sizeof(*str_list));
+
+  if (str_list == NULL)
+    {
+      _dl_error_printf("Memory allocation error\n");
+      return DLCONF_ERR_NOMEM;
+    }
+
+  str_list->string = strdup(str);
+
+  if (str_list->string == NULL)
+    {
+      free(str_list);
+      _dl_error_printf("Memory allocation error\n");
+      return DLCONF_ERR_NOMEM;
+    }
+
+  str_list->section = NULL;
+  str_list->next = NULL;
+
+  if (*list == NULL)
+    {
+      *list = str_list;
+    }
+  else
+    {
+      str_list->next = *list;
+      *list = str_list;
+    }
+
+  return DLCONF_ERR_OK;
+}
+
+static int section_string_list_reverse(struct section_string_list **list)
+{
+  struct section_string_list *prev = NULL;
+  struct section_string_list *current = *list;
+  struct section_string_list *next = NULL;
+
+  if (list == NULL)
+    {
+      _dl_error_printf("Invalid arguments\n");
+      return DLCONF_ERR_INVAL;
+    }
+
+  while (current != NULL)
+    {
+      next = current->next;
+      current->next = prev;
+      prev = current;
+      current = next;
+    }
+
+  *list = prev;
+  return DLCONF_ERR_OK;
+}
+
+static struct string_list* copy_string_list(const struct string_list *list)
+{
+  struct string_list *new = NULL;
+  struct string_list *prev = NULL;
+
+  if (list == NULL)
+      return NULL;
+
+  while (list != NULL)
+    {
+      struct string_list *cur = malloc(sizeof(*cur));
+      if (cur == NULL)
+        {
+          dlconf_free_string_list(new, true);
+          return NULL;
+        }
+
+      cur->string = strdup(list->string);
+      cur->next = NULL;
+
+      if (cur->string == NULL)
+        {
+          dlconf_free_string_list(new, true);
+          free(cur);
+          return NULL;
+        }
+
+      if (prev == NULL)
+          new = cur;
+      else
+          prev->next = cur;
+
+      prev = cur;
+      list = list->next;
+    }
+
+  return new;
+}
+
+static int get_section_name_and_type(char **raw_name, char **section_name, enum s_type *type)
+{
+  char *addon;
+  int result = DLCONF_ERR_OK;
+
+  if (raw_name == NULL || section_name == NULL || type == NULL)
+    {
+      _dl_error_printf("Invalid arguments\n");
+      return DLCONF_ERR_INVAL;
+    }
+
+  *type = ST_NORMAL;
+  addon = strchr(*raw_name, ':');
+
+  if (addon == NULL)
+    {
+      *section_name = *raw_name;
+      *raw_name = NULL;
+    }
+  else
+    {
+      char *sname = strndup(*raw_name, addon - *raw_name);
+
+      if (sname == NULL)
+        {
+          result = DLCONF_ERR_NOMEM;
+          _dl_error_printf("Memory allocation error\n");
+          goto exit;
+        }
+
+      if (strcmp(addon+1, "ext") == 0)
+          *type = ST_EXTEND;
+
+      *section_name = sname;
+      free(*raw_name);
+      *raw_name = NULL;
+    }
+
+exit:
+  if (result != DLCONF_ERR_OK)
+      *section_name = NULL;
+
+  return result;
+}
+
+static int add_new_section(struct sections *sections,
+                           char **section_name,
+                           struct section_string_list *paths,
+                           struct section_string_list *caches,
+                           struct section_string_list *dlopen_paths,
+                           bool isolation,
+                           bool dlopen_isolation)
+{
+  char *new_section_name = NULL;
+  enum s_type section_type;
+  int result = DLCONF_ERR_OK;
+  struct section *new_section = NULL;
+
+  if ((result = get_section_name_and_type(section_name,
+                                &new_section_name,
+                                &section_type)) != DLCONF_ERR_OK)
+    {
+      goto exit;
+    }
+
+  for (size_t i = 0; i < sections->count; i++)
+    {
+      if (sections->sections[i]->section_type == ST_NORMAL &&
+          sections->sections[i]->section_type == section_type &&
+          strcmp(sections->sections[i]->name, new_section_name) == 0)
+        {
+          /* New section with the same name and type. Ignore it. */
+          _dl_error_printf("Duplicated section: %s.\n", new_section_name);
+          result = DLCONF_ERR_OK;
+          goto exit;
+        }
+    }
+
+  new_section = malloc(sizeof(*new_section));
+
+  if (new_section == NULL)
+    {
+      _dl_error_printf("Memory allocation error\n");
+      result = DLCONF_ERR_NOMEM;
+      goto exit;
+    }
+
+  new_section->name = new_section_name;
+  new_section->section_type = section_type;
+  new_section->paths = paths;
+  new_section->caches = caches;
+  new_section->dlopen_paths = dlopen_paths;
+  new_section->isolation = isolation;
+  new_section->dlopen_isolation = dlopen_isolation;
+  new_section->processing = false;
+  new_section->removed = false;
+
+  if (sections->count >= sections->capacity)
+    {
+      sections->sections = dlconf_realloc(sections->sections,
+                                          sizeof(*sections->sections)*(sections->capacity + SECTIONS_CAP),
+                                          sizeof(*sections->sections)*sections->capacity);
+      if (sections->sections == NULL)
+        {
+          result = DLCONF_ERR_NOMEM;
+          _dl_error_printf("Memory allocation error\n");
+          goto exit;
+        }
+      sections->capacity += SECTIONS_CAP;
+    }
+   sections->sections[sections->count++] = new_section;
+
+exit:
+  if (result != DLCONF_ERR_OK)
+    {
+      if (new_section == NULL)
+	{
+	  /* If `new_section` is NULL, then perhaps is alredy allocated memory
+	     for `new_section_name`*/
+	  free (new_section_name);
+	}
+      else
+	{
+          /* If not, the `new_section_name` will be freed with `new_section` */
+	  free_section (new_section);
+	}
+    }
+  return result;
+}
+
+static int append_to_the_end(struct section_string_list **base, struct section_string_list *addon)
+{
+  if (base == NULL)
+    {
+      _dl_error_printf("Invalid arguments\n");
+      return DLCONF_ERR_INVAL;
+    }
+
+  if (*base == NULL)
+    {
+      *base = addon;
+      return DLCONF_ERR_OK;
+    }
+
+  struct section_string_list *last = *base;
+
+  while (last->next != NULL)
+      last = last->next;
+
+  last->next = addon;
+  return DLCONF_ERR_OK;
+}
+
+static int merge_sections(struct section *base, struct section *extensions)
+{
+  int result;
+  /*
+   * Settings from extension are added before settings that were already.
+   */
+  if ((result = append_to_the_end(&extensions->caches, base->caches)) != DLCONF_ERR_OK ||
+      (result = append_to_the_end(&extensions->paths, base->paths)) != DLCONF_ERR_OK ||
+      (result = append_to_the_end(&extensions->dlopen_paths, base->dlopen_paths)) != DLCONF_ERR_OK)
+    {
+      return result;
+    }
+
+  base->caches = extensions->caches;
+  extensions->caches = NULL;
+  base->paths = extensions->paths;
+  extensions->paths = NULL;
+  base->dlopen_paths = extensions->dlopen_paths;
+  extensions->dlopen_paths = NULL;
+
+  return DLCONF_ERR_OK;
+}
+
+static int apply_extension(struct section *section_a, struct section *section_b)
+{
+  int result;
+  struct section *a, *b;
+
+  /* Now we check which sections is BASE and which is the extension.
+     In case both are extensions, the order doesn't matter. */
+  if (section_a->section_type == ST_EXTEND)
+    {
+      a = section_b;
+      b = section_a;
+    }
+  else
+    {
+      a = section_a;
+      b = section_b;
+    }
+
+  if ((result = merge_sections (a, b)) != DLCONF_ERR_OK)
+    {
+      return result;
+    }
+
+  b->removed = true;
+
+  return DLCONF_ERR_OK;
+}
+
+struct section *
+find_section (const char *name, struct sections *sections, size_t *idx)
+{
+  size_t bottom = 0;
+  size_t upper = sections->count - 1;
+  ssize_t found_idx = -1;
+
+  while (bottom <= upper)
+    {
+      size_t mid = (bottom + upper) / 2;
+      struct section *current = sections->sections[mid];
+      int res = strcmp (name, current->name);
+
+      if (res == 0)
+        {
+          /* Found matching name - check if it's removed */
+          if (!current->removed)
+            {
+              found_idx = mid;
+              goto success;
+            }
+
+          /* Search for nearest non-removed section with same name
+             Check left side first */
+          for (ssize_t i = mid - 1; i >= 0; i--)
+            {
+              if (strcmp (name, sections->sections[i]->name) != 0)
+                {
+                  break;
+                }
+              if (!sections->sections[i]->removed)
+                {
+                  /* We want most left element, so save current index and keep
+                     looking */
+                  found_idx = i;
+                }
+            }
+
+          if (found_idx > -1)
+              goto success;
+          /* Then check right side */
+          for (size_t i = mid + 1; i < sections->count; i++)
+            {
+              if (strcmp (name, sections->sections[i]->name) != 0)
+                  break;
+
+              if (!sections->sections[i]->removed)
+                {
+                  found_idx = i;
+                  goto success;
+                }
+            }
+          return NULL;
+        }
+      else if (res < 0)
+        {
+          upper = mid - 1;
+        }
+      else
+        {
+          bottom = mid + 1;
+        }
+    }
+
+  return NULL;
+success:
+  if (idx != NULL)
+      *idx = found_idx;
+
+  return sections->sections[found_idx];
+}
+
+static int apply_extensions(struct sections *sections)
+{
+  if (sections == NULL)
+    {
+      _dl_error_printf("Invalid arguments\n");
+      return DLCONF_ERR_INVAL;
+    }
+  /* Go through all sections and look for section extension like:
+
+       [section:ext]
+   */
+  for (size_t i = 0; i < sections->count; i++)
+    {
+      struct section *cur_section = sections->sections[i];
+
+      if (cur_section->removed)
+        {
+          continue;
+        }
+
+      for (size_t n = i + 1; n < sections->count; n++)
+        {
+          struct section *next_section = sections->sections[n];
+          /* Ignore removed sections */
+          if (next_section->removed)
+              continue;
+
+          if (strcmp (cur_section->name, next_section->name) == 0)
+            {
+              int result;
+              if ((result = apply_extension (cur_section, next_section)) != DLCONF_ERR_OK)
+                  return result;
+            }
+          else
+            {
+              /* Sections are sorted, so if the name is not the same, we can
+                 break inner loop */
+              break;
+            }
+        }
+    }
+  return 0;
+}
+
+static void apply_links_string_list(struct section_string_list *list, struct sections *sections)
+{
+  while (list != NULL)
+    {
+      if (list->section != NULL)
+          continue; /* already processed */
+
+      /* If the string contains a link to another section, here we look for it
+         and save a pointer */
+      if (list->string != NULL && list->string[0] == ':')
+        {
+          list->section = find_section(&list->string[1], sections, NULL);
+        }
+      list = list->next;
+    }
+  return;
+}
+
+enum string_list_type {
+  SLT_PATH = 0,
+  SLT_CACHE,
+  SLT_DLOPEN_PATH
+};
+
+static int replace_section_string_list(struct section_string_list *list,
+                                   struct section_string_list *new_list)
+{
+  struct section_string_list *tmp_list;
+  struct section_string_list *cur_next_list;
+
+  if (list == NULL || new_list == NULL)
+    {
+      _dl_error_printf("Invalid arguments\n");
+      return DLCONF_ERR_INVAL;
+    }
+  /* Free our ":some_section" string */
+  free(list->string);
+  /* and assign the string from the first new_list item */
+  list->string = new_list->string;
+  new_list->string = NULL;
+
+  /* Remove link to the section, because it is not needed when we copy its
+     contents */
+  list->section = NULL;
+
+  /* Save the rest of the list */
+  cur_next_list = list->next;
+
+  /* Insert new_list as the next current item, or more precisely, the next
+     element in the "new_list" , because the string from the first element of
+     the "new_list" we copied earlier. We do this because we don't have a
+     pointer to the predecessor of the "list", and we can't do something like
+
+         list->prev->next = new_list
+
+     so we do:
+
+         list->string = new_list->string
+         list->next = new_list->next
+   */
+  list->next = new_list->next;
+  new_list->next = NULL;
+
+  /* Looking for the end of the list */
+  tmp_list = list;
+  while (tmp_list->next != NULL)
+      tmp_list = tmp_list->next;
+
+  /* Attach the original rest of the list at the end */
+  tmp_list->next = cur_next_list;
+  return DLCONF_ERR_OK;
+}
+
+static int replace_link_with_content(struct section_string_list *list, enum string_list_type type);
+
+static int get_linked_data(struct section *section,
+                           enum string_list_type type,
+                           struct section_string_list **out_list)
+{
+  int result = DLCONF_ERR_OK;
+  struct section_string_list *list = NULL;
+  struct section_string_list *last_element = NULL;
+
+  if (section == NULL || out_list == NULL)
+    {
+      _dl_error_printf("Invalid arguments\n");
+      return DLCONF_ERR_INVAL;
+    }
+
+  if (section->processing)
+    {
+      _dl_error_printf("Cycle detected in section %s\n", section->name);
+      return DLCONF_ERR_CYCLE;
+    }
+
+  section->processing = true;
+
+  switch (type) {
+    case SLT_PATH:
+      list = section->paths;
+      break;
+    case SLT_CACHE:
+      list = section->caches;
+      break;
+    case SLT_DLOPEN_PATH:
+      list = section->dlopen_paths;
+      break;
+  }
+
+  while (list != NULL)
+    {
+      struct section_string_list *new_list = NULL;
+
+      if (list->section == NULL)
+        {
+          new_list = malloc(sizeof(*new_list));
+
+          if (new_list == NULL)
+            {
+              result = DLCONF_ERR_NOMEM;
+              goto exit;
+            }
+
+          new_list->next = NULL;
+          new_list->section = NULL;
+          new_list->string = strdup(list->string);
+
+          if (new_list->string == NULL)
+            {
+              result = DLCONF_ERR_NOMEM;
+              _dl_error_printf("Memory allocation error\n");
+              free(new_list);
+              free_section_string_list(*out_list, true);
+              goto exit;
+            }
+
+          if (*out_list == NULL)
+            {
+              *out_list = new_list;
+              last_element = new_list;
+            }
+          else
+            {
+	      /* We want to append new item to the *out_list,
+	         so we have to find the last element */
+	      if (last_element == NULL)
+                {
+                  last_element = *out_list;
+
+                  while (last_element->next != NULL)
+                    {
+                      last_element = last_element->next;
+                    }
+                }
+              last_element->next = new_list;
+              last_element = new_list;
+            }
+        }
+      else
+        {
+          /* Replace the string like ":other_section" in current "list"
+             The list can looks like:
+
+                 /some/path1
+                 :other_section <-- here we are
+                 /some/path2
+           */
+          if ((result = replace_link_with_content(list, type)) != DLCONF_ERR_OK)
+            {
+              goto exit;
+            }
+          /* Now the list looks like this:
+                 /some/path1
+                 /some/other_section/path1 <-- here we are
+                 /some/other_section/path2
+                 /some/path2
+             We want to copy the current item, so we skip taking list->next item. */
+          continue;
+        }
+      list = list->next;
+    }
+
+exit:
+  section->processing = false;
+  return result;
+}
+
+static int replace_link_with_content(struct section_string_list *list, enum string_list_type type)
+{
+  int result = DLCONF_ERR_OK;
+  struct section_string_list *new_list = NULL;
+
+  if (list == NULL || list->section == NULL)
+      return result;
+
+  /* Get the content of the linked section, and insert it into the "list" */
+  if ((result = get_linked_data(list->section, type, &new_list)) == DLCONF_ERR_OK)
+      result = replace_section_string_list(list, new_list);
+
+  if (new_list != NULL)
+      free(new_list);
+
+  return result;
+}
+
+
+static int collect_linked_data(struct section_string_list *list,
+                               enum string_list_type type)
+{
+  int result = DLCONF_ERR_OK;
+  /* Go through the list and deal with entries like:
+
+       cache=:other_section
+   */
+  while (list != NULL)
+    {
+      /* At this point, we have pointers to sections, thanks to
+         apply_links_string_list(), so thats way we check if the list item is
+         "normal" path or link to other section */
+      if (list->section != NULL)
+        {
+          if ((result = replace_link_with_content(list, type)) != DLCONF_ERR_OK)
+              return result;
+        }
+      list = list->next;
+    }
+  return DLCONF_ERR_OK;
+}
+
+static int apply_links (struct sections *sections)
+{
+  if (sections == NULL)
+    {
+      _dl_error_printf("Invalid arguments\n");
+      return DLCONF_ERR_INVAL;
+    }
+  int result = DLCONF_ERR_OK;
+  /* Replacing links:
+
+       cache=:other_section
+
+     is done in two steps:
+     1 - sections referenced by the link are searched for and the pointers to them are saved in the list
+     2 - recursively retrieve paths from linked sections
+
+
+     First stage - link sections in section_string_list */
+
+  for (size_t i = 0; i < sections->count; i++)
+    {
+      if (sections->sections[i]->removed)
+          continue;
+
+      struct section *cur_section = sections->sections[i];
+      apply_links_string_list(cur_section->paths, sections);
+      apply_links_string_list(cur_section->caches, sections);
+      apply_links_string_list(cur_section->dlopen_paths, sections);
+    }
+
+  /* Second stage - collect caches, paths, and dlopen_paths */
+  for (size_t i = 0; i < sections->count; i++)
+    {
+      if (sections->sections[i]->removed)
+          continue;
+      struct section *cur_section = sections->sections[i];
+      if ((result = collect_linked_data(cur_section->paths, SLT_PATH)) != DLCONF_ERR_OK)
+          return result;
+
+      if ((result = collect_linked_data(cur_section->caches, SLT_CACHE)) != DLCONF_ERR_OK)
+          return result;
+
+      if ((result = collect_linked_data(cur_section->dlopen_paths, SLT_DLOPEN_PATH)) != DLCONF_ERR_OK)
+          return result;
+    }
+
+  return result;
+}
+
+static int add_new_cinfo(struct cache_info **cinfo,
+                         struct section_string_list *paths,
+                         struct section_string_list *caches,
+                         struct section_string_list *dlopen_paths,
+                         bool isolation,
+                         bool dlopen_isolation)
+{
+  struct section_string_list *l;
+  const struct section_string_list *cur_path = paths;
+
+  if (cinfo == NULL)
+      return DLCONF_ERR_INVAL;
+
+  /* If more paths than one were defined in the section, we need to create more
+     objects because one cache_info object can store only one path (for later
+     optimizations). */
+  while (cur_path != NULL)
+    {
+      int done;
+      struct string_list *caches_new;
+      struct string_list *dlopen_paths_new;
+      struct cache_info *c, *prev_c;
+      struct cache_info *cinfo_new = malloc(sizeof(*cinfo_new));
+
+      if (cinfo_new == NULL)
+        {
+          _dl_error_printf("Memory allocation error\n");
+          return DLCONF_ERR_NOMEM;
+        }
+
+      caches_new = convert_sec_str_list_to_str_list(caches);
+      dlopen_paths_new = convert_sec_str_list_to_str_list(dlopen_paths);
+      cinfo_new->path = strdup(cur_path->string);
+
+      if (cinfo_new->path == NULL)
+        {
+          dlconf_free_string_list(caches_new, true);
+          dlconf_free_string_list(dlopen_paths_new, true);
+          free(cinfo_new);
+          return DLCONF_ERR_NOMEM;
+        }
+
+      cinfo_new->caches = caches_new;
+      cinfo_new->dlopen_paths = dlopen_paths_new;
+      cinfo_new->isolation = isolation;
+      cinfo_new->dlopen_isolation = dlopen_isolation;
+      cinfo_new->next = NULL;
+
+      if (*cinfo == NULL)
+        {
+          *cinfo = cinfo_new;
+          cur_path = cur_path->next;
+          continue;
+        }
+      c = *cinfo;
+      prev_c = NULL;
+      done = 0;
+
+      /* The new object of type cache_info is inserted into the list in
+         ascending order. */
+      while (c != NULL)
+        {
+          if (strcmp(cur_path->string, c->path) < 0)
+            {
+              cinfo_new->next = c;
+
+              if (prev_c == NULL)
+                  *cinfo = cinfo_new;
+              else
+                  prev_c->next = cinfo_new;
+
+              done = 1;
+              break;
+            }
+          prev_c = c;
+          c = c->next;
+        }
+
+      if (done == 0)
+          prev_c->next = cinfo_new;
+
+      cur_path = cur_path->next;
+    }
+
+  l = paths;
+
+  while (l != NULL)
+    {
+      struct section_string_list *t = l;
+      l = l->next;
+      free(t);
+    }
+  return DLCONF_ERR_OK;
+}
+
+static int convert_sections_to_cache_info(struct sections *sections, struct cache_info **cache_info)
+{
+  int result = DLCONF_ERR_OK;
+  for (size_t i = 0; i < sections->count; i++)
+    {
+      struct section *section = sections->sections[i];
+      if (section->removed)
+          continue;
+
+      if ((result = add_new_cinfo(cache_info,
+                                  section->paths,
+                                  section->caches,
+                                  section->dlopen_paths,
+                                  section->isolation,
+                                  section->dlopen_isolation)) != DLCONF_ERR_OK)
+        {
+          goto exit;
+        }
+    }
+exit:
+  if (result != DLCONF_ERR_OK)
+    {
+      free_cache_info(*cache_info);
+      *cache_info = NULL;
+    }
+  return result;
+}
+
+static bool path_already_defined (char *new_path, struct sections *sections)
+{
+  char tmp_path[PATH_MAX];
+  ENTRY item;
+  item.key = new_path;
+  item.data = NULL;
+  ENTRY *out = NULL;
+
+  hsearch_r(item, FIND, &out, &path_htab);
+  if (out != NULL)
+      return true;
+
+  /* Since paths don't have to end with a '/', as it's valid to point to a specific binary,
+     we need to check whether we already have the path defined both with and without the trailing slash,
+     to detect that '/some/a' is the same as '/some/a/'. */
+  size_t new_path_len = strlen (new_path);
+
+  if (new_path[new_path_len - 1] == '/')
+    {
+      strncpy (tmp_path, new_path, sizeof(tmp_path) - 1);
+      tmp_path[new_path_len - 1] = '\0';
+    }
+  else
+    {
+      strncpy (tmp_path, new_path, sizeof(tmp_path) - 1);
+      tmp_path[new_path_len] = '/';
+      tmp_path[new_path_len + 1] = '\0';
+    }
+
+  item.key = tmp_path;
+  out = NULL;
+  hsearch_r(item, FIND, &out, &path_htab);
+
+  return out != NULL;
+}
+
+static bool is_positive_value(const char *value, size_t value_len)
+{
+  return (strncmp(value, "yes", sizeof("yes")) == 0 ||
+          strncmp(value, "on", sizeof("on")) == 0 ||
+          strncmp(value, "1", sizeof("1")) == 0);
+}
+
+static int parse_paraini (const char *content, size_t content_len, struct sections *sections)
+{
+  char *section_name = NULL;
+  struct section_string_list *paths = NULL;
+  struct section_string_list *caches = NULL;
+  struct section_string_list *dlopen_paths = NULL;
+  bool isolation = false;
+  bool dlopen_isolation = false;
+
+  char line[1024];
+  int line_len;
+  int find_next_section = 1;
+  int result = DLCONF_ERR_OK;
+
+  if (content == NULL || sections == NULL)
+    {
+      _dl_error_printf ("Invalid arguments\n");
+      return DLCONF_ERR_INVAL;
+    }
+
+  while ((line_len = get_line (content, content_len, line, sizeof (line)))
+	 >= 0)
+    {
+      int key_len;
+      char *sname_start, *sname_end, *key, *value, *equal_sign;
+      /* Ignore empty lines */
+      if (line_len <= 0)
+	continue;
+
+      /* Ignore commented lines */
+      if (line[0] == '#')
+	continue;
+
+      sname_start = strchr (line, '[');
+      if (sname_start != NULL)
+	{
+	  sname_end = strchr (line, ']');
+
+	  if (sname_end == NULL)
+	    continue; /* Invalid section tag */
+
+	  /* New section */
+	  find_next_section = 0;
+
+	  if (paths != NULL || caches != NULL)
+	    {
+	      section_string_list_reverse (&paths);
+	      section_string_list_reverse (&caches);
+	      section_string_list_reverse (&dlopen_paths);
+	      if ((result = add_new_section (sections, &section_name, paths,
+		                             caches, dlopen_paths, isolation,
+		                             dlopen_isolation)) != DLCONF_ERR_OK)
+		{
+		  goto exit;
+		}
+	      section_name = NULL;
+	      paths = NULL;
+	      caches = NULL;
+	      dlopen_paths = NULL;
+	    }
+
+	  if (dlopen_paths != NULL)
+	    dlopen_paths = NULL;
+
+	  section_name = strndup (sname_start + 1, sname_end - sname_start - 1);
+	  if (section_name == NULL)
+	    {
+	      result = DLCONF_ERR_NOMEM;
+	      _dl_error_printf ("Memory allocation error\n");
+	      goto exit;
+	    }
+
+	  isolation = false;
+	  dlopen_isolation = false;
+	  continue;
+	}
+      else if (find_next_section == 1)
+	{
+	  /* We are looking for the next section. */
+	  continue;
+	}
+      /* We have a section, so we are looking for key=value. */
+      equal_sign = strchr (line, '=');
+
+      if (equal_sign == NULL)
+	{
+	  /* Unexpected line (empty?), let's try the next one. */
+	  continue;
+	}
+
+      key_len = equal_sign - line - 1;
+      key = line;
+
+      /* Remove spaces from the end of a key */
+      while (key_len > 0 && key[key_len] == ' ')
+	key_len--;
+
+      key_len++;
+      if (key_len == 0)
+	continue;
+
+      key[key_len] = '\0';
+
+      value = equal_sign + 1;
+
+      /* Remove spaces from the begin of a value */
+      while (*value == ' ')
+	value++;
+
+      int value_len = line + line_len - value;
+
+      if (value_len == 0)
+	continue;
+
+      value[value_len] = '\0';
+
+      if (strncmp (key, "path", sizeof ("path")) == 0)
+	{
+	  struct section_string_list *path_list = paths;
+	  int skip_this_path = 0;
+	  while (path_list != NULL)
+	    {
+	      if (strcmp (path_list->string, value) == 0)
+		{
+		  /* The same path is duplicated in the same section. Skip it. */
+		  skip_this_path = 1;
+		  break;
+		}
+	      path_list = path_list->next;
+	    }
+
+	  if (skip_this_path == 1)
+	    continue;
+
+	  if (path_already_defined (value, sections))
+	    {
+	      /* Specified path is already defined by a higher priority config
+	         file. */
+	      _dl_error_printf ("Path %s already defined. This path from %s "
+				"section will be ignored.\n",
+				value, section_name);
+	      continue;
+	    }
+
+	  if ((result = section_string_list_add (value, &paths))
+	      != DLCONF_ERR_OK)
+	    {
+	      goto exit;
+	    }
+
+	  /* Add path to the hashmap */
+	  ENTRY item;
+	  item.key = paths->string;
+	  item.data = (void *) 0;
+	  ENTRY *out = NULL;
+	  hsearch_r (item, ENTER, &out, &path_htab);
+	}
+      else if (strncmp (key, "cache", sizeof ("cache")) == 0)
+	{
+	  if ((result = section_string_list_add (value, &caches)) != DLCONF_ERR_OK)
+	      goto exit;
+	}
+      else if (strncmp (key, "dlopen_path", sizeof ("dlopen_path")) == 0)
+	{
+	  if ((result = section_string_list_add (value, &dlopen_paths)) != DLCONF_ERR_OK)
+	      goto exit;
+	}
+      else if (strncmp (key, "isolation", sizeof ("isolation")) == 0)
+	{
+	  if (is_positive_value (value, value_len))
+	    isolation = true;
+	  else
+	    isolation = false;
+	}
+      else if (strncmp (key, "dlopen_isolation", sizeof ("dlopen_isolation"))
+	       == 0)
+	{
+	  if (is_positive_value (value, value_len))
+	      dlopen_isolation = true;
+	  else
+	      dlopen_isolation = false;
+	}
+    }
+  if (caches != NULL || paths != NULL)
+    {
+      section_string_list_reverse (&paths);
+      section_string_list_reverse (&caches);
+      section_string_list_reverse (&dlopen_paths);
+      result = add_new_section (sections, &section_name, paths, caches,
+				dlopen_paths, isolation, dlopen_isolation);
+    }
+exit:
+  free(section_name);
+  if (result != DLCONF_ERR_OK)
+    {
+      free_section_string_list(paths, true);
+      free_section_string_list(caches, true);
+      free_section_string_list(dlopen_paths, true);
+    }
+  return result;
+#undef PREFIX_MAX_LEN
+#undef CACHE_STR
+#undef PATH_STR
+}
+
+static bool file_exists (const char *path);
+
+static int read_ld_so_config(const char *path, struct sections *sections)
+{
+  int result;
+  size_t file_size = 0;
+  char *file = (char*)_dl_sysdep_read_whole_file(path, &file_size, PROT_READ);
+
+  if (file == MAP_FAILED)
+    {
+      /* Probably a config file is missing. We can live with that. */
+      return DLCONF_ERR_IO;
+    }
+
+  result = parse_paraini(file, file_size, sections);
+
+  __munmap(file, file_size);
+  return result;
+}
+
+static void try_read_ld_so_config(const char *path, struct sections *sections)
+{
+  int result = read_ld_so_config(path, sections);
+  if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_LIBS && result != DLCONF_ERR_OK))
+    _dl_debug_printf ("Error while reading configuration file: %s (%d)\n", path, result);
+}
+
+static char *dlconf_find_cached_path (const char *name, const struct string_list *list)
+{
+  assert(name);
+  char *cached = NULL;
+
+  while (list != NULL)
+    {
+      char *cache_path = list->string;
+      if (cache_path != NULL)
+        {
+          cached = dlconf_load_cache_lookup_from(name, cache_path);
+	  if (cached != NULL)
+	    break;
+	}
+      list = list->next;
+    }
+  return cached;
+}
+
+static int compare_sections(const void *l, const void *r)
+{
+  const struct section *a = *(struct section**)l;
+  const struct section *b = *(struct section**)r;
+
+  int res = strcmp(a->name, b->name);
+
+  if (res == 0)
+    res = a->section_type - b->section_type;
+
+  return res;
+}
+
+static int read_ld_so_configs(struct cache_info **cinfo)
+{
+  int result = DLCONF_ERR_OK;
+
+  struct config_path {
+    const char *directory;
+    const char *file;
+  };
+
+  /* Configuration files have a certain priority.
+     Those defined first have the highest priority
+     Files in the directory are sorted in ascending order and have a higher
+     priority than the conf file defined in structure below.
+
+     The motivation is so that it is possible to provide (for example by
+     overlayfs) a configuration that will overwrite the configuration that came
+     with the system. */
+  struct config_path configs[] = {
+    { DLCONF_CONF_PATH ".d/", DLCONF_CONF_PATH },
+    { NULL, NULL },
+  };
+  size_t i = 0;
+  struct config_path *cur_config = &configs[i];
+  struct sections sections = {
+    .sections = NULL,
+    .count = 0,
+    .capacity = 0
+  };
+
+  memset(&path_htab, 0, sizeof(path_htab));
+  hcreate_r (MAX_PATHS_LIMIT, &path_htab);
+
+  while (cur_config->directory != NULL && cur_config->file != NULL)
+    {
+      struct string_list *s_list = get_file_list(cur_config->directory);
+      struct string_list *element = s_list;
+
+      /* Potential errors don't pervent us from reading the next config files. */
+      while (element != NULL)
+        {
+          try_read_ld_so_config(element->string, &sections);
+          element = element->next;
+        }
+
+      try_read_ld_so_config(cur_config->file, &sections);
+      i++;
+      cur_config = &configs[i];
+      dlconf_free_string_list(s_list, true);
+    }
+
+  hdestroy_r (&path_htab);
+
+  if (sections.sections == NULL)
+    {
+      /* No sections. We can finish the work. */
+      return result;
+    }
+  /* Sort sections array so that binary search would be possible */
+  qsort (sections.sections,
+         sections.count,
+         sizeof (*sections.sections),
+         compare_sections);
+
+  if ((result = apply_extensions (&sections)) != DLCONF_ERR_OK
+      || (result = apply_links (&sections)) != DLCONF_ERR_OK
+      || (result = convert_sections_to_cache_info (&sections, cinfo)) != DLCONF_ERR_OK)
+    {
+      free_sections (&sections);
+      return result;
+    }
+  free_sections (&sections);
+
+  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
+      print_cinfo(*cinfo);
+
+  return result;
+}
+
+static int add_list_to_g_list(struct string_list_array *glist,
+                         struct string_list *paths_list,
+                         int32_t *path_index)
+{
+  if (paths_list == NULL)
+    {
+      *path_index = -1;
+    }
+  else
+    {
+      int32_t idx = add_to_g_list(glist, paths_list);
+      if (idx == -1)
+        {
+          _dl_error_printf("Memory allocation error\n");
+          return DLCONF_ERR_NOMEM;
+        }
+      *path_index = idx;
+    }
+
+  return DLCONF_ERR_OK;
+}
+
+static int insert_to_trie (struct node *base_node, char *token, const struct cache_info *cinfo, char **saveptr)
+{
+  int result = DLCONF_ERR_OK;
+  char *new_token;
+  struct node *new_child;
+  struct node **new_children;
+
+  for (int i = 0; i < base_node->children_count; i++)
+    {
+      if (strcmp(token, base_node->children[i]->name) == 0)
+        {
+          char *new_token = strtok_r(NULL, "/", saveptr);
+          if (new_token == NULL)
+              return DLCONF_ERR_OK;
+
+          return insert_to_trie(base_node->children[i], new_token, cinfo, saveptr);
+        }
+    }
+
+  new_child = malloc(sizeof(*new_child));
+
+  if (new_child == NULL)
+    {
+      _dl_error_printf("Memory allocation error\n");
+      return DLCONF_ERR_NOMEM;
+    }
+
+  new_child->children_count = 0;
+  new_child->name = strdup(token);
+
+  if (new_child->name == NULL)
+    {
+      _dl_error_printf("Memory allocation error\n");
+      free(new_child);
+      return DLCONF_ERR_NOMEM;
+    }
+
+  new_child->children = NULL;
+
+  new_children = dlconf_realloc(base_node->children,
+                                sizeof(struct node*)*(base_node->children_count + 1),
+                                sizeof(struct node*)*(base_node->children_count));
+  if (new_children == NULL)
+    {
+      _dl_error_printf("Memory allocation error\n");
+      result = DLCONF_ERR_NOMEM;
+      goto exit;
+    }
+
+  new_children[base_node->children_count] = new_child;
+  base_node->children_count++;
+  base_node->children = new_children;
+
+  new_token = strtok_r(NULL, "/", saveptr);
+
+  if (new_token != NULL)
+    {
+      new_child->isolation = base_node->isolation;
+      new_child->dlopen_isolation = base_node->dlopen_isolation;
+      new_child->caches_idx = base_node->caches_idx;
+      new_child->dlopen_paths_idx = base_node->dlopen_paths_idx;
+
+     if ((result = insert_to_trie(new_child, new_token, cinfo, saveptr)) != DLCONF_ERR_OK)
+      {
+        goto exit;
+      }
+    }
+  else
+    {
+      if ((result = add_list_to_g_list(&g_caches,
+                                       cinfo->caches,
+                                       &new_child->caches_idx)) != DLCONF_ERR_OK ||
+          (result = add_list_to_g_list(&g_dlopen_paths,
+                                       cinfo->dlopen_paths,
+                                       &new_child->dlopen_paths_idx)) != DLCONF_ERR_OK)
+        {
+          goto exit;
+        }
+
+      new_child->isolation = cinfo->isolation;
+      new_child->dlopen_isolation = cinfo->dlopen_isolation;
+    }
+
+exit:
+  if (result != DLCONF_ERR_OK)
+    {
+      if (new_children != NULL)
+        {
+          new_children[base_node->children_count] = NULL;
+          base_node->children_count--;
+        }
+      free(new_child->name);
+      free(new_child);
+    }
+
+  return result;
+}
+
+void free_node(struct node *node)
+{
+  for (size_t i = 0; i < node->children_count; i++)
+    free_node (node->children[i]);
+
+  free(node->name);
+  free(node->children);
+  free(node);
+}
+
+static struct node *convert_to_trie (const struct cache_info *cinfo)
+{
+  struct node *base_node = malloc(sizeof(*base_node));
+
+  if (base_node == NULL)
+    return NULL;
+
+  /* Create the root node */
+  base_node->children_count = 0;
+  base_node->children = NULL;
+  base_node->name = strdup("/");
+
+  if (base_node->name == NULL)
+    goto error;
+
+  base_node->isolation = 0;
+  base_node->dlopen_isolation = 0;
+  base_node->dlopen_paths_idx = -1;
+  base_node->caches_idx = -1;
+
+  while (cinfo != NULL)
+    {
+      char *path = strdup(cinfo->path);
+
+      if (path == NULL)
+	goto error;
+
+      /* The base_node is handled differently */
+      if (strncmp(path, "/", 2) == 0)
+        {
+          free(path);
+          if (add_list_to_g_list(&g_caches, cinfo->caches, &base_node->caches_idx) != DLCONF_ERR_OK ||
+              add_list_to_g_list(&g_dlopen_paths, cinfo->dlopen_paths, &base_node->dlopen_paths_idx) != DLCONF_ERR_OK)
+            {
+              goto error;
+            }
+          base_node->isolation = cinfo->isolation;
+          base_node->dlopen_isolation = cinfo->dlopen_isolation;
+        }
+      else
+        {
+          char *saveptr;
+          char *token = strtok_r(path, "/", &saveptr);
+          if (token == NULL || insert_to_trie(base_node, token, cinfo, &saveptr) != DLCONF_ERR_OK)
+            {
+              free(path);
+              goto error;
+            }
+          free(path);
+        }
+      cinfo = cinfo->next;
+    }
+  return base_node;
+error:
+  free_node(base_node);
+  return NULL;
+}
+
+static void add_to_list_at_end (struct string_list **new_list, char *token)
+{
+  struct string_list *n_ele = malloc(sizeof(*n_ele));
+
+  if (n_ele != NULL)
+    {
+      n_ele->string = token;
+      n_ele->next = NULL;
+    }
+
+  if (*new_list == NULL)
+    *new_list = n_ele;
+  else
+    (*new_list)->next = n_ele;
+}
+
+static int count_list_strlen (const struct string_list *list)
+{
+  int res = 1;
+
+  while (list != NULL)
+    {
+      res += strlen(list->string)+1;
+      list = list->next;
+    }
+
+  return res;
+}
+
+static int count_lists_strlen (const struct string_list_array *list)
+{
+  int res = 0;
+
+  for (size_t i = 0; i < list->count; i++)
+    res += count_list_strlen (list->elements[i]);
+
+  return res;
+}
+
+static int count_nodes (const struct node *node)
+{
+  int res = 1;
+
+  for (int i = 0; i < node->children_count; i++)
+    res += count_nodes (node->children[i]);
+
+  return res;
+}
+
+static int count_nodes_names_length (const struct node *node)
+{
+  int res = strlen(node->name) + 1;
+
+  for (int i = 0; i < node->children_count; i++)
+    res += count_nodes_names_length (node->children[i]);
+
+  return res;
+}
+
+static int compute_glist_offset (struct string_list **list, int idx)
+{
+  int res = 0;
+
+  for (size_t i = 0; i < idx; i++)
+    res += count_list_strlen (list[i]);
+
+  return res;
+}
+
+static bool file_exists (const char *path)
+{
+  return (access(path, F_OK) == 0);
+}
+
+static int save_names (const struct node *node, int fd)
+{
+  int result = _dl_write(fd, node->name, strlen(node->name)+1);
+
+  for (int32_t i = 0; i < node->children_count && result >= 0; i++)
+    result = save_names (node->children[i], fd);
+
+  return result >= 0 ? 0 : DLCONF_ERR_IO;
+}
+
+static int save_conf_dat_header (int fd)
+{
+  int8_t version = DLCONF_VERSION;
+  char reserved[DLCONF_RESERVED_LEN];
+
+  write_or_return(fd, DLCONF_MAGIC, DLCONF_MAGIC_LEN);
+  write_or_return(fd, &version, DLCONF_VERSION_LEN);
+  write_or_return(fd, reserved, DLCONF_RESERVED_LEN);
+
+  return DLCONF_ERR_OK;
+}
+
+static void free_g_list (struct string_list_array *g_list, bool free_strings)
+{
+  for (int32_t i = 0; i < g_list->count; i++)
+    dlconf_free_string_list (g_list->elements[i], free_strings);
+
+  free(g_list->elements);
+  g_list->elements = NULL;
+  g_list->count = 0;
+}
+
+static int save_list (struct string_list_array *glist, int fd)
+{
+  int result = 0;
+
+  for (size_t i = 0; i < glist->count && result >= 0; i++)
+    {
+      struct string_list *list = glist->elements[i];
+      while (list != NULL)
+	{
+	  result = _dl_write (fd, list->string, strlen (list->string) + 1);
+	  if (result < 0)
+	    goto exit;
+
+	  list = list->next;
+	}
+      result = _dl_write (fd, "\0", 1);
+    }
+exit:
+  return result >= 0 ? 0 : DLCONF_ERR_IO;
+}
+
+static int _save_trie (const struct node *node, int fd, int32_t *name_offset, int32_t cache_start, int32_t dlpath_start)
+{
+  int result = 0;
+  int32_t all_subnodes = count_nodes(node);
+  int32_t no_value = -1;
+
+  write_or_return(fd, &all_subnodes, sizeof(all_subnodes));
+  write_or_return(fd, &node->children_count, sizeof(node->children_count));
+  write_or_return(fd, name_offset, sizeof(*name_offset));
+
+  if (node->caches_idx >= 0)
+    {
+      int cache_offset = cache_start + compute_glist_offset(g_caches.elements, node->caches_idx);
+      write_or_return(fd, &cache_offset, sizeof(cache_offset));
+    }
+  else
+    {
+      write_or_return(fd, &no_value, sizeof(no_value));
+    }
+  if (node->dlopen_paths_idx >= 0)
+    {
+      int dlpath_offset = dlpath_start + compute_glist_offset(g_dlopen_paths.elements, node->dlopen_paths_idx);
+      write_or_return(fd, &dlpath_offset, sizeof(dlpath_offset));
+    }
+  else
+    {
+      write_or_return(fd, &no_value, sizeof(no_value));
+    }
+
+  write_or_return(fd, &node->isolation, sizeof(node->isolation));
+  write_or_return(fd, &node->dlopen_isolation, sizeof(node->dlopen_isolation));
+
+  *name_offset += strlen(node->name)+1;
+
+  for (int i = 0; i < node->children_count && result >= 0; i++)
+    result = _save_trie (node->children[i], fd, name_offset, cache_start,
+			 dlpath_start);
+
+  return result;
+}
+
+static int save_trie (const struct node *base_node, const char *path)
+{
+  int result;
+  int fd = open(DLCONF_DAT_PATH, O_CREAT | O_WRONLY, 0600);
+
+  if (fd < 0)
+    {
+      _dl_error_printf("File %s open error\n", DLCONF_DAT_PATH);
+      return DLCONF_ERR_IO;
+    }
+
+  int all_nodes = count_nodes(base_node);
+  int name_offset = all_nodes*DLCONF_NODE_SIZE_IN_BYTES + DLCONF_DAT_HEADER_SIZE;
+  int cache_offset = name_offset + count_nodes_names_length(base_node);
+  int dlopen_path_offset = cache_offset + count_lists_strlen(&g_caches);
+
+  for (size_t i = 0; i < g_caches.count; i++)
+    {
+      struct string_list *l = g_caches.elements[i];
+      while (l != 0)
+        {
+          l = l->next;
+        }
+    }
+
+  if (save_conf_dat_header(fd) != DLCONF_ERR_OK)
+    {
+      close(fd);
+      _dl_error_printf("File %s save error\n", DLCONF_DAT_PATH);
+      return DLCONF_ERR_IO;
+    }
+
+  result = _save_trie(base_node, fd, &name_offset, cache_offset, dlopen_path_offset);
+  if (result != DLCONF_ERR_OK)
+    {
+      goto exit;
+    }
+  if ((result = save_names(base_node, fd)) == DLCONF_ERR_OK &&
+      (result = save_list(&g_caches, fd)) == DLCONF_ERR_OK)
+    {
+      result = save_list(&g_dlopen_paths, fd);
+    }
+exit:
+  close(fd);
+  return result;
+}
+
+static int save_conf_dat (const struct cache_info *cache_info)
+{
+  int result;
+  struct node *base_node = convert_to_trie(cache_info);
+
+  if (base_node == NULL)
+    return DLCONF_ERR_NOMEM;
+
+  result = save_trie(base_node, DLCONF_DAT_PATH);
+  free_node(base_node);
+  free_g_list(&g_caches, true);
+  free_g_list(&g_dlopen_paths, true);
+  return result;
+}
+
+static struct string_list *read_string_list (void *file, int32_t offset)
+{
+  struct string_list *list = NULL;
+  struct string_list **list_tail = &list;
+  char *str_ptr = (char*)(file+offset);
+
+  while (*str_ptr != '\0')
+    {
+      add_to_list_at_end(list_tail, (char*)str_ptr);
+
+      if (*list_tail == NULL)
+	break;
+
+      str_ptr += strlen((*list_tail)->string)+1;
+      list_tail = &((*list_tail)->next);
+    }
+
+  return list;
+}
+
+static struct conf_dat_entry *_find_conf_dat_entry (const char *token, void *file, int start, char **saveptr)
+{
+  int32_t *child_count = (int32_t*)(file + start + DLCONF_CHILD_COUNT_OFFSET);
+  for (size_t i = 0; i < *child_count; i++)
+    {
+      int next_node_start  = start + DLCONF_NODE_SIZE_IN_BYTES;
+
+      int *name_start = (int32_t*)(file + next_node_start + DLCONF_NAME_START_OFFSET);
+      char *name = (char*)(file + *name_start);
+
+      if (strncmp(token, name, strlen(token)+1) == 0)
+        {
+          char *next_token = strtok_r(NULL, "/", saveptr);
+          if (next_token == NULL)
+            {
+              return dlconf_read_one_conf_dat_entry(file, next_node_start);
+            }
+          else
+            {
+              struct conf_dat_entry* result =  _find_conf_dat_entry(next_token, file, next_node_start, saveptr);
+
+	      if (result == NULL)
+		return dlconf_read_one_conf_dat_entry (file, next_node_start);
+	      else
+		return result;
+	    }
+        }
+      int32_t subtree_count = *(int32_t*)(file + next_node_start);
+      start += subtree_count * DLCONF_NODE_SIZE_IN_BYTES;
+    }
+  return NULL;
+}
+
+static struct conf_dat_entry *find_conf_dat_entry (const char *path, void *file)
+{
+  struct conf_dat_entry* result = NULL;
+  char *npath = strdup(path);
+  char *saveptr, *token;
+  if (npath == NULL)
+      return NULL;
+
+  token = strtok_r(npath, "/", &saveptr);
+
+  if (token != NULL)
+    result
+	= _find_conf_dat_entry (token, file, DLCONF_DAT_HEADER_SIZE, &saveptr);
+
+  if (result == NULL)
+    result = dlconf_read_one_conf_dat_entry (file, DLCONF_DAT_HEADER_SIZE);
+
+  free(npath);
+  return result;
+}
+
+static bool check_file_header(void *file)
+{
+  struct conf_dat_header
+  {
+    char magic[DLCONF_MAGIC_LEN];
+    int8_t version;
+  };
+
+  struct conf_dat_header *header = file;
+
+  if (strncmp(header->magic, DLCONF_MAGIC, DLCONF_MAGIC_LEN) == 0 ||
+      header->version == DLCONF_VERSION)
+    {
+      return true;
+    }
+  return false;
+}
+
+int load_conf_dat (const char *path)
+{
+  size_t file_size = 0;
+  void *file = (void*)_dl_sysdep_read_whole_file(path, &file_size, PROT_READ);
+
+  if (file == MAP_FAILED)
+    {
+      _dl_error_printf("File %s open error\n", path);
+      return DLCONF_ERR_IO;
+    }
+
+  if (!check_file_header(file))
+    {
+      __munmap(file, file_size);
+      _dl_error_printf("Invalid header\n");
+      return DLCONF_ERR_INVAL;
+    }
+
+  conf_data = file;
+  conf_data_size = file_size;
+  return 0;
+}
+
+static struct conf_dat_entry *dlconf_find_proper_conf_dat_entry (const char *path)
+{
+  if (__glibc_unlikely(conf_data == NULL))
+    {
+      if (file_exists (DLCONF_DAT_PATH))
+	load_conf_dat (DLCONF_DAT_PATH);
+    }
+
+  if (__glibc_likely(conf_data != NULL))
+    {
+      struct conf_dat_entry *result = find_conf_dat_entry(path, conf_data);
+      return result;
+    }
+  return NULL;
+}
+
+/*
+ * Public functions
+ */
+
+void dlconf_free_g_lists(void)
+{
+  free_g_list(&g_caches, false);
+  free_g_list(&g_dlopen_paths, false);
+}
+
+void dlconf_free_string_list (struct string_list *slist, bool free_string)
+{
+  struct string_list *s = slist;
+
+  while (s != NULL)
+    {
+      struct string_list *t = s;
+      s = s->next;
+
+      if (free_string)
+	free (t->string);
+
+      free(t);
+    }
+}
+
+void free_section_string_list (struct section_string_list *slist, bool free_string)
+{
+  if (slist == NULL)
+      return;
+
+  struct section_string_list *s = slist;
+  while (s != NULL)
+    {
+      struct section_string_list *t = s;
+      s = s->next;
+
+      if (free_string)
+	free (t->string);
+
+      free(t);
+    }
+}
+
+struct conf_dat_entry *dlconf_read_one_conf_dat_entry (void *file, int start)
+{
+  struct conf_dat_entry *node = malloc(sizeof(*node));
+  struct conf_dat_raw_entry *rnode;
+
+  if (node == NULL)
+    return NULL;
+
+  rnode = (struct conf_dat_raw_entry*)(file+start);
+
+  node->isolation = rnode->isolation;
+  node->dlopen_isolation = rnode->dlopen_isolation;
+  node->dlopen_paths = NULL;
+  node->caches = NULL;
+
+  if (rnode->dlopen_paths_offset >= 0)
+    node->dlopen_paths = read_string_list (file, rnode->dlopen_paths_offset);
+
+  if (rnode->caches_offset >= 0)
+    node->caches = read_string_list (file, rnode->caches_offset);
+
+  return node;
+}
+
+
+struct caches* dlconf_find_cache (const char *cache_path)
+{
+  struct caches *new_cache;
+  struct caches *c = cache_list;
+
+  while (c)
+    {
+      if (strcmp (c->cache_name, cache_path) == 0)
+	return c;
+
+      c = c->next;
+    }
+
+  new_cache = malloc(sizeof(*new_cache));
+
+  if (__glibc_unlikely (new_cache == NULL))
+    return NULL;
+
+  new_cache->cache = NULL;
+  new_cache->cache_new = NULL;
+  new_cache->cachesize = 0;
+  new_cache->next = cache_list;
+  new_cache->cache_name = strdup(cache_path);
+
+  if (__glibc_unlikely(new_cache->cache_name == NULL))
+    {
+      free(new_cache);
+      return NULL;
+    }
+  cache_list = new_cache;
+
+  return new_cache;
+}
+
+bool dlconf_allowed_dlopen (const char *file, const void *caller_dlopen)
+{
+  char buff[PATH_MAX];
+  struct string_list *s;
+  char *caller_path;
+  struct conf_dat_entry *centry;
+  struct link_map *l = _dl_find_dso_for_object ((ElfW (Addr)) caller_dlopen);
+
+  if (l == NULL)
+    return true;
+
+  if (l->l_type == lt_executable)
+    {
+      buff[sizeof (buff) - 1] = '\0';
+      caller_path = dlconf_get_executable_path (buff, sizeof (buff) - 1);
+    }
+  else
+    {
+      caller_path = l->l_name;
+    }
+
+  /* If we can't find the path to the main program, let dlopen() execute after
+     all. Blocking dlopen() is not a security mechanism, but only to help modularize
+     the system. */
+  if (caller_path == NULL)
+    return true;
+
+  centry = dlconf_find_proper_conf_dat_entry(caller_path);
+
+  if (centry == NULL)
+    return true;
+
+  if (!centry->dlopen_isolation)
+    {
+      free_centry(centry);
+      return true;
+    }
+
+  s = centry->dlopen_paths;
+
+  while (s != NULL)
+    {
+      if (strncmp(file, s->string, strlen(s->string)) == 0)
+        {
+          free_centry(centry);
+          return true;
+        }
+      s = s->next;
+    }
+
+  free_centry(centry);
+  return false;
+}
+
+char *dlconf_get_cached_path(const char *name, const char *libname, bool *try_default)
+{
+  struct conf_dat_entry *centry;
+  char *cached;
+
+  if (try_default)
+    *try_default = true;
+
+  if (name == NULL)
+    return NULL;
+
+  centry = dlconf_find_proper_conf_dat_entry(name);
+
+  if (centry == NULL)
+    {
+      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
+        {
+          _dl_debug_printf("No entry for: %s %s\n", name, libname);
+        }
+      return NULL;
+    }
+
+  if (try_default)
+    *try_default = !centry->isolation;
+
+  cached = dlconf_find_cached_path(libname, centry->caches);
+
+  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
+    {
+      if (cached != NULL)
+          _dl_debug_printf("Found entry: %s for %s %s\n", cached, name, libname);
+      else
+          _dl_debug_printf("Not found entry for %s %s\n", name, libname);
+    }
+
+  free_centry(centry);
+  return cached;
+}
+
+void dlconf_unload_cache (void)
+{
+  struct caches *c = cache_list;
+
+  while (c)
+    {
+      struct caches *next;
+      struct cache_file *cache = c->cache;
+      size_t cachesize = c->cachesize;
+
+      if (cache != NULL && cache != (struct cache_file *) -1)
+        {
+          __munmap (cache, cachesize);
+          cache = NULL;
+        }
+#ifdef SHARED
+      /* This marks the glibc_hwcaps_priorities array as out-of-date.  */
+      c->glibc_hwcaps_priorities_length = 0;
+#endif
+      next = c->next;
+      free(c->cache_name);
+      free(c);
+      c = next;
+    }
+
+  cache_list = NULL;
+  dlconf_free_g_lists();
+
+  if (conf_data != NULL)
+    {
+      __munmap(conf_data, conf_data_size);
+      conf_data = NULL;
+      conf_data_size = 0;
+    }
+}
+
+int dlconf_generate_conf_dat (void)
+{
+  struct cache_info *cache_info = NULL;
+  int result;
+
+  if ((result = read_ld_so_configs(&cache_info)) == DLCONF_ERR_OK)
+    {
+      if (save_conf_dat(cache_info) != DLCONF_ERR_OK)
+        {
+          _dl_error_printf("Error converting configuration files\n");
+          return DLCONF_ERR_IO;
+        }
+       free_cache_info(cache_info);
+    }
+
+  return result;
+}
diff --git a/elf/dlconf.h b/elf/dlconf.h
new file mode 100644
index 0000000000..00f408a839
--- /dev/null
+++ b/elf/dlconf.h
@@ -0,0 +1,175 @@ 
+/* Copyright (C) 2026 Samsung Electronics Co., Ltd.
+
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+#ifndef _DLCONF_H
+#define _DLCONF_H
+
+#include <unistd.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <stddef.h>
+
+
+#define DLCONF_ERR_OK          0  /* Success */
+#define DLCONF_ERR_INVAL      -1  /* Invalid parameters */
+#define DLCONF_ERR_NOMEM      -2  /* Memory allocation failure */
+#define DLCONF_ERR_IO         -3  /* File I/O error */
+#define DLCONF_ERR_CYCLE      -4  /* Cycle detected in configuration */
+
+/*
+ * Struct that stores cache data (eg. ld.so.cache). It ensures that each cache
+ * file is loaded at most once. The next time data from the structure will be
+ * use, instad of reading file from disk.
+ *
+ * The same mechanism was there before, the current solution has added support
+ * for linked list handling.
+ */
+struct caches {
+  char *cache_name;
+  struct cache_file *cache;
+  struct cache_file_new *cache_new;
+  size_t cachesize;
+  struct caches *next;
+  #ifdef SHARED
+/* This is used to cache the priorities of glibc-hwcaps
+   subdirectories.  The elements of _dl_cache_priorities correspond to
+   the strings in the cache_extension_tag_glibc_hwcaps section.  */
+  uint32_t *glibc_hwcaps_priorities;
+  uint32_t glibc_hwcaps_priorities_length;
+  uint32_t glibc_hwcaps_priorities_allocated;
+
+  /* True if the full malloc was used to allocated the array.  */
+  bool glibc_hwcaps_priorities_malloced;
+
+  #endif /* SHARED */
+};
+
+/*
+ * This structure represents an entry in ldconfig.dat file
+ */
+struct conf_dat_raw_entry {
+    int32_t subtree_count;
+    int32_t nodes_count;
+    int32_t name_offset;
+    int32_t caches_offset;
+    int32_t dlopen_paths_offset;
+    int32_t isolation;
+    int32_t dlopen_isolation;
+};
+
+/*
+ * List of strings.
+ */
+struct string_list {
+  char *string;
+  struct string_list *next;
+};
+
+/*
+ * This structure represents a one entry read from ldconfig.dat file
+ */
+struct conf_dat_entry {
+  bool isolation;
+  bool dlopen_isolation;
+
+  struct string_list *caches;
+  struct string_list *dlopen_paths;
+};
+
+/*
+ * Description of dlconfig.dat header
+ */
+#define DLCONF_MAGIC "DLC"
+#define DLCONF_VERSION 1
+#define DLCONF_MAGIC_LEN 4 /* DLC\0 */
+#define DLCONF_VERSION_LEN 1
+#define DLCONF_RESERVED_LEN 3
+#define DLCONF_DAT_HEADER_SIZE (DLCONF_MAGIC_LEN + DLCONF_VERSION_LEN + DLCONF_RESERVED_LEN)
+
+/*
+ * Offsets of the data in ldconfig.dat entry
+ */
+#define DLCONF_SUBNODE_COUNT_OFFSET 0
+#define DLCONF_CHILD_COUNT_OFFSET offsetof(struct conf_dat_raw_entry, nodes_count)
+#define DLCONF_NAME_START_OFFSET offsetof(struct conf_dat_raw_entry, name_offset)
+#define DLCONF_CACHES_START_OFFSET offsetof(struct conf_dat_raw_entry, caches_offset)
+#define DLCONF_DLPATHS_START_OFFSET offsetof(struct conf_dat_raw_entry, dlopen_paths_offset)
+#define DLCONF_ISO_OFFSET offsetof(struct conf_dat_raw_entry, isolation)
+#define DLCONF_DLOPEN_ISO_OFFSET offsetof(struct conf_dat_raw_entry, dlopen_isolation)
+#define DLCONF_NODE_FIELDS_COUNT 7
+#define DLCONF_NODE_SIZE_IN_BYTES DLCONF_NODE_FIELDS_COUNT*sizeof(int32_t)
+
+/*
+ * Free global caches paths and dlopen paths
+ */
+void dlconf_free_g_lists(void) attribute_hidden;
+
+/*
+ * Free string list. If `free_string` argument is true, then additionally strings will be freed
+ */
+void dlconf_free_string_list(struct string_list *list, bool free_string) attribute_hidden;
+
+/*
+ * Function read from `file` at specified offset `start` one dlconfig.dat entry
+ */
+struct conf_dat_entry *dlconf_read_one_conf_dat_entry(void *file, int start) attribute_hidden;
+
+/*
+ * Function returns the path to the library with the given name using specified cache file.
+ * This is wrapper for _dl_load_cache_lookup(), which allows to provide custom
+ * ld.so.cache path.
+ */
+extern char *dlconf_load_cache_lookup_from (const char *name, const char *cache_path) attribute_hidden;
+
+/*
+ * Returns structure which contains cache data for a cache from a specified path.
+ */
+extern struct caches* dlconf_find_cache (const char *cache_path) attribute_hidden;
+
+/*
+ * Free caches list.
+ */
+extern void dlconf_unload_cache(void) attribute_hidden;
+
+/*
+ * Return true or false depending on whether the library can be dlopened
+ */
+extern bool dlconf_allowed_dlopen (const char *file, const void *caller_dlopen) attribute_hidden;
+
+/*
+ * Returns path to the library with a given libname.
+ * name argument is a program name with a path, for which the library is loading.
+ * try_default will be set to true or false depending on whether isolation is true or false.
+ */
+extern char *dlconf_get_cached_path(const char *name, const char *libname, bool *try_default) attribute_hidden;
+
+/*
+ * Generate ldconfig.dat from /etc/ldconfig.conf and /etc/ldconfig.conf.d/* files
+ */
+extern int dlconf_generate_conf_dat (void) attribute_hidden;
+
+/*
+ * Print ldconfig.conf dat content
+ */
+extern void dlconf_print_conf_dat (void) attribute_hidden;
+
+/*
+ * Return the path to the main executable.
+ */
+char *dlconf_get_executable_path (char *buff, size_t bufflen) attribute_hidden;
+
+#endif /* _DLCONF_H */
diff --git a/elf/rtld.c b/elf/rtld.c
index bfdf632e77..f70bc7e18c 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -53,6 +53,7 @@ 
 #include <dl-find_object.h>
 #include <dl-audit-check.h>
 #include <dl-call_tls_init_tp.h>
+#include <dlconf.h>
 
 #include <assert.h>
 
@@ -1491,6 +1492,22 @@  dl_main (const ElfW(Phdr) *phdr,
 	    --_dl_argc;
 	    ++_dl_argv;
 	  }
+      #ifdef DLCONF
+	else if (! strcmp (_dl_argv[1], "--build-ldconfig-dat"))
+	  {
+	    state.mode = rtld_mode_build_dlconf_dat;
+
+	    --_dl_argc;
+	    ++_dl_argv;
+	  }
+	else if (! strcmp (_dl_argv[1], "--print-ldconfig-dat"))
+	  {
+	    state.mode = rtld_mode_print_dlconf_dat;
+
+	    --_dl_argc;
+	    ++_dl_argv;
+	  }
+    #endif /* DLCONF */
 	else if (strcmp (_dl_argv[1], "--help") == 0)
 	  {
 	    state.mode = rtld_mode_help;
@@ -1511,6 +1528,20 @@  dl_main (const ElfW(Phdr) *phdr,
 	else
 	  break;
 
+#ifdef DLCONF
+      if (__glibc_unlikely (state.mode == rtld_mode_build_dlconf_dat))
+	{
+	  int res = dlconf_generate_conf_dat ();
+	  _exit (res);
+	}
+
+      if (__glibc_unlikely (state.mode == rtld_mode_print_dlconf_dat))
+	{
+	  dlconf_print_conf_dat ();
+	  _exit (0);
+	}
+#endif /* DLCONF */
+
       if (__glibc_unlikely (state.mode == rtld_mode_list_tunables))
 	{
 	  __tunables_print ();
@@ -1969,6 +2000,12 @@  dl_main (const ElfW(Phdr) *phdr,
 			 state.mode == rtld_mode_trace, 0);
     rtld_timer_accum (&load_time, start);
   }
+  #ifdef DLCONF
+  /* The dependencies are already loaded, so we can free the cache now because
+     we don't need it. Especially since freeing them after
+     __rtld_malloc_init_real() will not be allowed. */
+  dlconf_unload_cache();
+  #endif /* DLCONF */
 
   /* Mark all objects as being in the global scope.  */
   for (i = main_map->l_searchlist.r_nlist; i > 0; )
diff --git a/scripts/ld_so_cache_maker.py b/scripts/ld_so_cache_maker.py
new file mode 100644
index 0000000000..5d49b1bf0d
--- /dev/null
+++ b/scripts/ld_so_cache_maker.py
@@ -0,0 +1,76 @@ 
+#!/usr/bin/python3
+# Copyright (C) 2026 Samsung Electronics Co., Ltd.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <https://www.gnu.org/licenses/>.
+import os
+import sys
+import struct
+
+ARCH_FLAG=0x0A03
+
+def read_library_list(input_file):
+    if not os.path.exists(input_file):
+        raise FileNotFoundError(f"File {input_file} not exist.")
+
+    with open(input_file, "r") as f:
+        libraries = [line.strip() for line in f if line.strip()]
+
+    libraries.sort(reverse=True, key=lambda x: os.path.basename(x))
+    return libraries
+
+def generate_ld_so_cache(libraries, output_file):
+    entries = []
+
+    for lib in libraries:
+
+        lib_name = os.path.basename(lib)
+        entries.append((lib_name, lib))
+
+    magic = b"ld.so-1.7.0"
+    entry_count = len(entries)
+
+    with open(output_file, "wb") as f:
+        f.write(magic + b"\0" * (12 - len(magic)))
+        f.write(struct.pack("I", entry_count))
+
+        offsets = []
+        data_section = b""
+
+        for lib_name, lib_path in entries:
+            name_offset = len(data_section)
+            data_section += lib_name.encode("utf-8") + b"\0"
+
+            path_offset = len(data_section)
+            data_section += lib_path.encode("utf-8") + b"\0"
+
+            offsets.append((name_offset, path_offset))
+        for name_offset, path_offset in offsets:
+            f.write(struct.pack("III", ARCH_FLAG, name_offset, path_offset))
+
+        f.write(data_section)
+
+    print(f"Cache {output_file} with {entry_count} entries was generated successful")
+
+def main():
+    try:
+        libraries = read_library_list(sys.argv[1])
+        generate_ld_so_cache(libraries, sys.argv[2])
+    except Exception as e:
+        print(f"[ERROR] {e}")
+
+if __name__ == "__main__":
+    main()
+
diff --git a/sysdeps/unix/sysv/linux/dlconf-getexecutable.c b/sysdeps/unix/sysv/linux/dlconf-getexecutable.c
new file mode 100644
index 0000000000..51f4202263
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dlconf-getexecutable.c
@@ -0,0 +1,61 @@ 
+/* Copyright (C) 2026 Samsung Electronics Co., Ltd.
+
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+#include "ldsodefs.h"
+#include <string.h>
+#include <linux/limits.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+char *dlconf_get_executable_path(char *buff, size_t bufflen) {
+  if (buff == NULL || bufflen == 0)
+    {
+      return NULL;
+    }
+
+  char link_path[PATH_MAX];
+  char pid_str_buff[12];
+  size_t offset;
+  ssize_t bytes;
+
+  pid_str_buff[sizeof(pid_str_buff) - 1] = '\0';
+  char *pid_str
+      = _itoa (getpid (), pid_str_buff + sizeof (pid_str_buff) - 1, 10, 0);
+
+  offset = 0;
+
+  strncpy (link_path + offset, "/proc/", sizeof (link_path) - 1);
+  offset += sizeof("/proc/") - 1;
+  strncpy(link_path + offset, pid_str, sizeof(link_path) - offset - 1);
+  offset += strlen(pid_str);
+  strncpy(link_path + offset, "/exe", sizeof(link_path) - offset - 1);
+  offset += sizeof("/exe") - 1;
+
+  link_path[offset] = '\0';
+
+  if ((bytes = readlink (link_path, buff, bufflen - 1)) == -1)
+    {
+      if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_LIBS))
+        {
+          _dl_debug_printf ("Read link of %s error\n", link_path);
+        }
+      return NULL;
+    }
+
+  buff[bytes] = '\0';
+  return buff;
+}