How to find all shared libraries actually used during execution in Linux? - linux

I have an executable and I would like to find out which shared libraries were actually used during a specific run. I know ldd would list all the shared library dependencies of that executable but I would like to find out the subset of those that were actually used during a specific run*. Is this possible?
*what I mean with specific run is running the executable with certain input parameters that would cause only a small part of the code to be run.

You can use ltrace(1) for this:
$ PROG='ls -l'
# Collect call info
$ ltrace -o calls.txt -l '*' $PROG &> /dev/null
# Analyze collected data
$ cat calls.txt | sed -ne '/->/{ s/^\(.*\)->.*/\1/; p }' | sort -u
libacl.so.1
libcap.so.2
libc.so.6
libselinux.so.1
ls
# Compare with ldd
$ ldd /bin/ls | wc -l
10

You could use strace and grep for open .so files.
strace $MYPROG | grep -E '^open*\.so
lsof also should work to grep for open libraries.
lsof -p $PID | awk '{print $9}' | grep '\.so'
This assumes the shared libraries have .so extension

Related

how to get PIDs spawn from a particular folder

I have a program that in distributed mode creates a folder and spawns a bunch of sub processes. Is there any way to find all PIDs that were executed from this folder? Sort of opposite of
$ pwdx pid
where you give a path name and you get a bunch of pids.
thanks
Reporting all processes which absolute path is inside '/usr/bin/' may be done like this:
ls -l /proc/*/exe 2>/dev/null | grep /usr/bin/ | sed 's#.*/proc/##;s#/exe.*##;' | grep -v "self"
Reporting all processes which working directory (working directory can be changed by a simple cd) is inside /tmp/a could be done like this:
ps axo pid | xargs -n1 pwdx 2>/dev/null | grep ': /tmp/a' | sed 's/:.*//'

readelf reports so file as NEEDED, but no functions (or other symbols) is used from it

Readelf reports, that libutil.so.1 is needed by ssh utility:
$readelf -s /usr/bin/ssh | grep libutil
0x0000000000000001 (NEEDED) Shared library: [libutil.so.1]
As nm says, libutil.so.1 has only 6 external symbols:
$nm -D /lib64/libutil.so.1 | grep "T "
000000331e0015a0 T forkpty
000000331e000fa0 T login
000000331e0010f0 T login_tty
000000331e001190 T logout
000000331e0012e0 T logwtmp
000000331e0013d0 T openpty
But these symbols not referenced from ssh:
$nm -D /usr/bin/ssh | grep forkpty
$nm -D /usr/bin/ssh | grep login
$nm -D /usr/bin/ssh | grep login_tty
$nm -D /usr/bin/ssh | grep logout
$nm -D /usr/bin/ssh | grep logwtmp
$nm -D /usr/bin/ssh | grep openpty
(grep not showing any matches)
What's going on? Why library referenced as NEEDED, but no symbols from it used? This is not sole example. I met many such "empty" references, while analysing dependencies of another executables.
The DT_NEEDED tagging is generated by the link editor (ld) based on the -l flags provided. The default for the GNU link editor and most other editors, with some exceptions, is to create a DT_NEEDED tag for each -l flag provided.
When using GNU ld or gold, you can pass --as-needed before the -l flags to only emit DT_NEEDED tags for the libraries that are indeed used. This may still emit not obvious tags if the symbols are actually used indirectly.
I have actually written a significant amount about --as-needed and how it works, so you can look through my blog posts if you are more curious.

grep and tee to identify errors during installation

In order to identify if my installation has errors that I should notice, I am using grep command on the file and write the file using tee because I need to elevate permissions.
sudo grep -inw ${LOGFOLDER}/$1.log -e "failed" | sudo tee -a ${LOGFOLDER}/$1.errors.log
sudo grep -inw ${LOGFOLDER}/$1.log -e "error" | sudo tee -a ${LOGFOLDER}/$1.errors.log
The thing is that the file is created even if the grep didn't find anything.
Is there any way I can create the file only if the grep found a match ?
Thanks
You may replace tee with awk, it won't create file if there is nothing to write to it:
... | sudo awk "{print; print \$0 >> \"errors.log\";}"
But such feature of awk is rarely used. I'd rather remove empty error file if nothing is found:
test -s error.log || rm -f error.log
And, by the way, you may grep for multiple words simultaneously:
grep -E 'failed|error' ...

Get bin directory of process

I want to show only the directories where the binaries are installed. Like
/bin
for
/bin/ls
This is what I've done so far:
ps aux | awk '{print $11}' | grep -x -e "/.*"
But its displaying the filename too, and I dont want that, and example of the output:
/usr/lib/firefox/firefox
But id like it like this:
/usr/lib/firefox
Thank you!
The command in order to extract the name of the directory is dirname "path/to/file". Now as you probably see, it requires an argument (does not read from stdin). You can however use xargs to fix this:
xargs dirname
Now you simply need to add this at the end of your pipeline:
ps aux | awk '{print $11}' | grep -x -e "/.*" | xargs dirname
Demo
Ran this on my Linux machine:
$ ps aux | awk '{print $11}' | grep -x -e "/.*" | xargs dirname | head
/sbin
/lib/systemd
/lib/systemd
/sbin
/usr/sbin
/usr/sbin
/usr/sbin
/usr/sbin
/usr/sbin
/usr/bin
In order to make your command space-safe (a remark by #hek2mgl), you can use:
ps aux | awk '{print $11}' | grep -x -e "/.*" | xargs -I file dirname "file"
Mind this will have an impact on performance: whereas using xargs dirname without any flags would use the loop mechanism of dirname handling multiple parameters, and thus resulting in a tight loop, using the latter will spawn a dirname process for each line individually.
More elegant way
Your program makes use of a lot of text processing, which can be tricky, error prone and furthermore sensitive to changes of the format (of ps,...). A less error prone way can be:
ps -A -o pid | xargs -I pid readlink "/proc/pid/exe" | xargs -I file dirname "file"

Bash grep command finding the same file 5 times

I'm building a little bash script to run another bash script that's found in multiple directories. Here's the code:
cd /home/mainuser/CaseStudies/
grep -R -o --include="Auto.sh" [\w] | wc -l
When I execute just that part, it finds the same file 5 times in each folder. So instead of getting 49 results, I get 245. I've written a recursive bash script before and I used it as a template for this problem:
grep -R -o --include=*.class [\w] | wc -l
This code has always worked perfectly, without any duplication. I've tried running the first code with and without the " ", I've tried -r as well. I've read through the bash documentation and I can't seem to find a way to prevent, or even why I'm getting, this duplication. Any thoughts on how to get around this?
As a separate, but related question, if I could launch Auto.sh inside of each directory so that the output of Auto.sh was dumped into that directory; without having to place Auto.sh in each folder. That would probably be much more efficient that what I'm currently doing and it would also probably fix my current duplication problem.
This is the code for Auto.sh:
#!/bin/bash
index=1
cd /home/mainuser/CaseStudies/
grep -R -o --include=*.class [\w] | wc -l
grep -R -o --include=*.class [\w] |awk '{print $3}' > out.txt
while read LINE; do
echo 'Path '$LINE > 'Outputs/ClassOut'$index'.txt'
javap -c $LINE >> 'Outputs/ClassOut'$index'.txt'
index=$((index+1))
done <out.txt
Preferably I would like to make it dump only the javap outputs for the application its currently looking at. Since those .class files could be in any number of sub-directories, I'm not sure how to make them all dump in the top folder, without executing a modified Auto.sh in the top directory of each application.
Ok, so to fix the multiple find:
grep -R -o --include="Auto.sh" [\w] | wc -l
Should be:
grep -R -l --include=Auto.sh '\w' | wc -l
The reason this was happening, was that it was looking for instances of the letter w in Auto.sh. Which occurred 5 times in the file.
However, the overall fix that doesn't require having to place Auto.sh in every directory, is something like this:
MAIN_DIR=/home/mainuser/CaseStudies/
cd $MAIN_DIR
ls -d */ > DirectoryList.txt
while read LINE; do
cd $LINE
mkdir ProjectOutputs
bash /home/mainuser/Auto.sh
cd $MAIN_DIR
done <DirectoryList.txt
That calls this Auto.sh code:
index=1
grep -R -o --include=*.class '\w' | wc -l
grep -R -o --include=*.class '\w' | awk '{print $3}' > ProjectOutputs.txt
while read LINE; do
echo 'Path '$LINE > 'ProjectOutputs/ClassOut'$index'.txt'
javap -c $LINE >> 'ProjectOutputs/ClassOut'$index'.txt'
index=$((index+1))
done <ProjectOutputs.txt
Thanks again for everyone's help!

Resources