Implement Android Linux PTRACE support
* Fork upstream libunwind and cross-compile with Android NDK
* libunwind is also used for proc symbol resolve instead of bfd
* Replace BDF lib with capstone for disassembling actions
* Engineer build & patch scripts to support Android ARM, ARM64, x86, x86_64
* Rename linux/ptrace.* to linux/ptrace_utils.* since it conflicts with Android platform includes for ptrace API
* Wrap register & instruction sizes under macros to reflect sizes for supported CPU arch (makes debugging easier)
* Add / Implement missing definitions / functions for Android
* Improve Android build process
diff --git a/Makefile b/Makefile
index e8915ee..e0b305c 100644
--- a/Makefile
+++ b/Makefile
@@ -103,6 +103,14 @@
INTERCEPTOR_SRCS = $(wildcard interceptor/*.c)
INTERCEPTOR_LIBS = $(INTERCEPTOR_SRCS:.c=.so)
+# Control Android builds
+ANDROID_DEBUG_ENABLED ?= false
+ANDROID_APP_ABI ?= armeabi-v7a
+NDK_BUILD_ARGS :=
+ifeq ($(ANDROID_DEBUG_ENABLED),true)
+ NDK_BUILD_ARGS += V=1 NDK_DEBUG=1 APP_OPTIM=debug
+endif
+
all: warn_libs $(BIN) $(INTERCEPTOR_LIBS)
%.o: %.c
@@ -141,7 +149,8 @@
.PHONY:android
android:
- ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./android/Android.mk APP_PLATFORM=android-21
+ ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./android/Android.mk \
+ APP_PLATFORM=android-21 APP_ABI=$(ANDROID_APP_ABI) $(NDK_BUILD_ARGS)
# DO NOT DELETE
@@ -151,7 +160,7 @@
fuzz.o: common.h fuzz.h arch.h files.h log.h report.h util.h
util.o: common.h log.h
report.o: common.h report.h log.h util.h
-linux/arch.o: common.h arch.h linux/perf.h linux/ptrace.h log.h util.h
+linux/arch.o: common.h arch.h linux/perf.h linux/ptrace_utils.h log.h util.h
linux/bfd.o: common.h linux/bfd.h files.h log.h util.h
linux/perf.o: common.h linux/perf.h log.h
linux/ptrace.o: common.h linux/ptrace.h files.h linux/bfd.h linux/unwind.h
diff --git a/android/Android.mk b/android/Android.mk
index d1b6090..b11f09b 100644
--- a/android/Android.mk
+++ b/android/Android.mk
@@ -14,18 +14,103 @@
# limitations under the License.
LOCAL_PATH := $(abspath $(call my-dir)/..)
+
+# Enable Linux ptrace inesead of POSIX by default
+ANDROID_WITH_PTRACE ?= true
+
+ifeq ($(APP_ABI),$(filter $(APP_ABI),armeabi armeabi-v7a))
+ ARCH_ABI := arm
+ UNW_ARCH := arm
+else ifeq ($(APP_ABI),$(filter $(APP_ABI),x86))
+ ARCH_ABI := x86
+ UNW_ARCH := x86
+ # TODO: Remove this when x86 testing is completed
+ $(info $(APP_ABI) Android not fully tested yet (consider providing feedback if tested))
+else ifeq ($(APP_ABI),$(filter $(APP_ABI),arm64-v8a))
+ ARCH_ABI := arm64
+ UNW_ARCH := aarch64
+ # TODO: Remove this when arm64 testing is completed
+ $(info $(APP_ABI) Android not fully tested yet (consider providing feedback if tested))
+else ifeq ($(APP_ABI),$(filter $(APP_ABI),x86_64))
+ ARCH_ABI := x86_64
+ UNW_ARCH := x86_64
+ $(error $(APP_ABI) Android not supported (issues with libunwind))
+else
+ # ndk-build will have already failed, so just in case
+ $(error Unknown APP_API '$(APP_ABI)')
+endif
+
+ifeq ($(ANDROID_WITH_PTRACE),true)
+ # Upstream libunwind compiled from sources with Android NDK toolchain
+ LIBUNWIND_A := third_party/android/libunwind/$(ARCH_ABI)/libunwind-$(UNW_ARCH).a
+ ifeq ("$(wildcard $(LIBUNWIND_A))","")
+ $(error libunwind-$(UNW_ARCH). is missing. Please execute \
+ 'third_party/android/scripts/compile-libunwind.sh third_party/android/libunwind $(ARCH_ABI)')
+ endif
+
+ include $(CLEAR_VARS)
+ LOCAL_MODULE := libunwind
+ LOCAL_SRC_FILES := third_party/android/libunwind/$(ARCH_ABI)/libunwind.a
+ LOCAL_EXPORT_C_INCLUDES := third_party/android/libunwind/include
+ include $(PREBUILT_STATIC_LIBRARY)
+
+ include $(CLEAR_VARS)
+ LOCAL_MODULE := libunwind-arch
+ LOCAL_SRC_FILES := third_party/android/libunwind/$(ARCH_ABI)/libunwind-$(UNW_ARCH).a
+ LOCAL_EXPORT_C_INCLUDES := third_party/android/libunwind/include
+ include $(PREBUILT_STATIC_LIBRARY)
+
+ include $(CLEAR_VARS)
+ LOCAL_MODULE := libunwind-ptrace
+ LOCAL_SRC_FILES := third_party/android/libunwind/$(ARCH_ABI)/libunwind-ptrace.a
+ LOCAL_EXPORT_C_INCLUDES := third_party/android/libunwind/include
+ include $(PREBUILT_STATIC_LIBRARY)
+
+ LOCAL_MODULE := libunwind-dwarf-generic
+ LOCAL_SRC_FILES := third_party/android/libunwind/$(ARCH_ABI)/libunwind-dwarf-generic.a
+ LOCAL_EXPORT_C_INCLUDES := third_party/android/libunwind/include
+ include $(PREBUILT_STATIC_LIBRARY)
+
+ # Upstream capstone compiled from sources with Android NDK toolchain
+ LIBCAPSTONE_A := third_party/android/capstone/$(ARCH_ABI)/libcapstone.a
+ ifeq ("$(wildcard $(LIBCAPSTONE_A))","")
+ $(error libunwind-$(UNW_ARCH). is missing. Please execute \
+ 'third_party/android/scripts/compile-capstone.sh third_party/android/capstone $(ARCH_ABI)')
+ endif
+ include $(CLEAR_VARS)
+ LOCAL_MODULE := libcapstone
+ LOCAL_SRC_FILES := $(LIBCAPSTONE_A)
+ LOCAL_EXPORT_C_INCLUDES := third_party/android/capstone/include
+ include $(PREBUILT_STATIC_LIBRARY)
+endif
+
+# Main honggfuzz module
include $(CLEAR_VARS)
LOCAL_MODULE := honggfuzz
LOCAL_SRC_FILES := honggfuzz.c log.c files.c fuzz.c report.c mangle.c util.c
LOCAL_CFLAGS := -std=c11 -I. \
-D_GNU_SOURCE \
- -Wall -Wextra -Wno-initializer-overrides -Wno-override-init -Wno-unknown-warning-option -Werror \
- -funroll-loops -O2
+ -Wall -Wextra -Wno-initializer-overrides -Wno-override-init \
+ -Wno-unknown-warning-option -Werror -funroll-loops -O2
LOCAL_LDFLAGS := -lm
-ARCH_SRCS := $(wildcard posix/*.c)
-ARCH = POSIX
+ifeq ($(ANDROID_WITH_PTRACE),true)
+ LOCAL_C_INCLUDES := third_party/android/libunwind/include third_party/android/capstone/include
+ LOCAL_STATIC_LIBRARIES := libunwind libunwind-arch libunwind-ptrace libunwind-dwarf-generic libcapstone
+ LOCAL_CFLAGS += -D__HF_USE_CAPSTONE__
+ ARCH_SRCS := linux/arch.c linux/ptrace_utils.c linux/perf.c linux/unwind.c
+ ARCH := LINUX
+ $(info $(shell (echo "********************************************************************")))
+ $(info $(shell (echo "Android PTRACE build: Will prevent debuggerd from processing crashes")))
+ $(info $(shell (echo "********************************************************************")))
+else
+ ARCH_SRCS := posix/arch.c
+ ARCH := POSIX
+ $(info $(shell (echo "********************************************************************")))
+ $(info $(shell (echo "Android POSIX build: Will allow debuggerd to process crashes")))
+ $(info $(shell (echo "********************************************************************")))
+endif
LOCAL_SRC_FILES += $(ARCH_SRCS)
LOCAL_CFLAGS += -D_HF_ARCH_${ARCH}
diff --git a/common.h b/common.h
index bcd357f..2183338 100644
--- a/common.h
+++ b/common.h
@@ -48,6 +48,9 @@
/* Align to the upper-page boundary */
#define _HF_PAGE_ALIGN_UP(x) (((size_t)x + (size_t)getpagesize() - (size_t)1) & ~((size_t)getpagesize() - (size_t)1))
+/* String buffer size for function names in stack traces produced from libunwind */
+#define _HF_FUNC_NAME_SZ 256 //Should be alright for mangled C++ procs too
+
typedef enum {
_HF_DYNFILE_NONE = 0x0,
_HF_DYNFILE_INSTR_COUNT = 0x1,
@@ -110,11 +113,11 @@
size_t dynamicFileSz;
} fuzzer_t;
-#define _HF_MAX_FUNCS 200
+#define _HF_MAX_FUNCS 80
typedef struct {
void *pc;
- char func[64];
- int line;
+ char func[_HF_FUNC_NAME_SZ];
+ size_t line;
} funcs_t;
#define ARRAYSIZE(x) (sizeof(x) / sizeof(*x))
diff --git a/fuzz.c b/fuzz.c
index 28e86de..b7e6617 100644
--- a/fuzz.c
+++ b/fuzz.c
@@ -52,6 +52,18 @@
#include "report.h"
#include "util.h"
+#if defined(__ANDROID__)
+#if defined(__aarch64__) && !defined(__NR_fork)
+#include <sys/syscall.h>
+
+pid_t honggfuzz_aarch64_fork(void)
+{
+ return syscall(__NR_clone, SIGCHLD, NULL);
+}
+# define fork honggfuzz_aarch64_fork
+#endif
+#endif
+
static int fuzz_sigReceived = 0;
static void fuzz_sigHandler(int sig, siginfo_t * si, void *v)
@@ -255,7 +267,7 @@
}
}
-#if defined(_HF_ARCH_LINUX)
+#if defined(_HF_ARCH_LINUX) && defined(__NR_fork)
#include <unistd.h>
#include <sys/syscall.h>
fuzzer.pid = syscall(__NR_fork);
@@ -407,7 +419,7 @@
}
hfuzz->sem = &semName;
#endif /* defined(__ANDROID__) */
-
+
if (hfuzz->sem == SEM_FAILED) {
LOGMSG_P(l_FATAL, "sem_open() failed");
}
diff --git a/linux/arch.c b/linux/arch.c
index a7a7721..fffd4bc 100644
--- a/linux/arch.c
+++ b/linux/arch.c
@@ -46,7 +46,7 @@
#include <unistd.h>
#include "linux/perf.h"
-#include "linux/ptrace.h"
+#include "linux/ptrace_utils.h"
#include "log.h"
#include "util.h"
@@ -271,7 +271,13 @@
for (;;) {
int status;
+
+ // wait3 syscall is no longer present in Android
+#if !defined(__ANDROID__)
pid_t pid = wait3(&status, __WNOTHREAD | __WALL, NULL);
+#else
+ pid_t pid = wait4(-1, &status, __WNOTHREAD | __WALL, NULL);
+#endif
LOGMSG_P(l_DEBUG, "PID '%d' returned with status '%d'", pid, status);
@@ -284,7 +290,11 @@
break;
}
if (pid == -1) {
- LOGMSG_P(l_FATAL, "wait3() failed");
+#if !defined(__ANDROID__)
+ LOGMSG_P(l_FATAL, "wait3() failed");
+#else
+ LOGMSG_P(l_FATAL, "wait4() failed");
+#endif
}
uint64_t tmp;
diff --git a/linux/ptrace.c b/linux/ptrace_utils.c
similarity index 72%
rename from linux/ptrace.c
rename to linux/ptrace_utils.c
index 0b03289..eb371d7 100644
--- a/linux/ptrace.c
+++ b/linux/ptrace_utils.c
@@ -22,7 +22,7 @@
*/
#include "common.h"
-#include "ptrace.h"
+#include "ptrace_utils.h"
#include <ctype.h>
#include <dirent.h>
@@ -55,6 +55,132 @@
#include "log.h"
#include "util.h"
+#if defined(__ANDROID__)
+#include <linux/ptrace.h>
+#include <asm/ptrace.h>
+#include <sys/syscall.h>
+#include "capstone.h"
+#endif
+
+#if defined(__i386__) || defined(__arm__) || defined(__powerpc__)
+#define REG_TYPE uint32_t
+#define REG_PM PRIx32
+#define REG_PD "0x%08"
+#elif defined(__x86_64__) || defined(__aarch64__) || defined(__powerpc64__)
+#define REG_TYPE uint64_t
+#define REG_PM PRIx64
+#define REG_PD "0x%016"
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+#define MAX_INSTR_SZ 16
+#elif defined(__arm__) || defined(__powerpc__) || defined(__powerpc64__)
+#define MAX_INSTR_SZ 4
+#elif defined(__aarch64__)
+#define MAX_INSTR_SZ 8
+#endif
+
+#ifdef __ANDROID__
+#ifndef WIFCONTINUED
+#define WIFCONTINUED(x) WEXITSTATUS(0)
+#endif
+#endif
+
+#if defined(__ANDROID__)
+#if defined(__NR_process_vm_readv)
+static ssize_t honggfuzz_process_vm_readv(pid_t pid,
+ const struct iovec *lvec,
+ unsigned long liovcnt,
+ const struct iovec *rvec,
+ unsigned long riovcnt,
+ unsigned long flags)
+{
+ return syscall(__NR_process_vm_readv, (long)pid, lvec, liovcnt, rvec, riovcnt, flags);
+}
+# define process_vm_readv honggfuzz_process_vm_readv
+#else /* defined(__NR_process_vm_readv) */
+# define process_vm_readv(...) (errno = ENOSYS, -1)
+#endif /* !defined(__NR_process_vm_readv) */
+
+// Naming compatibilities
+#if !defined(PT_TRACE_ME)
+# define PT_TRACE_ME PTRACE_TRACEME
+#endif
+
+#if !defined(PT_READ_I)
+# define PT_READ_I PTRACE_PEEKTEXT
+#endif
+
+#if !defined(PT_READ_D)
+# define PT_READ_D PTRACE_PEEKDATA
+#endif
+
+#if !defined(PT_READ_U)
+# define PT_READ_U PTRACE_PEEKUSR
+#endif
+
+#if !defined(PT_WRITE_I)
+# define PT_WRITE_I PTRACE_POKETEXT
+#endif
+
+#if !defined(PT_WRITE_D)
+# define PT_WRITE_D PTRACE_POKEDATA
+#endif
+
+#if !defined(PT_WRITE_U)
+# define PT_WRITE_U PTRACE_POKEUSR
+#endif
+
+#if !defined(PT_CONT)
+# define PT_CONT PTRACE_CONT
+#endif
+
+#if !defined(PT_CONTINUE)
+# define PT_CONTINUE PTRACE_CONT
+#endif
+
+#if !defined(PT_KILL)
+# define PT_KILL PTRACE_KILL
+#endif
+
+#if !defined(PT_STEP)
+# define PT_STEP PTRACE_SINGLESTEP
+#endif
+
+#if !defined(PT_GETFPREGS)
+# define PT_GETFPREGS PTRACE_GETFPREGS
+#endif
+
+#if !defined(PT_ATTACH)
+# define PT_ATTACH PTRACE_ATTACH
+#endif
+
+#if !defined(PT_DETACH)
+# define PT_DETACH PTRACE_DETACH
+#endif
+
+#if !defined(PT_SYSCALL)
+# define PT_SYSCALL PTRACE_SYSCALL
+#endif
+
+#if !defined(PT_SETOPTIONS)
+# define PT_SETOPTIONS PTRACE_SETOPTIONS
+#endif
+
+#if !defined(PT_GETEVENTMSG)
+# define PT_GETEVENTMSG PTRACE_GETEVENTMSG
+#endif
+
+#if !defined(PT_GETSIGINFO)
+# define PT_GETSIGINFO PTRACE_GETSIGINFO
+#endif
+
+#if !defined(PT_SETSIGINFO)
+# define PT_SETSIGINFO PTRACE_SETSIGINFO
+#endif
+
+#endif /* defined(__ANDROID__) */
+
/* *INDENT-OFF* */
struct {
bool important;
@@ -76,7 +202,7 @@
};
/* *INDENT-ON* */
-static size_t arch_getProcMem(pid_t pid, uint8_t * buf, size_t len, uint64_t pc)
+static size_t arch_getProcMem(pid_t pid, uint8_t * buf, size_t len, REG_TYPE pc)
{
/*
* Let's try process_vm_readv first
@@ -93,6 +219,9 @@
return len;
}
+ // Debug if failed since it shouldn't happen very often
+ LOGMSG_P(l_DEBUG, "process_vm_readv() failed");
+
/*
* Ok, let's do it via ptrace() then.
* len must be aligned to the sizeof(long)
@@ -115,6 +244,9 @@
return memsz;
}
+// Non i386 / x86_64 ISA fail build due to unused pid argument
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
uint64_t arch_ptraceGetCustomPerf(honggfuzz_t * hfuzz, pid_t pid)
{
if ((hfuzz->dynFileMethod & _HF_DYNFILE_CUSTOM) == 0) {
@@ -197,8 +329,9 @@
#endif
return 0ULL;
}
+#pragma GCC diagnostic pop
-static bool arch_getPC(pid_t pid, uint64_t * pc)
+static bool arch_getPC(pid_t pid, REG_TYPE *pc, REG_TYPE *status_reg)
{
char buf[1024];
struct iovec pt_iov = {
@@ -266,6 +399,7 @@
if (pt_iov.iov_len == sizeof(struct user_regs_struct_32)) {
struct user_regs_struct_32 *r32 = (struct user_regs_struct_32 *)buf;
*pc = r32->eip;
+ *status_reg = r32->eflags;
return true;
}
/*
@@ -274,6 +408,7 @@
if (pt_iov.iov_len == sizeof(struct user_regs_struct_64)) {
struct user_regs_struct_64 *r64 = (struct user_regs_struct_64 *)buf;
*pc = r64->ip;
+ *status_reg = r64->flags;
return true;
}
LOGMSG(l_WARN, "Unknown PTRACE_GETREGSET structure size: '%d'", pt_iov.iov_len);
@@ -286,10 +421,23 @@
};
if (pt_iov.iov_len == sizeof(struct user_regs_struct_32)) {
struct user_regs_struct_32 *r32 = (struct user_regs_struct_32 *)buf;
+
#ifndef ARM_pc
+#ifdef __ANDROID__ /* Building with NDK headers */
+#define ARM_pc uregs[15]
+#else /* Building with glibc headers */
#define ARM_pc 15
+#endif
#endif /* ARM_pc */
+
+#ifdef __ANDROID__
+ *pc = r32->ARM_pc;
+ *status_reg = r32->ARM_cpsr;
+#else
*pc = r32->uregs[ARM_pc];
+ *status_reg = r32->uregs[ARM_cpsr];
+#endif
+
return true;
}
LOGMSG(l_WARN, "Unknown PTRACE_GETREGSET structure size: '%d'", pt_iov.iov_len);
@@ -305,6 +453,7 @@
if (pt_iov.iov_len == sizeof(struct user_regs_struct_64)) {
struct user_regs_struct_64 *r64 = (struct user_regs_struct_64 *)buf;
*pc = r64->pc;
+ *status_reg = r64->pstate;
return true;
}
LOGMSG(l_WARN, "Unknown PTRACE_GETREGSET structure size: '%d'", pt_iov.iov_len);
@@ -375,18 +524,20 @@
return false;
}
-static void arch_getInstrStr(pid_t pid, uint64_t * pc, char *instr)
+static void arch_getInstrStr(pid_t pid, REG_TYPE *pc, char *instr)
{
/*
* We need a value aligned to 8
* which is sizeof(long) on 64bit CPU archs (on most of them, I hope;)
*/
- uint8_t buf[16];
+ uint8_t buf[MAX_INSTR_SZ];
size_t memsz;
+ REG_TYPE status_reg = 0;
+
snprintf(instr, _HF_INSTR_SZ, "%s", "[UNKNOWN]");
- if (!arch_getPC(pid, pc)) {
+ if (!arch_getPC(pid, pc, &status_reg)) {
LOGMSG(l_WARN, "Current architecture not supported for disassembly");
return;
}
@@ -396,8 +547,49 @@
return;
}
+#if !defined(__ANDROID__)
arch_bfdDisasm(pid, buf, memsz, instr);
+#else
+#if defined(__arm__)
+ cs_arch arch = CS_ARCH_ARM;
+ cs_mode mode = (status_reg & 0x20) ? CS_MODE_THUMB : CS_MODE_ARM;
+#elif defined(__aarch64__)
+ // We shouldn't need any execution detection logic here
+ cs_arch arch = CS_ARCH_ARM64;
+ cs_mode mode = CS_MODE_ARM;
+#elif defined(__i386__)
+ cs_arch arch = CS_ARCH_X86;
+ cs_mode mode = CS_MODE_32;
+#elif defined(__x86_64__)
+ cs_arch arch = CS_ARCH_X86;
+ cs_mode mode = CS_MODE_64;
+#else
+ LOGMSG(l_ERROR, "Unknown/unsupported Android CPU architecture");
+#endif
+
+ csh handle;
+ cs_err err = cs_open(arch, mode, &handle);
+ if (err != CS_ERR_OK) {
+ LOGMSG(l_WARN, "Capstone initilization failed: '%s'", cs_strerror(err));
+ return;
+ }
+
+ cs_insn *insn;
+ size_t count = cs_disasm(handle, buf, sizeof(buf), *pc, 0, &insn);
+
+ if (count < 1) {
+ LOGMSG(l_WARN, "Couldn't disassemble the assembler instructions' stream: '%s'",
+ cs_strerror(cs_errno(handle)));
+ cs_close(&handle);
+ return;
+ }
+
+ snprintf(instr, _HF_INSTR_SZ, "%s %s", insn[0].mnemonic, insn[0].op_str);
+ cs_free(insn, count);
+ cs_close(&handle);
+#endif
+
for (int x = 0; instr[x] && x < _HF_INSTR_SZ; x++) {
if (instr[x] == '/' || instr[x] == '\\' || isspace(instr[x])
|| !isprint(instr[x])) {
@@ -423,8 +615,18 @@
util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "INSTRUCTION: %s\n", instr);
util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "STACK:\n");
for (size_t i = 0; i < funcCnt; i++) {
- util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), " <0x%016" PRIx64 "> [%s():%d]\n",
- (uint64_t) (long)funcs[i].pc, funcs[i].func, funcs[i].line);
+#ifdef __HF_USE_CAPSTONE__
+ util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), " <"REG_PD REG_PM "> ",
+ (REG_TYPE) (long)funcs[i].pc, funcs[i].func, funcs[i].line);
+ if (funcs[i].func[0] != '\0')
+ util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "[%s + 0x%x]\n",
+ funcs[i].func, funcs[i].line);
+ else
+ util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), "[]\n");
+#else
+ util_ssnprintf(fuzzer->report, sizeof(fuzzer->report), " <"REG_PD REG_PM "> [%s():%u]\n",
+ (REG_TYPE) (long)funcs[i].pc, funcs[i].func, funcs[i].line);
+#endif
}
return;
@@ -432,7 +634,7 @@
static void arch_ptraceSaveData(honggfuzz_t * hfuzz, pid_t pid, fuzzer_t * fuzzer)
{
- uint64_t pc = 0ULL;
+ REG_TYPE pc = 0;
char instr[_HF_INSTR_SZ] = "\x00";
siginfo_t si;
@@ -446,7 +648,7 @@
LOGMSG(l_DEBUG,
"Pid: %d, signo: %d, errno: %d, code: %d, addr: %p, pc: %"
- PRIx64 ", instr: '%s'", pid, si.si_signo, si.si_errno, si.si_code, si.si_addr, pc,
+ REG_PM ", instr: '%s'", pid, si.si_signo, si.si_errno, si.si_code, si.si_addr, pc,
instr);
if (si.si_addr < hfuzz->ignoreAddr) {
@@ -459,14 +661,14 @@
char newname[PATH_MAX];
if (hfuzz->saveUnique) {
snprintf(newname, sizeof(newname),
- "%s.PC.%" PRIx64 ".CODE.%d.ADDR.%p.INSTR.%s.%s.%s",
+ "%s.PC.%" REG_PM ".CODE.%d.ADDR.%p.INSTR.%s.%s.%s",
arch_sigs[si.si_signo].descr, pc, si.si_code, si.si_addr,
instr, fuzzer->origFileName, hfuzz->fileExtn);
} else {
char localtmstr[PATH_MAX];
util_getLocalTime("%F.%H:%M:%S", localtmstr, sizeof(localtmstr));
snprintf(newname, sizeof(newname),
- "%s.PC.%" PRIx64 ".CODE.%d.ADDR.%p.INSTR.%s.%s.%d.%s.%s",
+ "%s.PC.%" REG_PM ".CODE.%d.ADDR.%p.INSTR.%s.%s.%d.%s.%s",
arch_sigs[si.si_signo].descr, pc, si.si_code, si.si_addr,
instr, localtmstr, pid, fuzzer->origFileName, hfuzz->fileExtn);
}
@@ -476,6 +678,8 @@
} else {
if (errno == EEXIST) {
LOGMSG(l_INFO, "It seems that '%s' already exists, skipping", newname);
+ // Don't bother unwinding & generating reports for duplicate crashes
+ return;
} else {
LOGMSG_P(l_ERROR, "Couldn't link '%s' to '%s'", fuzzer->fileName, newname);
}
@@ -488,8 +692,12 @@
,
};
+#if !defined(__ANDROID__)
size_t funcCnt = arch_unwindStack(pid, funcs);
arch_bfdResolveSyms(pid, funcs, funcCnt);
+#else
+ size_t funcCnt = arch_unwindStack(pid, funcs);
+#endif
arch_ptraceGenerateReport(pid, fuzzer, funcs, funcCnt, &si, instr);
}
@@ -512,13 +720,25 @@
}
if (WIFSTOPPED(status)) {
+ int curStatus = WSTOPSIG(status);
+
/*
* If it's an interesting signal, save the testcase
*/
if (arch_sigs[WSTOPSIG(status)].important) {
arch_ptraceSaveData(hfuzz, pid, fuzzer);
+
+ /*
+ * An kind of ugly (although necessary) hack due to custom signal handlers
+ * in Android from debuggerd. If we pass one of the monitored signals,
+ * we'll end-up running the processing routine twice. A cost that we
+ * don't want to pay.
+ */
+#if defined(__ANDROID__)
+ curStatus = SIGINT;
+#endif
}
- ptrace(PT_CONTINUE, pid, 0, WSTOPSIG(status));
+ ptrace(PT_CONTINUE, pid, 0, curStatus);
return;
}
diff --git a/linux/ptrace.h b/linux/ptrace_utils.h
similarity index 93%
rename from linux/ptrace.h
rename to linux/ptrace_utils.h
index 7d4215f..ec3d7a0 100644
--- a/linux/ptrace.h
+++ b/linux/ptrace_utils.h
@@ -21,8 +21,8 @@
*
*/
-#ifndef _LINUX_PTRACE_H_
-#define _LINUX_PTRACE_H_
+#ifndef _LINUX_PTRACE_UTILS_H_
+#define _LINUX_PTRACE_UTILS_H_
extern bool arch_ptraceEnable(honggfuzz_t * fuzz);
extern void arch_ptraceAnalyze(honggfuzz_t * fuzz, int status, pid_t pid, fuzzer_t * fuzzer);
diff --git a/linux/unwind.c b/linux/unwind.c
index b612da9..6a585f4 100644
--- a/linux/unwind.c
+++ b/linux/unwind.c
@@ -28,33 +28,69 @@
#include "log.h"
-size_t arch_unwindStack(pid_t pid, funcs_t * funcs)
+#if defined(__ANDROID__)
+#include <sys/endian.h> /* For __BYTE_ORDER */
+#endif
+
+/*
+ * WARNING: Ensure that _UPT-info structs are not shared between threads
+ * http://www.nongnu.org/libunwind/man/libunwind-ptrace(3).html
+ */
+
+/*
+ * TODO: Subtract from load map to have relative PC stored in report file.
+ * link_map seems to be the easiest road for that.
+ */
+
+// libunwind error codes used for debugging
+static const char *UNW_ER[] = {
+ "UNW_ESUCCESS", /* no error */
+ "UNW_EUNSPEC", /* unspecified (general) error */
+ "UNW_ENOMEM", /* out of memory */
+ "UNW_EBADREG", /* bad register number */
+ "UNW_EREADONLYREG", /* attempt to write read-only register */
+ "UNW_ESTOPUNWIND", /* stop unwinding */
+ "UNW_EINVALIDIP", /* invalid IP */
+ "UNW_EBADFRAME", /* bad frame */
+ "UNW_EINVAL", /* unsupported operation or bad value */
+ "UNW_EBADVERSION", /* unwind info has unsupported version */
+ "UNW_ENOINFO" /* no unwind info found */
+};
+
+#ifndef __ANDROID__
+size_t arch_unwindStack(pid_t pid, funcs_t *funcs)
{
- size_t ret = 0;
+ size_t frames = 0;
void *ui = NULL;
unw_addr_space_t as = unw_create_addr_space(&_UPT_accessors, __BYTE_ORDER);
if (!as) {
- LOGMSG(l_ERROR, "unw_create_addr_space(pid='%d') failed", pid);
+ LOGMSG(l_ERROR, "[pid='%d'] unw_create_addr_space failed", pid);
goto out;
}
ui = _UPT_create(pid);
if (ui == NULL) {
- LOGMSG(l_ERROR, "_UPT_create(pid='%d') failed", pid);
+ LOGMSG(l_ERROR, "[pid='%d'] _UPT_create failed", pid);
goto out;
}
unw_cursor_t c;
- if (unw_init_remote(&c, as, ui) != 0) {
- LOGMSG(l_ERROR, "unw_init_remote(pid='%d') failed", pid);
+ int ret = unw_init_remote(&c, as, ui);
+ if (ret < 0) {
+ LOGMSG(l_ERROR, "[pid='%d'] unw_init_remote failed (%s)", pid, UNW_ER[-ret]);
goto out;
}
- for (ret = 0; unw_step(&c) > 0 && ret < _HF_MAX_FUNCS; ret++) {
+ for (frames = 0; unw_step(&c) > 0 && frames < _HF_MAX_FUNCS; frames++) {
unw_word_t ip;
- unw_get_reg(&c, UNW_REG_IP, &ip);
- funcs[ret].pc = (void *)ip;
+ ret = unw_get_reg(&c, UNW_REG_IP, &ip);
+ if (ret < 0) {
+ LOMGSG(l_ERROR, "[pid='%d'] [%d] failed to read IP (%s)", pid, frames, UNW_ER[-ret]);
+ funcs[ret].pc = 0;
+ }
+ else
+ funcs[ret].pc = (void *)ip;
}
out:
@@ -62,3 +98,81 @@
as ? unw_destroy_addr_space(as) : 0;
return ret;
}
+
+#else /* !defined(__ANDROID__) */
+size_t arch_unwindStack(pid_t pid, funcs_t *funcs)
+{
+ size_t num_frames = 0;
+ struct UPT_info *ui = NULL;
+ unw_addr_space_t as = NULL;
+
+ as = unw_create_addr_space(&_UPT_accessors, __BYTE_ORDER);
+ if (!as) {
+ LOGMSG(l_ERROR, "[pid='%d'] unw_create_addr_space failed", pid);
+ goto out;
+ }
+
+ ui = (struct UPT_info*)_UPT_create(pid);
+ if (ui == NULL) {
+ LOGMSG(l_ERROR, "[pid='%d'] _UPT_create failed", pid);
+ goto out;
+ }
+
+ unw_cursor_t cursor;
+ int ret = unw_init_remote(&cursor, as, ui);
+ if (ret < 0) {
+ LOGMSG(l_ERROR, "[pid='%d'] unw_init_remote failed (%s)", pid, UNW_ER[-ret]);
+ goto out;
+ }
+
+ do {
+ unw_word_t pc = 0, offset = 0;
+ char buf[_HF_FUNC_NAME_SZ] = { 0 };
+
+ unw_proc_info_t frameInfo;
+ ret = unw_get_proc_info(&cursor, &frameInfo);
+ if (ret < 0) {
+ LOGMSG(l_DEBUG, "[pid='%d'] [%d] unw_get_proc_info (%s)",
+ pid, num_frames, UNW_ER[-ret]);
+ // Not safe to keep reading
+ goto out;
+ }
+
+ ret = unw_get_reg(&cursor, UNW_REG_IP, &pc);
+ if (ret < 0) {
+ LOGMSG(l_ERROR, "[pid='%d'] [%d] failed to read IP (%s)",
+ pid, num_frames, UNW_ER[-ret]);
+ // We don't want to try to extract info from an arbitrary IP
+ // TODO: Maybe abort completely (goto out))
+ goto skip_frame_info;
+ }
+
+ ret = unw_get_proc_name(&cursor, buf, sizeof(buf), &offset);
+ if (ret < 0) {
+ LOGMSG(l_DEBUG, "[pid='%d'] [%d] unw_get_proc_name() failed (%s)",
+ pid, num_frames, UNW_ER[-ret]);
+ buf[0] = '\0';
+ }
+
+skip_frame_info:
+ // Compared to bfd, line var plays the role of offset from func_name
+ // Reports format is adjusted accordingly to reflect in saved file
+ funcs[num_frames].line = offset;
+ funcs[num_frames].pc = (void *)pc;
+ memcpy(funcs[num_frames].func, buf, sizeof(funcs[num_frames].func));
+
+ num_frames++;
+
+ ret = unw_step(&cursor);
+ } while (ret > 0 && num_frames < _HF_MAX_FUNCS);
+
+ out:
+ ui ? _UPT_destroy(ui) : NULL;
+ as ? unw_destroy_addr_space(as) : NULL;
+
+ ui = NULL;
+ as = NULL;
+
+ return num_frames;
+}
+#endif /* defined(__ANDROID__) */
diff --git a/third_party/android/patches/aarch64-libunwind.patch b/third_party/android/patches/aarch64-libunwind.patch
new file mode 100644
index 0000000..b156800
--- /dev/null
+++ b/third_party/android/patches/aarch64-libunwind.patch
@@ -0,0 +1,43 @@
+--- libunwind/include/libunwind-aarch64.h 2015-08-03 19:24:10.000000000 +0300
++++ patches/libunwind-aarch64.h 2015-08-05 05:22:46.000000000 +0300
+@@ -168,15 +168,37 @@
+ }
+ unw_tdep_save_loc_t;
+
+-
+ /* On AArch64, we can directly use ucontext_t as the unwind context. */
+ typedef ucontext_t unw_tdep_context_t;
+
+ #include "libunwind-common.h"
+ #include "libunwind-dynamic.h"
+
+-#define unw_tdep_getcontext(uc) (getcontext (uc), 0)
+-#define unw_tdep_is_fpreg UNW_ARCH_OBJ(is_fpreg)
++/* There is no getcontext in Android. */
++#define unw_tdep_getcontext(uc) (({ \
++ unw_tdep_context_t *unw_ctx = (uc); \
++ register uint64_t *unw_base asm ("x0") = (uint64_t*) unw_ctx->uc_mcontext.regs; \
++ __asm__ __volatile__ ( \
++ "stp x0, x1, [%[base], #0]\n" \
++ "stp x2, x3, [%[base], #16]\n" \
++ "stp x4, x5, [%[base], #32]\n" \
++ "stp x6, x7, [%[base], #48]\n" \
++ "stp x8, x9, [%[base], #64]\n" \
++ "stp x10, x11, [%[base], #80]\n" \
++ "stp x12, x13, [%[base], #96]\n" \
++ "stp x14, x13, [%[base], #112]\n" \
++ "stp x16, x17, [%[base], #128]\n" \
++ "stp x18, x19, [%[base], #144]\n" \
++ "stp x20, x21, [%[base], #160]\n" \
++ "stp x22, x23, [%[base], #176]\n" \
++ "stp x24, x25, [%[base], #192]\n" \
++ "stp x26, x27, [%[base], #208]\n" \
++ "stp x28, x29, [%[base], #224]\n" \
++ "str x30, [%[base], #240]\n" \
++ "mov x1, sp\n" \
++ "stp x1, x30, [%[base], #248]\n" \
++ : [base] "+r" (unw_base) : : "x1", "memory"); \
++ }), 0)
+
+ extern int unw_tdep_is_fpreg (int);
+
diff --git a/third_party/android/patches/x86-libunwind.patch b/third_party/android/patches/x86-libunwind.patch
new file mode 100644
index 0000000..aee4751
--- /dev/null
+++ b/third_party/android/patches/x86-libunwind.patch
@@ -0,0 +1,29 @@
+--- libunwind/src/x86/Gos-linux.c 2015-08-03 19:24:10.000000000 +0300
++++ patches/Gos-linux.c 2015-08-05 05:34:22.000000000 +0300
+@@ -283,7 +283,9 @@
+ x86_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg)
+ {
+ struct cursor *c = (struct cursor *) cursor;
++#if !defined(__ANDROID__)
+ ucontext_t *uc = c->uc;
++#endif
+
+ /* Ensure c->pi is up-to-date. On x86, it's relatively common to be
+ missing DWARF unwind info. We don't want to fail in that case,
+@@ -296,12 +298,16 @@
+ struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr;
+
+ Debug (8, "resuming at ip=%x via sigreturn(%p)\n", c->dwarf.ip, sc);
++#if !defined(__ANDROID__)
+ sigreturn (sc);
++#endif
+ }
+ else
+ {
+ Debug (8, "resuming at ip=%x via setcontext()\n", c->dwarf.ip);
++#if !defined(__ANDROID__)
+ setcontext (uc);
++#endif
+ }
+ return -UNW_EINVAL;
+ }
diff --git a/third_party/android/scripts/compile-capstone.sh b/third_party/android/scripts/compile-capstone.sh
new file mode 100755
index 0000000..5474bbb
--- /dev/null
+++ b/third_party/android/scripts/compile-capstone.sh
@@ -0,0 +1,121 @@
+#!/bin/bash
+#
+# honggfuzz capstone build help script
+# -----------------------------------------
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+if [ -z "$NDK" ]; then
+ # Search in $PATH
+ if [[ $(which ndk-build) != "" ]]; then
+ $NDK=$(dirname $(which ndk-build))
+ else
+ echo "[-] Could not detect Android NDK dir"
+ exit 1
+ fi
+fi
+
+if [ $# -ne 2 ]; then
+ echo "[-] Invalid arguments"
+ echo "[!] $0 <CAPSTONE_DIR> <ARCH>"
+ echo " ARCH: arm arm64 x86 x86_64"
+ exit 1
+fi
+
+readonly CAPSTONE_DIR=$1
+
+# Fetch if not already there
+if [ ! -d $CAPSTONE_DIR ]; then
+ echo "[!] capstone not found. Fetching a fresh copy"
+ git clone https://github.com/aquynh/capstone $CAPSTONE_DIR
+fi
+
+case "$2" in
+ arm|arm64|x86|x86_64)
+ readonly ARCH=$2
+ if [ ! -d $CAPSTONE_DIR/$ARCH ] ; then mkdir -p $CAPSTONE_DIR/$ARCH; fi
+ ;;
+ *)
+ echo "[-] Invalid CPU architecture"
+ exit 1
+ ;;
+esac
+
+case "$ARCH" in
+ arm)
+ CS_ARCH=arm
+ CS_BUILD_BIN="./make.sh cross-android $ARCH"
+ ;;
+ arm64)
+ CS_ARCH=aarch64
+ CS_BUILD_BIN="./make.sh cross-android $ARCH"
+ ;;
+ x86)
+ CS_ARCH=x86
+ CS_BUILD_BIN="make"
+ TOOLCHAIN=i686-linux-android
+ TOOLCHAIN_S=x86-4.9
+ ;;
+ x86_64)
+ CS_ARCH=x86
+ CS_BUILD_BIN="make"
+ TOOLCHAIN=x86_64-linux-android
+ TOOLCHAIN_S=x86_64-4.9
+ ;;
+esac
+
+# Capstone handles internally only Android ARM cross builds not Intel x86/x86_x64
+# We need to prepare the Android NDK toolchains manually for these builds
+if [[ "$ARCH" == "x86" || "$ARCH" == "x86_64" ]]; then
+ if [ -z "$NDK" ]; then
+ # Search in $PATH
+ if [[ $(which ndk-build) != "" ]]; then
+ $NDK=$(dirname $(which ndk-build))
+ else
+ echo "[-] Could not detect Android NDK dir"
+ exit 1
+ fi
+ fi
+
+ # Support both Linux & Darwin
+ HOST_OS=$(uname -s | tr '[:upper:]' '[:lower:]')
+ HOST_ARCH=$(uname -m)
+
+ SYSROOT="$NDK/platforms/android-21/arch-$ARCH"
+ export CC="$NDK/toolchains/$TOOLCHAIN_S/prebuilt/$HOST_OS-$HOST_ARCH/bin/$TOOLCHAIN-gcc --sysroot=$SYSROOT"
+ export CXX="$NDK/toolchains/$TOOLCHAIN_S/prebuilt/$HOST_OS-$HOST_ARCH/bin/$TOOLCHAIN-g++ --sysroot=$SYSROOT"
+ export PATH="$NDK/toolchains/$TOOLCHAIN_S/prebuilt/$HOST_OS-$HOST_ARCH/bin":$PATH
+ # We need to construct a cross variable that capstone Makefile can pick ar, strip & ranlib from
+ export CROSS="$NDK/toolchains/$TOOLCHAIN_S/prebuilt/$HOST_OS-$HOST_ARCH/bin/$TOOLCHAIN-" CFLAGS="--sysroot=$SYSROOT" LDFLAGS="--sysroot=$SYSROOT"
+fi
+
+# Change workdir to simplify args
+cd $CAPSTONE_DIR
+
+# Build it
+make clean
+
+NDK=$NDK CAPSTONE_BUILD_CORE_ONLY=yes CAPSTONE_ARCHS=$CS_ARCH \
+CAPSTONE_SHARED=no CAPSTONE_STATIC=yes \
+eval $CS_BUILD_BIN
+if [ $? -ne 0 ]; then
+ echo "[-] Compilation failed"
+ exit 1
+else
+ echo "[*] '$ARCH' libcapstone available at '$CAPSTONE_DIR/$ARCH'"
+fi
+
+cp libcapstone.a $ARCH/
+
+# Revert workdir to caller
+cd -
diff --git a/third_party/android/scripts/compile-libunwind.sh b/third_party/android/scripts/compile-libunwind.sh
new file mode 100755
index 0000000..aac271f
--- /dev/null
+++ b/third_party/android/scripts/compile-libunwind.sh
@@ -0,0 +1,143 @@
+#!/bin/bash
+#
+# honggfuzz libunwind build help script
+# -----------------------------------------
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+if [ -z "$NDK" ]; then
+ # Search in $PATH
+ if [[ $(which ndk-build) != "" ]]; then
+ $NDK=$(dirname $(which ndk-build))
+ else
+ echo "[-] Could not detect Android NDK dir"
+ exit 1
+ fi
+fi
+
+if [ $# -ne 2 ]; then
+ echo "[-] Invalid arguments"
+ echo "[!] $0 <LIBUNWIND_DIR> <ARCH>"
+ echo " ARCH: arm arm64 x86 x86_64"
+ exit 1
+fi
+
+readonly LIBUNWIND_DIR=$1
+
+# Fetch if not already there
+if [ ! -d $LIBUNWIND_DIR ]; then
+ echo "[!] libunwind not found. Fetching a fresh copy"
+ git clone git://git.sv.gnu.org/libunwind.git $LIBUNWIND_DIR
+fi
+
+case "$2" in
+ arm|arm64|x86|x86_64)
+ readonly ARCH=$2
+ if [ ! -d $LIBUNWIND_DIR/$ARCH ] ; then mkdir -p $LIBUNWIND_DIR/$ARCH; fi
+ ;;
+ *)
+ echo "[-] Invalid architecture"
+ exit 1
+ ;;
+esac
+
+# Change workdir to simplify args
+cd $LIBUNWIND_DIR
+
+# Prepare toolchain
+case "$ARCH" in
+ arm)
+ TOOLCHAIN=arm-linux-androideabi
+ TOOLCHAIN_S=arm-linux-androideabi-4.9
+ ;;
+ arm64)
+ TOOLCHAIN=aarch64-linux-android
+ TOOLCHAIN_S=aarch64-linux-android-4.9
+ ;;
+ x86)
+ TOOLCHAIN=i686-linux-android
+ TOOLCHAIN_S=x86-4.9
+ ;;
+ x86_64)
+ TOOLCHAIN=x86_64-linux-android
+ TOOLCHAIN_S=x86_64-4.9
+ ;;
+esac
+
+# Apply patches required for Android
+# TODO: Automate global patching when all archs have been tested
+if [ "$ARCH" == "arm64" ]; then
+ # Missing libc functionality
+ patch -N --dry-run --silent include/libunwind-aarch64.h < ../patches/aarch64-libunwind.patch &>/dev/null
+ if [ $? -eq 0 ]; then
+ patch include/libunwind-aarch64.h < ../patches/aarch64-libunwind.patch
+ if [ $? -ne 0 ]; then
+ echo "[-] aarch64-libunwind patch failed"
+ exit 1
+ fi
+ fi
+fi
+
+if [ "$ARCH" == "x86" ]; then
+ # Missing syscalls
+ patch -N --dry-run --silent src/x86/Gos-linux.c < ../patches/x86-libunwind.patch &>/dev/null
+ if [ $? -eq 0 ]; then
+ patch src/x86/Gos-linux.c < ../patches/x86-libunwind.patch
+ if [ $? -ne 0 ]; then
+ echo "[-] x86-libunwind patch failed"
+ exit 1
+ fi
+ fi
+fi
+
+# Support both Linux & Darwin
+HOST_OS=$(uname -s | tr '[:upper:]' '[:lower:]')
+HOST_ARCH=$(uname -m)
+
+SYSROOT="$NDK/platforms/android-21/arch-$ARCH"
+export CC="$NDK/toolchains/$TOOLCHAIN_S/prebuilt/$HOST_OS-$HOST_ARCH/bin/$TOOLCHAIN-gcc --sysroot=$SYSROOT"
+export CXX="$NDK/toolchains/$TOOLCHAIN_S/prebuilt/$HOST_OS-$HOST_ARCH/bin/$TOOLCHAIN-g++ --sysroot=$SYSROOT"
+export PATH="$NDK/toolchains/$TOOLCHAIN_S/prebuilt/$HOST_OS-$HOST_ARCH/bin":$PATH
+
+if [ ! -f configure ]; then
+ autoreconf -i
+ if [ $? -ne 0 ]; then
+ echo "[-] autoreconf failed"
+ exit 1
+ fi
+ # Patch configure
+ sed -i -e 's/-lgcc_s/-lgcc/g' configure
+else
+ make clean
+ if [ $? -ne 0 ]; then
+ echo "[-] Old build detected, although clean failed. Consider manual clean-up."
+ exit 1
+ fi
+fi
+
+./configure --host=$TOOLCHAIN --disable-coredump
+if [ $? -ne 0 ]; then
+ echo "[-] configure failed"
+ exit 1
+fi
+
+make CFLAGS="-static" LDFLAGS="-static"
+if [ $? -ne 0 ]; then
+ echo "[-] Compilation failed"
+ cd -
+ exit 1
+else
+ echo "[*] '$ARCH' libunwind available at '$LIBUNWIND_DIR/$ARCH'"
+ cp src/.libs/*.a $ARCH
+ cd -
+fi