Cannot link to shared library - c++

I'm trying to compile a minimal shared library and link to it and have been failing for two hours now. Here is ALL the code:
// rect.h
class Rect{
private:
int width_, height_;
public:
Rect(int width, int height);
int width();
int height();
};
// rect.cpp
#include "rect.h"
Rect::Rect(int width, int height)
:width_(width), height_(height){}
int Rect::width() { return width_; }
int Rect::height() { return height_; }
// client.cpp
#include "rect.h"
#include <iostream>
int main() {
std::cout << Rect(1,2).width();
return 0;
}
And this is how I try to compile it:
$ g++ -shared -o librect.so rect.cpp
$ g++ -L. -lrect -Wl,-rpath,'.' client.cpp -o client
/tmp/cc0Xe7ms.o: In function `main':
client.cpp:(.text+0x1a): undefined reference to `Rect::Rect(int, int)'
client.cpp:(.text+0x26): undefined reference to `Rect::width()'
collect2: error: ld returned 1 exit status
The library compiles just fine and the Rect class is properly exported from what I can tell:
$ nm -D librect.so
0000000000201028 B __bss_start
w __cxa_finalize
0000000000201028 D _edata
0000000000201030 B _end
0000000000000738 T _fini
w __gmon_start__
00000000000005b8 T _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w _Jv_RegisterClasses
0000000000000714 T _ZN4Rect5widthEv
0000000000000724 T _ZN4Rect6heightEv
00000000000006f0 T _ZN4RectC1Eii
00000000000006f0 T _ZN4RectC2Eii
The strangest thing is that this compiles fine and works on my work computer (Kubuntu 12.10 64bit) but fails to link properly on any other machine I've tried (4 in total, all 64-bit Ubuntu/Kubuntu 12.04 and 12.10)
I tried everything I could think of. Passing the verbose option to the linker shows that the librect.so is indeed found successfully.
Does anybody have a clue what the problem might be?

The libraries have to go after the local translation units:
g++ -L. -Wl,-rpath,'.' client.cpp -o client -lrect
# ^^^^^^
It has to do with how unresolved symbols are looked up by the linker; search the internet for a plethora of information on this if you're curious.

Related

ldd does not show the dependency which make then needs

I have the program which needs v2xmvtest.so. When i try to build it via make i get undefined reference to *
Seems like that function from libssl1.0. (If i install it, it's built fine)
But i do not see the place where these function are used. More than that, when i try ldd v2xmvtest.so it does show only libvssl1.1 dependency.
Summary:
Is there a way to find out where those finctions from libvssl1.0 are used in the program ? (i have source code of the v2xmvtest.so and try to search, but there no any of these)
I need a description why ldd does not show me libssl1.0 dependency, but during linkning it's needed
Thank you!
Libraries are not required to fully define used symbols. For instance you can have lib.cpp:
int foo();
int bar(int x)
{
return x + foo();
}
and it compiles even foo is actually not defined yet:
g++ lib.cpp -shared -o lib.so
Then you can have main.cpp:
#include <iostream>
int bar(int);
int foo()
{
return 10;
}
int main()
{
std::cout << bar(42) << std::endl;
}
which defines foo and compiles successfully with lib.so:
g++ main.cpp lib.so -o main
The foo function in lib.so is expected just to be provided when the application is linked and is not specified where exactly from, even not necessary from a library.
You can check for undefined symbols in a library using nm tool:
nm -C -u lib.so
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U foo()
w __cxa_finalize##GLIBC_2.17
w __gmon_start__
And finally you can force gcc to ensure no undefined symbols are used by the object files by compiling with -Wl,-z,defs linker flag:
g++ lib.cpp -shared -Wl,-z,defs -o lib.so
...
lib.cpp:(.text+0x24): undefined reference to `foo()'

Undefined symbol error in a shared library loaded at runtime

(I know various iterations of this question are asked here routinely but I didn't find anything that helped)
I'm trying to write a simple dynamic library and load it at runtime.
The library files:
mul.h
int mul(int a, int b);
mul.cpp
#include "mul.h"
int mul(int a, int b) {
return a * b;
}
Then I have this Makefile:
mul.cpp: mul.h
mul_lib: mul.cpp
g++ -Wall -fPIC -shared $< -o bin/libmul.so
bin/test.o: mul_lib
g++ test.cpp -ldl -o bin/test
So I have created an .so file, and I checked using nm that there's a symbol for mul... or rather what I assume is the symbol for mul:
>nm -D bin/libmul.so
w __cxa_finalize
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
00000000000010f9 T _Z3mulii
In test.cpp, I'm trying to load this library with dlopen:
typedef int (*mul_func)(int a, int b);
void* mul_lib = dlopen("bin/libmul.so", RTLD_NOW);
if (!mul_lib) {
/* fail to load the library */
fprintf(stderr, "Error: %s\n", dlerror()); // we don't exit here, library loaded
return 2;
}
mul_func* func = (mul_func*)dlsym(mul_lib, "mul");
if (!func) {
fprintf(stderr, "Error: %s\n", dlerror()); // we exit here
return 2;
}
(*func)(2, 4);
dlclose(mul_lib);
But I'm getting bin/libmul.so: undefined symbol: mul
I don't understand why. The symbol is present in the object file, it seems, and it's loaded correctly (I assume), so why is the symbol absent?
Turns out declaring a function like this works:
extern "C" int mul(int a, int b);
For more information on why, see this comment.

Are people able to hijack compiled libraries with their own implementations?

I have some legacy code that passes around a pointer to an internal data structure that the library uses to manipulate and check state variables. Thinking about refactoring this code, I became curious if creating a bunch of classes that are meant to only be used internally in my library would cause security concerns. Could others link in their own implementations of these methods? Say I have a Config class that reads an encrypted license and sets settings correctly. Would it be possible to manipulate this class somehow?
Here's some code I wrote to try initial feasibility
shared.cpp
#include "shared.h"
Config::Config() : max_clients()
{
}
Config::~Config()
{
}
int Config::getMaxClients() const
{
return max_clients;
}
void Config::setMaxClients(int num)
{
max_clients = num;
}
I compiled this into a shared library with
g++ -c shared.cpp -fpic
g++ -shared -o libshared.so shared.o
Once compiled we can see what other people can see about our Config class in our library.
$ nm -C libshared.so
0000000000201030 B __bss_start
0000000000201030 b completed.7696
w __cxa_finalize
0000000000000780 t deregister_tm_clones
0000000000000810 t __do_global_dtors_aux
0000000000200e20 t __do_global_dtors_aux_fini_array_entry
0000000000201028 d __dso_handle
0000000000200e58 d _DYNAMIC
0000000000201030 D _edata
0000000000201038 B _end
00000000000008f8 T _fini
0000000000000850 t frame_dummy
0000000000200e18 t __frame_dummy_init_array_entry
0000000000000a50 r __FRAME_END__
0000000000201000 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
0000000000000910 r __GNU_EH_FRAME_HDR
0000000000000720 T _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
00000000000007c0 t register_tm_clones
0000000000201030 d __TMC_END__
U operator delete(void*, unsigned long)
00000000000008de T Config::setMaxClients(int)
000000000000085a T Config::Config()
000000000000085a T Config::Config()
00000000000008a0 T Config::~Config()
0000000000000882 T Config::~Config()
0000000000000882 T Config::~Config()
00000000000008cc T Config::getMaxClients() const
0000000000200e48 V typeinfo for Config
0000000000000908 V typeinfo name for Config
0000000000200e28 V vtable for Config
U vtable for __cxxabiv1::__class_type_info
There's probably a lot more analysis I don't know about here.
mal.cpp
With this information we could guess a bit about return types on setMaxClients and create our own implementation.
#include <iostream>
class Config {
public:
Config() {}
void setMaxClients(int num);
};
void Config::setMaxClients(int num)
{
std::cout << "do something bad" << std::endl;
}
I compiled this file with
g++ -c mal.cpp -fpic
g++ -shared -o libmal.so mal.o
main.cpp
class Config {
public:
Config();
virtual ~Config();
int getMaxClients();
void setMaxClients(int num);
};
int main()
{
Config config;
config.setMaxClients(4);
return 0;
}
Finally, I compiled main.cpp with linking my mal library first then shared like so
g++ main.cpp -lmal -lshared
This produced a binary which when run I get
$ LD_LIBRARY_PATH=. ./a.out
do something bad
*** stack smashing detected ***: <unknown> terminated
[1] 7842 abort (core dumped) LD_LIBRARY_PATH=. ./a.out
Yes it crashed but I'm not more experienced to know where to go next. Is this not a security concern because you wouldn't actually be able to change any of the classes fields since you don't know the member variables? Or is there a concern here that requires best practices in C++?
Your replacement class should ideally be the same size as the original class. The config in main.cpp has a virtual dtor, whereas the one in mal.cpp does not. This will cause the latter to be 8bytes larger (assuming 64bit OS), and will invariably lead to a problem on destruction of the class.
If this is a real concern, a simple next step is to perform a checksum on the dll, and validate that at runtime (quit if checksum does not match). It wont help a great deal for someone who really cares though. If you know enough about asm, you can usually patch a binary exe with some no-ops until your check returns true.

How to use the __attribute__((visibility("default")))?

Reading Visibility in the GNU wiki, it is clear.
Taking this example from C++ Tutorials
// classes example
#include <iostream>
using namespace std;
class Rectangle {
int width, height;
public:
void set_values (int,int);
int area() {return width*height;}
};
void Rectangle::set_values (int x, int y) {
width = x;
height = y;
}
Is it possible to make area() public and set_values(int,int) local as shown in the first link without altering the code?
I wrote my makefile to get the .so
someproj.so : someproj.cpp
g++ --std=c++11 -O3 -fPIC -shared someproj.cpp -o someproj.so
Modified to make all symbols hidden by adding -fvisibility=hidden
someproj.so : someproj.cpp
g++ --std=c++11 -O3 -fvisibility=hidden -fPIC -shared someproj.cpp -o someproj.so
Is it possible to customized which functions are exposed by modifying the compilation command above?
Currently using 4.7.2 version of gcc
Is it possible to customize which functions are exposed by modifying the compilation command above?
No. Compilation option -fvisibility=[default|internal|hidden|protected]
(and note it is not a linkage option) makes the compiler attribute the specified dynamic visibility type to all global symbols
generated in the compilation unit except those that are specifically excluded by having a countervailing __attribute__((visibility(....)))
applied in the source code. Which makes the answer to your other question:
Is it possible to make area() public and set_values(int,int) local as shown in the first link without altering the code?
also No.
How would you change the source code to make Rectangle::area() dynamically
visible while all other global symbols are hidden for dynamic linkage by -fvisibility=hidden?
Here is a walk-through:
Let's start with:
rectangle.cpp (1)
class Rectangle {
int width, height;
public:
void set_values (int,int);
int area() {return width*height;}
};
void Rectangle::set_values (int x, int y) {
width = x;
height = y;
}
and simply compile it to a PIC rectangle.o so:
$ g++ -Wall -c -fPIC rectangle.cpp
Then check the global symbol table:
$ nm -C rectangle.o
0000000000000000 T Rectangle::set_values(int, int)
Note that Rectangle::area isn't there. It's not available for
linkage at all, so the question of its dynamic visibility just does not arise.
That is because it is defined inline in the class definition and never called
in the compilation unit, so gcc need not even compile its definition. It vanishes
from the object file.
Rectangle::set_values, on the other hand, is not defined inline, so the compiler
emits a global symbol and definition.
To make Rectangle::area eligible for some visibility type, we first need to make
it a global symbol by not defining it inline:
rectangle.cpp (2)
class Rectangle {
int width, height;
public:
void set_values (int,int);
int area();
};
int Rectangle::area() {return width*height;}
void Rectangle::set_values (int x, int y) {
width = x;
height = y;
}
Recompile and again check the global symbol table:
$ g++ -Wall -c -fPIC rectangle.cpp
$ nm -C rectangle.o
000000000000001a T Rectangle::set_values(int, int)
0000000000000000 T Rectangle::area()
Good. Now a global definition of Rectangle::area appears.
Next let's make a shared library librectangle.so from rectangle.o:
$ g++ -o librectangle.so --shared rectangle.o
Here are the Rectangle::* symbols in its global symbol table:
$ nm -C librectangle.so | grep 'Rectangle::'
00000000000005d4 T Rectangle::set_values(int, int)
00000000000005ba T Rectangle::area()
And here are the Rectangle::* symbols in its dynamic symbol table:
$ nm -CD librectangle.so | grep 'Rectangle::'
00000000000005d4 T Rectangle::set_values(int, int)
00000000000005ba T Rectangle::area()
They're the same.
Now let's hide those symbols for dynamic linkage. We need to recompile rectangle.cpp
then relink the shared library:
$ g++ -Wall -c -fPIC -fvisibility=hidden rectangle.cpp
$ g++ -o librectangle.so --shared rectangle.o
Here again are the Rectangle::* symbols now in the global symbol table:
$ nm -C librectangle.so | grep 'Rectangle::'
0000000000000574 t Rectangle::set_values(int, int)
000000000000055a t Rectangle::area()
They're the same as before.
And here are the Rectangle::* symbols now in the dynamic symbol table:
$ nm -CD librectangle.so | grep 'Rectangle::'; echo Done
Done
Now there are none, thanks to -fvisibility=hidden.
Finally, let's make just Rectangle::area dynamically visible, keeping all
the other global symbols dynamically hidden. We need to change the source code
again:
rectangle.cpp (3)
class Rectangle {
int width, height;
public:
void set_values (int,int);
__attribute__((visibility("default"))) int area();
};
int Rectangle::area() {return width*height;}
void Rectangle::set_values (int x, int y) {
width = x;
height = y;
}
Then recompile and relink:
$ g++ -Wall -c -fPIC -fvisibility=hidden rectangle.cpp
$ g++ -o librectangle.so --shared rectangle.o
The global symbol table still shows:
$ nm -C librectangle.so | grep 'Rectangle::'
00000000000005a4 t Rectangle::set_values(int, int)
000000000000058a T Rectangle::area()
And the dynamic symbol table only shows:
$ nm -CD librectangle.so | grep 'Rectangle::'
000000000000058a T Rectangle::area()
Rectangle::area is now the only symbol that the shared library exposes for
dynamic linkage.
And before you go...
One thing more about:
Is it possible to make area() public and set_values(int,int) local as shown in the first link without altering the code?
Making a symbol hidden for dynamic linkage doesn't make it local. Dynamic visibility (default|internal|hidden|protected)
is an attribute of global symbols only. For linkage purposes, local symbols don't exist. The only ways to
make a symbol local that would otherwise be global are:-
In C or C++ source, qualify its definition with the static keyword
In C++ source, enclose its definition in an anonymous namespace
Then the symbol does not appear in the global, or dynamic, symbol tables.

g++ says references are undefined even though `nm` lists the symbol definitions in my object files

When the Makefile for a project I'm working on hits this line:
g++ -g build_complex.o simplex.o simplex_src.o split.o permutation_src.o permutation_parity.o tropmod.o main.o -L/usr/lib/x86_64-linux-gnu -L/usr/local/lib -L/usr/lib/sagemath/local/lib/ -ligraph -lm -o cpptest
I get the following error:
main.o: In function `main':
/home/xander/Desktop/tropicalmoduli/main.cpp:5: undefined reference to `(anonymous namespace)::ConfigSpace::ConfigSpace(int, int)'
/home/xander/Desktop/tropicalmoduli/main.cpp:9: undefined reference to `(anonymous namespace)::PermWrapper::PermWrapper(permutation*)'
/home/xander/Desktop/tropicalmoduli/main.cpp:11: undefined reference to `(anonymous namespace)::ConfigSpace::getTraceOfPerm((anonymous namespace)::PermWrapper)'
collect2: error: ld returned 1 exit status
Those are functions that are defined in the file tropmod.cpp, which was compiled to tropmod.o. When I run nm tropmod.o spits out:
U __cxa_atexit
U __dso_handle
U free
U _GLOBAL_OFFSET_TABLE_
000000000000037c t _GLOBAL__sub_I_tropmod.cpp
U igraph_destroy
U malloc
U _Z10split_freeP5split
U _Z12simplex_freeP7simplex
U _Z13simplex_traceP7simplexP11permutation
U _Z14get_1_skeletoniiPPP5split
U _Z16get_mid_skeletoniiiPiPP5splitP8igraph_s
0000000000000333 t _Z41__static_initialization_and_destruction_0ii
00000000000001f0 t _ZN12_GLOBAL__N_111ConfigSpace14getTraceOfPermENS_11PermWrapperE
000000000000010c t _ZN12_GLOBAL__N_111ConfigSpace7destroyEv
0000000000000000 t _ZN12_GLOBAL__N_111ConfigSpaceC2Eii
0000000000000322 t _ZN12_GLOBAL__N_111PermWrapper14getPermutationEv
0000000000000304 t _ZN12_GLOBAL__N_111PermWrapperC2EP11permutation
U _ZNSt8ios_base4InitC1Ev
U _ZNSt8ios_base4InitD1Ev
0000000000000000 b _ZStL8__ioinit
All the "undefined references" are listed here, with a "t", which should mean that they're defined in this object file. Both tropmod.o and main.o were compiled from .cpp files which include the header file tropmod.hpp, where the declarations can be found:
#include <iostream>
#include <string>
#include "build_complex.h"
#ifdef tmboost
#include <boost/python/list.hpp>
#include <boost/python/extract.hpp>
typedef boost::python::list bplist;
#endif
namespace {
// allows a permutation to be converted from python::boost::list
// to permutation* just once, and then reused
class PermWrapper {
permutation *perm;
public:
#ifdef tmboost
PermWrapper(bplist);
#endif
PermWrapper(permutation*);
permutation *getPermutation();
};
class ConfigSpace {
int n,d;
int *num_cells;
int num_facets;
split **all_splits;
simplex ***skels;
public:
ConfigSpace(int n, int d);
void destroy();
int getTraceOfPerm(PermWrapper perm);
};
}
For completeness, here is main.cpp:
#include "tropmod.hpp"
int main() {
ConfigSpace cs = ConfigSpace(2, 2);
int p_d[4] = {1, 0, 3, 2};
permutation *p = perm_alloc_data(4, p_d);
PermWrapper pw = PermWrapper(p);
printf("trace: %i\n", cs.getTraceOfPerm(pw));
}
Both cpp files were compiled with g++ -g -fPIC -c (and without the tmboost flag).
I looked at several other questions on here concerning this kind of linking error, but none of the answers I read are obviously applicable here. It seems like all the pieces are there, does anyone have an idea why the linker can't put it together?
In C++, anonymous namespaces are local to their translation unit, and they cannot be referenced from other translation units.
If you read nm's documentation, it clearly states that lowercase letters specify local symbol types.
If these were global symbols, they would be marked as "T", and not "t".