Is it possible to specify linker options from code? - c

I wonder if it's possible to specify a linker option from code? Compilers allow #pragma directives to suppress warnings; how far can we extend this?

(I'm considering implicitly linking on Linux systems and using the GCC compiler; you probably could adapt the answer to your OS)
No, it is impossible to specify link options in code, and notice that the linker is working on object files (not on individual functions inside them).
However, your build procedure could extract linker options from the source code. For instance, if you have a directory with many single-source programs (that is, aa.cc C++ source file compiled into aa.bin executable, bb.cc compiled into bb.bin, etc) you might have a Makefile mentioning
%.bin: %.cc
$(CXX) $(CXXFLAGS) $(shell awk /Link:/{print $2} $<) $^ -o $#
and in aa.cc a comment like:
/* the link option is
Link: -lfoo
*/
BTW, you might also have some GCC plugin which registers and handles your specific #pragma; If you use clang instead of gcc you can also have some Clang plugin; that new pragma could add something in a common Sqlite database which is later used at link time.
So you could do sophisticated things, but I would simply suggest to have your own make rules to handle your linking.

Related

Can IAR produce a static library that GCC can link to?

There is a vendor whose software I'd like to work with. They have a code base which they can only compile using IAR Embedded Workbench (as far as I know, their code does not compile with GCC). Unfortunately their hardware only works with their software stack, so I don't really have a choice about whether or not I'd like to use it. They distribute this code as a .a static library file (and accompanying headers) compiled for the ARM Cortex-M4 CPU. (They don't want to distribute sources.) For the sake of this discussion, let's call it evil_sw_stack.a.
I'd like to use this piece of code but I don't have an IAR license and have zero expertise with IAR. I'd like to use GCC.
Is there a way to make IAR produce such a static library that GCC can link to? What kind of compiler option would the vendor need to use to produce such a binary?
(I would guess that the ABI of the resulting binary can be somehow specified and set to a setting which statisfies GCC. )
Example usage of GCC
Their default software stack is very GCC-friendly, this specific one is the only one in their offering which isn't. Generally, I can compile a simple piece of example code if I have the following:
startup_(devicename).S: GCC-specific assembly file
system_(devicename).c
(devicename).ld: linker script
Some header files for the specific device
For example, I can compile a simple piece of example like this:
$ arm-none-eabi-gcc helloworld.c startup_(devicename).S system_(devicename).c -T (devicename).ld -o helloworld -D(devicename) -I. -fno-builtin -ffunction-sections -fdata-sections -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -mcpu=cortex-m4 -mthumb -mno-sched-prolog -Wl,--start-group -lgcc -lc -lnosys -Wl,--end-group
So far, so good. No warnings, no errors.
How I try to use the static library
For the sake of this discussion, let's call it evil_sw_stack.a.
This is how I attempted to use it:
$ arm-none-eabi-gcc evil_sw_stack.a helloworld.c startup_(devicename).S system_(devicename).c -T (devicename).ld -o helloworld -D(devicename) -I. -fno-builtin -ffunction-sections -fdata-sections -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -mcpu=cortex-m4 -mthumb -mno-sched-prolog -Wl,--start-group -lgcc -lc -lnosys -Wl,--end-group
Unfortunately this complains about multiple definitions of a bunch of functions that are defined in system_(devicename).c. Maybe they accidentally compiled that into this library? Or maybe IAR just compiled it this way? Now, if I try to remove system_(devicename).c from the GCC command line and simply link to the .a file, I get these errors:
/usr/lib/gcc/arm-none-eabi/5.2.0/../../../../arm-none-eabi/bin/ld: warning: thelibrary.a(startup_chipname.o) uses 2-byte wchar_t yet the output is to use 4-byte wchar_t; use of wchar_t values across objects may fail
undefined reference to `__iar_program_start'
undefined reference to `CSTACK$$Limit'
undefined reference to `__iar_program_start'
Poking the file with readelf gets me nowhere:
$ readelf -h evil_sw_stack.a
readelf: Error: evil_sw_stack.a: did not find a valid archive header
Interestingly though, this seems to be getting somewhere:
$ arm-none-eabi-ar x evil_sw_stack.a
Now I've got a bunch of object files which do have ELF headers according to readelf, and yup, they did compile a startup file (of another of their devices) into the library... I'm wondering why, but I think this is a mistake.
This also works:
$ arm-none-eabi-objdump -t evil_sw_stack_objfile.o
So now the question is, is it safe to try to compile these object files into my own application using GCC? According to this other SO question, the object file formats are not compatible.
I assume that the startup code is mistakenly compiled into the library. I can delete it:
$ arm-none-eabi-ar d evil_sw_stack.a startup_(otherdevicename).o
$ arm-none-eabi-ar d evil_sw_stack.a system_(otherdevicename).o
Now I get an evil_sw_stack.a which gcc can accept as an input without complaining.
However, there is one thing that still worries me. When I use the object files instead of the static library, I get these warnings:
/usr/lib/gcc/arm-none-eabi/5.2.0/../../../../arm-none-eabi/bin/ld: warning: evil_objfile.o uses 2-byte wchar_t yet the output is to use 4-byte wchar_t; use of wchar_t values across objects may fail
/usr/lib/gcc/arm-none-eabi/5.2.0/../../../../arm-none-eabi/bin/ld: warning: evil_objfile.o uses 32-bit enums yet the output is to use variable-size enums; use of enum values across objects may fail
So it seems that evil_sw_stack.a was compiled with (the IAR equivalents of) -fno-short-enums and -fshort-wchar. GCC doesn't complain about this when I use evil_sw_stack.a at its command line but it does complain when I try to use any object file that I extracted from the library. Should I worry about this?
I don't use wchar_t in my code so I believe that one doesn't matter, but I would like to pass enums between my code and the library.
Update
Even though the linker doesn't complain, it doesn't work when I actually call some functions from the static library. In that case, make sure to put the libraries in the correct order when you call the linker. According to the accepted answer to this question, they need to be in reverse order of dependency. After doing this, it still misses some IAR crap:
undefined reference to `__aeabi_memclr4'
undefined reference to `__aeabi_memclr'
undefined reference to `__aeabi_memmove'
undefined reference to `__aeabi_memset4'
undefined reference to `__aeabi_memset'
undefined reference to `__iar_vla_alloc2'
undefined reference to `__iar_vla_dealloc2'
undefined reference to `__aeabi_memclr4'
I've found out that the __aeabi functions are defined in libgcc but even though I link to libgcc too, the definition in libgcc doesn't seem to be good enough for the function inside evil_sw_stack.a.
EDIT: after some googling around, it seems that arm-none-eabi-gcc doesn't support these specific __aeabi functions. Take a look at this issue.
Anyway, after taking a look at ARM's runtime ABI docs, the missing __aeabi functions can be trivially implemented using their standard C library equivalents. But I'm not quite sure how __iar_vla_alloc2 and __iar_vla_dealloc2 should work and couldn't find any documentation on them online. The only thing I found out is that VLA means "variable length array".
So, it seems that this will never work unless the chip vendor can compile their static library in such a way that it doesn't use these symbols. Is that right?
Disclaimer
I'd prefer not to disclose who the vendor is and not to disclose which product I work with. They are not proud that this thing doesn't work properly and asked me not to. I'm asking this question to help and not to discredit them.

How to make a makefile properly, Why is this makefile not correct

I am trying to understand how to make a make file in Linux, and some one suggested to me to use this:
target: mytalkc.c mytalkd.c
gcc mytalkc.c -o mytalkc
gcc mytalkd.c -o mytalkd
When executing the above code exactly using "make" in the terminal everything ran great and everything compiled and made a executable successfully. So I thought this was correct, and I went with this for my makefile.
After futher documentation I learned that the correct way to make a makefile was not what I had above....
My question is, why does "target:" work and why was make was able to complete everything with no errors. Also why is things like "all:" used in make files over "target:" ?
Thanks in advance and sorry if it seems obvious, but I am new to Linux programming.
Typically a makefile is structured into multiple target rules, which contain dependencies that may rely on other dependences and so on. The word "all" is suggestive of "all targets", which means any dependencies of the "all" target are built. Additionally, some implementations of make allow for phony targets, such as "all", that tell make not to look for a file with one of the suffixes in a predefined suffix list, such as "all.c" to build the binary (program) with the filename "all".
Your actual question regarding why "target" works pertains to the behavior of make. It automatically makes the first target in the makefile unless a specific target is given. In your case, "target" is the first target in your file, so "make" by itself will make it (often "all" is used instead, but it is just a name; you should use "all" to simply because it is common practice).
Here is an example of how your file might be reworked with multiple targets (see the documentation for your make utility to understand the syntax, such as "info make" or "man make"):
all: mytalkc mytalkd
mytalkc: mytalkc.c
gcc $< -o $#
mytalkd: mytalkd.c
gcc $< -o $#
There are a lot of other things to address outside the scope of this answer, including implicit suffix rules and compiling several individual parts of a program/library together to create the program (splitting makefile rules into program/library: object1.o object2.o main.o and then individual rules for e.g. object1.o: object1.c). However, those are the basics that you need to know for now.
For what i know if you compile using gcc mytalkc.c -o mytalkc you are just compiling the source file as an object, it means that the compilation went well, but the file is not executable still because you haven't done any liking.
Try with this:
all: mytalkc.o mytalkd.o
mytalkc.o: mytalkc.c
gcc mytalkc.c -c mytalkc.c
mytalkd.o: mytalkd.d
gcc mytalkc.c -c mytalkd.c
invoking make all

C: Compiling pre-compiled code as inline

For some rather complicated reason, I have a set of files which I would like to compile seperatly and then link, but so that the functions in one are placed inline in the second. This is because I would like them to be compiled with different flags in GCC. I know I could fix the problem by looking into how I could get around that, but I would like to know if this is possible.
EDIT 1:
If not, is it possible to compile the 'external' functions into a form of assembly that I could include in the other file. Yes crazy but also cool...
Having a quick look, this could well be an option. I guess it would be impossible to automatically compile it in, so could someone please give me a bit of information about assembly? I've only used basic ARM assembly. I've compiled to toy functions with the -S flag in GCC. How do I link registers with variables? Will they always be in the same order? The function will be highly optimised. When should I start and end the extract? Should I include .cfi_startproc at the start and .cfi_def_cfa 7, 8 at the end?#
EDIT 2:
This post details how gcc can do link-time optimisations like this with -flto. Sadly this is only available with version 4.5, which I do not have nor have the ability to install since I do not have root access of the machine I need to compile this on. Another possible solution would be to explain how I could install a different version of GCC into a folder on a unix machine.
As far as I know gcc doesn't do linktime optimizations (inlining in particular), at least with the standard ld linker (it could be that the new gold linker does it, but I really don't think so). Clang in principle should be capable of doing it, since it depends on LLVM, which supports link time optimizations (it seems that your question is gcc spacific, though).
From your question though, it seems you are looking for a a way to merge object files after compilation, not necessarily by inlining their contained functions. This can be done in multiple ways:
Archiving them into a static library with ar: e.g. ar libfoo.a obj1.o obj2.o.
Combining them together into a third relocatable object (ld's --relocatable option). gcc -Wl,--relocatable -o obj3.o obj1.o obj2.o
Putting them into a shared library (beware that this requires compiling the objects with -fPIC) e.g. gcc -shared -o libfoo.so obj1.o obj2.o
You could compile with the -c option to create a set of .o files, or even make a .so file. Then use the sequence you like in the linking phase of gcc.

Specifying Exact Library to Link with In Automake

I am having extreme trouble cross compiling a project related to gstreamer. I am trying to link it to a library on my cross compile machine's /usr/lib
If I do the standard linker flags -L{FILESYS_DIR}/usr/lib -lGLESv2 I get pthread complaints from my cross compile toolchain. Thus, I am trying to link to this library without using the -L flag.
No matter what I do, I am getting undefined symbol glFramebuffer2D. However a quick readelf -Wc $FILESYS_DIR/usr/lib/libGLESv2.so | glFrame shows me a glFramebuffer2D symbol.
I'm pulling my hair out because no matter what flags I specify to autoconf, something called libtool throws away my link request unless I use the -L -l approach...
Edit: I had another idea, I tried -Wl, $FILESYS_DIR/usr/lib/libGLESv2.so which worked in compiling and linking but not during runtime... Obvious to me (now) because the host machine root is $FILESYS_DIR. Anyways, this is on the right approach, but I guess I need relative names.
libtool: link: arm-none-linux-gnueabi-gcc -shared .libs/libgstbla_la-gstblaoverlay.o
.libs/libgstbla_la-gstblastabilize.o .libs/libgstbla_la-gles2_utilities.o -Wl,-
rpath -Wl,/home/z3/z3-netra/filesys/fs//opt/gstreamer-0.4/lib -Wl,-rpath -
Wl,/home/z3/z3-netra/filesys/fs//opt/gstreamer-0.4/lib -L/home/z3/z3-
netra/filesys/fs/opt/gstreamer/lib /home/z3/z3-netra/filesys/fs//opt/gstreamer-
0.4/lib/libgstbase-0.10.so /home/z3/z3-netra/filesys/fs//opt/gstreamer-
0.4/lib/libgstreamer-0.10.so /home/z3/z3-netra/filesys/fs//opt/gstreamer-
0.4/lib/libgstvideo-0.10.so /home/z3/z3-netra/filesys/fs//opt/gstreamer-
0.4/lib/libgobject-2.0.so /home/z3/z3-netra/filesys/fs//opt/gstreamer-
0.4/lib/libgmodule-2.0.so /home/z3/z3-netra/filesys/fs//opt/gstreamer-
0.4/lib/libgthread-2.0.so -lrt /home/z3/z3-netra/filesys/fs//opt/gstreamer-
0.4/lib/libglib-2.0.so -pthread -Wl,-soname -Wl,libgstbla.so -Wl,-version-script -
Wl,.libs/libgstbla.ver -o .libs/libgstbla.so
If needed, in one line as well:
libtool: link: arm-none-linux-gnueabi-gcc -shared .libs/libgstbla_la-gstblaoverlay.o .libs/libgstbla_la-gstblastabilize.o .libs/libgstbla_la-gles2_utilities.o -Wl,-rpath -Wl,/home/z3/z3-netra/filesys/fs//opt/gstreamer-0.4/lib -Wl,-rpath -Wl,/home/z3/z3-netra/filesys/fs//opt/gstreamer-0.4/lib -L/home/z3/z3-netra/filesys/fs/opt/gstreamer/lib /home/z3/z3-netra/filesys/fs//opt/gstreamer-0.4/lib/libgstbase-0.10.so /home/z3/z3-netra/filesys/fs//opt/gstreamer-0.4/lib/libgstreamer-0.10.so /home/z3/z3-netra/filesys/fs//opt/gstreamer-0.4/lib/libgstvideo-0.10.so /home/z3/z3-netra/filesys/fs//opt/gstreamer-0.4/lib/libgobject-2.0.so /home/z3/z3-netra/filesys/fs//opt/gstreamer-0.4/lib/libgmodule-2.0.so /home/z3/z3-netra/filesys/fs//opt/gstreamer-0.4/lib/libgthread-2.0.so -lrt /home/z3/z3-netra/filesys/fs//opt/gstreamer-0.4/lib/libglib-2.0.so -pthread -Wl,-soname -Wl,libgstbla.so -Wl,-version-script -Wl,.libs/libgstbla.ver -o .libs/libgstbla.so
/usr/lib should already be on the library search path, so you shouldn't need to specify an RPATH. What you do need to do though, is to tell your (cross-)linker where to find the libraries. I think that includes transitively dependencies, such as libpthread. Do you have $FILESYS_DIR/usr/lib/libpthread.so? Does it point to /lib/libpthread.so.N? Oh wait, I see something now that I wrote it out: notice the (likely) absence of $FILESYS_DIR there: so it's possible that your linker is looking for libpthread transitively needed by libGLESv2, but not finding it, since $FILESYS_DIR/lib is not on the library include path. Add -L $FILESYS_DIR/lib to your linker flags and try again.
All, this was related to the following question: set global gcc default search paths
The problem here is related to the pthread.so (also glib.so). /usr/lib/pthread.so on a lot of systems is an ASCII script which then further links to the system's /lib/pthread.so.0 (which is a soft link). When compiling, my $(FILESYS_DIR) was correct, however the the libpthread.so there pointed to the host systems pthread.so
I made a HUGE mistake here which could have been easily avoided with correct cross compilation management. When compiling for a target system, do not use files on the target system filesystem (if it's a NFS as mine was.) Use local libraries compiled for that target system. FURTHERMORE, specify -Wl,-rpath-link=/[local location where your *.so reside]
That compiler/linker flag allows for the runtime path to be on the local system during compile and link, but maintain the standard runtime path during runtime.... Hope that made sense.

Portable way to link statically against one of the libraries

I am creating a utility which depends on libassuan aside other depends. While these ‘others’ provide shared libraries, libassuan comes with static one only.
libassuan comes with simple libassuan-config tool which is meant to provide CFLAGS & LDFLAGS for the compiler/linker to use. These LDFLAGS refer to the library as -lassuan.
The result of standard call of make is then:
cc -I/usr/include/libmirage -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -lmirage -lglib-2.0 -L/usr/lib64 -lassuan -o mirage2iso mirage2iso.c mirage-getopt.o mirage-wrapper.o mirage-password.o
mirage-password.o: In function `mirage_input_password':
mirage-password.c:(.text+0x1f): undefined reference to `assuan_pipe_connect'
mirage-password.c:(.text+0x32): undefined reference to `assuan_strerror'
collect2: ld returned 1 exit status
make: *** [mirage2iso] Error 1
(I've just started writing this unit and that's why there aren't more errors)
So, if I understand the result correctly, gcc doesn't want to link the app to libassuan.a.
Using -static here will cause gcc to prefer static libraries over shared which is unindented. I've seen solution suggesting using something like that:
-Wl,-Bstatic -lassuan -Wl,-Bdynamic
but I don't think it would be a portable one.
I think the best solution would be to provide full path to the static library file but libassuan-config doesn't provide much of help (all I can get from it is -L/usr/lib64 -lassuan).
Maybe I should just try to create the static library path by ‘parsing’ returned LDFLAGS and using -L for the directory name and -l for the library name — and then hoping that in all cases libassuan-config will return it like that.
What do you think about that? Is there any good, simple and portable solution to resolve the issue?
PS. Please note that although I'm referring to gcc here, I would like to use something that will work fine with other compilers.
PS2. One additional question: if package does install static library only, returning such LDFLAGS instead of full .la path can be considered as a bug?
gcc will link to libassuan.a if it doesn't find libassuan.so
It's probably the order symbols are looked up in the static library when you link. The order matters.
)
Assuming gcc can find libassuan.a and it actually provides the functions the linker complains about, try:
cc -I/usr/include/libmirage -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -lmirage -lglib-2.0 -L/usr/lib64 -o mirage2iso mirage2iso.c mirage-getopt.o mirage-wrapper.o mirage-password.o -lassuan
Since you say libassuan is under /usr/lib64 it's probably a 64 bit library, are your app and the other libraries 64 bit as well ?
Compiler's command-line options are not a portable thing. There's no standard for it. Every compiler uses its own and several can merely informally agree to comply with each other in command-line format. The most portable way for your linking is to use libassuan-config, of course. I think, it can generate not only flags for gcc, but for other compilers as well. If it can't, then no portable way exists, I suppose (other than CMake or something on higher level).
The command line to cc you shown is totally correct. If you have a static library libassuan.la and path to it is supplied to -L option, then the compiler does link against it. You can see it from its output: has it not found the static library, would it complain with error message like "can't find -lassuan". I
Moreover, if no libassuan.so is found, then compiler links against your library statically, even if you haven't used -Wl,-Bstatic stuff or -static flag.
Your problem may be in persistence of several versions of libassuan in your system. Other that that, I don't see any errors in what you've provided.
Which directory is libassuan.a in
I think the first error is not gcc doesn't want to link the app to libassuan.a it is more gcc does not know where libassuan.a . You need to pass gcc a -L parameter giving the path to libassuan.a .
e.g.
-L /home/path

Resources