明日にはでっかい太陽が昇るかもしれません。

「覚悟」とは!! 暗闇の荒野に!!進むべき道を切り開く事だッ!

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 すると、 gasmbstowcs がない!というエラーになる。

binutils-2.25/gas/read.c:1642: undefined reference to `mbstowcs'

とはいえ、ほしいのは libbfd.alibiberty.alibintl.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]

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