We will look at shared libraries from two perspectives: that of the library provider, and that of the customer or the library user.
As the provider of a shared library, you want to ensure the functionality provided by that library meets the needs of as many potential customers as possible. This also means that virtually no one customer will use all of the functionality provided by that library. Wouldn't it be great if the unused functionality could be removed to reduce the overhead introduced by the library?
Ideally, the libraries/dlls provided to a customer would contain only what that customer actually needed. But this would be a support nightmare. The alternative? Let the customer build the custom shared libs/dlls themselves. An added bonus would be to do this without the vendor having to make the source public or require the customer to work with large numbers of object files. The answer is the creation of "static shared libraries."
A static shared library is a collection of objects compiled to be position-independent and added to an archive instead of being linked into an actual shared lib. How do you build a static shared library? Write the code the same as you would for a normal shared library, keeping in mind that the use of globals should be kept to a minimum. Compile the .cs with the -shared and -c options. The -shared option will cause position-independent code to be generated; the -c option prevents the link from occurring. This results in the creation of pic object files which can be used to create a shared library/DLL or a static shared library. To create the static shared lib simply add the pic .os to an archive.
qcc -shared -c *.c
qcc -A libmyS *.o
NOTE: the above is Qcc NOT Gcc
Static shared libs are best suited as an object repository for building custom shared libraries or dlls. Using a simple script like the one provided below makes creating shared libraries a simple task. The resulting shared library contains only the functionality specified by the customer, imposing minimal impact on storage media and on memory footprint at runtime.
Static shared libs are indicated with a capital S just before the .a to make them more easily identifiable, for example, libcS.a, libphS.a, libmS.a. We will be releasing static shared versions of all of our standard shared libs in the very near future.
To generate custom libs from the static shared libs discussed above, it requires a little more information. For instance, how do you determine which portions of a library are needed? And, how do you use this information to create a custom shared library?
Determine which symbols are needed to run specific applications.
objdump -T executable |grep UND
Undefine the symbols found in step 1 while linking against the desired source static shared lib.
qcc -shared -L. -Wl,-h libph.so.1 -o libph.so.1 -Wl,-u myfunc -Wl,-u nextfunc ... -l phS
Below is a simple script that takes a source library, a file containing a list of executables, and a destination library. The script will look at all the executables and find all the unique undefined symbols that will be resolved by shared libs. It then creates a build command file and executes it to generate the target library, which contains only the code required by the applications indicated.
A file list may contain
[my_apps]
/usr/photon/bin/pwm
/usr/photon/bin/ped
/usr/photon/bin/voyager
/usr/photon/bin/voyager.server
/usr/photon/bin/phfontFA
/usr/photon/bin/io-graphics
customize_lib ph my_apps ph
This will take everything needed to run the apps in my_apps from libphS.a and put it into a shared lib called libph.so.1 which can simply replace the Photon® lib on the target device.
There is an extra bonus as well. The resultant library is fully supported as long as none of the functions in the static shared lib have been overridden in the destination shared lib. (topic for next article)
----- BEGIN SCRIPT customize_lib----
#! /bin/ksh
usage() {
echo Usage: customize_lib src_lib exec_list_file target_lib
echo \\ex.
echo \\$ customize_lib ph /project1/my_apps proj1_ph
echo \\takes from *libphS.a* all syms needed to run *my_apps* and
echo \\creates a lib called libproj1_ph.so.1
echo \\the command used to build the custom lib is stored in
echo \\build_proj1_ph_lib
echo \\the build_proj1_ph_lib file can be manually editted to
echo \\remove undefined syms that need not
echo \\be undefined in the target lib. ie. syms marked as
echo \\undefined which do not exist in the source lib.
echo
echo \\The exec_list_file should be a file containing the
echo \\fullpath of one executable per line that
echo \\uses the src_lib... \(objdump --private-headers executable
echo \\to see which libs an exe uses\)
echo \\If the applications in exec_list_file are not to be
echo \\relinked, the target_lib name should be the
echo \\same as the src_lib.
echo \\ex. customize_lib ph my_photon_apps ph
exit
}
find_lib() {
for i in $( echo $LD_LIBRARY_PATH |sed 's/:/ /g'); do
if [ -r $i/$lib ] ; then
libpath=$i/$lib
return 1
fi
done
return 0
}
if test $# -ne 3 ; then
usage
fi
echo Building lib$3.so.1 from lib$1S.a
# Start to construct our build command
echo qcc -shared -olib$3.so.1 -L$LD_LIBRARY_PATH \\ >build_$3_lib
rm -f symlist
found=0
# find all syms needed by our apps.
while read name ; do
objdump -T $name | awk '($3=="*UND*") || ($4==".bss") {print $NF}' >>symlist
objdump --private-headers $name | awk -v sl=lib$1.so.1 '($1=="NEEDED") && ($2!=sl)
{print $NF}' >>needed
done <$2
sort -u needed >neededlibs
rm needed
while read lib ; do
find_lib
if [ $? -eq 1 ] ; then
objdump -T $libpath | awk '($3=="*UND*") || ($4==".bss") {print $NF}' >>symlist
fi
done <neededlibs
rm neededlibs
sort -u symlist >usymlist
# find the source static shared library
lib=lib$1S.a
find_lib
if [ $? -eq 0 ] ; then
echo Could not find lib$1S.a in LD_LIBRARY_PATH
rm symlist
rm usymlist
exit
fi
nm $libpath |awk '$2=="T" || $2=="D" {print $3}' >symlist
sort -u symlist >>usymlist
sort usymlist >symlist
a=""
# only want syms that are needed by our apps and are in the source lib
while read b ; do
if [ "$a" = "$b" ] ; then
echo -Wl,-u$a \\ >>build_$3_lib
echo adding $a
read b
fi
a="$b"
done <symlist
rm symlist
rm usymlist
echo -l$1S \\ >>build_$3_lib
# ensure we include any needed libs (libs our lib depends on)
objdump --private-headers $i/lib$1.so |awk '$1 == "NEEDED" {print "-l"$2}' |sed "s/lib//"
|sed "s/\.so.*/ \\\\/g" >>build_$3_lib
echo >>build_$3_lib
echo strip lib$3.so.1 >>build_$3_lib
echo echo custom build of lib$3.so.1 complete >>build_$3_lib
sh build_$3_lib
if [ -r lib$3.so.1 ] ; then
echo Generated lib$3.so.1 from lib$1S.a \(see build_$3_lib file\)
else
echo Custom lib was NOT generated
fi
----- END SCRIPT ----
NOTE: This script assumes that there is a normal shared lib in the same directory as the static shared lib we are using as a source lib (for the purposes of pulling in needed slibs).
Put the above script into a file (customize_lib), make it executable, and you'll be building custom shared libs in no time. It's easy :-)
Hint, to make the script executable: chmod a+x customize_lib
Another hint: When an updated static shared lib is received just run the build again...
For example,
customize_lib ph my_apps ph
# we now have our own libph.so.1
mv build_ph_lib build_custom1_phlib
# time passes... got new libphS.a
sh build_custom1_phlib
# we now have an updated custom libph.so.1
Enjoy.