Linking with another start-up file - c

I am trying to link a program with my own start-up file by using the STARTUP directive in a LD script:
...
ENTRY(_start)
STARTUP(my_crt1.o)
...
GCC driver is used to link the program (not to bother with library paths like libgcc, etc.):
gcc -T my_script.ld ...
Unfortunately, it only works with a GCC compiled for powerpc targets, while arm or i686 targets don't and still include crt0.o in collect2. For example:
arm-eabi-g++ -v -T my_script.ld ...
gives me:
collect2 ... /opt/lib/gcc/arm-eabi/4.8.0/../../../../arm-eabi/lib/crt0.o ...
and thus:
crt0.S:101: multiple definition of `_start'
It seems the STARTUP directive is totally ignored (the powerpc target uses its default crt0 too unless the STARTUP directive is specified) and there is no way to disable the default crt0.
Is there a portable way to link against another start-up file?
My start-up file uses libgcc functions (to call ctors and dtors) so crtbegin.o, crtend.o, etc. are needed so I would like to avoid the -nostartfiles option which disables crt*.o - I need to disable crt0.o only.
Thank you

I am trying to link a program with my own start-up file ...
GCC driver is used to link the program ...
In that case, you must also supply -nostartfiles flag to GCC.

This limitation indeed forces you to disable the default startup files with -nostartfiles (I prefer -nostdlib). You then need to build by yourself the list of run-time objects. gcc has the option -print-file-name to print the absolute path of libraries it was compiled with (crtbegin.o, crtend.o, libgcc.a...). For example: arm-eabi-g++ <FLAGS> -print-file-name=crtbegin.o
Here is the GNU Make macro I use (providing gcc and cflags):
define m.in/toolchain/gnu/locate =
$(strip
$(shell $(m.in/toolchain/gnu/bin/gcc) $(m.in/toolchain/gnu/cflags) \
-print-file-name=$(m.in/argv/1))
)
endef
crtn := $(call m.in/toolchain/gnu/locate, crtn.o)

Related

How do I use an external library with gcc?

I am attempting to compile this code:
#include <GLFW/glfw3.h>
int main() {
glfwInit();
glfwTerminate();
return 0;
}
Using this command in MSYS2 on Windows 10:
gcc -Wall runVulkan.c -o runVulkan
as well as this:
gcc -Wall -Llibs/glfw runVulkan.c -o runVulkan
libs/glfw is where I downloaded the library to.
For some reason I keep getting this:
runVulkan.c:1:10: fatal error: GLFW/glfw3.h: No such file or directory
1 | #include <GLFW/glfw3.h>
| ^~~~~~~~~~~~~~
compilation terminated.
It seems like I'm getting something very basic wrong.
I'm just getting started with C, I'm trying to import Vulkan libraries.
Run pacman -S mingw-w64-x86_64-glfw to install GLFW.
Then build using gcc -Wall runVulkan.c -o runVulkan runVulkan.c `pkg-config --cflags --libs glfw3`.
The pkg-config command prints the flags necessary to use GLFW, and the ` backticks pass its output to GCC as flags. You can run it separately and manually pass any printed flags to GCC.
Note that any -l... flags (those are included in pkg-config output) must be specified after .c or .o files, otherwise they'll have no effect.
For me pkg-config prints -I/mingw64/include -L/mingw64/lib -lglfw3.
-I fixes No such file or directory. It specifies a directory where the compiler will look for #included headers. Though it's unnecessrary when installing GLFW via pacman, since /mingw64/include is always searched by default.
-l fixes undefined reference errors, which you'd get after fixing the previous error. -lglfw3 needs a file called libglfw3.a or libglfw3.dll.a (or some other variants).
-L specifies a directory where -l should search for the .a files, though it's unnecessrary when installing GLFW via pacman, since /mingw64/lib is always searched by default.
#include are just headers, for declarations. gcc, as any compilers, needs to know where those .h should be searched.
You can specify that with -I option (or C_INCLUDE_PATH environment variable).
You'll also need -L option, this times to provide the library itself (.h does not contain the library. Just declarations that the compiler needs to know how to compile codes that use the library function's and types).
-L option tells the compiler where to search for libraries.
But here, you haven't specify any libraries (just headers. And I know that it seems logical that they go together. But strictly speaking, there is no way to guess from #include <GLFW/glfw3.h> which library that file contain headers for (that is not just theory. In practice, for example, the well known libc declarations are in many different headers)
So, you will also have to specify a -l option. In your case -lglfw.
This seems over complicated, because in your case you compile and like in a single command (goes from .c to executable directly). But that are two different operations done in one command.
Creation of an executable from .c code source is done in two stage.
Compilation itself. Creating .o from .c (many .c for big codes), so many compilation commands. Using command such as
gcc -I /path/where/to/find/headers -c mycode.c -o mycode.o
Those are not related to the library. So no -l (and therefore no -L) for that. What is compiled is your code, so just your code is needed at this stage. Plus the header files, because your code refers to unknown function and types, and the compiler needs to know, not their code, but at least declarations that they really exist, and what are the types expected and returned by the functions is the headers files.
Then, once all the .o are compiled, you need to put together all compiled code, yours (the .o) and the libraries (which are somehow a sort of .zip of .o) to create an executable. That is called linking. And is done with commands like
gcc -o myexec mycode1.o mycode2.o -L /path/where/to/search/for/libraries -lrary
(-lbla is a compact way to include /path/where/to/search/for/libraries/libbla.so or /path/where/to/search/for/libraries/libbla.a)
At this stage, you no longer need -I or anything related to headers. The code is already compiled, headers has no role left. But you need everything needed to find the compile code of the libraries.
So, tl;dr
At compilation stage (the stage that raises the error you have for now), you need -I option so that the compiler knows where to find GLFW/glfw3.h
But that alone wont avoid you the next error that will occur at linking stage. At this stage, you need -lglfw to specify that you want to use that library, and a -L option so that the compiler knows where to find a libglfw.so

How to load C library functions into assembly, and use assembly library functions in another C or assembly project

Currently I am building a foo.h and foo.c with:
$ clang -I . -dynamiclib \
-undefined dynamic_lookup \
-o foo.dylib foo.c
I am able to use this in other C libraries like this:
clang -I . -dynamiclib \
-undefined dynamic_lookup \
-o bar.dylib bar.c foo.dylib
I would like to use this library in an assembly project.
$ nasm -f macho64 test.asm \
&& ld -e start -macosx_version_min 10.13.0 -static -o test test.o foo.dylib
$ ./test
ld: warning: foo.dylib, ignoring unexpected dylib file
Wondering how I link together the C -> asm system to get the C functions working in asm. Then I would like to go further and use that compiled asm to use in either a C or asm project, so wondering how to do that.
When using the assembly in C, I would like for you to basically get functions and import #include "myassembly.h" or something like that, so it feels like a real library. Then you have a function like myfunc which is defined in assembly, but you can use it in c as myfunc(1, 2, 3); sort of thing.
If I change it from static to dynamic linking with the -lSystem flag (and removing -static), I get this:
dyld: Library not loaded: foo.dylib
Referenced from: ./test
Reason: image not found
make: *** [...] Abort trap: 6
You're specifying -static which means:
-static Produces a mach-o file that does not use the dyld. Only used
building the kernel.
dyld is the dynamic loader. If you're not using the dynamic loader, you can't use dynamic libraries.
Update for edited question:
When a dylib is created, it gets an "install name". When an executable is linked to that dylib, the executable stores the install name of the dylib in its reference to it. (Note, it does not store the link-time path of the dylib file it linked against.) When the executable is loaded, the dynamic loader looks for the dylib using the install name it recorded, by default.
You can specify the install name using the -install_name <name> option to the linker. It could be the absolute path to where you expect the library to be installed (e.g. /usr/local/lib/foo.dylib), if you expect it to be installed in a fixed location. Often, though, that's not useful. You want a more flexible means for the dynamic loader to find the dylib.
The dynamic loader understands certain special path prefixes on install names to support such flexibility. See the dyld(1) man page. For example, if you specify an install name of #executable_path/foo.dylib then, at load time, the loader will look next to the executable for the library.
You can see the install name of a dylib by using otool -D foo.dylib. Your dylib may not have an install name, in which case its effective install name is just its file name with no path.
If the loader doesn't find the library by using its install name, it has a search strategy. By default, it looks in ~/lib:/usr/local/lib:/lib:/usr/lib. You can use some environment variables to alter the search strategy. For example, you can set DYLD_FALLBACK_LIBRARY_PATH to a colon-delimited list of directories to search, instead. These environment variables are also listed in the dyld(1) man page.

Is it possible to specify linker options from code?

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.

Compiling a static executable [duplicate]

I'm trying to compile an executable (ELF file) that does not use a dynamic loader. I built a cross compiler that compiles mips from linux to be used on a simulator I made. I asserted the flag -static-libgcc on compilation of my hello.cpp file (hello world program). Apparently this is not enough though. Because there is still a segment in my executable which contains the name/path of the dynamic loader. What flags do I use to generate an executable which contains EVERYTHING needed to be run? Do I need to rebuild my cross compiler?
Use the following flags for linking
-static -static-libgcc -static-libstdc++
Use these three flags to link against the static versions of all dependencies (assuming gcc). Note, that in certain situation you don't necessarily need all three flags, but they don't "hurt" either. Therefore just turn on all three.
Check if it actually worked
Make sure that there is really no dynamic linkage
ldd yourexecutable
should return "not a dynamic executable" or something equivalent.
Make sure that there are no unresolved symbols left
nm yourexecutable | grep " U "
The list should be empty or should contain only some special kernel-space symbols like
U __tls_get_addr
Finally, check if you can actually execute your executable
Try using the -static flag?

How to replace LD variable in a Makefile to link C objects

I'm writing a Makefile for C. I want be able to specify different programs for compilation and linking via environmental variables. However, I want it works without any additional variables too. I was trying to link with ld. However, the default doesn't link with standard C library.
The question:
How to link C program with ld or $LD
Is it possible to get appropriate flags from cc?
I cannot use $(CC) in place of $(LD). The LD ?= cc doesn't work too.
I want something like this to be true:
Environment variable CC set to tcc.
Environment variable LD unset.
My Makefile compile using tcc and link using system default linker for C.
Unfortunately, some C compilers are unable to link some libraries. I have this problem with tcc and glfw.
P.S.
Linux user
The conditional assignment $(LD) ?= cc can not work, since $(LD) is predefined.
If you want to start make without predefined variables, use the option -R:
> make -p | grep LD
...
LD = ld
...
> make -p -R | grep LD
>
Instead of using ld as the linker, use gcc or g++. They add the appropriate command line options for getting libraries and startup code, etc. In other words:
ld -o main main.o
is equivalent to:
gcc -o main main.o
except that gcc adds all the command line parameters when it calls ld.
In other words: LD=gcc.
One of the main features of tcc is that :
tcc is a compiler and a linker [...] it compile and execute C source directly. No linking or assembly necessary
There's more detail in tcc documentation, it says about linking that :
Dynamic ELF libraries can be output but the C compiler does not generate position independent code (PIC). It means that the dynamic library code generated by TCC cannot be factorized among processes yet.
It means that, if you want to use tcc, you'll need to link with tcc.
As I see, you can define rule-dependent values for variables.
build_with_tcc: CC=tcc compile_tcc
compile_tcc:
## Commands to do a full build with tcc.
build_with_gcc: CC=gcc LD=g++ link_gcc
compile_gcc:
## Commands to compile with gcc.
link_gcc: compile_gcc
## Commands to link with g++.
And build it by calling the appropriate rule.
If you wish, in other hand, to be able to pass an arbitrary compiler toolchain, you will have to have some resctrictions anyway.
The rule:
build_with_arbitrary: compile_arbitrary link_arbitrary
Implies that your build must be done in two steps and the respective rules (compile_arbitrary and link_arbitrary) must obey the same commandline.
So you can invoke make with custom CC and LD variables:
CC=any_cc LD=any_ld make build_with_arbitrary
Lastly, you can add a dirty check for LD being empty in the linker step, and only perform it if not.
link_arbitrary:
[ -n "$(LD)" ] && do_linker_stuff
So you could use build_with_arbitrary even for a compiler that does everything in a single step just by passing:
CC=any_cc LD= make build_with_arbitrary
I hope to have correctly understood your question. Sorry if I misunderstood, and please tell me were I am wrong.

Resources