Creating static Mac OS X C build - c

How can i create a static build of a .c file on Mac OS X ? When i try:
gcc -o test Main.c -static
I get:
ld: library not found for -lcrt0.o
collect2: ld returned 1 exit status

It is not supported in Mac OS X's gcc:
http://discussions.apple.com/message.jspa?messageID=11053384
Perhaps that "-static" flag flat out won't work on MacOS X. Not all features of gcc are implemented on MacOS X. Apple won't even be using gcc in future versions of the OS.
I don't know how to link using "-static". I can't think of any reason to do so on MacOSX. If I knew why you wanted to use "-static" I might be more interested in the problem. Right now, I just don't get it. By asking for help, you are essentially asking for collaborators on the project - even if it is only for 10 minutes. You need to get me interested.
And http://developer.apple.com/library/mac/#qa/qa2001/qa1118.html
Static linking of user binaries is not supported on Mac OS X. Tying user binaries to the internal implementation of Mac OS X libraries and interfaces would limit our ability to update and enhance Mac OS X. Instead, dynamic linking is supported (linking against crt1.o automatically instead of looking for crt0.o, for example).
We strongly recommend that you consider the limitations of statically linking very carefully, and consider your customer and their needs, plus the long-term support you will need to provide.
Update: The prohibited is a static binary. But you still can compile some static library and use it with you another program. Program will be linked statically with your library, but other libraries like libc will be dynamic, so program will be a dynamic executable.

A binary that has no dynamic loaded libraries can not be built under OSX. I tried both apple llvm-gcc and macports gcc. However what no answer mentioned so far is that this is not needed. You can link the c/c++ library statically (and live with some dynamic part).
File hello.cpp:
#include <iostream>
using namespace std;
int main()
{
cout << "Hello World!";
}
Compile as usual:
g++ hello.cpp -o hello
Check linkage:
otool -L hello
hello:
/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 52.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
We can not get rid of the libSystem.B.dylib dependency but with macports gcc we can do this:
g++-mp-4.6 -static-libgcc -static-libstdc++ hello.cpp -o hello
otool -L hello
hello:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
Apparently just Apple does not support static linking:
llvm-g++ -static-libgcc -static-libstdc++ hello.cpp -o hello
otool -L hello
hello:
/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 52.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)

Imagine that you want to convert some functions into a library.
File: example.c
#include <stdio.h>
void aFunction( int a )
{
printf( "%d\n", a );
}
File: example.h
void aFunction( int a );
File: main.c
#include "example.h"
int main( )
{
aFunction( 3 );
return 0;
}
To create the library:
gcc -c example.c
ar -r libmylibrary.a example.o
To link the library:
gcc main.c -lmylibrary -L. -I.
And then the file example.c is a static build of the entire program.

Related

Why does gcc/clang know to link to libc by default?

When I run clang/gcc to compile a .c file, I don't need to explicitly link to libc. But it still works as libc and two additional libraries are automatically linked. Why does gcc/clang know to link automatically? Where is this behavior mentioned?
$ cat main.c
/* vim: set noexpandtab tabstop=2: */
#include <stdio.h>
int main() {
puts("Hello World!");
return 0;
}
$ clang -o main.exe main.c # or gcc
$ ./main.exe
Hello World!
$ nm -D /lib/x86_64-linux-gnu/libc-2.27.so | grep -w puts
00000000000809c0 W puts
$ ldd main.exe
linux-vdso.so.1 (0x00007ffe743ba000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f397ce7b000)
/lib64/ld-linux-x86-64.so.2 (0x00007f397d26c000)
Why does gcc/clang know to link automatically?
The GCC developers built this into GCC as a convenience. Which libraries are linked by default is partly affected by the language being compiled, which is deduced from the file names and may be controlled with the -x switch.
Where is this behavior mentioned?
This page in the GCC documentation mentions there are some libraries linked in by default and says you can disable or modify this behavior with -nostdlib and other switches, but I do not see an explicit list of the libraries that are linked in by default. It might vary by system/platform as well as by language. You can use the -v switch to ask GCC to show you the commands it is executing, and the link command (using ld) should reveal the libraries.

Unable to run windows binary compiled with clang/lld/mingw

I have downloaded and installed clang on windows 10 from http://releases.llvm.org/download.html
and mingw from https://sourceforge.net/projects/mingw-w64/
I am trying to compile a very basic C program using clang/lld/mingw:
int main(int argc, char* argv[argc + 1])
{
return 0;
}
To compile I invoke:
clang.exe -target x86_64-windows-gnu -fuse-ld=lld.exe -g -gcodeview -Wl,/debug,/pdb:example.pdb example.c -o example.exe
This creates an exe which faults on startup in mainCRTStartup (__security_init_cookie to be precise).
However, running with default ld from binutils is successful:
clang.exe -target x86_64-windows-gnu example.c -o example.exe
Please note that I wish to use mingw headers, not msvc.
In total I tried:
x86_64-8.1.0-posix-seh-rt_v6-rev0
x86_64-7.3.0-posix-seh-rt_v5-rev0
x86_64-8.1.0-win32-seh-rt_v6-rev0
x86_64-8.1.0-win32-sjlj-rt_v6-rev0
without any luck producing a functional program.
So I am wondering, is there something obvious I am doing wrong here?
EDIT:
I have also tried with msys2 to no avail. Specifically:
pacman -S mingw-w64-x86_64-clang mingw-w64-x86_64-lld
According to https://bugs.llvm.org/show_bug.cgi?id=40568
Linking against mingw import libraries from a normal mingw installation is a new feature, first present in LLD 8.
Unless wanting to compile a pre-release version of lld, have to wait for binary release of llvm 8.0.0. This will hopefully be sometime in March.

Unable to use compiled netlib BLAS on mac OS X

I'm trying to make a repository collecting all the examples, tutorials and instructions I could find on the internet for C mathematical and algebra libraries (BLAS, CBLAS, LAPACK, CLAPACK, LAPACKE, ATLAS, openblas, GSL...). but it seems that I just can't get the compiled BLAS .a files working on mac OS X.
So far I have been able to compile BLAS and use it on ubuntu:
BLAS source code from netlib website downloaded and compiled (rename blas_LINUX.a to libblas.a)
Then I can compile the C file on ubuntu using the command below:
gcc foo.c path/to/libblas.a
On my mac OS X (EL Capitan), I can compile BLAS (changing LINUX in the make.inc to DARWIN), but when I try to compile a C code using the command above I get errors like below:
Undefined symbols for architecture x86_64:
"_ddot_", referenced from:
_main in foo-3a35db.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
("ddot" part differs for different functions)
possibilities:
Maybe I'm not compiling the library correctly on mac and there are some differences I'm not aware of
The builtin Accelerate framework of mac OS X is messing up with the compiling process
P.S. Guys I know BLAS/LAPACK is already built into the mac OS X Accelerate framework and I can easily compile using the command gcc foo.c -lblas or gcc foo.c -framework Accelerate but I want to use the compiled .a from netlib. I want to know why it works properly on ubuntu but not mac OS X?
P.S.2. Please notice that I can compile the source code successfully without any errors on mac OS X. I just can use it!
Example code: source
#include <stdio.h>
#include <stdlib.h>
double ddot_(const int *N, const double *a, const int *inca, const double *b, const int *incb);
int main(int argc, char **argv) {
double *a = (double *)malloc(3 * sizeof(double));
a[0] = 1.0;
a[1] = 2.0;
a[2] = 3.0;
// on the stack
double b[3] = {4.0, 5.0, 6.0};
int N = 3, one = 1; // one really doesn't look good in C
double dot_product = ddot_(&N, a, &one, b, &one);
printf(" The dot product is: %f \n", dot_product);
return 0;
}
(edit1) solution:
open make.inc
change the line OPTS = -O3 to OPTS = -O3 -pipe -c and make.
(edit2): better solution:
since I asked this question I have realised that I have been doing everything wrong. Netlib BLAS is actually a collection of fortran routines/subroutines/functions. and the Makefile in the source code just gives us a static library libblas.a which is a collection of all .o object files compiled with gfortran. when we want to compile a C code which want to call one of those routines, we also need to link to the gfortran library libgfortran.* so if you have gcc installed (brew install gcc). look for libgfrotran* (sudo find / -name "libgfortrn.*") and then link your gcc to this folder too. to make it easy I put a Makefile here:
all:
gcc -c foo.c
gcc -o bar.out foo.o -L path/to/libgfortran.*/ -lgfortran -L path/to/libblas.a -lblas
or alternatively compile the code directly with gfortran:
all:
gcc -c foo.c
gfortran -o bar.out foo.o -L path/to/libblas.a -lblas
or simply compile with:
gcc foo.c bar.out -L path/to/libblas.a -lblas -L path/to/libgfortran.*/ -lgfortran
the wonder is how/why the former solution actually worked and why on ubuntu you don't have to link to -lgfortran!
It seems you compiled BLAS library from Netlib with compiler options that changed the mangling scheme of Fortran routines.
By default, Netlib's make.inc uses gfortran to compile BLAS:
$ grep FORTRAN make.inc
# Modify the FORTRAN and OPTS definitions to refer to the
FORTRAN = gfortran
It is compiled without any flags:
gfortran -O3 -pipe -c ddot.f -o ddot.o
and you get the ddot() routine:
$ grep -i ddot ddot.o libblas.a
Binary file ddot.o matches
Binary file libblas.a matches
And you can find it with the command line tools:
$ nm ddot.o libblas.a | grep -i ddot
ddot.o:
0000000000000000 T _ddot_
libblas.a(ddot.o):
0000000000000000 T _ddot_
Your example compiles with the library:
cc ex.c libblas.a
or with the ddot.o file:
cc -pipe ex.c ddot.o
I cannot reproduce your problem. You should use the nm and grep commands to find out what happened to the name of the ddot() routine.
PS. Your code has extra semicolon ; after the end of definition of main().

a linker issue when learning static library [duplicate]

When I try to build the following program:
#include <stdio.h>
int main(void)
{
printf("hello world\n");
return 0;
}
On OS X 10.6.4, with the following flags:
gcc -static -o blah blah.c
It returns this:
ld: library not found for -lcrt0.o
collect2: ld returned 1 exit status
Has anyone else encountered this, or is it something that noone else has been affected with yet? Any fixes?
Thanks
This won’t work. From the man page for gcc:
This option will not work on Mac OS X unless all libraries (including libgcc.a) have also been compiled with -static. Since neither a static version of libSystem.dylib nor crt0.o are provided, this option is not useful to most people.
Per Nate's answer, a completely static application is apparently not possible - see also man ld:
-static Produces a mach-o file that does not use the dyld. Only used building the kernel.
The problem in linking with static libraries is that, if both a static and a dynamic version of a library are found in the same directory, the dynamic version will be taken in preference. Three ways of avoiding this are:
Do not attempt to find them via the -L and -l options; instead, specify the full paths, to the libraries you want to use, on the compiler or linker command line.
$ g++ -Wall -Werror -o hi /usr/local/lib/libboost_unit_test_framework.a hi.cpp
Create a separate directory, containing symbolic links to the static libraries, use the -L option to have this directory searched first, and use the -l option to specify the libraries you want to use.
$ g++ -Wall -Werror -L ./staticBoostLib -l boost_unit_test_framework -o hi hi.cpp
Instead of creating a link of the same name in a different directory, create a link of a different name in the same directory, and specify that name in a -l argument.
$ g++ -Wall -Werror -l boost_unit_test_framework_static -o hi hi.cpp
You may also try LLVM LLD linker - I did prebuilt version for my two major OSes - https://github.com/VerKnowSys/Sofin-llds
This one allows me to link for exmple: "Qemu" properly - which is impossible with ld preinstalled by Apple.
And last one is - to build GCC yourself with libstdc++ (don't).

How to make Scons look for libstdc++ in nonstandard directory

I'm trying to use Scons to build a simple project on a server on which I have rights to install stuff only in specific locations (and not in /usr/ ). Since I'm not happy with default compiler the server is offering me, I installed g++4.8 and verified it works just fine. But when I try to use Scons to build a simple project, while it picks up correct g++ (I can get that by checking the version), it's looking for libstdc++ in /usr/ directories instead of the directory where g++4.8 installation resides. E.g. code compiles, but upon execution fails with:
./main: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.9' not found (required by ./main)
Again - this doesn't happen when I call the compiler myself from the terminal.
Even when I add the lib path containing libraries for g++4.8 with LIBPATH option, I get the same error.
Here's my SConscript file:
Import('env')
COMPILER_FLAGS = '-Wall -fopenmp -O3 -std=c++11'
LINK_FLAGS = '-Wall -fopenmp -O3 -std=c++11'
LIB_PATH = 'myfolder/gcc-4.8.2/lib64'
PROGRAM = 'main'
SRC = ['main.cpp', 'Foo.cpp']
env.Append(CPPFLAGS = COMPILER_FLAGS)
env.Append(LINKFLAGS = LINK_FLAGS)
env.Program(target = PROGRAM, source = SRC, LIBPATH = LIB_PATH)
and SConstruct is just
import os
env = Environment(ENV = os.environ)
SConscript('./SConscript', exports=['env'], duplicate=0)
Edit:
I made sure location of my compiler comes in the path before default compiler. But even if I set it explicitly with Environment(CXX=...) it's the same story. Here's the build output:
/mypath/gcc-4.8.2/bin/g++ -o Foo.o -c -Wall -fopenmp -O3 -std=c++11 Foo.cpp
/mypath/gcc-4.8.2/bin/g++ -o main.o -c -Wall -fopenmp -O3 -std=c++11 main.cpp
/mypath/gcc-4.8.2/bin/g++ -o main -Wall -fopenmp -O3 -std=c++11 main.o Foo.o -L/mypath/gcc-4.8.2/lib64
scons: done building targets.
-bash-3.2$
-bash-3.2$
-bash-3.2$ ./main
./main: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.9' not found (required by ./main)
./main: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.11' not found (required by ./main)
-bash-3.2$
Yet another edit:
ldd on both manual and scons compile reveal:
linux-vdso.so.1 => (0x00007fff513fd000)
libstdc++.so.6 => /usr/lib64/libstdc++.so.6(0x0000003e7f600000)
libm.so.6 => /lib64/libm.so.6 (0x0000003e79600000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x0000003e7de00000)
libc.so.6 => /lib64/libc.so.6 (0x0000003e79200000)
/lib64/ld-linux-x86-64.so.2 (0x0000003e78e00000)
So indeed even manual compile doesn't look for the libs in the right directory (or where I installed the compiler) and the problem isn't with the scons itself, but likely that I didn't configure something right, but then I'm really puzzled as to why the executable runs fine, while it doesn't for scons.
Ok, so my problem wasn't with scons, but with me not giving explicit paths to nonstandard locations of libstdc++ and friends. SO answer over here explains this in more detail:
Linking g++ 4.8 to libstdc++
You're misinterpreting the error. GCC always knows how to find its own libraries, including libstdc++. The problem is that after you've compiled the program the runtime linker (which is not part of GCC, it's part of your OS and comes from glibc) doesn't know how to find the newer libstdc++, so it finds the default system one, which is too old.
The problem and solution are described at in the Libstdc++ FAQ, "How do I insure that the dynamically linked library will be found?", and manual, "Finding Dynamic or Shared Libraries"
This doesn't sound right.
Can you show us what you do to override the compiler?
If you are only doing the above, I don't think your compiler will be overridden with the new version.
You need to do something like
env = Environment(CC='/path/to/gcc')
Or Environment(CXX='/path/to/g++') if you want to override the c++ compiler
Or is your path on your environment setup to have the directory of the custom compiler before the standard compilers directory?
It might help to clean and then run with scons with --debug=presub which will show you the command line used to build each target.
Also your environment is a dictionary, so try printing out different keys to make sure they match what you expect:
print env['CC']
print env['CXX']

Resources