I have an annoying CMake issue. I have a project with more than one directories, such as (theoretically):
compiler -> generates compiler.exe and compiler.dll
This is pure C++
However the compiler.dll needs to link to machine.dll (see below) due to use of some fancy things ...
and the compiler.dll is a SHARED library
machine -> generates machine.exe and machine.dll -
this is half C and half C++. The C++ part needs to link to compiler.dll
So here a circular dependency arose, which is very elegantly solved on Linux, however on MSVS2012, the linker complains with LNK2019 ... unresolved external symbol. Which is understandable, since when I see the compilation, the order is the following:
it compiles the machine's C files
it tries to compile the machine's C++ files ... and here it fails at the linking, since the compilers' C++ files were not compiled yet...
How can I resolve this issue?
Problem was solved by converting the shared libraries into static ones and extracting the necessary parts into a shared library, without keeping the circularity.
Related
I'm trying to call the STM32 Cube Programmer C libraries from Rust.
The entire code, and branches showing various attempts, are available here:
https://github.com/becky112358/rust_c_linking_stm32_cube_programmer
Attempt 1 (in my GitHub repository, branch main)
Following the Rust Bindgen tutorial: https://rust-lang.github.io/rust-bindgen/
This is my preferred method. A Rust crate wraps the C library. Other Rust crates can then include the Rust wrapper crate, and not have to worry about any C libraries.
... in theory.
The Rust crate wrapping the C library (libstm32_cube_programmer_sys) builds ok. Its tests run ok.
The Rust crate calling the Rust crate which wraps the C library (caller) does not build, but reports:
= note: LINK : fatal error LNK1181: cannot open input file '.\drivers\CubeProgrammer_API.lib'
Why is caller even trying to look for the C library? I expected libstm32_cube_programmer_sys to handle all C library to Rust conversion, and that any Rust crates then calling libstm32_cube_programmer_sys could be purely Rusty (with maybe some unsafeness).
In build.rs I initially mis-wrote the C library name, and libstm32_cube_programmer_sys did not build. Correcting the library name allowed libstm32_cube_programmer_sys to build successfully. So it seems like libstm32_cube_programmer_sys does open the C library.
I tried adding the path to the drivers folder to my PATH.
I tried listing the absolute path to the C library:
println!("cargo:rustc-link-lib=C:/[blah blah]/drivers/CubeProgrammer_API");
I could not find how to feed in the path correctly, without Rust reporting:
error: renaming of the library `C` was specified, however this crate contains no `#[link(...)]` attributes referencing this library.
Attempt 2 (branch all_in_one)
In the main branch it seemed like maybe the problem was that libstm32_cube_programmer_sys could find the C library but caller could not. So I tried discarding the separate Rust crate, and having a single Rust crate which both wraps the C library and calls the C functions.
This time I get the following error, plus a bonus warning:
= note: caller.59pofysds2mkvvjr.rcgu.o : error LNK2019: unresolved external symbol disconnect referenced in function _ZN6caller4main17ha79648c0a9e86ed0E
.\drivers\CubeProgrammer_API.lib : warning LNK4272: library machine type 'x86' conflicts with target machine type 'x64'
Attempt 3 (branch link_search)
I searched a lot on the internet and found lots of different ways to call a C library from Rust. One way is to use link-search rather than link-lib. This surely only makes things harder for the compiler because you make it do more work. But I am stuck and need to try different things!
This time I get the following error, plus the bonus warning:
= note: caller.59pofysds2mkvvjr.rcgu.o : error LNK2019: unresolved external symbol __imp_disconnect referenced in function _ZN6caller4main17ha79648c0a9e86ed0E
.\drivers\CubeProgrammer_API.lib : warning LNK4272: library machine type 'x86' conflicts with target machine type 'x64'
Question
How do I make this work? Ideally from Attempt 1, but I'll take anything!
When we have:
C library <- Rust library <- Rust code
It seems that
when compiling, the Rust code needs to be able to see the C library, even though it is also calling the Rust library
when running, there may be C dlls which you need to store alongside the Rust exe
That was my main misunderstanding when I posted my original question.
Some other tips / reminders:
Make sure to use the correct lib file! (x64, x86 etc)
Resolve warnings too!
I just read a paragraph about linker and complitation in C so i this is what I understood from it : compiler in c/c++ turns the file instead of .c/.cpp (except checking if the code is valid lets assume that the code is valid) to .o/.obj which inside of it the code is written in machine code(binary) and when you run the code it does linking and it takes the object file which was created from the original .c / .cpp and turns it into and executable file that the machine can read ?
can you plz tell me if that is truth ?
The truth but somewhat simplistic.
The compiler generates object code from source code - it compiles a single translation unit. The object code comprises of certainly machine code, but it is not directly executable since it contains unresolved symbols to library code and to code from other separately compiled translation units. Thes symbolic links are calls or references to code and data whose address is not yet resolved.
The linker brings together multiple object code modules from compiled translation units and from library code (such as the standard library, operating system API etc.), and resolves unresolved symbols(i.e. links everything together). In an OS hosted environment, the linker also adds information required by the OS to load the executable.
When a program is compiled , one or more object files are created which are essentially machine code (0,1).
These files are the ingredients of your ultimate executable but in raw form.
So when linking (this is just a general idea) these files will be connected and included to make a perfect exe that can be run separately.
However if error occurs while linking it might mean that some of your
function is not defined (or there are multiple defintions) in the file
that you've included in your code!
I'm compiling my shared library, which is meant to be used in another (main) shared library of mine.
So, the whole set-up is compiled using cmake roughly as follows:
For the "main" shared library:
ADD_LIBRARY(lib_outer SHARED
....
)
TARGET_LINK_LIBRARIES(lib_outer
lib_inner
...
)
For the additional shared library:
ADD_LIBRARY(lib_inner SHARED
....
)
Now, the lib_inner uses some functions defined and implemented in the second lib_outer, Which leads to Undefined symbols for architecture x86_64 linking error.
Can i somehow tell the cmake to ignore those?
I shall i use something like 'externalwhen using those functions insidelib_inner` ?
I guess the solution is simple, but i never came across such a problem.
After some discussion with colleagues, it seems that the only good option is to compile the two libraries simultaneously as a whole. Effectively combining the CMakeLists.txt files which before were used to produce two separate, but dependent libraries.
I'm getting errors in the lua plugin that I'm writing that are symptomatic of linking in two copies of the lua runtime, as per this message:
http://lua-users.org/lists/lua-l/2008-01/msg00671.html
Quote:
Which in turn means the equality test for dummynode is failing.
This is the usual symptom, if you've linked two copies of the Lua
core into your application (causing two instances of dummynode to
appear).
A common error is to link C extension modules (shared libraries)
with the static library. The linker command line for extension
modules must not ever contain -llua or anything similar!
The Lua core symbols (lua_insert() and so on) are only to be
exported from the executable which contains the Lua core itself.
All C extension modules loaded afterwards can then access these
symbols. Under ELF systems this is what -Wl,-E is for on the
linker line. MACH-O systems don't need this since all non-static
symbols are exported.
This is exactly the error I'm seeing... what I don't know is what I should be doing instead.
I've added the lua src directory to the include path of the DLL that is the c component of my lua plugin, but when I link it I get a pile of errors like:
Creating library file: libmo.dll.a
CMakeFiles/moshared.dir/objects.a(LTools.c.obj): In function `moLTools_dump':
d:/projects/mo-pong/deps/mo/src/mo/lua/LTools.c:38: undefined reference to `lua_gettop'
d:/projects/mo-pong/deps/mo/src/mo/lua/LTools.c:47: undefined reference to `lua_type'
d:/projects/mo-pong/deps/mo/src/mo/lua/LTools.c:48: undefined reference to `lua_typename'
d:/projects/mo-pong/deps/mo/src/mo/lua/LTools.c:49: undefined reference to `lua_tolstring'
So, in summary, I have this situation:
A parent binary that is statically linked to the lua runtime.
A lua library that loads a DLL with C code in it.
The C code in the DLL needs to invoke the lua c api (eg. lua_gettop())
How do I link that? Surely the dynamic library can't 'see' the symbols in the parent binary, because the parent binary isn't loading them from a DLL, they're statically linked.
...but if I link the symbols in as part of the plugin, I get the error above.
Help? This seems like a problem that should turn up a lot (dll depends on symbols in parent binary, how do you link it?) but I can't seem to see any useful threads about it.
(before you ask, no, I dont have control over the parent binary and I cant get it to load the lua symbols from the DLL)
It's probably best to use libtool for this to make your linking easier and more portable. The executable needs to be linked with -export-dynamic to export all the symbols in it, including the Lua symbols from the static library. The module needs to then be linked with -module -shared -avoid-version and, if on Windows, additionall -no-undefined; if on MacOS, additionally -no-undefined -flat_namespace -undefined suppress -bundle; Linux and FreeBSD need no other symbols. This will leave the module with undefined symbols that are satisfied in the parent. If there are any missing, the module will fail to be dlopened by the parent.
The semantics are slightly different for each environment, so it might take some fiddling. Sometimes order of the flags matters. Again, libtool is recommended since it hides much of the inconsistency.
Say I have 2 static libs
ex1.a
ex2.a
In both libs I will define 10 same functions
When Compiling a sample test code say "test.c" , I link with both static libs ex1.a and ex2.a
In "test.c" I will call only 3 functions, then I will get the
linker error "same symbols deifned in both ex1.a and ex2.a libraries" This is Ok.
My Question here is :
1. Why this error only display 3 functions as multiple defined.. Why not it list all 10 functions
In VC8 How can I list all multiple defined symbols without actualy calling that function in test code ...
Thanks,
Thats because, linker tries to resovle a symbol name, when it compiles and links a code which has the function call. Only when the code has some function calls, linker would try to resolve it in either the test code or the libraries linked along and thats when it would find multiple definitions. If no function called, then I guess no problem.
What you experience is the optimizing part of the linker: By default it won't include code that isn't referenced. The compiler will create multiple object files with most likely unresolved dependencies (calls that couldn't be satisfied by the code included). So the linker takes all object files passed and tries to find solutions for the unresolved dependencies. If it fails, it will check the available library files. If there are multiple options with the same exact name/signature it will start complaining cause it won't be able to decide which one to pick (for identical code this won't matter but imagine different implementations using different "behind the scenes" work on memory, such as debug and release stuff).
The only (and possibly easiest way) I could think of to detect all these multiple definitions would be creating another static library project including all source files used in both static libs. When creating a library the linker will include everything called or exported - you won't need specific code calling the stuff for the linker to see/include everything as long as it's exported.
However I still don't understand what you're actually trying to accomplish as a whole. Trying to find code shared between two libraries?