I'd like to build a portable static C library that can be linked into macOS binaries regardless of macOS version. The C library does not have any dependencies (i.e. no stdlib, no OS specific calls). The only dependency is the CPU which has to be x86_64.
When extracting the relevant compiler triplet, I get x86_64-apple-darwin16.7.0 for Sierra, and x86_64-apple-darwin17.2.0 for High Sierra. Also, when I build on High Sierra, there is a warning if I use the static library from Sierra (even when I specify --target x86_64-apple-darwin16.7.0 for clang).
How can I build a truly portable static library for macOS? If it is not possible, what is the way to go? One different version per major macOS release that is built from the same release? Some hackery by copying SDKs from outdated Xcode versions? Keep in mind, my library is completely portable and does not depend on macOS or the stdlib in any way.
Does the version part at the end of the compiler triplet matter?
You can just create a static library directly by using macosx libtool
Here's what I just tested out on 3 different versions of Mac OS X (Sierra, High Sierra and Mavrick):
$ ls -al ⏎
total 64
drwxr-xr-x 8 masud staff 272 Dec 5 11:12 .
drwxr-xr-x 7 masud staff 238 Dec 5 10:35 ..
-rw-r--r-- 1 masud staff 386 Dec 5 11:11 Makefile
-rwxr-xr-x 1 masud staff 8464 Dec 5 10:38 bar
-rw-r--r-- 1 masud staff 69 Dec 5 11:11 bar.c
-rw-r--r-- 1 masud staff 53 Dec 5 11:10 foo.c
-rw-r--r-- 1 masud staff 84 Dec 5 11:11 foo.h
-rw-r--r-- 1 masud staff 83 Dec 5 11:11 test.c
$ cat Makefile⏎
LIBTOOL=libtool
STATIC=-static
LIBFOO_A=libfoo.a
SRC=foo.c bar.c
OBJ=$(SRC:.c=.o)
LIBFOOCFLAGS=-mmacosx-version-min=10.1
TESTSRC=test.c
TESTCFLAGS=-mmacosx-version-min=10.7
$(LIBFOO_A): $(OBJ)
$(LIBTOOL) $(STATIC) -o $# $(OBJ)
%.o: %.c
$(CC) $(LIBFOOCFLAGS) -c -o $# $<
test: $(LIBFOO_A) test.c
$(CC) $(TESTCFLAGS) -o $# test.c -L. -lfoo
clean:
$(RM) test $(LIBFOO_A) $(OBJ)
$ cat foo.c⏎
int foo(void) {
volatile int a = 0;
return a;
}
$ cat foo.h⏎
#ifndef __FOO_H
#define __FOO_H
extern int foo(void);
#endif
$ cat bar.c⏎
#include "foo.h"
int bar(int k) {
return k-1;
}
In my tests, libfoo.a could be generated on any (of the three) platform(s) and copied to any (of the three) platform(s) and be used to generate test locally.
nm test on all three shows library statically linked into the binary.
Hope this helps.
on macosx libtool
P.S.: macosx libtool is NOT the same as the gnu libtool except in name. Completely different usage and functionality. the macosx libtool is essentially AR + ranlib + some darwinisms.
Related
So here's something I don't understand : Every single time you include something in c, you have to do #include "dir/subdir/file.h". Since the .c file is in the exact same place, why do I also have to do gcc main.c dir/subdir/file.c -o main ?
It doesn't seem to make sense, every single other language I've used (arguably not that many, but still, several) was able to make sense of imports on its own. Am I missing something ? Is there a way to tell gcc to "just find them yourself" ?
gcc main.c dir/subdir/file.c -o main is not the typical command form for most projects. It is used only for simple projects, such as student assignments.
Much commercial code consists of dozens, hundreds, or thousands of source files. It is not compiled all at once. Build tools such as make or Xcode are used to manage the project, and they will compile each source file separately to its own object file.
Object files may be grouped by subproject, may be collected into libraries, and may be further processed before being linked into an executable file or other product.
When the project is built, there is no need to compile all of the source files because they have been compiled previously. It is only necessary to compile source files for object files that may change due to source edits that have been made since the last time the project was built. Thus, the build tool may compile only a few of the hundreds of source files in the project, using an individual compile command for each. Having a single command that compiled all of the source files would be wasteful.
In some projects, there may be several variants of the source file for one header file. One header file may declare common interfaces, but several different source files may provide different implementations for those interfaces, to target different types of destination systems or to provide different characteristics, such as prioritizing speed over memory or vice-versa. In such a situation, knowing the header file does not identify which source file is to be compiled.
If you want your project to compile all source files that are implied by header files, you can write your own program that scans the main.c file to find included header files (possibly recursively) and generate commands to compile the associated source files.
C is a product of early 1970s, and this is one of the areas where that really shows. An #include statement simply loads the contents of the specified file before it gets compiled - the path of the included file isn't being analyzed or preserved anywhere. Similarly, gcc isn't preserving the path of any of the files on the command line - it just loads the source from the specified file, translates it, and spits out an object file at the end.
C doesn't specify any kind of package management semantics. Each file on the gcc command line is compiled separately, and gcc doesn't know anything about the environment other than what's specified on the command line.
Yes, you could build a compiler that does all that analysis and tries to be smart about paths and such, but it's a crapload of work, there's no specification for it, no two compilers would do it the same way, and it kind of violates the spirit of C (the programmer is assumed to always know what they're doing, even when they don't).
Having said all that...
With gcc, you can use the -I option to specify additional include paths:
gcc -o main -I /dir/subdir main.c /dir/subdir/file.c
so in your source you don't need to use an explicit path in the #include:
#include "file.h" // no need for /dir/subdir path
Similarly, you can use -L to specify additional directories to search for libraries (linked with -l):
gcc -o main -I /dir/subdir main.c /dir/subdir/file.c -L /anotherdir/subdir/libs -lmylib.a
Like Eric says, if you're having to manage source across multiple directories, you should be using make or a similar tool - doing all this manually on the command line rapidly gets unscalable.
EDIT
I think an example may be helpful. I have a simple C program made up of three files - main.c, foo.c, and bar.c:
/**
* main.c
*/
#include <stdio.h>
/**
* Notice no explicit path on either include
*/
#include "foo.h"
#include "bar.h"
int main( void )
{
printf( "calling foo: " );
foo();
printf( "calling bar: " );
bar();
return 0;
}
/**
* foo.c
*/
#include <stdio.h>
#include "foo.h"
void foo( void )
{
puts( "In function foo" );
}
/**
* bar.c
*/
#include <stdio.h>
#include "bar.h"
void bar( void )
{
puts( "In function bar" );
}
Because I'm insane, I've split the source code up over multiple directories like so:
make_example/
Makefile
build/
include/
package/
src/
main.c
subdir.mk
Makefile
subdir1/
Makefile
foo.c
foo.h
subdir2/
Makefile
bar.c
bar.h
I want to do the following:
Build foo.c and bar.c and write their object files to make_example/build;
Copy foo.h and bar.h to make_example/include;
Build main.c, which includes the headers for foo and bar from make_example/includes, and write its object file to make_example/build;
Build an executable named demo from main.o, foo.o, and bar.o and save it to the make_example/package directory.
IOW, after my build is done, I want the following:
make_example/
Makefile
build/
bar.o
foo.o
main.o
include/
bar.h
foo.h
package/
demo
src/
main.c
subdir.mk
Makefile
subdir1/
Makefile
foo.c
foo.h
subdir2/
Makefile
bar.c
bar.h
I have a Makefile at the top level directory and each of my source directories. Here's the top-level Makefile (make_example/Makefile):
CC=gcc
CFLAGS=-std=c11 -pedantic -Wall -Werror
TARGET=package/demo
SUBDIRS=src
all: $(TARGET)
#
# Makes sure we build all source subdirectories before
# attempting to build the target
#
$(TARGET) : $(SUBDIRS)
$(CC) -o $# $(CFLAGS) $(wildcard build/*.o)
#
# Executes the Makefile in each source subdirectory
# with the current goal (all or clean)
#
$(SUBDIRS):
$(MAKE) -C $# $(MAKECMDGOALS)
clean: $(SUBDIRS)
rm -rf $(TARGET)
.PHONY: $(SUBDIRS) all clean
Here's the Makefile for the top-level source directory (make_example/src/Makefile):
CC=gcc
CFLAGS=-std=c11 -pedantic -Wall -Werror -g
TOPDIR=..
BLDDIR=$(TOPDIR)/build
INCDIR=$(TOPDIR)/include
#
# Look for foo.h and bar.h in the ../include
# subdirectory.
#
CFLAGS += -I $(INCDIR)
SUBDIRS=$(wildcard subdir*/.)
SRCS=main.c
OBJS=$(SRCS:%.c=$(BLDDIR)/%.o)
TARGET=main
#
# Required for the $(OBJS) target - expands
# all of the $$ expressions
#
.SECONDEXPANSION:
all: $(TARGET)
#
# Makes sure the subdirectories are
# built before attempting to build our
# target
#
$(TARGET) : $(SUBDIRS) $(OBJS)
#
# Executes the Makefile in each of the source
# subdirectories with the current goal (all or clean)
#
$(SUBDIRS):
$(MAKE) -C $# $(MAKECMDGOALS)
$(OBJS): $$(patsubst $(BLDDIR)/%.o, %.c, $$#)
$(CC) -c -o $# $(CFLAGS) $<
clean: $(SUBDIRS)
rm -rf $(OBJS)
.PHONY: all $(TARGET) $(SUBDIRS)
Since the Makefile for each of subdir1 and subdir2 is identical except for the file names, I created the subdir.mk file for the stuff that's common between the two:
CC=gcc
CFLAGS=-std=c11 -pedantic -Wall -Werror -g
TOPDIR=../..
BLDDIR=$(TOPDIR)/build
INCDIR=$(TOPDIR)/include
OBJS=$(SRCS:%.c=$(BLDDIR)/%.o)
HDRS=$(SRCS:%.c=$(INCDIR)/%.h)
all: $(OBJS) $(HDRS)
clean:
rm -rf $(OBJS) $(HDRS)
#
# Required for the $(OBJS) and $(HDRS) targets - expands
# all of the $$ expressions
#
.SECONDEXPANSION:
$(OBJS) : $$(patsubst $(BLDDIR)/%.o,%.c,$$#)
$(CC) -c -o $# $(CFLAGS) $<
#
# Copy the header files to the ../../include
# directory
#
$(HDRS) : $$(patsubst $(INCDIR)/%.h,%.h,$$#)
cp $< $#
.PHONY: all clean
Then each of the make_example/src/subdir* Makefiles is
SRCS=foo.c
include ../subdir.mk
and
SRCS=bar.c
include ../subdir.mk
So, at the top level (make_example), we simply type
make
which gives us
$ make
make -C src
make[1]: Entering directory '/home/john/Development/make_example/src'
make -C subdir2/.
make[2]: Entering directory '/home/john/Development/make_example/src/subdir2'
gcc -c -o ../../build/bar.o -std=c11 -pedantic -Wall -Werror -g bar.c
cp bar.h ../../include/bar.h
make[2]: Leaving directory '/home/john/Development/make_example/src/subdir2'
make -C subdir1/.
make[2]: Entering directory '/home/john/Development/make_example/src/subdir1'
gcc -c -o ../../build/foo.o -std=c11 -pedantic -Wall -Werror -g foo.c
cp foo.h ../../include/foo.h
make[2]: Leaving directory '/home/john/Development/make_example/src/subdir1'
gcc -c -o ../build/main.o -std=c11 -pedantic -Wall -Werror -g -I ../include main.c
make[1]: Leaving directory '/home/john/Development/make_example/src'
gcc -o package/demo -std=c11 -pedantic -Wall -Werror build/foo.o build/main.o build/bar.o
And now we have all our build artifacts where we want them:
$ ls -l */*
-rw-rw-r-- 1 john john 5744 Oct 23 16:13 build/bar.o
-rw-rw-r-- 1 john john 5744 Oct 23 16:13 build/foo.o
-rw-rw-r-- 1 john john 5984 Oct 23 16:13 build/main.o
-rw-rw-r-- 1 john john 56 Oct 23 16:13 include/bar.h
-rw-rw-r-- 1 john john 56 Oct 23 16:13 include/foo.h
-rwxrwxr-x 1 john john 21752 Oct 23 16:13 package/demo
-rw-rw-r-- 1 john john 165 Oct 23 12:54 src/main.c
-rw-rw-r-- 1 john john 555 Oct 23 15:50 src/Makefile
-rw-rw-r-- 1 john john 481 Oct 23 15:46 src/subdir.mk
src/subdir1:
total 12
-rw-rw-r-- 1 john john 89 Oct 23 12:29 foo.c
-rw-rw-r-- 1 john john 56 Oct 23 12:07 foo.h
-rw-rw-r-- 1 john john 32 Oct 23 15:16 Makefile
src/subdir2:
total 12
-rw-rw-r-- 1 john john 89 Oct 23 15:03 bar.c
-rw-rw-r-- 1 john john 56 Oct 23 14:49 bar.h
-rw-rw-r-- 1 john john 32 Oct 23 15:17 Makefile
And our program runs as
$ ./package/demo
calling foo: In function foo
calling bar: In function bar
To clean up, all I have to do is type make clean:
$ make clean
make -C src clean
make[1]: Entering directory '/home/john/Development/make_example/src'
make -C subdir2/. clean
make[2]: Entering directory '/home/john/Development/make_example/src/subdir2'
rm -rf ../../build/bar.o ../../include/bar.h
make[2]: Leaving directory '/home/john/Development/make_example/src/subdir2'
make -C subdir1/. clean
make[2]: Entering directory '/home/john/Development/make_example/src/subdir1'
rm -rf ../../build/foo.o ../../include/foo.h
make[2]: Leaving directory '/home/john/Development/make_example/src/subdir1'
rm -rf ../build/main.o
make[1]: Leaving directory '/home/john/Development/make_example/src'
rm -rf package/demo
and all our build artifacts are gone:
$ ls -l */*
-rw-rw-r-- 1 john john 165 Oct 23 12:54 src/main.c
-rw-rw-r-- 1 john john 555 Oct 23 15:50 src/Makefile
-rw-rw-r-- 1 john john 481 Oct 23 15:46 src/subdir.mk
src/subdir1:
total 12
-rw-rw-r-- 1 john john 89 Oct 23 12:29 foo.c
-rw-rw-r-- 1 john john 56 Oct 23 12:07 foo.h
-rw-rw-r-- 1 john john 32 Oct 23 15:16 Makefile
src/subdir2:
total 12
-rw-rw-r-- 1 john john 89 Oct 23 15:03 bar.c
-rw-rw-r-- 1 john john 56 Oct 23 14:49 bar.h
-rw-rw-r-- 1 john john 32 Oct 23 15:17 Makefile
I am trying to generate an executable file test from a c file test.c.
test.c includes the main() function, but I also want to include functions from stuff.c. I was told to declare functions in stuff.h and then run the command gcc -Wall -std=c99 stuff.c stuff.h test.c -o test.
I know what each component of this does, but this is the first time I've tried to use gcc with multiple input files. When I try to run this command, it fails, and I get the error clang: error: cannot specify -o when generating multiple output files.
As far as I'm aware, I'm only trying to generate one output file, so I assume this command is set up incorrectly. How can I fix it?
Here are your errors
you are not suppose to compile .h files in c so try this:
gcc -Wall -std=c99 stuff.c test.c -o test
gcc depends also on environment that you run it on. On my system if I do gcc -Wall -std=c99 stuff.c stuff.h test.c -o test it will work, because compiler is set that way. On some machines it might be different. So if for some strange reason #1 does not work you have to do following:
gcc -Wall -std=c99 -c stuff.c test.c
gcc -Wall -std=c99 stuff.o test.o -o test
./test
But your error clang: error: cannot specify -o when generating multiple output files is due to some code error, perhaps header file problems. So post your code
The difference seems to be due to a difference of behavior between gcc and clang as shown in the following example.
~/test $ ls -ltr
total 4
-rw-rw-r-- 1 arusaha arusaha 0 Apr 3 13:19 b.h
-rw-rw-r-- 1 arusaha arusaha 0 Apr 3 13:19 b.c
-rw-rw-r-- 1 arusaha arusaha 14 Apr 3 13:20 a.c
~/test $ clang -Wall -std=c99 b.c b.h a.c -o ab
clang: error: cannot specify -o when generating multiple output files
~/test $ gcc -Wall -std=c99 b.c b.h a.c -o ab
~/test $ ls -ltr
total 16
-rw-rw-r-- 1 arusaha arusaha 0 Apr 3 13:19 b.h
-rw-rw-r-- 1 arusaha arusaha 0 Apr 3 13:19 b.c
-rw-rw-r-- 1 arusaha arusaha 14 Apr 3 13:20 a.c
-rwxrwxr-x 1 arusaha arusaha 8576 Apr 3 13:21 ab
~/test $
Compiling multiple files is supported in gcc, see http://www.network-theory.co.uk/docs/gccintro/gccintro_11.html
I was curious how gcc command can throw a clang error and that turned out to be a clue :). It is possible that OP's gcc is pointing to clang (and that can be confusing).
From gcc manpage at http://man7.org/linux/man-pages/man1/gcc.1.html
-o file
Place output in file file. This applies to whatever sort of
output is being produced, whether it be an executable file, an
object file, an assembler file or preprocessed C code.
If -o is not specified, the default is to put an executable file
in a.out, the object file for source.suffix in source.o, its
assembler file in source.s, a precompiled header file in
source.suffix.gch, and all preprocessed C source on standard
output.
From clang manpage at https://clang.llvm.org/docs/CommandGuide/clang.html
-o <file> Write output to file.
To me, the difference of behavior is not exactly clear by just reading the man page but I could be wrong.
I am trying to make use of a library, https://github.com/go-steem/rpc, that makes use of some C code, which references a library.
The C library can be found here, https://github.com/bitcoin-core/secp256k1
I followed the steps to get that installed
$ ./autogen.sh
$ ./configure
$ make
$ ./tests
$ sudo make install # optional
And have this output;
$ sudo make install
Password:
CC src/libsecp256k1_la-secp256k1.lo
CCLD libsecp256k1.la
CC src/tests-tests.o
CCLD tests
CC src/exhaustive_tests-tests_exhaustive.o
CCLD exhaustive_tests
build-aux/install-sh -c -d '/usr/local/lib'
/bin/sh ./libtool --mode=install /usr/bin/install -c libsecp256k1.la '/usr/local/lib'
libtool: install: /usr/bin/install -c .libs/libsecp256k1.0.dylib /usr/local/lib/libsecp256k1.0.dylib
libtool: install: (cd /usr/local/lib && { ln -s -f libsecp256k1.0.dylib libsecp256k1.dylib || { rm -f libsecp256k1.dylib && ln -s libsecp256k1.0.dylib libsecp256k1.dylib; }; })
libtool: install: /usr/bin/install -c .libs/libsecp256k1.lai /usr/local/lib/libsecp256k1.la
libtool: install: /usr/bin/install -c .libs/libsecp256k1.a /usr/local/lib/libsecp256k1.a
libtool: install: chmod 644 /usr/local/lib/libsecp256k1.a
libtool: install: /usr/bin/ranlib /usr/local/lib/libsecp256k1.a
build-aux/install-sh -c -d '/usr/local/include'
/usr/bin/install -c -m 644 include/secp256k1.h '/usr/local/include'
build-aux/install-sh -c -d '/usr/local/lib/pkgconfig'
/usr/bin/install -c -m 644 libsecp256k1.pc '/usr/local/lib/pkgconfig'
I try to run the upvote example from that Go library, go-steem/rpc/examples/upvote/ and get the following output;
$ go run main.go
# github.com/go-steem/rpc/transactions
../../transactions/signing.c:5:10: fatal error: 'secp256k1.h' file not found
Already it feels as though the wheels are falling off...
Please bear with me as I do not develop in C, so I get a bit hack-y.
After much reading, and googling I decide to copy the files from the 'include' directory where I compiled libsecp256k1 into the same directory as the error is originating from.
You can see the files are not there;
$ ls -la ../../transactions/
total 48
drwxr-xr-x 8 shaunmorrow staff 272 May 8 18:09 .
drwxr-xr-x 15 shaunmorrow staff 510 May 8 18:09 ..
-rw-r--r-- 1 shaunmorrow staff 256 Apr 27 17:53 chains.go
-rw-r--r-- 1 shaunmorrow staff 3731 May 8 18:09 signed_transaction.go
-rw-r--r-- 1 shaunmorrow staff 1849 May 8 18:09 signed_transaction_test.go
-rw-r--r-- 1 shaunmorrow staff 3075 Apr 27 17:53 signing.c
-rw-r--r-- 1 shaunmorrow staff 408 Apr 27 17:53 signing.h
-rw-r--r-- 1 shaunmorrow staff 1049 May 8 18:09 transactions.go
and after the copy;
$ ls -la ../../transactions/
total 128
drwxr-xr-x 11 shaunmorrow staff 374 Jul 18 19:08 .
drwxr-xr-x 15 shaunmorrow staff 510 May 8 18:09 ..
-rw-r--r-- 1 shaunmorrow staff 256 Apr 27 17:53 chains.go
-rw-r--r-- 1 shaunmorrow staff 27071 Jul 18 19:08 secp256k1.h
-rw-r--r-- 1 shaunmorrow staff 1014 Jul 18 19:08 secp256k1_ecdh.h
-rw-r--r-- 1 shaunmorrow staff 4700 Jul 18 19:08 secp256k1_recovery.h
-rw-r--r-- 1 shaunmorrow staff 3731 Jul 18 19:05 signed_transaction.go
-rw-r--r-- 1 shaunmorrow staff 1849 May 8 18:09 signed_transaction_test.go
-rw-r--r-- 1 shaunmorrow staff 3075 Apr 27 17:53 signing.c
-rw-r--r-- 1 shaunmorrow staff 408 Apr 27 17:53 signing.h
-rw-r--r-- 1 shaunmorrow staff 1049 May 8 18:09 transactions.go
Now I get a new error;
$ go run main.go
# github.com/go-steem/rpc/transactions
ld: library not found for -lsecp256k1
clang: error: linker command failed with exit code 1 (use -v to see invocation)
This has me reading and googling some more,
Finally I get even more hack-y and change transactions.go;
// #cgo LDFLAGS: -lsecp256k1
// #include <stdlib.h>
// #include "signing.h"
import "C"
becomes
// #cgo LDFLAGS: -L/usr/local/lib
// #include <stdlib.h>
// #include "signing.h"
import "C"
which fails, output on that later
I also try;
// #cgo LDFLAGS: -L/usr/local/lib -I/usr/local/include
// #include <stdlib.h>
// #include "signing.h"
import "C"
and copy the .h files into the /usr/local/include directory.
None of this works and now I am stuck with an error like this
$ go run main.go
# github.com/go-steem/rpc/transactions
ld: library not found for -lsecp256k1
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ShaunsSePc-2:upvote shaunmorrow$ go run main.go
# github.com/go-steem/rpc/transactions
Undefined symbols for architecture x86_64:
"_secp256k1_context_create", referenced from:
_sign_transaction in signing.o
_verify_recoverable_signature in signing.o
"_secp256k1_context_destroy", referenced from:
_sign_transaction in signing.o
_verify_recoverable_signature in signing.o
"_secp256k1_ec_pubkey_serialize", referenced from:
_verify_recoverable_signature in signing.o
"_secp256k1_ecdsa_recover", referenced from:
_verify_recoverable_signature in signing.o
"_secp256k1_ecdsa_recoverable_signature_convert", referenced from:
_verify_recoverable_signature in signing.o
"_secp256k1_ecdsa_recoverable_signature_parse_compact", referenced from:
_verify_recoverable_signature in signing.o
"_secp256k1_ecdsa_recoverable_signature_serialize_compact", referenced from:
_sign_transaction in signing.o
"_secp256k1_ecdsa_sign_recoverable", referenced from:
_sign_transaction in signing.o
"_secp256k1_ecdsa_verify", referenced from:
_verify_recoverable_signature in signing.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
At this point I really have no idea how to continue.
As you can see I am not experienced in C at all and have no idea how to test if the library libsecp256k1 is even installed properly!
This is where I ended up, but it's highly likely I took a wrong turn early in my journey, I would appreciate any help given as I have been struggling with this for a few nights already now :(
Not sure whats needed so here some env variables
$ go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/shaunmorrow/Work/go/"
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/86/hlqptn5101z5bcydjz05qy8m0000gn/T/go-build689438019=/tmp/go-build -gno-record-gcc-switches -fno-common"
CXX="clang++"
CGO_ENABLED="1"
PKG_CONFIG="pkg-config"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
Version is go version go1.8.3 darwin/amd64 with no old versions lying around.
Thanks!
Your problem is twofold:
Improperly configured libsecp256k1 package
Your C compiler not searching /usr/local directories for installed headers/libs
Fixing the improperly libsecp256k1 configured package
Your "undefined symbols" issue when it comes to linking C libraries sometimes points to an improperly configured package (in the sense of an Autotools package or a CMake package, not a Go package). Running ./configure --help, I see there is an option named --enable-module-recovery. Judging by names like _secp256k1_ecdsa_sign_recoverable and _secp256k1_ecdsa_recover, you need to add that option when configuring, meaning instead of executing the simpler ./configure, you should execute this:
./configure --enable-module-recovery
Is the C compiler broken?
Since the secp256k1.h header file isn't found in /usr/local/include, despite the fact that the header file most definitely exists after sudo make install is finished, it means your compiler doesn't search /usr/local.
Barring any fixes in the linked question, you can work around the issue by altering the source of the Go package as needed to add/modify the CFLAGS and LDFLAGS used when dealing with import "C" statements like this:
// #cgo CFLAGS: -I/usr/local/include
// #cgo LDFLAGS: -L/usr/local/lib -lsecp256k1
// #include <secp256k1.h>
import "C"
If you have pkg-config installed, you can use that instead of setting CFLAGS and LDFLAGS manually:
Export the PKG_CONFIG_PATH environment variable with a custom set of paths. Because no prefix (i.e. directory serving as the install root) was specified, /usr/local is assumed, meaning /usr/local/include will contain headers and /usr/local/lib will contain libraries. This means you need to export PKG_CONFIG_PATH on your command line as in export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:/usr/local/share/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig. The default setting of PKG_CONFIG_PATH when unset includes /usr/lib/pkgconfig and /usr/share/pkgconfig in that order on Linux and are specified as a fallback for other packages that might be used, though it may not be necessary in this case. The default set of paths may differ on OS X, so consult your pkg-config man page for reference.
Use // #cgo pkg-config: libsecp256k1, and CFLAGS and LDFLAGS will be set as necessary without you needing to do anything to them. Since not all systems have pkg-config installed, relying on this is perhaps a bad idea if you want a package to remain portable. Then again, I think it'd be preferable to the mess you dealt with since pkg-config simply wouldn't be found.
Change to the upvote directory and type make to build a working upvote binary.
Additional customization
Custom prefix
If you use something other than /usr/local as your prefix (e.g. ./configure --enable-module-recovery --prefix=/opt/libsecp256k1), then you'll need to adjust some things accordingly:
// #cgo CFLAGS: -I/opt/libsecp256k1/include
// #cgo LDFLAGS: -L/opt/libsecp256k1/lib -lsecp256k1
// #include "secp256k1.h"
import "C"
// or just use pkg-config and export the PKG_CONFIG_PATH environment
// variable containing the following paths:
// /opt/libsecp256k1/lib/pkgconfig
// /opt/libsecp256k1/share/pkgconfig
// /usr/lib/pkgconfig
// /usr/share/pkgconfig
You'll also need to modify the provided Makefile in the upvote directory to set the runtime path of the binary that gets built, else libsecp256k1.0.dylib will not be found:
# If you copy and paste this, replace the spaces in front of `go build`
# with a single horizontal tab character, else `make` will fail.
#
# Note that the "ldflags" specified are for the Go linker (go tool link),
# not the system's linker (ld).
build:
go build -ldflags="-r /opt/libsecp256k1/lib"
For more information about working with cgo, check out the following resources:
"C? Go? Cgo!" introduction to cgo on The Go Blog
Cgo documentation
The following questions are relevant but do not answer my question:
Linking partially static and partially dynamic in GCC
Linking a dynamic library to a static library that links to other static libraries
GCC: static linking only some libraries
Static link of shared library function in gcc
I asked a very similar question earlier, but since the previous question started by me got somewhat cluttered in the comment section and not fully answered (but I flagged it as answered since it was a good effort and did at least partially answer it) I will ask a new question. The question is specifically how to link libc as static, while linking some other library (e.g. libm) dynamically. This was suggested that cannot be done in the first question, is that true? If so it would be very interesting to know why not.
Is it even possible to do this? Someone made a comment (which was removed for some reason, maybe it was incorrect?) that it is possible, but there must then also exist a dynamically linked version of libc, since it will be required by the dynamic library (e.g. dynamic libm will require dynamic libc (?)).
This is fine for me, but it is not obvious to me how to tell GCC to do this, i.e. link in libc as both static and dynamic. How do I do this (I made a couple attempts, some are shown later in the question)? Or is there some other way to do what I want?
We first see that by simply running gcc test.c -lm, everything is linked in dynamically, as follows:
$ gcc test.c -lm
$ ldd a.out
linux-vdso.so.1 (0x00007fffb37d1000)
libm.so.6 => /lib64/libm.so.6 (0x00007f3b0eeb6000)
libc.so.6 => /lib64/libc.so.6 (0x00007f3b0eb10000)
/lib64/ld-linux-x86-64.so.2 (0x00007f3b0f1b0000)
To link only libm as static, while allowing libc to remain dynamic, we can do (as Z boson pointed out in one of the aforementioned questions):
$ gcc test.c /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libm.a
$ ldd a.out
linux-vdso.so.1 (0x00007fff747ff000)
libc.so.6 => /lib64/libc.so.6 (0x00007f09aaa0c000)
/lib64/ld-linux-x86-64.so.2 (0x00007f09aadb2000)
However, attempting the same procedure to link libc static and libm dynamic, does not seem to work:
$ gcc test.c /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a -lm
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: dynamic STT_GNU_IFUNC symbol `strcmp' with pointer equality in `/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a(strcmp.o)' can not be used when making an executable; recompile with -fPIE and relink with -pie
collect2: error: ld returned 1 exit status
What does this error message mean?
Some other attempts (most were also included in my first question):
$ gcc test.c /usr/lib64/libc.a
linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: dynamic STT_GNU_IFUNC symbol `strcmp' with pointer equality in `/usr/lib64/libc.a(strcmp.o)' can not be used when making an executable; recompile with -fPIE and relink with -pie
urned 1 exit status
$ gcc test.c -Wl,-Bdynamic -lm -Wl,-Bstatic -lc
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: cannot find -lgcc_s
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: cannot find -lgcc_s
collect2: error: ld returned 1 exit status
$ gcc -Wl,-Bdynamic -lm -Wl,-Bstatic -lc test.c
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: cannot find -lgcc_s
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: cannot find -lgcc_s
collect2: error: ld returned 1 exit status
$ gcc -Wl,-Bstatic -lc -Wl,-Bdynamic -lm test.c
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: dynamic STT_GNU_IFUNC symbol `strcmp' with pointer equality in `/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a(strcmp.o)' can not be used when making an executable; recompile with -fPIE and relink with -pie
collect2: error: ld returned 1 exit status
$ gcc test.c /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.so -lm
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: dynamic STT_GNU_IFUNC symbol `strcmp' with pointer equality in `/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a(strcmp.o)' can not be used when making an executable; recompile with -fPIE and relink with -pie
collect2: error: ld returned 1 exit status
$ gcc test.c /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.so /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a -lm
Note that the last one compiled/linked successfully. However libc has not been linked in statically, only dynamically, so it is another failed attempt.
The test program is simply the following:
$ cat test.c
#include <stdio.h>
#include <math.h>
int main(int argc, char **argv)
{
int i;
int result;
for(i = 0; i < 65535; i++) {
result = sin(i);
}
return 0;
}
Edit:
I've also tried statifier and ermine, as suggested in this question:
Static link of shared library function in gcc
Neither works.
Basically, your first approach is the correct way to do this:
gcc test.c libc.a -lm
After gcc adds the implicit libraries it'll look (conceptually) like this:
gcc crt1.o test.c libc.a -lm -lc -lgcc -lc
So that means that any libc functions called by either crt1.o or test.c will be pulled in from libc.a and linked statically, whereas any functions called solely from libm or libgcc will be linked dynamically (but it will reuse the static functions if libm calls something already pulled in).
The linker always starts at the left-most file/library, and works rightwards; it never goes back. .c and .o files are linked in unconditionally, but .a files and -l options are only used to find functions that are already referenced but not yet defined. Therefore, a library at the left is pointless (and -lc must appear twice because -lc depends on -lgcc, and -lgcc depends on -lc). Link order is important!
Unfortunately, you appear to have been foiled by what might be a bug in strcmp (or rather in the libc that contains strcmp): the STT_GNU_IFUNC thing is a clever feature that allows multiple versions of a function to be included, and the most optimal one to be selected at runtime, based on what hardware is available. I'm not sure, but it looks like this feature is only available in a PIE (Position Independent Executable) or shared library build.
Why that would be in a static libc.a is a mystery to me, but there's an easy workaround: implement your own strcmp (a basic, slow implementation is only a few lines of C), and link it in before libc.a.
gcc test.c mystrcmp.c libc.a -lm
Alternatively, you can extract the functions from libc.a that you really want, and link only those in statically:
ar x libc.a
gcc test.c somefile.o -lm
ar is to .a files, as tar is to .tar files, although the command usage varies a bit, so this example extracts the .o files from the .a file, and then links them explicitly.
Based on the answer by ams I did the follow
mystrcmp.c
int strcmp(const char *s1, const char *s2) {
}
Compile
gcc -c test.c
gcc -c mystrcmp.c
Setup files
ln -s `gcc -print-file-name=crt1.o`
ln -s `gcc -print-file-name=crti.o`
ln -s `gcc -print-file-name=crtn.o`
ln -s `gcc -print-file-name=libgcc_eh.a`
ln -s `gcc -print-file-name=libc.a`
ln -s `gcc -print-file-name=libm.so`
Link
ld -m elf_x86_64 -o math crt1.o crti.o test.o mystrcmp.o libc.a libgcc_eh.a libc.a libm.so -dynamic-linker /lib64/ld-linux-x86-64.so.2 crtn.o
This links and runs correctly. However, ldd shows
linux-vdso.so.1 => (0x00007fff51911000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f8182470000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f81820a9000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8182793000)
It appears that dynamic libm requires dynamic libc. Actually, that's easy to show
ldd libm.so reports
linux-vdso.so.1 => (0x00007fff20dfe000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcaf74fe000)
/lib64/ld-linux-x86-64.so.2 (0x00007fcaf7bed000)
So it's impossible to link to libm.so without linking libc.so as well unless you manage to compile libm without the dependency on libc.
just use
gcc sample_uart.c -static -static-libgcc -static-libstdc++
# lddtree a.out
a.out => ./a.out (interpreter => none)
#################### test ############################
ar x /usr/lib/arm-linux-gnueabihf/libc.a
-rw-r--r-- 1 root root 792 Oct 28 00:38 wmemset.o
-rw-r--r-- 1 root root 2456 Oct 28 00:38 wmemstream.o
-rw-r--r-- 1 root root 1616 Oct 28 00:38 wordcopy.o
-rw-r--r-- 1 root root 18544 Oct 28 00:38 wordexp.o
-rw-r--r-- 1 root root 1384 Oct 28 00:38 wprintf_chk.o
-rw-r--r-- 1 root root 1088 Oct 28 00:38 wprintf.o
-rw-r--r-- 1 root root 884 Oct 28 00:38 write_nocancel.o
-rw-r--r-- 1 root root 1368 Oct 28 00:38 write.o
-rw-r--r-- 1 root root 1340 Oct 28 00:38 writev.o
-rw-r--r-- 1 root root 1084 Oct 28 00:38 wscanf.o
-rw-r--r-- 1 root root 3924 Oct 28 00:38 wstrops.o
-rw-r--r-- 1 root root 2112 Oct 28 00:38 xcrypt.o
-rw-r--r-- 1 root root 1504 Oct 28 00:38 xdr_array.o
-rw-r--r-- 1 root root 836 Oct 28 00:38 xdr_float.o
-rw-r--r-- 1 root root 2344 Oct 28 00:38 xdr_intXX_t.o
-rw-r--r-- 1 root root 1740 Oct 28 00:38 xdr_mem.o
-rw-r--r-- 1 root root 4696 Oct 28 00:38 xdr.o
-rw-r--r-- 1 root root 3880 Oct 28 00:38 xdr_rec.o
-rw-r--r-- 1 root root 1640 Oct 28 00:38 xdr_ref.o
-rw-r--r-- 1 root root 1556 Oct 28 00:38 xdr_sizeof.o
-rw-r--r-- 1 root root 2464 Oct 28 00:38 xdr_stdio.o
-rw-r--r-- 1 root root 1688 Oct 28 00:38 xlocale.o
-rw-r--r-- 1 root root 936 Oct 28 00:38 xmknodat.o
-rw-r--r-- 1 root root 944 Oct 28 00:38 xmknod.o
-rw-r--r-- 1 root root 1052 Oct 28 00:38 xpg_basename.o
-rw-r--r-- 1 root root 1640 Oct 28 00:38 xpg-strerror.o
-rw-r--r-- 1 root root 900 Oct 28 00:38 xstat64.o
-rw-r--r-- 1 root root 1184 Oct 28 00:38 xstatconv.o
-rw-r--r-- 1 root root 1196 Oct 28 00:38 xstat.o
root#hi3798mv100:~/sample/uart#
root#hi3798mv100:~/sample/uart#
root#hi3798mv100:~/sample/uart# gcc sample_uart.c -lm
root#hi3798mv100:~/sample/uart# gcc sample_uart.c *.o -lm
/usr/bin/ld: dso_handle.o:(.data.rel.ro.local+0x0): multiple definition of `__dso_handle'; /usr/lib/gcc/arm-linux-gnueabihf/9/crtbeginS.o:(.data.rel.local+0x0): first defined here
/usr/bin/ld: rcmd.o: in function `__validuser2_sa':
(.text+0x418): warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
collect2: error: ld returned 1 exit status
I madeC programs named drive.c and mylib.c.
drive.c is main module mylib.c is sub modulle that I want work as shared library .
I can compile them with this step on MINGW
gcc –fPIC –g –c –Wall mylib.c
gcc -shared -Wl,-soname,libmylib.so.1 -o /c/opt/lib/libmylib.so.1.0.1 mylib.o -lc
gcc -g -Wall -Wextra -pedantic -I./ -L/c/opt/lib -o drive.exe drive.c –l:libmylib.so.1
Finally I did drive.exe
Then Windows Dialog Message was shown program can start because libmylib.so.1.0.1 is missing.
LD_LIBRARY_PATH is set.
$ set|grep LD
LD_LIBRARY_PATH=:/c/opt/lib
$
And there is libmylib.so.1.0.1
$ ls -la /c/opt/lib
total 98
drwxr-xr-x 2 JAC484 Administrators 4096 Mar 18 14:44 .
drwxr-xr-x 7 JAC484 Administrators 4096 Mar 14 15:47 ..
-rwxr-xr-x 1 JAC484 Administrators 45356 Mar 18 14:23 libmylib.so.1
-rwxr-xr-x 1 JAC484 Administrators 45356 Mar 18 14:23 libmylib.so.1.0.1
If I copied libmylib.so.1.0.1 in same directory of drive.exe,drive.exe can run.
How Can I tell the system where libmylib.so.1.0.1 is?
Windows doesn't use LD_LIBRARY_PATH. Your shared library DLL will need to be in the same directory as drive.exe or in a directory in the PATH.
The full details of Windows' DLL search is documented here:
MSDN: Dynamic-Link Library Search Order