Skip to content

feat(ld-preload): add standalone libtermux-paths-ld-preload.so for /etc/ file access redirection#36

Open
rios0rios0 wants to merge 3 commits intotermux:masterfrom
rios0rios0:feat/etc-file-redirect
Open

feat(ld-preload): add standalone libtermux-paths-ld-preload.so for /etc/ file access redirection#36
rios0rios0 wants to merge 3 commits intotermux:masterfrom
rios0rios0:feat/etc-file-redirect

Conversation

@rios0rios0
Copy link
Copy Markdown

@rios0rios0 rios0rios0 commented Mar 21, 2026

Summary

  • Added FileAccessIntercept module that redirects accesses of standard Linux /etc/ configuration files to their Termux $PREFIX/etc/ equivalents
  • Added interceptors for open, openat, fopen, access, faccessat, stat, and lstat in a separate, opt-in LD_PRELOAD library (libtermux-paths-ld-preload.so)
  • Redirected /etc/resolv.conf, /etc/hosts, /etc/nsswitch.conf for DNS resolution
  • Redirected Go's hardcoded SSL CA certificate paths to $PREFIX/etc/tls/cert.pem
  • The libtermux-exec-direct-ld-preload.so remains exec-only — file access interception is fully decoupled

Problem

On Android/Termux, standard Linux paths like /etc/resolv.conf, /etc/hosts, and /etc/ssl/certs/ either don't exist or contain incomplete data. Termux maintains proper versions under $PREFIX/etc/, but many dynamically linked programs hardcode the standard /etc/ paths.

This causes DNS resolution failures and TLS certificate verification errors for tools like Python, Node.js, and cgo-enabled Go programs running in Termux.

Solution

Introduce a new standalone libtermux-paths-ld-preload.so library that intercepts file-access libc functions (open, openat, fopen, access, faccessat, stat, lstat) and redirects a fixed set of /etc/ paths to their $PREFIX/etc/ equivalents.

This library is separate from termux-exec and fully opt-in. Users enable it by adding it to LD_PRELOAD:

LD_PRELOAD=libtermux-exec-ld-preload.so:libtermux-paths-ld-preload.so

Or standalone (without exec interception):

LD_PRELOAD=libtermux-paths-ld-preload.so

The redirect only applies when:

  1. The path exactly matches one of the entries in the redirect table
  2. The corresponding Termux file exists (checked via raw faccessat syscall to avoid recursion)

Architecture

The redirect logic (fileAccess_redirectPath()) lives in the shared library libtermux-exec_nos_c_tre and is reusable. The new entry point (TermuxPathsLDPreloadEntryPoint.c) provides the LD_PRELOAD hooks that call this logic. The existing TermuxExecDirectLDPreloadEntryPoint.c remains unchanged from upstream — exec-only.

libtermux-exec_nos_c_tre.a
├── FileAccessIntercept.c    (redirect logic, reusable)
├── ExecIntercept.c          (exec logic)
└── ...

libtermux-exec-direct-ld-preload.so  (exec-only, unchanged)
└── TermuxExecDirectLDPreloadEntryPoint.c

libtermux-paths-ld-preload.so        (file access only, NEW)
└── TermuxPathsLDPreloadEntryPoint.c

Redirected paths

Source Destination
/etc/resolv.conf $PREFIX/etc/resolv.conf
/etc/hosts $PREFIX/etc/hosts
/etc/nsswitch.conf $PREFIX/etc/nsswitch.conf
/etc/ssl/certs/ca-certificates.crt $PREFIX/etc/tls/cert.pem
/etc/pki/tls/certs/ca-bundle.crt $PREFIX/etc/tls/cert.pem
/etc/ssl/ca-bundle.pem $PREFIX/etc/tls/cert.pem
/etc/pki/tls/cacert.pem $PREFIX/etc/tls/cert.pem
/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem $PREFIX/etc/tls/cert.pem
/etc/ssl/cert.pem $PREFIX/etc/tls/cert.pem

Limitations

This only fixes dynamically linked programs. Statically linked Go binaries (e.g., terraform, gh, kubectl) use raw syscalls and bypass libc entirely — they require a different solution (seccomp user_notif or proot).

Test plan

  • Verify fopen("/etc/resolv.conf") returns Termux content
  • Verify fopen("/etc/hosts") returns Termux content
  • Verify open("/etc/ssl/certs/ca-certificates.crt") returns Termux cert bundle
  • Verify unrelated /etc/ paths (e.g., /etc/passwd) are NOT redirected
  • Verify no regression in existing execve() interception tests
  • Verify libtermux-exec-direct-ld-preload.so exports only exec symbols
  • Verify libtermux-paths-ld-preload.so exports only file access symbols

Closes termux/termux-packages#10277

- added `FileAccessIntercept` module that redirects reads of standard
  Linux `/etc/` configuration files to their Termux `$PREFIX/etc/`
  equivalents
- added interceptors for `open`, `openat`, `fopen`, `access`,
  `faccessat`, `stat`, and `lstat` to the direct LD_PRELOAD entry point
- redirected `/etc/resolv.conf`, `/etc/hosts`, `/etc/nsswitch.conf`
  for DNS resolution
- redirected Go's hardcoded SSL CA certificate paths
  (`/etc/ssl/certs/ca-certificates.crt`, `/etc/pki/tls/certs/ca-bundle.crt`,
  etc.) to `$PREFIX/etc/tls/cert.pem`

This fixes DNS resolution and TLS certificate verification for
dynamically linked programs on Termux, where Android does not provide
these files at their standard Linux paths.

Closes termux/termux-packages#10277

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 21, 2026 19:17
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the libtermux-exec-direct-ld-preload.so LD_PRELOAD layer to redirect access to select standard Linux /etc/* DNS and CA-bundle paths to their Termux $PREFIX/etc/* equivalents, improving DNS resolution and TLS verification for dynamically linked programs on Android/Termux.

Changes:

  • Added a FileAccessIntercept module with a fixed redirect table and existence-checking logic.
  • Hooked libc file-access APIs (open, openat, fopen, access, faccessat, stat, lstat) in the direct LD_PRELOAD entry point to apply redirection.
  • Updated build inputs to compile the new interception module into the library.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

File Description
Makefile Adds the new FileAccessIntercept.c source to the static library build inputs.
lib/termux-exec_nos_c/tre/src/.../FileAccessIntercept.c Implements redirect-table lookup + $PREFIX path construction + existence check via raw syscall.
lib/termux-exec_nos_c/tre/include/.../FileAccessIntercept.h Declares redirect structures and fileAccess_redirectPath() API for consumers.
app/.../TermuxExecDirectLDPreloadEntryPoint.c Introduces LD_PRELOAD interceptors for file-access libc functions and applies redirection.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +109 to +120
/*
* File access interceptors for `/etc/` path redirection.
*
* These intercept libc file-access functions and redirect reads of
* standard Linux `/etc/` configuration files (resolv.conf, hosts,
* nsswitch.conf, SSL CA certificates) to their Termux equivalents
* under `$PREFIX/etc/`.
*
* This fixes DNS resolution and TLS certificate verification for
* dynamically linked programs on Termux, where Android does not
* provide these files at their standard paths.
*/
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are runtime-binary tests for the existing exec interceptors, but no automated tests were added for the new /etc/* redirection behavior (open/openat/fopen/access/stat/lstat). Adding a small runtime test that validates redirected reads for one DNS file and one CA-bundle path (and validates a non-redirected control like /etc/passwd) would help prevent regressions.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid feedback. The existing test infrastructure
(ExecIntercept_RuntimeBinaryTests.c) uses a complex
fork + LD_PRELOAD setup focused on exec
interception. Adding file access tests would require a
similar but separate test harness. Suggest addressing
this in a follow-up PR to keep this one focused.
Happy to discuss if a specific test approach is
preferred.

- resolved dlsym symbols at load time via __attribute__((constructor)) for thread safety
- added NULL checks with ENOSYS errno for all file intercept functions
- updated comments to clarify all access modes are redirected, not just reads
- added missing /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem to header docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@TomJo2000
Copy link
Copy Markdown
Member

Hi thanks for the PR.
I'm not qualified to do an in depth review on this so I'll keep this brief.

  • 1.) Thank you for disclosing that you have used AI for this contribution, that helps us when reviewing submissions.
  • 2.) Please consider disabling Copilot review on this PR.
    We have no idea what it's gonna point out, highlight or suggest and those things may not be something we'd want to request as part of the review.
    The Copilot reviews add a significant amount of extra content to the PR that a maintainer needs to read, evaluate and respond to before getting to the actual content of the PR.

Thank you for your submission, please be aware that it may take some time before a maintainer has time to review your PR.

  • Tom, Termux packaging team.

@rios0rios0
Copy link
Copy Markdown
Author

Hi thanks for the PR. I'm not qualified to do an in depth review on this so I'll keep this brief.

  • 1.) Thank you for disclosing that you have used AI for this contribution, that helps us when reviewing submissions.
  • 2.) Please consider disabling Copilot review on this PR.
    We have no idea what it's gonna point out, highlight or suggest and those things may not be something we'd want to request as part of the review.
    The Copilot reviews add a significant amount of extra content to the PR that a maintainer needs to read, evaluate and respond to before getting to the actual content of the PR.

Thank you for your submission, please be aware that it may take some time before a maintainer has time to review your PR.

  • Tom, Termux packaging team.

Hi Tom, thanks for the quick feedback!

  1. Of course — transparency about tooling is important, especially for contributions that touch low-level code like this.
  2. Good point, I've solved the Copilot review (the ones that made sense). I had already addressed its comments in the follow-up commit, but I understand the noise it adds for maintainers. I'll keep it disabled on future PRs to this repo (and I won't trigger its review again).

I appreciate the team's time.
Happy to address any feedback whenever a maintainer gets a chance to look at the code.

Copy link
Copy Markdown
Member

@sylirre sylirre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

termux-exec package intended only to hook exec* functions. This is why it has such name.

Your pull request adds unrelated functionality that implicitly redirects file access path and does not have any way to opt out.

IMO /etc/file fix should go as separate LD_PRELOAD lib.

@robertkirkman
Copy link
Copy Markdown
Member

IMO this would be a good change for an experimental 3rd party Termux fork, like a fork where experimental changes that might have unexpected tradeoffs can be tested with various packages and people can report back which things were broken by them.

Here is an example of a precedent of a different project doing that:

Flips

https://github.com/Alcaro/Flips

has split into 2 new projects, This one, which is a non-AI project, https://git.disroot.org/Sir_Walrus/Flips

and this one, which is an AI-only project, https://github.com/Alcaro/FlAIps

both forks are maintained by the original creator and they are an experiment to find out what would happen if a project had an AI version and a non-AI version,

I don't think that Termux needs to have as strict of a setup as that, but basically my idea is that this PR, and maybe other similar changes together with it, should go in another build of Termux, if their creators are motivated to make one, and then people can test that build and give feedback on the special features, and then from there if there are certain good features people like they can be added to another PR directly to the regular Termux repositories, and get added like that.

@rios0rios0
Copy link
Copy Markdown
Author

termux-exec package intended only to hook exec* functions. This is why it has such name.

Your pull request adds unrelated functionality that implicitly redirects file access path and does not have any way to opt out.

IMO /etc/file fix should go as separate LD_PRELOAD lib.

Where is that one? Could you point the right direction so I can go there and fix? Because the current issue is really annoying. I can't run natively GitHub CLI, Kubernetes, Terraform and any other binary that refers to /etc.

@robertkirkman
Copy link
Copy Markdown
Member

termux-exec package intended only to hook exec* functions. This is why it has such name.
Your pull request adds unrelated functionality that implicitly redirects file access path and does not have any way to opt out.
IMO /etc/file fix should go as separate LD_PRELOAD lib.

Where is that one? Could you point the right direction so I can go there and fix? Because the current issue is really annoying. I can't run natively GitHub CLI, Kubernetes, Terraform and any other binary that refers to /etc.

Could you show which version of GitHub CLI you are trying to use?

Is it one from pkg install gh, or somewhere else?

@rios0rios0
Copy link
Copy Markdown
Author

termux-exec package intended only to hook exec* functions. This is why it has such name.
Your pull request adds unrelated functionality that implicitly redirects file access path and does not have any way to opt out.
IMO /etc/file fix should go as separate LD_PRELOAD lib.

Where is that one? Could you point the right direction so I can go there and fix? Because the current issue is really annoying. I can't run natively GitHub CLI, Kubernetes, Terraform and any other binary that refers to /etc.

Could you show which version of GitHub CLI you are trying to use?

Is it one from pkg install gh, or somewhere else?

Yes, official package. But look, any other GOOS=Linux that is built outside Android, does not work as well. They expect Linux paths, and the PREFIX Termux adds are not respected.

@robertkirkman
Copy link
Copy Markdown
Member

Could you show which gh command has an error for you? For me, gh auth login works.

@sylirre
Copy link
Copy Markdown
Member

sylirre commented Mar 22, 2026

Where is that one? Could you point the right direction so I can go there and fix?

My point is that basically any extra functionality added to termux-exec must be optional. This can be done either by offloading it into separate lib (e.g. libtermux-paths.so, doesn't exist currently) or make it possible to disable in other way.

Your changes are reasonable but I don't want to disable whole termux-exec just to ensure that stat() won't see ghost files.

Screenshot_20260322-042038_Termux Screenshot_20260322-043929_Termux

termux-exec originally was intended only to handle exec functions (did you check description?).

@rios0rios0
Copy link
Copy Markdown
Author

rios0rios0 commented Mar 22, 2026

Could you show which gh command has an error for you? For me, gh auth login works.

The command to run without proot:

GH_DEBUG=1 ~/.local/bin/gh_linux_arm64 auth status

The error is: dial tcp: lookup api.github.com on [::1]:53: read udp [::1]:51780->[::1]:53: read: connection refused

Go's net package can't resolve DNS because it looks for /etc/resolv.conf (doesn't exist natively on Android) and falls back to [::1]:53 which also doesn't work.

…one `libtermux-paths-ld-preload.so`

- moved `/etc/` path redirection interceptors (`open`, `openat`, `fopen`,
  `access`, `faccessat`, `stat`, `lstat`) from
  `TermuxExecDirectLDPreloadEntryPoint.c` into a new standalone entry point
  `TermuxPathsLDPreloadEntryPoint.c`
- reverted `TermuxExecDirectLDPreloadEntryPoint.c` to exec-only interception
- added `build-libtermux-paths-ld-preload` Makefile target with install,
  uninstall, format, and check support

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@rios0rios0 rios0rios0 changed the title feat(ld-preload): add /etc/file access interception for DNS and TLS feat(ld-preload): add standalone libtermux-paths-ld-preload.so for /etc/ file access redirection Mar 22, 2026
@rios0rios0
Copy link
Copy Markdown
Author

Where is that one? Could you point the right direction so I can go there and fix?

My point is that basically any extra functionality added to termux-exec must be optional. This can be done either by offloading it into separate lib (e.g. libtermux-paths.so, doesn't exist currently) or make it possible to disable in other way.

Your changes are reasonable but I don't want to disable whole termux-exec just to ensure that stat() won't see ghost files.

Screenshot_20260322-042038_Termux Screenshot_20260322-043929_Termux
termux-exec originally was intended only to handle exec functions (did you check description?).

Done! Could you take a look again please?

@robertkirkman
Copy link
Copy Markdown
Member

GH_DEBUG=1 ~/.local/bin/gh_linux_arm64 auth status

this is not pkg install gh like I suggested. What happens if you use pkg install gh and then $PREFIX/bin/gh command?

@rios0rios0
Copy link
Copy Markdown
Author

GH_DEBUG=1 ~/.local/bin/gh_linux_arm64 auth status

this is not pkg install gh like I suggested. What happens if you use pkg install gh and then $PREFIX/bin/gh command?

You are right about that. I was installing the raw binary built with GOOS=Linux instead of PKG version. Anyways, thanks. But it doesn't solve the PR point, because not all binaries have Termux versions for them and it is also very costly to keep alternative versions of all packages always whenever a useful stuff lunches or when gets updated. The easiest would be making Termux compatible with those Linux binaries.

@robertkirkman
Copy link
Copy Markdown
Member

robertkirkman commented Mar 22, 2026

Actually you are right, and I do think the idea is interesting and could be useful, I am just concerned that it might have unexpected effects on some programs that previously didn't have a libtermux-paths-ld-preload.so applied to them.

Something I was wondering is, do you know of any specific precompiled Linux executables that need this that I could use to test the behavior of this PR easily? Something that starts to work in Termux, but does not work properly without this PR because of the problem with the hardcoded /etc path?

I recently added a package cargo-binstall that can be used to install precompiled Linux executables like that, so if this PR could enable something that comes from that to work better, that would be interesting and make the change potentially useful, but I would want to prioritize making sure that none of the official Termux packages are negatively affected by this PR.

@robertkirkman
Copy link
Copy Markdown
Member

Also like others said, probably this change should be tested as an individual package if it's possible to do that. Do you think it's possible for this to be refactored to be built in a standalone project separate from termux-exec, or is that very difficult here?

@robertkirkman
Copy link
Copy Markdown
Member

And is it possible for this to operate in a way that has no relationship with termux-exec, or are they irrevocably connected together?

@rios0rios0
Copy link
Copy Markdown
Author

Actually you are right, and I do think the idea is interesting and could be useful, I am just concerned that it might have unexpected effects on some programs that previously didn't have a libtermux-paths-ld-preload.so applied to them.

Something I was wondering is, do you know of any specific precompiled Linux executables that need this that I could use to test the behavior of this PR easily? Something that starts to work in Termux, but does not work properly without this PR because of the problem with the hardcoded /etc path?

I recently added a package cargo-binstall that can be used to install precompiled Linux executables like that, so if this PR could enable something that comes from that to work better, that would be interesting and make the change potentially useful, but I would want to prioritize making sure that none of the official Termux packages are negatively affected by this PR.

You can use with the official GH CLI, just download the raw binary and you'll see.

@rios0rios0
Copy link
Copy Markdown
Author

And is it possible for this to operate in a way that has no relationship with termux-exec, or are they irrevocably connected together?

By the analysis I did, yes. I did not see other way of doing it. If you find, please point me the right direction and I'll do, I would perform the tests and show the evidences if you guys wish.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: getaddrinfo() should read $PREFIX/etc/hosts instead of /etc/hosts

5 participants