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]
このログ情報をハンドリングして良い方法で保存すればコンソールがデバッグログであふれることはなくなる、、、のかな?