Linking to libraries with $ld$hide$os10.X$ symbols in OSX - macos

I'm trying to link against the BLAS implementation in OSX (/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib, for the curious) but I don't want to link against the libLAPACK.dylib in that same directory, as I want to use my own build of LAPACK from netlib, as it is much more recent and up to date.
My problem is that there are symbols in the BLAS library that are typically stored in an LAPACK library, and as such are causing name clashes. As a concrete example, the spotrf function is defined in libBLAS.dylib:
$ nm /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib | grep spotrf
0000000000010c05 T $ld$hide$os10.7$_spotrf
0000000000010c05 T $ld$hide$os10.8$_spotrf
000000000000746e T _spotrf
Those first two symbols made me a little suspicious, so to double-check, I check out libLAPACK.dylib as well:
$ nm /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.dylib | grep spotrf
00000000000010c8 T $ld$hide$os10.4$_spotrf
00000000000010c8 T $ld$hide$os10.5$_spotrf
00000000000010c8 T $ld$hide$os10.6$_spotrf
000000000000765b T _spotrf
From what limited information I have been able to find, it seems that this prefix somehow instructs the dynamic linker to ignore these symbols if the users is compiling against a given OSX version. This makes sense if Apple moved symbols from libBLAS.dylib to libLAPACK.dylib between 10.6 and 10.7.
My question is, how can I inform the dynamic linker that it SHOULD hide spotrf etc... that are inside libBLAS.dylib?

First off, you don’t actually need to do anything special to get your desired behavior, as you’re linking dynamically. Ensure that your netlib-built LAPACK appears before -lblas in the link command, and all LAPACK interfaces will be picked up from the netlib LAPACK (ld matches all the undefined symbols it can against each of the libraries being linked against in order).
Alternatively, assuming that your netlib LAPACK is linked against the system BLAS, you should be able to configure the link of LAPACK so that it reexports all of the BLAS symbols. Then you can simply link against your LAPACK and leave -lblas out of your link command entirely.
The $show$ and $hide$ symbols come into play if and only if you are linking against both the system BLAS and the system LAPACK libs. A simple example will show how they work in that case:
Kronecker:~ scanon$ cat foo.c
void dgetrf_(void);
int main(void) { dgetrf_(); return 0; }
Building this on 10.9 and breaking on dgetrf_, we see that the symbol was picked up from libLAPACK.dylib, even though -lblas appears first in the build command. This is because the symbol is hidden in libBLAS.dylib when targeting 10.9:
Kronecker:~ scanon$ clang foo.c -lblas -llapack
Kronecker:~ scanon$ lldb a.out
Current executable set to 'a.out' (x86_64).
(lldb) b dgetrf_
Breakpoint 1: 2 locations.
(lldb) run
...
frame #0: 0x00007fff89237b70 libLAPACK.dylib`dgetrf
libLAPACK.dylib`dgetrf:
...
If we specify -mmacosx-version-min=10.6 (telling the compiler and linker that we want to produce an executable that can run on 10.6 Snow Leopard), we will see that the symbol is picked up from libBLAS.dylib instead. This is because the symbol doesn’t exist in libLAPACK.dylib on 10.6, so our executable wouldn’t be able to run on that platform if the symbol in libLAPACK.dylib were used:
Kronecker:~ scanon$ clang foo.c -lblas -llapack -mmacosx-version-min=10.6
Kronecker:~ scanon$ lldb a.out
Current executable set to 'a.out' (x86_64).
(lldb) b dgetrf_
Breakpoint 1: 2 locations.
(lldb) run
...
frame #0: 0x00007fff8c9d6841 libBLAS.dylib`DGETRF_
libBLAS.dylib`DGETRF_:
...

Related

Statically linking any library causes libc to fail to be linked against

My system is an older NAS running 2.6.32. I have found that when using -static for any subsequent library, it will also try to statically link any other library that I might need.
When I add the -Wl,-Bdynamic flag first and then explicitly name those libraries using -lc, such as "-Wl,-Bdynamic -lc -lstdc++" then it works. So what happens is that libc and others fail to be statically linked.
The static libc on the system is called /opt/lib/libc_nonshared.a.
The contents of /opt/lib/libc.so is this:
OUTPUT_FORMAT(elf32-littlearm)
GROUP ( /lib/libc.so.6 /opt/lib/libc_nonshared.a )
The gcc version is 4.2.3. The current build command I am facing adds -dynamic at the end but this doesn't help much. When I add some static library directly using its .a name, and not using a -l flag, then there is no issue.
The problem seems to be that the dynamic library of libc came with the NAS, but the static version sits in /opt/lib.
I run:
gcc hamming.c -static -L. -L/opt/lib -l:matrix.a -o hamming
I get:
/opt/lib/gcc/arm-none-linux-gnueabi/4.2.3/../../../../arm-none-linux-gnueabi/bin/ld: cannot find -lc
collect2: ld returned 1 exit status
make: *** [hamming] Error 1
when I try to use static libc as is. Were I to perform a 'hack' to link libc_nonshared.a to libc.a, it suddenly does find it. But complains:
hamming.c:54: undefined reference to `malloc'
hamming.c:54: undefined reference to `memset'
And a zillion other errors of course. As mentioned above, /opt/libc.so contains the reference to both files (dynamic and static).
For libstdc++ only a .la file exists.
The -static linker flag does not take any argument. It is a boolean
flag that simply directs the linker to link no shared libraries, as
documented
-static
Do not link against shared libraries...
There is no need to explicitly direct the linker to link shared (dynamic)
libraries when it has a choice because that is the default bevaiour. If
you simply link, e.g.
gcc -o prog ... -lfoo ...
then the linker will link the first of libfoo.so (shared) or libfoo.a
(static) that it finds in any of the specified (-Ldir) or default
search directories, searched in commandline sequence. If it finds both
libfoo.so and libfoo.a in the same directory then it will choose
libfoo.so. Thus shared and static libraries may be freely intermixed
without any special options.
Specify -static only if you wish to link only static libraries.
If you wish to insist on linking a particular libfoo.a even when
libfoo.so is in the same directory and would be chosen by default,
use the explicit form of the -l option: -l:libfoo.a
Later
gcc hamming.c -static -L. -L/opt/lib -l:matrix.a -o hamming
This command is failing with:
ld: cannot find -lc
because the linker (ld) cannot find a static library libc.a in
any of the specified linker search directories (-L. -L/opt/lib) or
the default linker search directories. If you wish instead to link
/opt/lib/libc_nonshared.a then your command should be:
>gcc hamming.c -static -L. -L/opt/lib -l:matrix.a -lc_nonshared -o hamming
However, you have not explained why you want to link this program statically
(-static) in the first place, which is not the usual way and will require you to have installed
static versions of all libraries required for the linkage - both those
you explicitly link and the default libraries that gcc will add for C language
linkage (Standard C library, GCC runtime library).
Supposing you have a static library called (oddly) matrix.a (rather
than normally, libmatrix.a) that is located in /some/dir/, then the
normal way to compile and link your program would be:
gcc hamming.c -L/some/dir -l:matrix.a -o hamming
I suggest you start with that and deviate only as problems compel
you to.
The discovery of an /opt/lib/libc.so containing:
OUTPUT_FORMAT(elf32-littlearm)
GROUP ( /lib/libc.so.6 /opt/lib/libc_nonshared.a )
is misleading you. This is not your shared libc. A shared library
is a binary. This is a linker script, and it says that your shared libc
is in fact /lib/libc.so.6. The linker will almost certainly find and use it by default.

Link a "toy" OS using llvm/clang

Is it possible (within reason) to build a "toy" OS on a mac using llmv/clang (and the other "normal" build tools)? By "toy" OS, I mean the simple, "Hello, World" examples found on OSDev (http://wiki.osdev.org/Bare_Bones) and x86 Bare Metal (https://github.com/cirosantilli/x86-bare-metal-examples).
My main problem is I can't figure out how to specify precisely where the linker should place the code (i.e., that the starting point should be 0x7c00, that bytes 510 and 511 need to be 0xaa55, etc.).
I would say yes it is possible within reason, at least if you consider waiting for a build of lld (and its dependency llvm) reasonable. Instructions to build lld can be found on their website or as part of this answer.
Compiling and linking for a different target than the host is relatively easy with clang. You just have to set a target, for example -target i386-none-elf for an ELF binary. Cross-compilation using clang is explained in more detail here.
As for macOS, as Micheal Petch noted, you have to use another linker than the standard ld installed. You could in theory install binutils to get an ELF ld but then you have to compile it yourself to set the target. My recommendation is to use lld which can target many architectures without the need to recompile.
With clang and a lld in place we can compile sources with
clang -c -o file.o file.c -target i386-none-elf # freestanding flags omitted
and then link them with
clang -o kernel.bin file.o -target i386-linux-elf -nostdlib -Wl,linkerscript.ld -fuse-ld=lld
Note that for linking I am using i386-linux-elf because there is a bug in clang where they just forward their input to gcc. But when using -nostdlib it is essentially the same.
If you want to see a complete example ready to build, you can take a look at https://github.com/Henje/x86-Toy-OS.

Xcode "ld: library not found [...] for architecture x86_64"

I want to include libgpg-error and libgcrypt in my swift-project and created the following module.modulemaps:
libgpgerror:
module libgpgerror {
header "/Volumes/Xcode/Programme/Swifts/KCAnon/KCAnon_Client/Libs/libgpgerror/gpg-error.h"
link "'/Volumes/Xcode/Programme/Swifts/KCAnon/KCAnon_Client/Libs/libgpgerror/libgpgerror-1.21.dylib'"
export *
}
libgcrypt:
module libgcrypt {
header "/Volumes/Xcode/Programme/Swifts/KCAnon/KCAnon_Client/Libs/libgcrypt/gcrypt.h"
link "'/Volumes/Xcode/Programme/Swifts/KCAnon/KCAnon_Client/Libs/libgcrypt/libgcrypt-1.6.5.dylib'"
export *
}
I also added the "Swift Compiler - Search Path/Import Paths": /Volumes/Xcode/Programme/Swifts/KCAnon/KCAnon_Client/Libs/** to both project and target.
The modules are found, the paths are correct.
However if I want to compile the project I get the following error:
ld: library not found for -l'/Volumes/Xcode/Programme/Swifts/KCAnon/KCAnon_Client/Libs/libgpgerror/libgpgerror-1.21.dylib' for architecture x86_64
But if I do
file /Volumes/Xcode/Programme/Swifts/KCAnon/KCAnon_Client/Libs/libgpgerror/libgpgerror-1.21.dylib
I get the output
/Volumes/Xcode/Programme/Swifts/KCAnon/KCAnon_Client/Libs/libgpgerror/libgpgerror-1.21.dylib: Mach-O 64-bit dynamically linked shared library x86_64
So it seems the library is in the correct place and also has the correct architecture.
Edit
I found a workaround: I removed the link-directive from the modulemaps and linked the libraries manually; this seems to work. But why?
module libgpgerror {
header "/Volumes/Xcode/Programme/Swifts/KCAnon/KCAnon_Client/Libs/libgpgerror/gpg-error.h"
export *
}
The link directive specifies only the name of the linked library. That is it should specify the suffix of the linker flag for the library. It appears that the directive take "-l" and concatenates the name to produce the linker flag.
This means that the correct way to specify your module map is as follows.
module CGcrypt {
header "/Volumes/Xcode/Programme/Swifts/KCAnon/KCAnon_Client/Libs/libgcrypt/gcrypt.h"
link "gcrypt"
export *
}
This will generate the linker flag -lgcrypt which is the correct linker flag.
However, there is another problem which is that the linker needs to be able to find the dylib file for gcrypt and by default it only looks on certain paths. Those paths can be found by running clang -Xlinker -v. The output for me looks like this:
tylercloutier$ clang -Xlinker -v
#(#)PROGRAM:ld PROJECT:ld64-264.3.101
configured to support archs: armv6 armv7 armv7s arm64 i386 x86_64 x86_64h armv6m armv7k armv7m armv7em (tvOS)
Library search paths:
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/lib
... more stuff ...
Now I'm not sure, but I suspect that the normal search paths are probably
/usr/lib
/usr/local/lib
but I think that Xcode has altered my search paths to point the the MacOSX10.11.sdk/usr/lib, which, incidentally, has basically the same set of files as /usr/lib (they are not symlinked). Indeed, in El Capitan, because of System Integrity Protection even sudo will not allow you to edit /usr/lib.
Thus the problem that I am having is that even though I've installed my libraries to /usr/local/lib, clang is not able to link them. In order to fix that I can specify the search path explicitly.
swift build -Xlinker -L/usr/local/lib/
And we're off to the races. I can even generate an xcodeproj which will have the appropriate linker flag already set in Other Linker Flags.
swift build -Xlinker -L/usr/local/lib/ --generate-xcodeproj
If you leave out the link directive in the module map file, you can specify it as a flag:
module CGcrypt {
header "/Volumes/Xcode/Programme/Swifts/KCAnon/KCAnon_Client/Libs/libgcrypt/gcrypt.h"
export *
}
Like so
swift build -Xlinker -L/usr/local/lib/ -lgcrypt
How to change the default library search paths, I don't know. But it would be great if someone else could shed light on this matter!

GHC error with System on OS X Lion

I tried to compile and link simple program using ghc, but it failed during linking:
import System (getArgs)
main = do
args <- getArgs
print args
I tried to compile with
% ghc -c -O Main.hs
% ghc -o Main Main.o
ld: warning: could not create compact unwind for .LFB3: non-standard register 5 being saved in prolog
Undefined symbols for architecture i386:
"___stginit_haskell98zm1zi1zi0zi1_System_", referenced from:
___stginit_Main_ in Main.o
ld: symbol(s) not found for architecture i386
collect2: ld returned 1 exit status
zsh: exit 1 ghc -o Main Main.o
However, when compiling with --make:
% ghc --make Main.hs
everything works (besides tons of ld warnings)
Some more informations about environment:
% ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.0.3
From Haskell Platform for Mac OS X 10.6 (Intel, 32 bit GHC)
System: Max OS X Lion 10.7.2
Any ideas what's wrong?
(Btw, I tried to install HP x64 but it failed during installation)
Michael is historically right. With --make, ghc figures out which packages it has to use and link in by itself (unless two installed packages expose the same module name, then it can't figure out which one to use), without --make, you have to tell it. However, as of 7.0, --make is the default mode of ghc, so plain ghc Main.hs is now the same as ghc --make Main.hs. The difference here is the two-step compilation. I don't know the precise details, but the cause is that the module System is in the haskell98 package (a propos, please use the hierarchical modules, getArgs should be imported via System.Environment, as of 7.2, haskell98 can't be used together with base), which by default is not linked in. So ghc -o Main Main.o doesn't find the symbol in the default packages. You'd have to tell it explicitly to look in the haskell98 package, ghc -c -O Main.hs; ghc -package haskell98 -o Main Main.o should work (and it works here, I've tested with 7.0.4 to make sure).
Perhaps it's because you're using something from System? ghc --make probably auto-detects which Haskell libraries it needs to link, and ghc by itself does not.

How to link to a specific version of the standard library (with gcc)

I've installed GCC 3.4 to /opt/gcc-3.4, and I'm using it to compile legacy code which is incompatible with GCC 4. This also means old versions of the C(++) standard libraries, binutils, and utility libraries.
It works fine for some libraries, but fails when compiling libtiff, because it picks up the system libraries in /usr/lib (see output below). This might be an autotools/configure issue, but I'm not sure. I can't find a configure switch or environment variable, and I'd rather not modify my system /usr/lib/libc.so .
So how to make sure it links to the standard library in /opt/gcc-3.4.4/lib, and ignores /lib and /usr/lib completely?
Output of make (excerpt):
libtool: link: g++ -shared -nostdlib /usr/lib/crti.o /opt/gcc-3.4.3/lib/gcc/i686-pc-linux-gnu/3.4.3/crtbeginS.o .libs/tif_stream.o -Wl,--whole-archive ../port/.libs/libport.a -Wl,--no-whole-archive -Wl,-rpath -Wl,/home/jason/d0src34/prereq/tiff-3.9.4/libtiff/.libs -Wl,-rpath -Wl,/opt/gcc-3.4.3/lib -Wl,-rpath -Wl,/home/jason/d0src34/prereq/usr/lib -Wl,-rpath -Wl,/opt/gcc-3.4.3/lib ../libtiff/.libs/libtiff.so -L/usr/lib /usr/lib/libjpeg.so -lz -L/opt/gcc-3.4.3/lib/gcc/i686-pc-linux-gnu/3.4.3 -L/opt/gcc-3.4.3/lib/gcc/i686-pc-linux-gnu/3.4.3/../../.. /opt/gcc-3.4.3/lib/libstdc++.so -L/home/jason/Downloads/gcc-3.4.3/build/i686-pc-linux-gnu/libstdc++-v3/src -L/home/jason/Downloads/gcc-3.4.3/build/i686-pc-linux-gnu/libstdc++-v3/src/.libs -L/home/jason/Downloads/gcc-3.4.3/build/gcc -lm -lc -lgcc_s /opt/gcc-3.4.3/lib/gcc/i686-pc-linux-gnu/3.4.3/crtendS.o /usr/lib/crtn.o -Wl,-soname -Wl,libtiffxx.so.3 -o .libs/libtiffxx.so.3.9.4
/home/jason/d0src34/prereq/usr/bin/ld:/usr/lib/libc.so: file format not recognized; treating as linker script
/home/jason/d0src34/prereq/usr/bin/ld:/usr/lib/libc.so:5: parse error
I've found a (hackish) answer to my own question:
I've been using binutils 2.15, because later versions have an incompatibility with GCC 3.4. In more recent versions, the format of /usr/lib/libc.so has changed, and the old binutils can't parse it.
I temporarily commented out the last line (with "GROUP"), and my code compiled:
/* GNU ld script
Use the shared library, but some functions are only in
the static library, so try that secondarily. */
OUTPUT_FORMAT(elf32-i386)
/* GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a AS_NEEDED ( /lib/ld-linux.so.2 ) ) */
However, I'm not really satisfied, since I can hardly tell other people who want to use the code to edit their system files. Also, I'm not sure if I've linked it to the correct glibc version, since the system /usr/lib is still in the search path, so I can't tell for sure if the binaries will work on other systems.

Resources