binutils がクロスコンパイルできない→とりあえずバックトレースにコードの行数を出せるようになりました
先日組んだバックトレースのコードを拡張して、ソースコードの行数を表示するために libbfd
を組み込もうとした話。
#!/bin/bash export PATH=/path/to/buildroot/output/host/usr/bin:$PATH ./configure \ --build=$MACHTYPE \ --host=arm-buildroot-linux-uclibcgnueabi \ --target=arm-buildroot-linux-uclibcgnueabi \ --with-build-sysroot=/path/to/buildroot/output/target/lib \ make
上のようなスクリプトで make
すると、 gas
で mbstowcs
がない!というエラーになる。
binutils-2.25/gas/read.c:1642: undefined reference to `mbstowcs'
とはいえ、ほしいのは libbfd.a
と libiberty.a
、 libintl.a
だったので今回は解決を見送った。(必要なオブジェクトはコンパイル済みだったので)
最終的には、以下のようなコードでバックトレースが出力できるようになりました。
# makefile for backtrace. TARGET := backtrace ARCH := arm ifeq ($(ARCH),arm) CROSS_COMPILE := /path/to/buildroot/output/host/usr/bin/arm-buildroot-linux-uclibcgnueabi- else CROSS_COMPILE := endif CC := $(CROSS_COMPILE)gcc CFLAGS := -g -O0 -I. LDFLAGS := -rdynamic -ldl ifeq ($(ARCH),arm) CFLAGS += -DHAVE_CONFIG CFLAGS += -I/path/to/binutils-2.25/bfd CFLAGS += -I/path/to/binutils-2.25/include LDFLAGS += -mapcs-frame LDFLAGS += /path/to/binutils-2.25/bfd/libbfd.a LDFLAGS += /path/to/binutils-2.25/libiberty/libiberty.a LDFLAGS += /path/to/binutils-2.25/intl/libintl.a else LDFLAGS += -fno-omit-frame-pointer -lbfd endif all: $(CC) -std=c99 $(CFLAGS) -o $(TARGET) $(TARGET).c log.c $(LDFLAGS) clean: rm -rf $(TARGET)
/** @file backtrace.c * @brief porting backtrace. * @sa http://stackoverflow.com/questions/2536021/any-porting-available-of-stacktrace-for-uclibc/2536136#2536136 * @sa http://0xcc.net/blog/archives/000073.html */ #ifndef _GNU_SOURCE #define _GNU_SOURCE /* for Dl_info */ #endif #if defined(HAVE_CONFIG) #include <config.h> #endif #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> #include <stdarg.h> #include <inttypes.h> #include <limits.h> #include <string.h> #include <dlfcn.h> #include <errno.h> #include <bfd.h> #include "log.h" extern void *__libc_stack_end; /** * Frame structure. */ #if defined(__x86_64) struct backtrace_frame { void *fp; void *lr; }; #elif defined(__ARM_ARCH_ISA_ARM) struct backtrace_frame { void *fp; void *sp; void *lr; void *pc; }; #endif static bfd *abfd; static asymbol **symbols; static int nsymbols; #define lengthof(array) (sizeof(array)/sizeof((array)[0])) __attribute__((constructor)) void init_bfd_stuff(void) { int size; abfd = bfd_openr("/proc/self/exe", NULL); if (abfd == NULL) { perror("bfd_openr"); exit(1); } bfd_check_format(abfd, bfd_object); size = bfd_get_symtab_upper_bound(abfd); if (size <= 0) { perror("fbd_get_symtab_upper_bound"); exit(2); } symbols = malloc(size); if (symbols == NULL) { perror("malloc"); exit(3); } nsymbols = bfd_canonicalize_symtab(abfd, symbols); } size_t Backtrace(void **frames, size_t size) { void *top_frame_p; void *current_frame_p; struct backtrace_frame *frame_p; size_t frame_count; top_frame_p = __builtin_frame_address(0); current_frame_p = top_frame_p; frame_count = 0; if ((current_frame_p != NULL) && (current_frame_p > (void *) &frame_count) && (current_frame_p < __libc_stack_end)) { while ((frame_count < size) && (current_frame_p != NULL) && (current_frame_p > (void *) &frame_count) && (current_frame_p < __libc_stack_end)) { #if defined(__x86_64) frame_p = (struct backtrace_frame *) (void **) current_frame_p; #elif defined(__ARM_ARCH_ISA_ARM) frame_p = (struct backtrace_frame *) ((void **) current_frame_p - 3); #endif frames[frame_count] = frame_p->lr; ++frame_count; current_frame_p = frame_p->fp; } } return frame_count; } void PrintBacktrace(void) { void *stack[128] = { NULL }, *address; Dl_info info; asection *section = bfd_get_section_by_name(abfd, ".debug_info"); const char *file_name; const char *func_name; int lineno; int found; int i; if (Backtrace(stack, lengthof(stack)) == 0) { fprintf(stderr, "not found backtrace...\n"); return; } for (i = 0; stack[i] != NULL; ++i) { address = stack[i]; if (section != NULL) { found = bfd_find_nearest_line(abfd, section, symbols, (long) address - 1, &file_name, &func_name, &lineno); } if (dladdr(address, &info) == 0) { fprintf(stderr, " unknown(?+?)[%p]\n", address); } else if (found == 0 || file_name == NULL) { fprintf(stderr, " %s(%s+%p)[%p]\n", info.dli_fname, info.dli_sname, address - info.dli_saddr, address); } else { fprintf(stderr, " %s(%s+%p)[%p] at %s:%d\n", info.dli_fname, info.dli_sname, address - info.dli_saddr, address, file_name, lineno); } } return; } int bar(void) { printf("%s(%d) %s: called\n", __FILE__, __LINE__, __func__); PrintBacktrace(); return 0; } int foo(void) { printf("%s(%d) %s: called\n", __FILE__, __LINE__, __func__); bar(); return 0; } int main(int argc, char **argv) { foo(); return 0; }
- strip なし(ARMV5)
# ./backtrace backtrace.c(163) foo: called backtrace.c(156) bar: called /root/backtrace(PrintBacktrace+0x4c)[0x12ce4] at /path/to/backtrace/backtrace.c:129 /root/backtrace(bar+0x24)[0x12eec] at /path/to/backtrace/backtrace.c:157 /root/backtrace(foo+0x24)[0x12f28] at /path/to/backtrace/backtrace.c:164 /root/backtrace(main+0x1c)[0x12f5c] at /path/to/backtrace/backtrace.c:170 /lib/libc.so.1(__uClibc_main+0x1fc)[0xb6f8972c]
- strip あり(X86_64)
backtrace.c(163) foo: called backtrace.c(156) bar: called ./backtrace(PrintBacktrace+0x54)[0x400ecd] ./backtrace(bar+0x27)[0x4010c6] ./backtrace(foo+0x27)[0x4010f4] ./backtrace(main+0x14)[0x40110f] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5)[0x7f1e5e7ebec5]
このログ情報をハンドリングして良い方法で保存すればコンソールがデバッグログであふれることはなくなる、、、のかな?