libbfdの使い方のめもです。利用してるバージョンはbinutils-devel-2.31.1-13.fc29.x86_64です。
nmもどき
アドレスとセクション名、それにdebug情報があればファイル名と行数を表示。連想配列が使いたかったのでヘッダファイルだけで実装されてるuthashというライブラリを使ってます。
simple_nm.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <bfd.h> #include "uthash.h" struct section_info { char name[64]; unsigned long vma; UT_hash_handle hh; }; static struct section_info *sections_info = NULL; static void delete_all_hash_values(void) { struct section_info *pos, *tmp; HASH_ITER(hh, sections_info, pos, tmp) { HASH_DEL(sections_info, pos); free(pos); } HASH_CLEAR(hh, sections_info); } static struct section_info *add_section(bfd *abfd, const char *sname) { struct section_info *info; asection *section = bfd_get_section_by_name(abfd, sname); if (!section) return NULL; info = malloc(sizeof(*info)); if (!info) { fprintf(stderr, "malloc failed\n"); exit(-1); } memset(info, 0x0, sizeof(*info)); strcpy(info->name, sname); info->vma = section->vma; HASH_ADD_STR(sections_info, name, info); return info; } static struct section_info *get_section_info(bfd *abfd, const char *sname) { struct section_info *info = NULL; HASH_FIND_STR(sections_info, sname, info); if (info) return info; return add_section(abfd, sname); } static void show_function_info(bfd *abfd, asection *debug_info, asymbol **symbols, int idx) { const char *file, *func; unsigned int line; const char *sname = symbols[idx]->section->name; const char *name = symbols[idx]->name; struct section_info *info = get_section_info(abfd, sname); if (!info) return ; unsigned long addr = symbols[idx]->value + info->vma; if (bfd_find_nearest_line(abfd, debug_info, symbols, addr, &file, &func, &line)) printf("0x%lx %s %s %s:%u\n", addr, sname, name, file, line); else printf("0x%lx %s %s\n", addr, sname, name); } static void show(char *prog) { bfd *abfd; asection *debug_info; size_t nsyms; asymbol **symbols; int ret; bfd_init(); abfd = bfd_openr(prog, NULL); if (!abfd) { fprintf(stderr, "failed to open %s\n", prog); exit(-1); } ret = bfd_check_format(abfd, bfd_object); if (!ret) { fprintf(stderr, "invalid object format\n"); exit(-1); } debug_info = bfd_get_section_by_name(abfd, ".debug_info"); symbols = malloc(bfd_get_symtab_upper_bound(abfd)); if (!symbols) { fprintf(stderr, "malloc failed\n"); exit(-1); } nsyms = bfd_canonicalize_symtab(abfd, symbols); for (int i = 0; i < nsyms; i++) show_function_info(abfd, debug_info, symbols, i); delete_all_hash_values(); free(symbols); bfd_close(abfd); } int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "usage: %s <file>\n", argv[0]); exit(-1); } show(argv[1]); return 0; }
Makefile
objs=simple_nm.o prog=simple_nm all: $(objs) $(CC) -O0 -g $(objs) -o $(prog) -lbfd .c.o: $(CC) -O0 -I. -g -c -Wall $< test: ./$(prog) $(prog) clean: rm -f core* $(objs) $(prog)
実行例
0xffffffff829e0018 .bss panic_param 0xffffffff827347e3 .init.text unknown_bootoption /home/masami/linux-kernel/init/main.c:294 0xffffffff81002010 .text trace_initcall_finish_cb /home/masami/linux-kernel/init/main.c:841 0xffffffff810028e3 .text trace_initcall_start_cb /home/masami/linux-kernel/init/main.c:832 0xffffffff81002911 .text run_init_process /home/masami/linux-kernel/init/main.c:1011 0xffffffff8100294f .text try_to_run_init_process /home/masami/linux-kernel/init/main.c:1020 0xffffffff82734977 .init.text trace_event_define_fields_initcall_level /home/masami/linux-kernel/./include/trace/events/initcall.h:10 0xffffffff8273499f .init.text trace_event_define_fields_initcall_start /home/masami/linux-kernel/./include/trace/events/initcall.h:27 0xffffffff827349c4 .init.text trace_event_define_fields_initcall_finish /home/masami/linux-kernel/./include/trace/events/initcall.h:48 0xffffffff81002050 .text perf_trace_initcall_start /home/masami/linux-kernel/./include/trace/events/initcall.h:27 0xffffffff81002120 .text perf_trace_initcall_finish /home/masami/linux-kernel/./include/trace/events/initcall.h:48 0xffffffff81002200 .text trace_raw_output_initcall_level /home/masami/linux-kernel/./include/trace/events/initcall.h:10 0xffffffff81002250 .text trace_raw_output_initcall_start /home/masami/linux-kernel/./include/trace/events/initcall.h:27 0xffffffff81002290 .text trace_raw_output_initcall_finish /home/masami/linux-kernel/./include/trace/events/initcall.h:48 0xffffffff810022e0 .text __bpf_trace_initcall_level /home/masami/linux-kernel/./include/trace/events/initcall.h:10 0xffffffff810022f0 .text __bpf_trace_initcall_start /home/masami/linux-kernel/./include/trace/events/initcall.h:27 0xffffffff81002300 .text __bpf_trace_initcall_finish /home/masami/linux-kernel/./include/trace/events/initcall.h:48 0xffffffff82734a1c .init.text loglevel /home/masami/linux-kernel/init/main.c:230 0xffffffff81002310 .text initcall_blacklisted /home/masami/linux-kernel/init/main.c:790 0xffffffff82213020 .data blacklisted_initcalls 0xffffffff82329a50 .data descriptor.60757 0xffffffff82734a77 .init.text set_debug_rodata /home/masami/linux-kernel/ini
ptrace(2)で呼び出す関数を動的に変更
bfdで関数のアドレスを調べてptrace(2)でブレークポイントを設定して、ブレークポイントにきたらripを呼び出したい関数に変えて本来の関数は実行しない形。inline化されてるものには対応してませんけども。。
simple_break_point.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/ptrace.h> #include <sys/wait.h> #include <sys/user.h> #include <assert.h> #include <bfd.h> static void stub_function(void) { printf("stub function is called\n"); } static void say_hello(void) { printf("hello, world\n"); } void *get_function_address(char *prog_name, char *func_name) { bfd *abfd; asection *text; size_t nsyms; asymbol **symbols; void *addr = NULL; int ret; bfd_init(); abfd = bfd_openr(prog_name, NULL); assert(abfd != NULL); ret = bfd_check_format(abfd, bfd_object); assert(ret != 0); text = bfd_get_section_by_name(abfd, ".text"); symbols = malloc(bfd_get_symtab_upper_bound(abfd)); assert(symbols != NULL); nsyms = bfd_canonicalize_symtab(abfd, symbols); for (int i = 0; i < nsyms; i++) { if (!strcmp(symbols[i]->name, func_name)) { addr = (void *) (symbols[i]->value + text->vma); break; } } free(symbols); bfd_close(abfd); return addr; } int main(int argc, char **argv) { pid_t pid; long ret; void *addr; if (argc != 2) { printf("usage: %s <function name>\n", argv[0]); exit(-1); } addr = get_function_address(argv[0], argv[1]); if (!addr) { printf("function %s is not found\n", argv[1]); exit(-1); } pid = fork(); assert(pid >= 0); if (!pid) { say_hello(); } else { int status; void *break_point; long orig_text; ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL); assert(ret != -1); waitpid(pid, &status, 0); orig_text = ptrace(PTRACE_PEEKTEXT, pid, addr, NULL); break_point = (void *) (unsigned long) (orig_text | 0xcc); ret = ptrace(PTRACE_POKETEXT, pid, addr, break_point); assert(pid != -1); printf("[*] set break point at %p\n", addr); ret = ptrace(PTRACE_CONT, pid, NULL, NULL); assert(pid != -1); waitpid(pid, &status, 0); if (WIFEXITED(status)) { printf("program normally finishd\n"); exit(0); } else if (WIFSTOPPED(status)) { struct user_regs_struct regs = { 0 }; ret = ptrace(PTRACE_GETREGS, pid, 0, ®s); assert(ret != -1); regs.rip = (unsigned long long) stub_function; ret = ptrace(PTRACE_SETREGS, pid, 0, ®s); assert(ret != -1); ret = ptrace(PTRACE_CONT, pid, NULL, NULL); assert(pid != -1); } else { printf("unkown error\n"); exit(-1); } } return 0; }
Makefile
objs=simple_break_point.o prog=simple_break_point all: $(objs) $(CC) -O0 -g $(objs) -o $(prog) -lbfd .c.o: $(CC) -O0 -g -c -Wall $< test: ./$(prog) say_hello clean: rm -f core* $(objs) $(prog)
実行例
ここでブレークポイントを設定する関数のアドレスはnmで見るとこうなってます。
masami@saga:~/codes/simple_break_point$ nm ./simple_break_point | grep "say_hello\|main" U __libc_start_main@@GLIBC_2.2.5 0000000000402a69 T main 00000000004028c7 t say_hello
ブレークポイントにヒットする場合
masami@saga:~/codes/simple_break_point$ ./simple_break_point say_hello [*] set break point at 0x4028c7 stub function is called
ブレークポイントにヒットしない場合
すでに実行されたmain()に設定することでブレークポイントにヒットしない感じで。
mi@saga:~/codes/simple_break_point$ ./simple_break_point main [*] set break point at 0x402a69 hello, world program normally finishd
- 作者: 西餅
- 出版社/メーカー: 講談社
- 発売日: 2014/07/23
- メディア: Kindle版
- この商品を含むブログ (7件) を見る