Gracefully handle a failed lazy symbol binding in C? - c

In short, I am looking for a way of handling this without the process crashing:
dyld: lazy symbol binding failed: Symbol not found: _evalstring
Referenced from: /Users/nrser/src/gh/nrser/my_first_ext/ext/my_first_ext/my_first_ext.bundle
Expected in: flat namespace
dyld: Symbol not found: _evalstring
Referenced from: /Users/nrser/src/gh/nrser/my_first_ext/ext/my_first_ext/my_first_ext.bundle
Expected in: flat namespace
./bin/console: line 2: 69989 Abort trap: 6 bundle exec pry -f -r my_first_ext
Is there a way to tell if evalstring is present before making the call, or trap the error in the function and return something else?
So far the resources I've been able to find focus on stopping the error form happening, not handling it when it does.
For context, I have a some C that runs both inside a Bash "loadable", where Bash's evalstring is available, and also outside of it, where evalstring is not.
It is an error to call functions that depend on evalstring unless inside the loadable, but I'd rather do something nicer than crash the process if users do.
Details
The evalstring function is declared as
extern int evalstring __P((char *, const char *, int));
in <BASH_DIR>/include/bash/builtins/common.h, at least as far as what my IDE thinks.
I understand that portability may be an issue. I'm targeting Linux and macOS/OSX at this time.

At least on Linux the only way to achieve this is to provide your own wrapper which intercepts calls to evalstring and then redirects them to actual implementation obtained via dlsym or fails in app-specific way. Such wrappers can be implemented manually or (if you need to achieve this for all functions in some library) via script (for example see Implib.so wrapper generator).

Related

Idris FFI "symbol not found"

I've been messing around with Idris lately and decided to try playing around with its Network.Socket library. I fired up the REPL, imported the module, and created a socket using the socket command. Upon attempting to execute the IO operation, I was met with the following error:
failed to construct ffun from (Builtins.MkPair (FFI_C.C_Types (Int)) (Int) (FFI_C.C_IntT (Int) (FFI_C.C_IntNative)) (2),Builtins.MkPair (FFI_C.C_Types (Int)) (Int) (FFI_C.C_IntT (Int) (FFI_C.C_IntNative)) (1),[])
Symbol "socket" not found
user error (Could not call foreign function "socket" with args [2,1,0])
To see whether the issue was Network.Socket specific, or just FFI in general, I made a dummy function.
printf : String -> IO ()
printf = foreign FFI_C "printf" (String -> IO ())
Executed :x printf "Hello World" yields a similar error:
Symbol "printf" not found
user error (Could not call foreign function "printf" with args ["hello world"])
Despite all this, putStr works fine.
I am running Idris 9.20, installed through cabal with -f FFI set at compile. I am using libffi version 3.4 installed through MacPorts.
I believe that this has to do with the fact that the Idris FFI operates differently depending on whether code is being compiled or interpreted. When code is being compiled, the FFI requires that at the stage of C codegen, the named C function be in scope, and that when linking the C executable, the correct name is linked in. Since Idris's RTS links against libc, this makes a lot of names from libc work without any extra effort (certain names might require a %include to make sure that the correct C header file is included to put them in scope). When code is being interpreted, the interpreter looks up FFI calls in a list of libraries that have been loaded dynamically, which requires a different directive: %dynamic in the file, or :dynamic in the interpreter. By default, no dynamic libraries are loaded, so even standard names in libc are not in scope. This can be remedied by including %dynamic "libc" in the file, or using the :dynamic "libc" at the REPL commandline for one session.

Must R Packages Unload Dynamic Libraries When They Unload?

From Hadley's C best practices:
Like with C++, whenever you use C code in your package, you should unload the DLL when the package is unloaded:
.onUnload <- function (libpath) {
library.dynam.unload("mypackage", libpath)
}
Writing R Extensions on the other hand doesn't even mention this. I can see how it would be polite to unload the dlls, but doing so seems to cause some weird problems for me with packages that are loaded/unloaded/reloaded (see example further down). Additionally, there are some mentions that suggest maybe unloading isn't required. From ?library.dynam:
Note that whether or not it is possible to unload a DLL and then reload a revised version of the same file is OS-dependent: see the ‘Value’ section of the help for dyn.unload.
though this shouldn't affect objects that are not modified. Then there is this comment from Brian Ripley in R-devel:
Having said all that, my experience is that unloading the DLL often does not help if you need to load it again (and that is why e.g. tcltk does not unload its DLL).
So is it acceptable to leave the C libraries loaded? I would prefer not to have to dig into why stuff like the below is happening (did not happen before I started unloading libraries).
R version 3.1.1 (2014-07-10)
Platform: x86_64-apple-darwin13.1.0 (64-bit)
> library(alike) # install_github("brodieg/alike", ref="fdaa578e"), if you're curious
> library(data.table)
data.table 1.9.2 For help type: help("data.table")
> detach("package:data.table", unload=T)
> detach("package:alike", unload=T)
> library(alike)
> library(data.table)
Error : .onLoad failed in loadNamespace() for 'data.table', details:
call: address(x)
error: object 'Caddress' not found
In addition: Warning messages:
1: In FUN(X[[9L]], ...) :
failed to assign RegisteredNativeSymbol for alike to alike since alike is already defined in the ‘data.table’ namespace
2: In FUN(X[[9L]], ...) :
failed to assign RegisteredNativeSymbol for typeof to typeof since typeof is already defined in the ‘data.table’ namespace
3: In FUN(X[[9L]], ...) :
failed to assign RegisteredNativeSymbol for type_alike to type_alike since type_alike is already defined in the ‘data.table’ namespace
Error: package or namespace load failed for ‘data.table’
The warnings are all related to alike functions. alike did not use to unload its dynamic libraries, and the above errors did not happen. After I implemented unloading the errors started happening. Note that data.table 1.9.2 did not unload its DLLs, though other packages that also don't unload DLLs didn't cause this problems. data.table 1.9.4 works fine.
Normally unloading a DLL would be a good idea. The resources it owns, would be completely freed, and re-loading would not be an issue.
In R, there is the complication of the R environment, because even if a DLL is unloaded, there may be some knowledge left behind in the R runtime. In this case, the result may be that the re-loaded DLL library does not share the same inferred state as the R variables which are intended to understand the DLL state, and thus errors occur.
I think it would be possible for an R package (DLL and R code) to be safely unloaded, but it would be easier for you to leave the DLLs loaded, unless you find particularly heavy resource usage.

Can I modify the dynamic linker and use without recompiling the glibc?

I am trying to modify the dynamic linker provided in the libc6(2.15-0ubuntu20.2) on a 64 bit Ubuntu machine.
So currently my code is using the same version of the glibc library. (I have downloaded the source code for the same and working on it). My question is that is it possible to modify and build only the linker source code which is present in glibc\elf\ directory without building the entire glibc library.
And if it is possible how can I make my test program to switch using the new version of dynamic linker that I have build myself instead of using the default unmodified linker.
Any pointers or suggestions are highly appreciated.
(If any more information is needed please let me know)
EDIT::
#constantius
I followed the steps in the post linked by you to build ld.so.
But I am getting following error on the make and I checked ld.so is not there in the elf.
The error is::
/var/services/homes/abhi/test/ld/eglibc-build/elf/librtld.os: In function `generic_getcwd':
/var/services/homes/abhi/test/ld/eglibc-2.15/elf/../sysdeps/posix/getcwd.c:356: undefined reference to `__closedir'
/var/services/homes/abhi/test/ld/eglibc-2.15/elf/../sysdeps/posix/getcwd.c:368: undefined reference to `__fdopendir'
/var/services/homes/abhi/test/ld/eglibc-2.15/elf/../sysdeps/posix/getcwd.c:384: undefined reference to `__readdir'
/var/services/homes/abhi/test/ld/eglibc-2.15/elf/../sysdeps/posix/getcwd.c:397: undefined reference to `rewinddir'
/var/services/homes/abhi/test/ld/eglibc-2.15/elf/../sysdeps/posix/getcwd.c:528: undefined reference to `__closedir'
/var/services/homes/abhi/test/ld/eglibc-2.15/elf/../sysdeps/posix/getcwd.c:490: undefined reference to `__closedir'
collect2: error: ld returned 1 exit status
make[2]: *** [/var/services/homes/abhi/test/ld/eglibc-build/elf/ld.so] Error 1
make[2]: Leaving directory `/var/services/homes/abhi/test/ld/eglibc-2.15/elf'
make[1]: *** [elf/subdir_lib] Error 2
make[1]: Leaving directory `/var/services/homes/abhi/test/ld/eglibc-2.15'
make: *** [all] Error 2
NOTE With the same infrastructure I can build and install the full GLIBC so I dont think there is an error with the infrastructure.
-- I guess the error is some where related to editing Makeconfig to all-subdirs = csu elf gmon io misc posix setjmp signal stdlib string time.
--Any suggestions on this..
SOLVED
Need to add dirent in the all-subdirs list in addition to what we edited before
Thanks
Citing this page. In case you don't get something, comment please — I'll try to explain.
Building
To compile Glibc (ld.so cannot be compiled independently) download and unpack Glibc source tarball.
1 Make sure the version of Glibc you downloaded is the same as the system's current one.
2 Make sure the environmental variable LD_RUN_PATH is not set.
3 Read the INSTALL and make sure all necessary tool chains (Make, Binutils, etc) are up-to-date.
4 Make sure the file system you are doing the compilation is case sensitive, or you will see weird errors like
/scratch/elf/librtld.os: In function `process_envvars':
/tmp/glibc-2.x.y/elf/rtld.c:2718: undefined reference to `__open'
...
5 ld.so should be compiled with the optimization flag on (-O2 is the default). Failing to do so will end up with weird errors (see Question 1.23 in FAQ)
6 Suppose Glibc is unpacked at
/tmp/glibc-2.x.y/
Then edit /tmp/glibc-2.x.y/Makefile.in: Un-comment the line
# PARALLELMFLAGS = -j 4
and change 4 to an appropriate number.
7 Since we are only interested in ld.so and not the whole Glibc, we only want to build the essential source files needed by ld.so. To do so, edit /tmp/glibc-2.x.y/Makeconfig: Find the line started with
all-subdirs = csu assert ctype locale intl catgets math setjmp signal \
...
and change it to
all-subdirs = csu elf gmon io misc posix setjmp signal stdlib string time
8 Find a scratch directory, say /scratch. Then
$ cd /scratch
$ /tmp/glibc-2.x.y/configure --prefix=/scratch --disable-profile
$ gmake
Since we are not building the entire Glibc, when the gmake stops (probably with some errors), check if /scratch/elf/ld.so exists or not.
ld.so is a static binary, which means it has its own implementation of standard C routines (e.g. memcpy, strcmp, etc) It has its own printf-like routine called _dl_debug_printf.
Testing
You can run the ld-linux.so directly. It will complain that this is probably not what you want (but you want exactly this) and offer you list of options with which you can run it. See also man ld-linux.so for debugging flags, i.e. there's LD_DEBUG environment variable you can define to see ld-linux.so debugging output.
While I'm not clear on whether the build system for glibc makes doing this easy, there's no fundamental reason why you can't build and use the glibc dynamic linker without building libc.so. I would peruse the top-level Makefile for ways to make this work.
As for testing it, there are two methods:
Explicitly invoke the dynamic linker to run a program, as in:
./ld-linux.so.2 a.out args ...
When linking your program, specify an alternate dynamic linker pathname (which will get stored in its PT_INTERP program header) by passing this option to the compiler driver:
-Wl,-dynamic-linker,/path/to/alternate/ld-linux.so.2

Why isn't asdf:load-system creating the package

I'm trying to to use asdf's functionality to run my test suite from the repl but when using quicklisps quickload if fails on the first attempt to load foo and success on the second.
(in-package :cl-user)
(defpackage :foo-system
(:use :cl :asdf))
(in-package :foo-system)
(asdf:defsystem :foo
:components ((:file "foo")))
(asdf:defsystem :foo-tests
:depends-on (:foo)
:components ((:file "foo-tests")))
(defmethod asdf:perform ((op test-op) (system (eql (find-system :foo))))
(asdf:load-system 'foo-tests)
(foo-tests:run-tests))
It makes sense because when I compile the asd file the error appears to be in the second form of the asdf:perfom defmethod. The error, replacing nclack by foo, is:
../../nclack/nclack.asd:36:27: read-error:
READ error during COMPILE-FILE:
Package NCLACK-TESTS does not exist.
Line: 36, Column: 27, File-Position: 1034
Stream: #<SB-SYS:FD-STREAM
for "file /Users/PuercoPop/quicklisp/local-projects/nclack/nclack.asd"
{1005DB11A3}>
which matches the (foo-tests:run-tests) line. So it appears to be that 'loading' a system is different from compiling its forms? Or why is the package not defined after loading the system? Any ideas? I'm at a loss.
When you compile-file this:
(defmethod asdf:perform ((op test-op) (system (eql (find-system :foo))))
(asdf:load-system 'foo-tests)
(foo-tests:run-tests))
the first step is to read the entire form. Reading includes interning of all symbols found. However, at read time, the form has not executed, so the system foo-tests is not yet loaded. Since that system includes the package foo-tests, and that is not loaded yet either, you cannot intern any symbols into that package at read time of this form.
That's why compile-file gives the error shown when trying to read the form.
It seems to me that you would need to (funcall (find-symbol "RUN-TESTS" #:foo-tests)).
First:
If you define a new package FOO-SYSTEM and use the package ASDF and then use the asdf symbols still with the prefix, then you kind of defeated the using of the package. Why use it, when you prefix the symbols anyway?
It is hard to say what the error is, since we don't see a backtrace and don't see the files.
Anyway, you need to make sure that a package is available during compilation of a file. For example compiling a DEFPACKAGE form may not change the compile-time environment. You need to execute the DEFPACKAGE form. You also need to make sure that the package definition gets loaded during a load system operation.
If the package does not exist, you need to make sure that it exists.

symbol lookup error on a command

i'm trying to do some code in a keyboard driver, a 3rd party software that looks like this can run the command i'm trying to do in a plugin file that compiles alongside the daemon that the command needs to be sent to. the command looks like this.
g15_send_cmd (g15screen_fd,G15DAEMON_MKEYLEDS,mled_state);
here's the code i'm working with and trying to run the command in (it compiles as a plugin with the daemon. in the uncompiled source it's
"g15daemon/plugin/g15_plugin_uinput.c"
the file that defines the command is in
(link)
"g15daemon/libg15daemon_client/g15daemon_clinet.h"
whereas with the g15macro (3rd software) is run from outside the daemon for various reasons i don't want to (and pretty much can't) use it, one being speed of execution of commands when keys are pressed.
so the program compiles like this without error it seems. but if the code i specified above activates, the driver(daemon) crashes giving
g15daemon: symbol lookup error:
/usr/lib/g15daemon/1.9.5.3/plugins/g15plugin_uinput.so: undefined
symbol: g15_send_cmd
what did i do wrong or what am i missing here? (and i'm sorry if the code in the plugin file is ugly down by that switch command, but i didn't know how to do any better since i don't know C much at all, all i have behind me are Py and C#)
Edit: the solution was given
but i don't know how to add a linker flag, also since it links to a part of the program being compiled will it even work?
You forgot to link your plugin with g15daemon_client library. The simple way to fix it is to add -lg15daemon_client to the linker flags.

Resources