C++ objcopy to make class method weak - c++

I have this header file:
weather.h
#ifndef _WEATHER_H_
#define _WEATHER_H_
#include <string>
using namespace std;
class Weather {
private:
int temp;
public:
Weather();
string announce();
};
#endif
When I compile the related source file, I want to make announce and Weather (the default constructor) weak during compile time.
To do so, the flow looks something like:
g++ -std=c++11 -g -Wall -c weather.cpp
objcopy --weaken-symbol=announce --weaken-symbol=Weather weather.o weather.o
However, when I then compile another version of weather without the weakening? I still get a duplicate symbol error.
I know --weaken exists, but this is just a sample and I do not want to blanket weaken every method in the class.

Weakening the symbols in the one file and linking all the objects together works for me. Make sure you are relinking all the objects (including all constructors).
weather1.cpp
Weather::Weather() {}
string Weather::announce()
{
return string("Bad weather");
}
weather2.cpp
Weather::Weather() {}
string Weather::announce()
{
return string("Bad weather 2");
}
build.sh
g++ weather1.cpp -c -o weather1.o
g++ weather2.cpp -c -o weather2.o
g++ test.cpp -c -o test.o
objcopy --weaken-symbol=_ZN7Weather8announceEv --weaken-symbol=_ZN7WeatherC2Ev --weaken-symbol=_ZN7WeatherC1Ev weather2.o weather2.o
g++ *.o -o test.out
Depending on whether I weaken weather1.o or weather2.o, I see different outputs from my test main function:
int main()
{
Weather w;
std::cout << w.announce() << "\n";
return 0;
}

Related

Force alle functions in shared library to be defined

I want to write a shared library and I want to get a compiler/linker error if I forgot to implement some functions.
Consider the following case:
test.h
class Test {
public:
Test();
};
test.cpp
#include "test.h"
main.cpp
#include "test.h"
int main() {
new Test();
}
If I create a library with this command gcc -c -fpic test.cpp && g++ -shared -o libtest.so -Wl,--no-undefined -Wl,--no-allow-shlib-undefined test.o there is no error message, but the library is broken. Is there a way to force the creation of a not broken library?
Edit: adding additional flag, but doesn't change result
These codes have been modified:
test.h :
class Test {
public:
Test();
};
test.cpp :
#include "test.h"
Test::Test(){} // you must implement the constructor
You must have to implement the constructor, and if not, you get an error "undefined reference to `Test::Test()'".
main.cpp :
#include <iostream>
#include "test.h"
using namespace std;
int main(void)
{
Test* t = new Test(); // you must define a pointer
cout << "test* was created: " << t << endl;
delete t;
t = nullptr;
return 0;
}
Now all the code is OK. Then we create a shared-library with the following command:
g++ -shared -o test.so -fPIC test.cpp
Finally, we compile the main.cpp file at the same time as referring to the test.so shared-library and get the exe output, by the command below:
g++ -g main.cpp test.so -o test.exe

External String Call Causes Segfault in 32-bit, Works in 64-bit

I am writing a program that calls an external string array from within a compiled static library.
When I compile and run the program in 64-bit, it works without issue. However, when I try to call the external array when compiling code in* 32-bit*, it give a Segmentation Fault when running main.
Here is the code:
Header declaration "hoenyB_lib.h:
#ifndef HONEYB_LIB_H_
#define HONEYB_LIB_H_
#include <string>
extern std::string honeyB_libs[];
#endif
Extern definition HoneyB_lib.cpp:
#include <string>
std::string honeyB_libs[] = { "libHoneyB.so", "libHoneyB3.so", "libHoneyB2.so", "" };
Extern use HoneyB_fcn.cpp:
deque<string> get_array()
{
deque<string> dst;
int i =0;
for(;;)
{
if(honeyB_libs[i] == "")
break;
else
{
dst.push_front(honeyB_libs[i]);
i++;
}
}
return dst;
}
The Makefile to compile this is as follows:
all:
$(CC) -c -Wall -fPIC source.cpp
$(CC) -g -c -fPIC honeyB_fcn.cpp
ar rcs libHB.a honeyB_fcn.o
g++ -g -c -fPIC honeyB_lib.cpp
g++ --whole-archive -shared -o libHoneyB.so source.o honeyB_lib.o libHB.a
g++ -L. -o main main.cpp -lHoneyB
This works without issue when main() is called. However, when I compile as 32-bit with the following:
all32:
$(CC) -m32 -c -Wall -fPIC source.cpp
$(CC) -m32 -g -c -fPIC honeyB_fcn.cpp
ar rcs libHB.a honeyB_fcn.o
g++ -m32 -g -c -fPIC honeyB_lib.cpp
g++ --whole-archive -m32 -shared -o libHoneyB.so source.o honeyB_lib.o libHB.a
g++ -m32 -L. -o main main.cpp -lHoneyB
The code give a Segmentation Fault. If I remove the call in honeyB_fct.cpp to honeyB_libs[], the code compiles and executes.
Does anybody have any idea why this fails for 32-bit, but works for 64?
Thanks in advance.
Order of initialization between different translation units is undefined. You have no guarantee that global variables in HoneyB_lib.cpp will be initialized before they are used in HoneyB_fcn.cpp. The only reason it worked for the 64-bit version is because you got lucky.
There are a couple workarounds:
Define the array in honeyB_lib.h, wrapped in an anonymous namespace to get around the ODR. Each TU that includes your header will have its own copy of the array.
Again, define the array in the header, but put it inside of a function that returns the array. The compiler should optimize it out everywhere, but if not you can make the array static in the scope of the function and return by reference (i.e. make it a singleton).
As a side note, I'd recommend a std::array instead of a raw array; this will let you do honeyB_libs.size() (or even for (auto&& lib : honeyB_libs) {...}) instead of relying on the "" sentinel value, which would clean up your get_array function a bit.
Thank you for the help. It appears that the problem had to do with the bit count of strings in 32-bit vs 64-bit. Changing honeyB_libs[] from a string array to a const char* array solved the issue.
honeyB_lib.h
extern const char* honeyB_libs[];
honeyB_lib.cpp
const char* honeyB_libs[] = { "libHoneyB.so", "libHoneyB3.so", "libHoneyB2.so", "" }
function.cpp
deque<string> get_array()
{
deque<string> dst;
string temp;
int i =0;
for(;;)
{
if(strlen(honeyB_libs[i]) == 0)
break;
else
{
temp = honeyB_libs[i];
dst.push_front(temp);
i++;
}
}
return dst;
}
Doing this allows my program to compile and run as 64-bit and 32-bit

Separate instance of static variable in static library for shared library

Consider the following setup consisting of two shared libraries which both use a static library:
static.cpp
#include "static.h"
static int a = 0;
int getA()
{
return a++;
}
static.h
#pragma once
int getA();
shareda.cpp
#include <iostream>
#include "shareda.h"
#include "static.h"
void printA()
{
std::cout << getA() << std::endl;
}
shareda.h
#pragma once
void printA();
sharedb.cpp
#include <iostream>
#include "sharedb.h"
#include "static.h"
void printB()
{
std::cout << getA() << std::endl;
}
sharedb.h
#pragma once
void printB();
main.cpp
#include "shareda.h"
#include "sharedb.h"
int main()
{
printA();
printA();
printB();
printA();
printB();
return 0;
}
I compiled and ran these files with the following commands (using Clang 3.8.0, compiled from source, and 64-bit Debian with GNU ld 2.25):
clang++ -c static.cpp -o static.o -fPIC
ar rcs libstatic.a static.o
clang++ -c shareda.cpp -o shareda.o -fPIC
clang++ -shared -o libshareda.so shareda.o libstatic.a
clang++ -c sharedb.cpp -o sharedb.o -fPIC
clang++ -shared -o libsharedb.so sharedb.o libstatic.a
clang++ -L. -lshareda -lsharedb -o main main.cpp
LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./main
To my surprise, the output was the following:
0
1
2
3
4
My expectation was this:
0
1
0
2
1
Apparently, despite the static keyword in front of a in static.cpp, only one instance of a exists. Is there a way to have two instances of a, one for each of the shared libraries?
Apparently, despite the static keyword in front of a in static.cpp, only one instance of a exists.
That is incorrect: two instances of a exist, but only one is actually used.
And that is happening because (contrary to your expectations) printB calls the first getA available to it (the one from libshareda.so, not the one from libsharedb.so). That is one major difference between UNIX shared libraries and Windows DLLs. UNIX shared libraries emulate what would have happened if your link was:
clang++ -L. -o main main.cpp shareda.o sharedb.o libstatic.a
So what can you do to "fix" this?
You could link libsharedb.so to prefer its own getA, by using -Bsymbolic.
You could hide getA inside libsharedb.so completely (as if it's a private implementation detail):
clang++ -c -fvisibility=hidden -fPIC static.cpp
ar rcs libstatic.a static.o
clang++ -shared -o libsharedb.so sharedb.o libstatic.a
You could achieve similar result using linker version script.
P.S. Your link command:
clang++ -L. -lshareda -lsharedb -o main main.cpp
is completely backwards. It should be:
clang++ -L. -o main main.cpp -lshareda -lsharedb
The order of sources/object files and libraries on command line matters, and libraries should follow object files that reference them.

C++ shared library undefined reference

i'm having something like these files:
libfoo.h
class foo
{
public:
foo() = default;
virtual ~foo();
};
libfoo.cpp
#include "libfoo.h"
foo::~foo() { /* code here */ }
test.cpp
#include <libfoo.h>
int main()
{
foo f;
}
i compile libfoo.h and libfoo.cpp into a shared library and all that is fine.
but when i then try to use the library in test.cpp i get undefined reference to the destructor ~foo().
this error however does not occur if i define the destructor directly in libfoo.h. i have this problem with all functions defined outside the class in my library so im guessing it has something to do with the compilation process (it compiles fine however)
i compile the library like this:
g++ -std=c++0x -Wall -Werror -fPIC -c -o libfoo.o libfoo.cpp
g++ -shared libfoo.o -o libfoo.so
any ideas as to what i might be doing wrong?
(all the functions that i declare inclass, like template functions works fine and causes no undefined reference)
I tried to reproduce the error, but I failed.
I created the files (with slight modifications):
// libfoo.h
struct foo { virtual ~foo(); };
// libfoo.cpp
#include "libfoo.h"
foo::~foo() {}
// test.cpp
#include "libfoo.h"
int main() { foo f; }
Built like this:
$ g++ -std=c++0x -Wall -Werror -fPIC -c -o libfoo.o libfoo.cpp
$ g++ -shared libfoo.o -o libfoo.so
$ g++ test.cpp -L. -lfoo
And ran like this:
$ env LD_LIBRARY_PATH=. ./a.out
I got no errors. Are you sure there is a problem with your code?

resolved: c++ : put normal method definition into source file while the templates method in header file

Since the definition of template must be put in header file, so I don't like it if the template class is big. so I want to make a normal class with some templated methods. Putting the defintion of templated method into header file, for others, put them into c++ source files. So here is what I am thinking.
// lambda.h
#include <iostream>
class X {
public:
std::function<bool(int)> filter;
template <class F>
void setFilter(F fn) {
filter = fn;
}
void big_function(int x);
};
// cat lambda.cpp
#include <iostream>
#include "lambda.h"
void X::big_function(int x) {
if (filter(x)) std::cout << x << std::endl;
}
// main2.cpp
#include <stdlib.h>
#include "lambda.h"
class Filter {
public:
bool operator()(int x) { return true; }
};
int main() {
X x;
x.setFilter(Filter());
x.big_function(3);
return 0;
}
// cat 2.sh
g++ -c lambda.cpp -ggdb
g++ -c main2.cpp -ggdb -std=c++11
g++ -o main2 main2.o lambda.o -ggdb
this program can compile, but got segment fault during executing (x.big_function(3));
#update
Q1: is my thinking is reasonable? is there any obvious error in my code?
Answer: Yes, it is reasonable, and no obvious error. Thanks to the first 4 comments, I did more test and works.
Q2: actually if I compile with -std=c++11, I will got segment fault. but no segment fault if I don't use std=c++11. ( I tried c++11 yesterday because I used lambda expression rather than function object for "Filter" at beginning). And it my real case, I can't discard c++11 features.
Answer: shame about my fault. fixed the issue by adding -std=c++11 for every compile unit.
zhifan$ sh -x 2.sh
+ g++ -c lambda.cpp -ggdb
+ g++ -c main2.cpp -ggdb
+ g++ -o main2 main2.o lambda.o -ggdb
zhifan$ ./main2
3
zhifan$ vim 2.sh
hifan$ sh -x 2.sh
+ g++ -c lambda.cpp -ggdb **-std=c++11**
+ g++ -c main2.cpp -ggdb -std=c++11
+ g++ -o main2 main2.o lambda.o -ggdb
zhifan$ ./main2
Segmentation fault: 11
zhifan$ g++ -v
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
Target: x86_64-apple-darwin13.4.0
Thread model: posix