If there is a problem when the process is running, we stop the process and observe the debugging log, and then update the code to try to reproduce the problem.
But when you stop this process, it means that the current environment may change.
So in this case livepatch can be used
When a program be loaded to memory, it will be loaded by elf format into the VMA. Loader will also link the dynamci lib to support it.
Use /proc/<PID>/maps to get the map
$ cat /proc/$(pidof testlive)/maps
55869ea92000-55869ea93000 r--p 00000000 00:2d 281 /home/roy/src-code/github/roy/00.project/livepatch/testlive
55869ea93000-55869ea94000 r-xp 00001000 00:2d 281 /home/roy/src-code/github/roy/00.project/livepatch/testlive
55869ea94000-55869ea95000 r--p 00002000 00:2d 281 /home/roy/src-code/github/roy/00.project/livepatch/testlive
55869ea95000-55869ea96000 r--p 00002000 00:2d 281 /home/roy/src-code/github/roy/00.project/livepatch/testlive
55869ea96000-55869ea97000 rw-p 00003000 00:2d 281 /home/roy/src-code/github/roy/00.project/livepatch/testlive
55869f60d000-55869f62e000 rw-p 00000000 00:00 0 [heap]
7f5a33ea2000-7f5a33ea5000 rw-p 00000000 00:00 0
7f5a33ea5000-7f5a33ecd000 r--p 00000000 08:01 3862 /usr/lib/x86_64-linux-gnu/libc.so.6
7f5a33ecd000-7f5a34062000 r-xp 00028000 08:01 3862 /usr/lib/x86_64-linux-gnu/libc.so.6
7f5a34062000-7f5a340ba000 r--p 001bd000 08:01 3862 /usr/lib/x86_64-linux-gnu/libc.so.6
7f5a340ba000-7f5a340bb000 ---p 00215000 08:01 3862 /usr/lib/x86_64-linux-gnu/libc.so.6
7f5a340bb000-7f5a340bf000 r--p 00215000 08:01 3862 /usr/lib/x86_64-linux-gnu/libc.so.6
7f5a340bf000-7f5a340c1000 rw-p 00219000 08:01 3862 /usr/lib/x86_64-linux-gnu/libc.so.6
7f5a340c1000-7f5a340ce000 rw-p 00000000 00:00 0
7f5a340d3000-7f5a340d8000 rwxp 00000000 00:00 0
7f5a340d8000-7f5a340da000 rw-p 00000000 00:00 0
7f5a340da000-7f5a340dc000 r--p 00000000 08:01 3859 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7f5a340dc000-7f5a34106000 r-xp 00002000 08:01 3859 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7f5a34106000-7f5a34111000 r--p 0002c000 08:01 3859 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7f5a34112000-7f5a34114000 r--p 00037000 08:01 3859 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7f5a34114000-7f5a34116000 rw-p 00039000 08:01 3859 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7ffff3c6f000-7ffff3c90000 rw-p 00000000 00:00 0 [stack]
7ffff3d41000-7ffff3d45000 r--p 00000000 00:00 0 [vvar]
7ffff3d45000-7ffff3d47000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]
We can execute a process to hack it and load the fix function into the target process
Then use modify the target process. Let call bug_func be replaced by fix_func
As we know, the gdb can hack the runtime program. We can use the gdb to set paramater value, jump to the sepcific instruction. So how does the gdb make it? The answer is ptrace
So we can use the ptrace to control how the process executes. Like PTRACE_ATTACH to attach the process, PTRACE_PEEKDATA/PTRACE_POKEDATA to get and set data into the target process VMA.
Now, We have the method to control it, and we also need a library to help to get elf section. Bfd library is a good tool for us. Using this, we get the symbol table, the function address, and the most importart is getting .got/.got.plt/.rela.plt these section to help the fix_func to redirect the instruction in .plt section.
Q: Why do we need to redirect it ?
A: When we build the so file, this file only has the patch function, so the linker does not know "actual" address of the shared library in the target process. So the .plt section will be filled with "0". If we do not redirect it, the shared library be executed with the "0" insturction, this will make system fail.
the target program is
#include <stdio.h>
#include <time.h>
#include <limits.h>
#include <unistd.h>
#include <sys/mman.h>
int func_J(int a, int b)
{
char *str_P = "I'm wrong function...QQ";
printf("in %s\n", str_P);
sleep(3);
printf("exit %s\n", str_P);
return (a+b);
}
int main ()
{
while(1) {
int a = func_J(1, 2);
sleep(3);
printf("In main get calculating result: %d\n", a);
if (12 == a) {
printf("=== You fix it !!! (%d)\n ===", a);
break;
} else {
printf("=== No, this is not the right answer....(%d) ===\n", a);
}
}
}
the patch file is
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stddef.h>
#include <dlfcn.h>
int func1(int a, int b) {
printf("in fixup\n");
sleep(2);
printf("exit fixup\n");
return (a*10+b);
}
How to build it:
You can use readelf βrelocs to get the relocation section
$ readelf --relocs libfoo.a
Relocation section '.rela.dyn' at offset 0x488 contains 7 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000003e10 000000000008 R_X86_64_RELATIVE 1130
000000003e18 000000000008 R_X86_64_RELATIVE 10f0
000000004028 000000000008 R_X86_64_RELATIVE 4028
000000003fe0 000100000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_deregisterTM[...] + 0
000000003fe8 000300000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000003ff0 000400000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_registerTMCl[...] + 0
000000003ff8 000600000006 R_X86_64_GLOB_DAT 0000000000000000 __cxa_finalize@GLIBC_2.2.5 + 0
Relocation section '.rela.plt' at offset 0x530 contains 2 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000004018 000200000007 R_X86_64_JUMP_SLO 0000000000000000 puts@GLIBC_2.2.5 + 0
000000004020 000500000007 R_X86_64_JUMP_SLO 0000000000000000 sleep@GLIBC_2.2.5 +
for this example we need to relocate printf and sleep function to actual address.