forked from SUSE/libpulp
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Libpulp provides livepatching capabilities that is general enough to support many programming languages, not only C. However, writing livepatches in a language that allows more abstractions may not be as straightfoward as in C. To illustrate this we now provide the following examples in C++: 1-class: Show how C++ methods can be livepatched. 2-private_class: Show how private C++ functions can be livepatched. 3-indirect_call: Show how non-inlined private functions can be called from a livepatch to avoid code growth 4-global_var: Show how a private global variable can be accessed from a livepatch 5-queue: Show how template methods can be livepatched. Signed-off-by: Giuliano Belinassi <[email protected]>
- Loading branch information
1 parent
12a51e3
commit f1faf19
Showing
26 changed files
with
1,100 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Userspace Livepatch Examples. | ||
|
||
This folder contain livepatching examples. Once you compile the example hitting | ||
`make`, it will generate two files: | ||
|
||
1. `test` | ||
2. `a_livepatch1.so` | ||
|
||
The first file is a binary and should be run with `libpulp.so` loaded. Assuming | ||
libpulp is installed in `/usr/local/lib64/libpulp.so.0`, that is: | ||
``` | ||
$ LD_PRELOAD=/usr/local/lib64/libpulp.so.0 ./test | ||
``` | ||
|
||
The second file is the livepatch and should be applied with the `ulp` tool by | ||
running: | ||
``` | ||
$ ulp trigger a_livepatch.so | ||
``` | ||
|
||
Have fun and happy livepatching! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
CXX=g++ | ||
CXXFLAGS=-O2 -fpatchable-function-entry=16,14 -fPIC -g3 | ||
ULP=/usr/bin/ulp | ||
LDFLAGS= | ||
|
||
all: test a_livepatch1.so | ||
|
||
test: class.o | ||
$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) | ||
|
||
%.so: %.o %.dsc | ||
$(CXX) $(CXXFLAGS) -shared -o $@ $< | ||
$(ULP) packer $(word 2, $^) | ||
|
||
%.o: %.cpp | ||
$(CXX) $(CXXFLAGS) -c $^ -o $@ | ||
|
||
clean: | ||
rm -f test *.o *.so | ||
|
||
clena: clean |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Example: Live Patching a C++ ordinary class | ||
## About | ||
In this test we have two files: `class.cpp` and `a_livepatch1.cpp`. The first | ||
one contain code in C++ for a test program which print the contents of a | ||
`point` class, where the second one contains a livepatch that modifies the | ||
`Print` method so it prints content differently. | ||
|
||
## Live Patching C++ methods | ||
C++ methods can be live patched the same way as C functions. However, for | ||
libpulp to find the symbols in the original target binary, you should write | ||
the mangled name instead of the C++ original name of the method. | ||
|
||
So instead of writing `Point3D::Print`, one should write `_ZN7Point3D5PrintEv`. | ||
The original declaration of the class should be copied as well (see | ||
`a_livepatch1.cpp` and `a_livepatch1.dsc`). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
#include <iostream> | ||
|
||
class Point | ||
{ | ||
protected: | ||
int x, y; | ||
|
||
public: | ||
Point(int x, int y); | ||
|
||
int Get_X(void) const; | ||
int Get_Y(void) const; | ||
|
||
void Print_LP(void); | ||
}; | ||
|
||
class Point3D : public Point | ||
{ | ||
protected: | ||
int z; | ||
|
||
public: | ||
Point3D(int x, int y, int z); | ||
|
||
int Get_Z(void) const; | ||
|
||
void Print_LP(void); | ||
}; | ||
|
||
void Point::Print_LP(void) | ||
{ | ||
std::cout << x + 1 << ' ' << y + 1 << '\n'; | ||
} | ||
|
||
void Point3D::Print_LP(void) | ||
{ | ||
std::cout << x + 1 << ' ' << y + 1 << ' ' << z << '\n'; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
a_livepatch1.so | ||
@./test | ||
_ZN7Point3D5PrintEv:_ZN7Point3D8Print_LPEv |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
#include <iostream> | ||
#include <unistd.h> | ||
|
||
class Point | ||
{ | ||
protected: | ||
int x, y; | ||
|
||
public: | ||
Point(int x, int y); | ||
|
||
int Get_X(void) const; | ||
int Get_Y(void) const; | ||
|
||
void Print(void); | ||
}; | ||
|
||
class Point3D : public Point | ||
{ | ||
protected: | ||
int z; | ||
|
||
public: | ||
Point3D(int x, int y, int z); | ||
|
||
int Get_Z(void) const; | ||
|
||
void Print(void); | ||
}; | ||
|
||
int Point::Get_X(void) const | ||
{ | ||
return x; | ||
} | ||
|
||
int Point::Get_Y(void) const | ||
{ | ||
return y; | ||
} | ||
|
||
// Will be livepatched | ||
void Point::Print(void) | ||
{ | ||
std::cout << Get_X() << ' ' << Get_Y() << '\n'; | ||
} | ||
|
||
Point::Point(int x, int y) | ||
{ | ||
this->x = x; | ||
this->y = y; | ||
} | ||
|
||
Point3D::Point3D(int x, int y, int z) : Point(x, y) | ||
{ | ||
this->z = z; | ||
} | ||
|
||
int Point3D::Get_Z(void) const | ||
{ | ||
return z; | ||
} | ||
|
||
// Will be livepatched | ||
void Point3D::Print(void) | ||
{ | ||
std::cout << Get_X() << ' ' << Get_Y() << ' ' << Get_Z() << '\n'; | ||
} | ||
|
||
int main(void) | ||
{ | ||
Point3D p(3, 4, 5); | ||
while (1) { | ||
p.Print(); | ||
sleep(1); | ||
} | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
CXX=g++ | ||
CXXFLAGS=-O2 -fpatchable-function-entry=16,14 -fdump-ipa-clones -fPIC -g3 | ||
ULP=/usr/bin/ulp | ||
LDFLAGS= | ||
|
||
all: test a_livepatch1.so | ||
|
||
test: class.o | ||
$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) | ||
|
||
%.so: %.o %.dsc | ||
$(CXX) $(CXXFLAGS) -shared -o $@ $< | ||
$(ULP) packer $(word 2, $^) | ||
|
||
%.o: %.cpp | ||
$(CXX) $(CXXFLAGS) -c $^ -o $@ | ||
|
||
clean: | ||
rm -f test *.o *.so *.ipa-clones | ||
|
||
clena: clean |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Example: Live Patching a C++ ordinary class | ||
## About | ||
This example illustrates how we can patch static (private), non-inlined functions. | ||
Unfortunatelly, private functions are not publically exposed into the library you want to livepatch. Hence, those functions will not be present in the `.dynsym` table. Furthermore, the program can have multiple different functions with the same signature. | ||
|
||
If the target program or library did not have its debug symbols stripped, it is possible to find the private symbols in the `.symtab` section. In this case we can use `readelf` to find the correct address of the function we want to patch. If not, we have to do the same analysis with the original binary `debuginfo`, hopefully distributed by your distribution. | ||
|
||
## The example | ||
|
||
In this example we have two files: `class.cpp` and `a_livepatch1.cpp`. The first file contains code to calculate the distance to the origin of the 3D point in question. Notice that the method `Norm` calls a private function `norm`, which is not inlined. In the second file there is a livepatch function which will replace `norm` with `norm3_lp`, which will compute the 3-norm instead of the 2-norm. | ||
|
||
## Live Patching | ||
|
||
This example is crafted so that the function `norm` is not inlined into `Norm`, as can be seen by the `noinline` declaration of it. If you remove this keyword then the function will be inlined and the only way to do the patching would be to livepatch all callers of `norm`. In this case it would be only `Norm`, but in other scenarios there could be thousands of occurences. | ||
|
||
### Discovering if function was inlined. | ||
|
||
We compile the example with `-fdump-ipa-clones`, which dumps Interprocedural Analysis decisions by GCC -- one being inlining decisions. | ||
|
||
For `class.cpp`, a file is generated named `class.cpp.000i.ipa-clones` once you build the example. If you look for references of `norm` in this file you will see: | ||
``` | ||
Callgraph clone;_ZL3dotPdS_;1394;class.cpp;8;15;_ZL4normPd;1395;class.cpp;13;24;inlining to | ||
``` | ||
which translates that the function `dot` was *inlined into* `norm`. This is not a problem for us. There would only be a problem if `norm` was inlined somewhere, which is not the case. | ||
|
||
### Retrieving the offset of target private function | ||
|
||
A way to retrieve the target offset function is to use `readelf` to show the offset of each symbol in the target application/library. This piped with `grep` is enough to retrieve the symbol address if the function name is unique. If the function name is not unique then you should check if the function you desire to patch is actually at that address by looking into the assembly dump. If not, then you should proceed to the next occurence until you find it. | ||
|
||
To list all offset of symbols matching `norm` in the `test` binary, do: | ||
``` | ||
$ readelf -sW | grep 'norm' | ||
``` | ||
output: | ||
``` | ||
6: 000000000040122e 41 FUNC LOCAL DEFAULT 15 _ZL4normPd | ||
``` | ||
This means that the function `_ZL4nromPd` (mangled name for `double norm(double v[3]);`) is available in offset `40122e` hexadecimal. it is also a function (`FUNC`) and it has `LOCAL` visibility. | ||
|
||
### Description file with function offsets | ||
|
||
The offset of the target function can be specified by appending an extra `:<offset>` to the livepatch symbol replacement specification: | ||
``` | ||
_ZL4normPd:_Z8norm3_lpPd:40122e | ||
``` | ||
This should be enough so that libpulp patches the correct function at that address. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
#include <math.h> | ||
|
||
typedef double vec3_t[3]; | ||
|
||
double norm3_lp(vec3_t v) | ||
{ | ||
return cbrt(v[0]*v[0]*v[0] + v[1]*v[1]*v[1] + v[2]*v[2]*v[2]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
a_livepatch1.so | ||
@./test | ||
_ZL4normPd:_Z8norm3_lpPd:40122e |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
#include <iostream> | ||
#include <unistd.h> | ||
#include <math.h> | ||
|
||
typedef double vec3_t[3]; | ||
#define noinline __attribute__((noinline)) | ||
|
||
static double dot(vec3_t u, vec3_t v) | ||
{ | ||
return u[0]*v[0] + u[1]*v[1] + u[2]*v[2]; | ||
} | ||
|
||
// Will be livepatched. | ||
static noinline double norm(vec3_t v) | ||
{ | ||
return sqrt(dot(v, v)); | ||
} | ||
|
||
class Point | ||
{ | ||
protected: | ||
double x, y; | ||
|
||
public: | ||
Point(double x, double y); | ||
|
||
double Get_X(void) const; | ||
double Get_Y(void) const; | ||
}; | ||
|
||
class Point3D : public Point | ||
{ | ||
protected: | ||
double z; | ||
|
||
public: | ||
Point3D(double x, double y, double z); | ||
double Norm(void); | ||
|
||
double Get_Z(void) const; | ||
|
||
}; | ||
|
||
double Point::Get_X(void) const | ||
{ | ||
return x; | ||
} | ||
|
||
double Point::Get_Y(void) const | ||
{ | ||
return y; | ||
} | ||
|
||
Point::Point(double x, double y) | ||
{ | ||
this->x = x; | ||
this->y = y; | ||
} | ||
|
||
Point3D::Point3D(double x, double y, double z) : Point(x, y) | ||
{ | ||
this->z = z; | ||
} | ||
|
||
double Point3D::Get_Z(void) const | ||
{ | ||
return z; | ||
} | ||
|
||
double Point3D::Norm(void) | ||
{ | ||
vec3_t v = {x, y, z}; | ||
return norm(v); | ||
} | ||
|
||
extern "C" double some_function(vec3_t v) | ||
{ | ||
return norm(v) * norm(v); | ||
} | ||
|
||
int main(void) | ||
{ | ||
Point3D p(3, 4, 5); | ||
while (1) { | ||
std::cout << p.Norm() << '\n'; | ||
sleep(1); | ||
} | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
CXX=g++ | ||
CXXFLAGS=-O2 -fpatchable-function-entry=16,14 -fdump-ipa-clones -fPIC -g3 | ||
ULP=/usr/bin/ulp | ||
LDFLAGS= | ||
|
||
all: test a_livepatch1.so | ||
|
||
test: class.o | ||
$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) | ||
|
||
%.so: %.o %.dsc | ||
$(CXX) $(CXXFLAGS) -shared -o $@ $< | ||
$(ULP) packer $(word 2, $^) | ||
|
||
%.o: %.cpp | ||
$(CXX) $(CXXFLAGS) -c $^ -o $@ | ||
|
||
clean: | ||
rm -f test *.o *.so *.ipa-clones | ||
|
||
clena: clean |
Oops, something went wrong.