I'm developing a macOS console application that uses ODBC to connect to PostgreSQL. The application works fine when run normally, but fails to load the ODBC driver when debugging with LLDB(under root works fine as well).
Error Details When running the application through LLDB, I get this sandbox denial in the system log (via log stream): Error 0x0 0 0 kernel: (Sandbox) Sandbox: logd_helper(587) deny(1) file-read-data /opt/homebrew/lib/psqlodbcw.so The application cannot access the PostgreSQL ODBC driver located at /opt/homebrew/lib/psqlodbcw.so(also tried copy to /usr/local/lib/...). Environment
macOS Version: Latest Sequoia LLDB: Using LLDB from Xcode 16.3 (/Applications/Xcode16.3.app/Contents/Developer/usr/bin/lldb) ODBC Driver: PostgreSQL ODBC driver installed via Homebrew Code Signing: Application is signed with Apple Development certificate
What is the recommended approach for debugging applications that need to load dynamic libraries? Are there specific entitlements or configurations that would allow LLDB to access ODBC drivers during debugging sessions?
Any guidance would be greatly appreciated. Thank you for any assistance!
I'm developing a macOS console application
“Console application” isn’t a term we use on Apple platforms. An application is, by definition, something the user double clicks in the Finder, shows up in the Dock, has a menu bar, and so on. I’m presuming that you’re building a command-line tool, something that the user runs from Terminal, or is run by the system as, say, a launchd
daemon. Please let me know if that’s not the case.
What is the recommended approach for debugging applications that need to load dynamic libraries?
There are a number of factors in play. I’ll come back to that later. But first I want to dig into your log message:
Error Details When running the application through LLDB, I get this sandbox denial in the system log… logd_helper(587)
Look at that message carefully. It originates from a macOS system process, logd_helper
. So this is not the case that your program is unable to load this shared library. Rather, it’s that some system process is unable to load this shared library [1]. So, this log message doesn’t indicate a problem with your code.
Note logd_helper
has a man page, although it’s not super helpful.
Other than this log message, is there any problem with your code when you run it under LLDB? If not, I’d treat this as log noise.
Coming back to the rule for loading code into your process:
- If the process is sandboxed, the code must be within the process’s static sandbox. Note that your process is unlikely to be sandboxed because it’s not an app.
- If the process has library validation enabled, that applies constraints. More on that below.
- If the process has the hardened runtime enabled, that enables library validation by default. You can opt out of that with an entitlement.
- If the library is otherwise loadable, it can be blocked by library constraints. See Applying launch environment and library constraints.
Library validation is an important security feature, ensuring that all code you load is either signed by you or signed by Apple. There are reasons you might disable it in a real product - for example, your process supports in-process plug-ins created by a range of third-party developers — but mostly you want to leave it enabled.
Disabling library validation makes it harder to pass notarisation. See Resolving common notarization issues.
Disabling library validation makes it harder to pass Gatekeeper. See Resolving Gatekeeper Problems Caused by Dangling Load Command Paths.
If you’re building a product that you plan to ship to a wide range of users, the correct approach to open source software like this is to bundle the version of the software you need and then re-sign that as your code. OTOH, if this is just a quick hack you’re cobbling together, it’s fine to disable library validation.
Having said that, the error you’re seeing is not related to an issue in your code, so the above is just an FYI.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] As to why that is, my best guess is this is due to a complex chain of events:
- Someone has logged some data.
- Someone is trying to present that data to the user.
- That data is in a custom format.
- So the logging subsystem is trying to load a plug-in to render the data. It spawns
logd_helper
to do this because, sheesh, we don’t want to be loading random code into the logging daemon directly. - Your third-party tooling has installed one of these plug-ins.
- And the system can’t load that because
logd_helper
is sandoxed.
However, that’s just speculation and I’d need to do a lot more research before I can say that definitively.