Beginner's question, trying to understand how the linker searches for a static library - macos

I have a working setup, where all files are in the same directory (Desktop). The Terminal output is like so:
$ gcc -c mymath.c
$ ar r mymath.a mymath.o
ar: creating archive mymath.a
$ ranlib mymath.a
$ gcc test.c mymath.a -o test
$ ./test
Hello World!
3.14
1.77
10.20
The files:
mymath.c:
float mysqrt(float n) {
return 10.2;
}
test.c:
#include <math.h>
#include <stdio.h>
#include "mymath.h"
main() {
printf("Hello World!\n");
float x = sqrt(M_PI);
printf("%3.2f\n", M_PI);
printf("%3.2f\n", sqrt(M_PI));
printf("%3.2f\n", mysqrt(M_PI));
return 0;
}
Now, I move the archive mymath.a into a subdirectory /temp. I haven't been able to get the linking to work:
$ gcc test.c mymath.a -o test -l/Users/telliott_admin/Desktop/temp/mymath.a
i686-apple-darwin10-gcc-4.2.1: mymath.a: No such file or directory
$ gcc test.c -o test -I/Users/telliott_admin/Desktop/temp -lmymath
ld: library not found for -lmymath
collect2: ld returned 1 exit status
What am I missing? What resources would you recommend?
Update: Thanks for your help. All answers were basically correct. I blogged about it here.

$ gcc test.c /Users/telliott_admin/Desktop/temp/mymath.a -o test
edit: gcc only needs the full path to the library for static libraries. You use -L to give a path where gcc should search in conjunction with -l.

To include the math libraries, use -lm, not -lmath. Also, you need to use -L with the subdirectory to include the library when linking (-I just includes the header for compiling).
You can compile and link with:
gcc test.c -o test -I/Users/telliott_admin/Desktop/temp /Users/telliott_admin/Desktop/temp/mymath.a
or with
gcc test.c -o test -I/Users/telliott_admin/Desktop/temp -L/Users/telliott_admin/Desktop/temp -lmymath
where mymath.a is renamed libmymath.a.
See link text for comments (search for "bad programming") on the practices of using -l:

In order for ld to find a library with -l, it must be named according to the pattern libyourname.a. Then you use -lmymath
So, there is no way to get it to take /temp/mymath.a with -l.
If you named it libmymath.a, then -L/temp -lmymath would find it.

Related

Why can't I rename a shared library after it's been built?

Here's a simple example:
lib.c:
#include <stdio.h>
void hello_world(void) {
puts("Hello, world!");
}
program.c:
void hello_world(void);
int main() {
hello_world();
return 0;
}
If I compile the library normally and link it, everything works fine:
$ cc lib.c -fPIC -shared -o libmylib.dylib
$ cc program.c -L. -lmylib
$ ./a.out
Hello, world!
But if I rename the library after building it (but before building the program), it doesn't work:
$ cc lib.c -fPIC -shared -o totally_different_name.dylib
$ mv totally_different_name.dylib libmylib.dylib
$ cc program.c -L. -lmylib
$ ./a.out
dyld: Library not loaded: totally_different_name.dylib
Referenced from: /private/tmp/./a.out
Reason: image not found
[1] 13229 abort ./a.out
Why is this the case? Why is dyld looking for the original library name?
This works okay on Linux, but when I try on macOS it completely fails (I don't know if it's a macOS vs Linux thing, or a clang vs gcc thing, since I'm using clang on macOS and gcc on Linux).
If it's relevant, cc --version is Apple LLVM version 8.0.0 (clang-800.0.42.1) x86_64-apple-darwin16.4.0.
Thanks to Florian Zwoch's comment, I was able to find a way to fix this. Run this command after renaming the dylib:
install_name_tool -id libmylib.dylib libmylib.dylib
That command changes the internal ID of the dylib. The command syntax is install_name_tool -id <new-id> <dylib-path>.
Originally, the dylib has the ID of totally_different_name.dylib. Renaming the file doesn't change the ID it has stored internally, and that has to be updated separately with install_name_tool.

gfortran makefile 'can't open included file'

I'm having an issue with a makefile compiling and I'm not sure where to start diagnosing this. It was generated for a colleague's OS X system, and I'm trying to implement it on my linux system. It worked on the OS X computer. I've updated library locations to represent where they live on my computer - and this is likely the biggest source of error, as gfortran procedure shouldn't be different, no?
The included files: file0.i, ... fileN.i all live in the same directory as the makefile.
I'm certain if I could compile the first object file I could get through the rest and complete my PhD or save the world or something.
A snippet of the file follows:
# %W% %G%
# Makefile for directory ~/Documents/workstuff/project/program
#
fflags = -O3 -I. -I/usr/local/include -frecord-marker=4 -ffree-form
## -fdefault-real-8 -fdefault-double-8
lflags = -L/usr/local/lib -lnetcdf -lnetcdff
# for debugging, use these options
fflags = -g
lflags = -g
chem = ~/Documents/workstuff/project/chem
main.o: $(chem)/code/main.f file0.i file1.i file2.i
gfortran -c $(fflags) $(chem)/code/main.f
And I receive the following error:
~/Documents/workstuff/project/program/chem/code/main.f:11: Error: Can't open included file 'file0.i'
makefile:14: recipe for target 'main.o' failed
make: *** [main.o] Error 1
I thought it might be an executable issue, so we went from 644 to 744:
username$ file file0.i
file0.i: ASCII text
username$ stat -c '%A %a %n' file0.i
-rwxr--r-- 744 file0.i
Still same error. Double-check that my flags are pointing to the right place:
username$ nf-config --fflags
-I/usr/local/include
username$ nf-config --flibs
-L/usr/local/lib -lnetcdff -lnetcdf -lnetcdf
For what its worth, file0.i contains the following, which I have of course filled with nonsense numbers for sharing online:
$Id: file0.i,v 1.12 2012/12/31 04:25:23 username Exp $
PARAMETER (NLT=19,NHT=51,DZETA=0.5/3.,Psurf=100.)
PARAMETER (NLT1=NLT+1,NHT1=NHT+2,NLT2=3*NLT+1,NHT2=4*NHT+1)
PARAMETER (NDYEAR=33,NTN=75,NTCV=14,NLV=21,NPOL=8,NGSP=3)
PARAMETER (NDIST=111,TD1=110.,NVC=1,NVSP=1,NVTIME=3)
Does anything obvious stick out to anyone?
I do not know gfortran very well, so I may be completely wrong, but for gcc, if a C source file contains:
#include <foo.h>
and if gcc is called with -I., foo.h will be searched in the same directory as the source file, not in the directory where you call gcc from. Example:
$ ls foo
bar.c bar.h
$ cat foo/bar.c
#include <bar.h>
int main(int argc, char **argv) {
return 0;
}
$ gcc -I. foo/bar.c
foo/bar.c:1:10: error: 'bar.h' file not found with <angled> include; use "quotes" instead
#include <bar.h>
^~~~~~~
"bar.h"
1 error generated.
$ gcc -Ifoo foo/bar.c
$
So, it could be that you should put your header files in the same directory as your source file or use a -I<path> where <path> is the absolute path of where you call gfortran from.

How to make CMake append linker flags instead of prepending them?

CMake seems to prepend linker flags at the front of a GCC compilation command, instead of appending it at the end. How to make CMake append linker flags?
Here is a simple example to reproduce the problem.
Consider this C++ code that uses clock_gettime:
// main.cpp
#include <iostream>
#include <time.h>
int main()
{
timespec t;
clock_gettime(CLOCK_REALTIME, &t);
std::cout << t.tv_sec << std::endl;
return 0;
}
This is a CMakeLists.txt to compile the C++ file above:
cmake_minimum_required(VERSION 2.8)
set(CMAKE_EXE_LINKER_FLAGS "-lrt")
add_executable(helloapp main.cpp)
Note that we have added -lrt since it has the definition of clock_gettime.
Compiling this using:
$ ls
CMakeLists.txt main.cpp
$ mkdir build
$ cd build
$ cmake ..
$ make VERBOSE=1
Which throws up this error, even though you can see -lrt in the command:
/usr/bin/c++ -lrt CMakeFiles/helloapp.dir/main.cpp.o -o helloapp -rdynamic
CMakeFiles/helloapp.dir/main.cpp.o: In function `main':
main.cpp:(.text+0x15): undefined reference to `clock_gettime'
collect2: ld returned 1 exit status
make[2]: *** [helloapp] Error 1
The problem here is the C++ compilation command generated by CMake has -lrt prepended at the front. The compilation works fine if it had been:
/usr/bin/c++ CMakeFiles/helloapp.dir/main.cpp.o -o helloapp -rdynamic -lrt
How to make CMake append the linker flags at the end?
In general you can't (I think), but in the specific case that you want to link against a particular library, you should be using the syntax
target_link_libraries(helloapp rt)
instead. CMake knows that this corresponds to passing -lrt on the linker command line.

How can I specify the rpath in a dylib?

I have a library: libfoo.dylib. The problem is illustrated in the commands:
$ install_name_tool -id "#rpath/libfoo.dylib" libfoo.dylib
$ install_name_tool -add_rpath "#executable_path/" libfoo.dylib
$ gcc -o foo foo.c -lfoo
$ ./foo #<==== I want this to work
dyld: Library not loaded: #rpath/libfoo.dylib
Referenced from: ~/./foo
Reason: image not found
$ install_name_tool -add_rpath "#executable_path/" foo #<=== I dont want to have to specify here where to look for the library
$ ./foo
Hello World
How do I achieve the goal of not having to specify at executable compile where the library is?
I must confess that I'm a little confused as to what you're trying to achieve. The entire point of using the runpath search path is that the images loading the library define the search path to be used when loading the library. What you're asking for is for the library to define where the executable should find it. That can be accomplished without using the runpath search path by simply setting the install name of the dylib to the appropriate value. Based on your particular example, it sounds like you want to set the install name to something like #loader_path/libfoo.dylib. Consider the following, which is along the same lines of your sample:
$ cat a.c
int a(void)
{
return 1;
}
$ cc -install_name "#loader_path/liba.dylib" -dynamiclib -o liba.dylib a.c
$ cat main.c
#include <stdio.h>
extern int a(void);
int main(int argc, char **argv)
{
fprintf(stderr, "A: %d\n", a());
return 0;
}
$ cc -L. -la -o main main.c
$ ./main
A: 1
$
The library tells executables that link against it how to find it by setting its install name, and nothing special needs to be done when linking the executable to have it find the library at runtime.
The only thing you need is to tell the linker to add the rpath in your binary. Actually, you tell gcc to tell the linker in the following way:
$ gcc -o foo foo.c -lfoo -Wl,-rpath=/some/path
Now if you use objdump to see what's in there:
$ objdump -x ./foo | less
You will see under Dynamic Section somthing like RPATH /some/path.
If having to type the same -Wl,-rpath=... is too cumbersome, ld accepts the #file option (I don't know about dyld but I suppose it does too):
$ echo "-rpath=/some/path" > ./ld-options
$ gcc ./foo.c -o foo -Wl,#ld-options

g++ output: file not recognized: File format not recognized

I am trying to build program with multiple files for the first time.
I have never had any problem with compliling program with main.cpp only.
With following commands, this is the result:
$ g++ -c src/CNumber.cpp src/CNumber.h -o src/CNumber.o
$ g++ -c src/CExprPart.cpp src/CExprPart.h -o src/CExprPart.o
$ g++ -c src/CExpr.cpp src/CExpr.h -o src/CExpr.o
$ g++ -c src/main.cpp -o src/main.o
$ g++ src/CNumber.o src/CExprPart.o src/CExpr.o src/main.o -o execprogram
src/CNumber.o: file not recognized: File format not recognized
collect2: error: ld returned 1 exit status
What could cause such error and what should I do with it?
Using Linux Mint with gcc (Ubuntu/Linaro 4.7.2-2ubuntu1).
Thank you
This is wrong:
g++ -c src/CNumber.cpp src/CNumber.h -o src/CNumber.o
You shouldn't "compile" .h files. Doing so will create precompiled header files, which are not used to create an executable.
The above should simply be
g++ -c src/CNumber.cpp -o src/CNumber.o
Similar for compiling the other .cpp files
I ran into this error in building something - it turned out to be due to a previous build failing while compiling a source file to an .o file - that .o file was incomplete or corrupted, so when I tried another build it gave this error on that file.
The solution was to just delete the .o file (or run make clean, if you have a makefile with that target).
Try putting all of the following files in one directory:
example.cpp:
#include<iostream>
#include<string>
#include "my_functions.h"
using namespace std;
int main()
{
cout << getGreeting() << "\n";
return 0;
}
my_functions.cpp:
#include<string>
using namespace std;
string getGreeting()
{
return "Hello world";
}
my_functions.h:
#ifndef _MY_FUNCTIONS_H
#define _MY_FUNCTIONS_H
#include<string>
using namespace std;
string getGreeting();
#endif
Then issue these commands:
$ g++ example.cpp my_functions.cpp -o myprogram
~/c++_programs$ ./myprogram
Hello world

Resources