aboutsummaryrefslogtreecommitdiff
path: root/src/client/linux/minidump_writer
diff options
context:
space:
mode:
authornealsid <nealsid@4c0a9323-5329-0410-9bdc-e9ce6186880e>2010-03-02 00:39:48 +0000
committernealsid <nealsid@4c0a9323-5329-0410-9bdc-e9ce6186880e>2010-03-02 00:39:48 +0000
commitde545c09d0363e6964822ec92529a80feaca152d (patch)
treeca6411127fed322c8f789a6b907ca93b9022b353 /src/client/linux/minidump_writer
parentBreakpad Linux symbol dumper: Handle programs linked with --gc-sections. (diff)
downloadbreakpad-de545c09d0363e6964822ec92529a80feaca152d.tar.xz
ARM support, with some build system changes to support x86-64, arm, and i386 in an autoconf style build in Linux. The O2 build for the unit tests is still broken but I'm checking this in to unblock people
A=nealsid R=ajwong, hannahtang, ted.mielczarek git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@541 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src/client/linux/minidump_writer')
-rw-r--r--src/client/linux/minidump_writer/line_reader_unittest.cc14
-rw-r--r--src/client/linux/minidump_writer/linux_dumper.cc48
-rw-r--r--src/client/linux/minidump_writer/linux_dumper.h18
-rw-r--r--src/client/linux/minidump_writer/linux_dumper_unittest.cc85
-rw-r--r--src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc64
-rw-r--r--src/client/linux/minidump_writer/minidump_writer.cc61
6 files changed, 231 insertions, 59 deletions
diff --git a/src/client/linux/minidump_writer/line_reader_unittest.cc b/src/client/linux/minidump_writer/line_reader_unittest.cc
index 222a098e..7c4c3ced 100644
--- a/src/client/linux/minidump_writer/line_reader_unittest.cc
+++ b/src/client/linux/minidump_writer/line_reader_unittest.cc
@@ -69,9 +69,9 @@ TEST(LineReaderTest, OneLineTerminated) {
LineReader reader(fd);
const char *line;
- unsigned len;
+ unsigned int len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
- ASSERT_EQ(len, 1);
+ ASSERT_EQ(len, (unsigned int)1);
ASSERT_EQ(line[0], 'a');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
@@ -90,7 +90,7 @@ TEST(LineReaderTest, OneLine) {
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
- ASSERT_EQ(len, 1);
+ ASSERT_EQ(len, (unsigned)1);
ASSERT_EQ(line[0], 'a');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
@@ -109,13 +109,13 @@ TEST(LineReaderTest, TwoLinesTerminated) {
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
- ASSERT_EQ(len, 1);
+ ASSERT_EQ(len, (unsigned)1);
ASSERT_EQ(line[0], 'a');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
ASSERT_TRUE(reader.GetNextLine(&line, &len));
- ASSERT_EQ(len, 1);
+ ASSERT_EQ(len, (unsigned)1);
ASSERT_EQ(line[0], 'b');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
@@ -134,13 +134,13 @@ TEST(LineReaderTest, TwoLines) {
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
- ASSERT_EQ(len, 1);
+ ASSERT_EQ(len, (unsigned)1);
ASSERT_EQ(line[0], 'a');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
ASSERT_TRUE(reader.GetNextLine(&line, &len));
- ASSERT_EQ(len, 1);
+ ASSERT_EQ(len, (unsigned)1);
ASSERT_EQ(line[0], 'b');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
diff --git a/src/client/linux/minidump_writer/linux_dumper.cc b/src/client/linux/minidump_writer/linux_dumper.cc
index 41929b8a..be199f7b 100644
--- a/src/client/linux/minidump_writer/linux_dumper.cc
+++ b/src/client/linux/minidump_writer/linux_dumper.cc
@@ -85,7 +85,7 @@ namespace google_breakpad {
LinuxDumper::LinuxDumper(int pid)
: pid_(pid),
- threads_suspened_(false),
+ threads_suspended_(false),
threads_(&allocator_, 8),
mappings_(&allocator_) {
}
@@ -96,22 +96,22 @@ bool LinuxDumper::Init() {
}
bool LinuxDumper::ThreadsSuspend() {
- if (threads_suspened_)
+ if (threads_suspended_)
return true;
bool good = true;
for (size_t i = 0; i < threads_.size(); ++i)
good &= SuspendThread(threads_[i]);
- threads_suspened_ = true;
+ threads_suspended_ = true;
return good;
}
bool LinuxDumper::ThreadsResume() {
- if (!threads_suspened_)
+ if (!threads_suspended_)
return false;
bool good = true;
for (size_t i = 0; i < threads_.size(); ++i)
good &= ResumeThread(threads_[i]);
- threads_suspened_ = false;
+ threads_suspended_ = false;
return good;
}
@@ -312,9 +312,9 @@ bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t>* result) const {
}
// Read thread info from /proc/$pid/status.
-// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailible,
+// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable,
// these members are set to -1. Returns true iff all three members are
-// availible.
+// available.
bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
assert(info != NULL);
char status_path[80];
@@ -371,6 +371,8 @@ bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
#elif defined(__x86_64)
memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
+#elif defined(__ARM_EABI__)
+ memcpy(&stack_pointer, &info->regs.uregs[R13], sizeof(info->regs.uregs[R13]));
#else
#error "This code hasn't been ported to your platform yet."
#endif
@@ -387,13 +389,9 @@ bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
// unwind. So we just grab, up to, 32k of stack.
bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
uintptr_t int_stack_pointer) {
-#if defined(__i386) || defined(__x86_64)
- static const bool stack_grows_down = true;
- static const uintptr_t page_size = 4096;
-#else
-#error "This code has not been ported to your platform yet."
-#endif
// Move the stack pointer to the bottom of the page that it's in.
+ const uintptr_t page_size = getpagesize();
+
uint8_t* const stack_pointer =
reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1));
@@ -403,26 +401,19 @@ bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
const MappingInfo* mapping = FindMapping(stack_pointer);
if (!mapping)
return false;
- if (stack_grows_down) {
- const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
- const ptrdiff_t distance_to_end =
- static_cast<ptrdiff_t>(mapping->size) - offset;
- *stack_len = distance_to_end > kStackToCapture ?
- kStackToCapture : distance_to_end;
- *stack = stack_pointer;
- } else {
- const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
- *stack_len = offset > kStackToCapture ? kStackToCapture : offset;
- *stack = stack_pointer - *stack_len;
- }
-
+ const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
+ const ptrdiff_t distance_to_end =
+ static_cast<ptrdiff_t>(mapping->size) - offset;
+ *stack_len = distance_to_end > kStackToCapture ?
+ kStackToCapture : distance_to_end;
+ *stack = stack_pointer;
return true;
}
// static
void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src,
size_t length) {
- unsigned long tmp;
+ unsigned long tmp = 55;
size_t done = 0;
static const size_t word_size = sizeof(tmp);
uint8_t* const local = (uint8_t*) dest;
@@ -430,8 +421,9 @@ void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src,
while (done < length) {
const size_t l = length - done > word_size ? word_size : length - done;
- if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1)
+ if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) {
tmp = 0;
+ }
memcpy(local + done, &tmp, l);
done += l;
}
diff --git a/src/client/linux/minidump_writer/linux_dumper.h b/src/client/linux/minidump_writer/linux_dumper.h
index 3e8869b5..7a4cd3a3 100644
--- a/src/client/linux/minidump_writer/linux_dumper.h
+++ b/src/client/linux/minidump_writer/linux_dumper.h
@@ -44,7 +44,7 @@ namespace google_breakpad {
typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t;
// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
-#if defined(__i386)
+#if defined(__i386) || defined(__ARM_EABI__)
typedef Elf32_auxv_t elf_aux_entry;
#elif defined(__x86_64__)
typedef Elf64_auxv_t elf_aux_entry;
@@ -64,16 +64,20 @@ struct ThreadInfo {
const void* stack; // pointer to the stack area
size_t stack_len; // length of the stack to copy
+
+#if defined(__i386) || defined(__x86_64)
user_regs_struct regs;
user_fpregs_struct fpregs;
+ static const unsigned kNumDebugRegisters = 8;
+ debugreg_t dregs[8];
#if defined(__i386)
user_fpxregs_struct fpxregs;
-#endif
-
-#if defined(__i386) || defined(__x86_64)
+#endif // defined(__i386)
- static const unsigned kNumDebugRegisters = 8;
- debugreg_t dregs[8];
+#elif defined(__ARM_EABI__)
+ // Mimicking how strace does this(see syscall.c, search for GETREGS)
+ struct user_regs regs;
+ struct user_fpregs fpregs;
#endif
};
@@ -141,7 +145,7 @@ class LinuxDumper {
mutable PageAllocator allocator_;
- bool threads_suspened_;
+ bool threads_suspended_;
wasteful_vector<pid_t> threads_; // the ids of all the threads
wasteful_vector<MappingInfo*> mappings_; // info from /proc/<pid>/maps
};
diff --git a/src/client/linux/minidump_writer/linux_dumper_unittest.cc b/src/client/linux/minidump_writer/linux_dumper_unittest.cc
index e060e53e..6f1043c3 100644
--- a/src/client/linux/minidump_writer/linux_dumper_unittest.cc
+++ b/src/client/linux/minidump_writer/linux_dumper_unittest.cc
@@ -29,10 +29,13 @@
#include <limits.h>
#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include "breakpad_googletest_includes.h"
#include "client/linux/minidump_writer/linux_dumper.h"
#include "common/linux/file_id.h"
-#include "breakpad_googletest_includes.h"
+#include "common/linux/memory.h"
using namespace google_breakpad;
@@ -67,7 +70,7 @@ TEST(LinuxDumperTest, ThreadList) {
LinuxDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
- ASSERT_GE(dumper.threads().size(), 1);
+ ASSERT_GE(dumper.threads().size(), (size_t)1);
bool found = false;
for (size_t i = 0; i < dumper.threads().size(); ++i) {
if (dumper.threads()[i] == getpid()) {
@@ -77,6 +80,55 @@ TEST(LinuxDumperTest, ThreadList) {
}
}
+TEST(LinuxDumperTest, VerifyStackReadWithMultipleThreads) {
+ static const int kNumberOfThreadsInHelperProgram = 5;
+ char kNumberOfThreadsArgument[2];
+ sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
+
+ pid_t child_pid = fork();
+ if (child_pid == 0) {
+ // Set the number of threads
+ execl("src/client/linux/linux_dumper_unittest_helper",
+ "linux_dumper_unittest_helper",
+ kNumberOfThreadsArgument,
+ NULL);
+ // Kill if we get here.
+ printf("Errno from exec: %d", errno);
+ FAIL() << "Exec failed: " << strerror(errno);
+ exit(0);
+ }
+ // The sleep is flaky, but prevents us from reading
+ // the child process before all threads have been created.
+ sleep(1);
+ LinuxDumper dumper(child_pid);
+ EXPECT_TRUE(dumper.Init());
+ EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size());
+ EXPECT_TRUE(dumper.ThreadsSuspend());
+
+ ThreadInfo one_thread;
+ for(size_t i = 0; i < dumper.threads().size(); ++i) {
+ EXPECT_TRUE(dumper.ThreadInfoGet(dumper.threads()[i], &one_thread));
+ // We know the threads are in a function which has allocated exactly
+ // one word off the stack to store its thread id.
+#if defined(__ARM_EABI__)
+ void* process_tid_location = (void *)(one_thread.regs.uregs[11] - 8);
+#elif defined(__i386)
+ void* process_tid_location = (void *)(one_thread.regs.ebp - 4);
+#elif defined(__x86_64)
+ void* process_tid_location = (void *)(one_thread.regs.rbp - 4);
+#else
+#error Platform not supported!
+#endif
+ pid_t one_thread_id;
+ dumper.CopyFromProcess(&one_thread_id,
+ dumper.threads()[i],
+ process_tid_location,
+ 4);
+ EXPECT_EQ(dumper.threads()[i], one_thread_id);
+ }
+ kill(child_pid, SIGKILL);
+}
+
TEST(LinuxDumperTest, BuildProcPath) {
const pid_t pid = getpid();
LinuxDumper dumper(pid);
@@ -106,28 +158,29 @@ TEST(LinuxDumperTest, BuildProcPath) {
#endif
}
+#if !defined(__ARM_EABI__)
TEST(LinuxDumperTest, MappingsIncludeLinuxGate) {
LinuxDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
void* linux_gate_loc = dumper.FindBeginningOfLinuxGateSharedLibrary(getpid());
- if (linux_gate_loc) {
- bool found_linux_gate = false;
-
- const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
- const MappingInfo* mapping;
- for (unsigned i = 0; i < mappings.size(); ++i) {
- mapping = mappings[i];
- if (!strcmp(mapping->name, kLinuxGateLibraryName)) {
- found_linux_gate = true;
- break;
- }
+ ASSERT_TRUE(linux_gate_loc);
+ bool found_linux_gate = false;
+
+ const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
+ const MappingInfo* mapping;
+ for (unsigned i = 0; i < mappings.size(); ++i) {
+ mapping = mappings[i];
+ if (!strcmp(mapping->name, kLinuxGateLibraryName)) {
+ found_linux_gate = true;
+ break;
}
- EXPECT_TRUE(found_linux_gate);
- EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr));
- EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
}
+ EXPECT_TRUE(found_linux_gate);
+ EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr));
+ EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
}
+#endif
TEST(LinuxDumperTest, FileIDsMatch) {
// Calculate the File ID of our binary using both
diff --git a/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc b/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc
new file mode 100644
index 00000000..f744d72c
--- /dev/null
+++ b/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Helper program for the linux_dumper class, which creates a bunch of
+// threads. The first word of each thread's stack is set to the thread
+// id.
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#pragma GCC optimize ("O0")
+void *thread_function(void *data) __attribute__((noinline, optimize("O2")));
+
+void *thread_function(void *data) {
+ pid_t thread_id = syscall(SYS_gettid);
+ while (true) ;
+ asm("");
+}
+
+int main(int argc, char *argv[]) {
+ int num_threads = atoi(argv[1]);
+ if (num_threads < 1) {
+ fprintf(stderr, "ERROR: number of threads is 0");
+ return 1;
+ }
+ pthread_t threads[num_threads];
+ pthread_attr_t thread_attributes;
+ pthread_attr_init(&thread_attributes);
+ pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED);
+ for (int i = 1; i < num_threads; i++) {
+ pthread_create(&threads[i], &thread_attributes, &thread_function, NULL);
+ }
+ thread_function(NULL);
+ return 0;
+}
diff --git a/src/client/linux/minidump_writer/minidump_writer.cc b/src/client/linux/minidump_writer/minidump_writer.cc
index 166b8e5c..1ee90981 100644
--- a/src/client/linux/minidump_writer/minidump_writer.cc
+++ b/src/client/linux/minidump_writer/minidump_writer.cc
@@ -61,7 +61,7 @@
#include "client/linux/handler/exception_handler.h"
#include "client/linux/minidump_writer/line_reader.h"
-#include "client/linux/minidump_writer//linux_dumper.h"
+#include "client/linux/minidump_writer/linux_dumper.h"
#include "common/linux/linux_libc_support.h"
#include "common/linux/linux_syscall_support.h"
@@ -307,6 +307,54 @@ static void CPUFillFromUContext(MDRawContextAMD64 *out, const ucontext *uc,
memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16);
}
+#elif defined(__ARMEL__)
+typedef MDRawContextARM RawContextCPU;
+
+static void CPUFillFromThreadInfo(MDRawContextARM *out,
+ const google_breakpad::ThreadInfo &info) {
+ out->context_flags = MD_CONTEXT_ARM_FULL;
+
+ for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i)
+ out->iregs[i] = info.regs.uregs[i];
+ // No CPSR register in ThreadInfo(it's not accessible via ptrace)
+ out->cpsr = 0;
+ out->float_save.fpscr = info.fpregs.fpsr |
+ (static_cast<u_int64_t>(info.fpregs.fpcr) << 32);
+ //TODO: sort this out, actually collect floating point registers
+ memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
+ memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
+}
+
+static void CPUFillFromUContext(MDRawContextARM *out, const ucontext *uc,
+ const struct _libc_fpstate* fpregs) {
+ out->context_flags = MD_CONTEXT_ARM_FULL;
+
+ out->iregs[0] = uc->uc_mcontext.arm_r0;
+ out->iregs[1] = uc->uc_mcontext.arm_r1;
+ out->iregs[2] = uc->uc_mcontext.arm_r2;
+ out->iregs[3] = uc->uc_mcontext.arm_r3;
+ out->iregs[4] = uc->uc_mcontext.arm_r4;
+ out->iregs[5] = uc->uc_mcontext.arm_r5;
+ out->iregs[6] = uc->uc_mcontext.arm_r6;
+ out->iregs[7] = uc->uc_mcontext.arm_r7;
+ out->iregs[8] = uc->uc_mcontext.arm_r8;
+ out->iregs[9] = uc->uc_mcontext.arm_r9;
+ out->iregs[10] = uc->uc_mcontext.arm_r10;
+
+ out->iregs[11] = uc->uc_mcontext.arm_fp;
+ out->iregs[12] = uc->uc_mcontext.arm_ip;
+ out->iregs[13] = uc->uc_mcontext.arm_sp;
+ out->iregs[14] = uc->uc_mcontext.arm_lr;
+ out->iregs[15] = uc->uc_mcontext.arm_pc;
+
+ out->cpsr = uc->uc_mcontext.arm_cpsr;
+
+ //TODO: fix this after fixing ExceptionHandler
+ out->float_save.fpscr = 0;
+ memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
+ memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
+}
+
#else
#error "This code has not been ported to your platform yet."
#endif
@@ -321,7 +369,12 @@ class MinidumpWriter {
: filename_(filename),
siginfo_(&context->siginfo),
ucontext_(&context->context),
+#if !defined(__ARM_EABI__)
float_state_(&context->float_state),
+#else
+ //TODO: fix this after fixing ExceptionHandler
+ float_state_(NULL),
+#endif
crashing_tid_(context->tid),
dumper_(crashing_pid) {
}
@@ -612,6 +665,10 @@ class MinidumpWriter {
uintptr_t GetStackPointer() {
return ucontext_->uc_mcontext.gregs[REG_RSP];
}
+#elif defined(__ARM_EABI__)
+ uintptr_t GetStackPointer() {
+ return ucontext_->uc_mcontext.arm_sp;
+ }
#else
#error "This code has not been ported to your platform yet."
#endif
@@ -644,6 +701,8 @@ class MinidumpWriter {
MD_CPU_ARCHITECTURE_X86;
#elif defined(__x86_64)
MD_CPU_ARCHITECTURE_AMD64;
+#elif defined(__arm__)
+ MD_CPU_ARCHITECTURE_ARM;
#else
#error "Unknown CPU arch"
#endif