diff options
author | noelallen@chromium.org <noelallen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-02 23:34:13 +0000 |
---|---|---|
committer | noelallen@chromium.org <noelallen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-02 23:34:13 +0000 |
commit | 9664e6a240dd053eed85ff171eb0ca82c278290a (patch) | |
tree | cba639620ded69a548b24b51cce493c9e2e33834 | |
parent | 6dc71404e5ba1644dcf70bb184981f0f184e4801 (diff) | |
download | chromium_src-9664e6a240dd053eed85ff171eb0ca82c278290a.zip chromium_src-9664e6a240dd053eed85ff171eb0ca82c278290a.tar.gz chromium_src-9664e6a240dd053eed85ff171eb0ca82c278290a.tar.bz2 |
Fix debugging example.
This example is now more of a tutorial on logging and crash handling practicies.
The example can now run within the packaged app.
R=binji@chromium.org
BUG=
Review URL: https://codereview.chromium.org/14659005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@198008 0039d316-1c4b-4281-b951-d872f2087c98
17 files changed, 609 insertions, 505 deletions
diff --git a/native_client_sdk/src/build_tools/build_sdk.py b/native_client_sdk/src/build_tools/build_sdk.py index 7d7cd48..2e7d4eb 100755 --- a/native_client_sdk/src/build_tools/build_sdk.py +++ b/native_client_sdk/src/build_tools/build_sdk.py @@ -251,6 +251,7 @@ HEADER_MAP = { 'nacl/dynamic_annotations.h': 'src/untrusted/valgrind/dynamic_annotations.h', 'nacl/nacl_dyncode.h': 'src/untrusted/nacl/nacl_dyncode.h', + 'nacl/nacl_exception.h': 'src/include/nacl/nacl_exception.h', 'nacl/nacl_startup.h': 'src/untrusted/nacl/nacl_startup.h', 'nacl/nacl_thread.h': 'src/untrusted/nacl/nacl_thread.h', 'pnacl.h': 'src/untrusted/nacl/pnacl.h', @@ -261,6 +262,7 @@ HEADER_MAP = { 'nacl/dynamic_annotations.h': 'src/untrusted/valgrind/dynamic_annotations.h', 'nacl/nacl_dyncode.h': 'src/untrusted/nacl/nacl_dyncode.h', + 'nacl/nacl_exception.h': 'src/include/nacl/nacl_exception.h', 'nacl/nacl_startup.h': 'src/untrusted/nacl/nacl_startup.h', 'nacl/nacl_thread.h': 'src/untrusted/nacl/nacl_thread.h', 'pnacl.h': 'src/untrusted/nacl/pnacl.h', diff --git a/native_client_sdk/src/build_tools/generate_make.py b/native_client_sdk/src/build_tools/generate_make.py index d6b2b6c..802a348 100644 --- a/native_client_sdk/src/build_tools/generate_make.py +++ b/native_client_sdk/src/build_tools/generate_make.py @@ -182,6 +182,7 @@ def ProcessProject(srcroot, dstroot, desc, toolchains, configs=None, template_dict = { 'rel_sdk': '/'.join(['..'] * (len(desc['DEST'].split('/')) + 1)), 'pre': desc.get('PRE', ''), + 'post': desc.get('POST', ''), 'tools': tools, 'targets': desc['TARGETS'], } diff --git a/native_client_sdk/src/build_tools/template.mk b/native_client_sdk/src/build_tools/template.mk index 2ae0f6f..1b3e5fe 100644 --- a/native_client_sdk/src/build_tools/template.mk +++ b/native_client_sdk/src/build_tools/template.mk @@ -106,3 +106,6 @@ endif # Specify the NMF to be created with no additional arguments. # $(eval $(call NMF_RULE,$(TARGET),)) + +{{post}} + diff --git a/native_client_sdk/src/examples/common.js b/native_client_sdk/src/examples/common.js index 319c1f2..79988ed 100644 --- a/native_client_sdk/src/examples/common.js +++ b/native_client_sdk/src/examples/common.js @@ -27,6 +27,7 @@ var common = (function () { moduleEl.setAttribute('id', 'nacl_module'); moduleEl.setAttribute('width', width); moduleEl.setAttribute('height',height); + moduleEl.setAttribute('path', path); moduleEl.setAttribute('src', path + '/' + name + '.nmf'); // Add any optional arguments @@ -80,16 +81,29 @@ var common = (function () { var listenerDiv = document.getElementById('listener'); listenerDiv.addEventListener('load', moduleDidLoad, true); listenerDiv.addEventListener('message', handleMessage, true); - + listenerDiv.addEventListener('crash', handleCrash, true); if (typeof window.attachListeners !== 'undefined') { window.attachListeners(); } } + + /** + * Called when the Browser can not communicate with the Module + * + * This event listener is registered in attachDefaultListeners above. + */ + function handleCrash(event) { + updateStatus('CRASHED') + if (typeof window.handleCrash !== 'undefined') { + window.handleCrash(common.naclModule.lastError); + } + } + /** * Called when the NaCl module is loaded. * - * This event listener is registered in createNaClModule above. + * This event listener is registered in attachDefaultListeners above. */ function moduleDidLoad() { common.naclModule = document.getElementById('nacl_module'); diff --git a/native_client_sdk/src/examples/tutorial/debugging/example.dsc b/native_client_sdk/src/examples/tutorial/debugging/example.dsc index d7edfc6..72cd94b 100644 --- a/native_client_sdk/src/examples/tutorial/debugging/example.dsc +++ b/native_client_sdk/src/examples/tutorial/debugging/example.dsc @@ -4,7 +4,6 @@ 'SEARCH': [ '.', '../..', - '../../../tools', ], 'TARGETS': [ { @@ -12,28 +11,21 @@ 'TYPE' : 'main', 'SOURCES' : [ 'hello_world.c', - 'string_stream.c', - 'string_stream.h', - 'untrusted_crash_dump.c', - 'untrusted_crash_dump.h' ], 'CCFLAGS': ['-fno-omit-frame-pointer'], + 'DEPS' : ['error_handling'], 'LIBS' : ['ppapi', 'pthread'] } ], - # The debugging example needs to use a different HTTP server to handle POST - # messages from the NaCl module. - 'PRE': """ -CHROME_ARGS+=--no-sandbox -CHROME_ENV:=NACL_DANGEROUS_ENABLE_FILE_ACCESS=1 -CHROME_ENV+=NACL_SECURITY_DISABLE=1 -CHROME_ENV+=NACL_UNTRUSTED_EXCEPTION_HANDLING=1 + 'POST': """ +# +# Specify the MAP files to be created. +# +$(eval $(call MAP_RULE,$(TARGET),$(TARGET))) """, - 'DATA': [ 'example.js', - 'handler.py' ], 'DEST': 'examples/tutorial', 'NAME': 'debugging', diff --git a/native_client_sdk/src/examples/tutorial/debugging/example.js b/native_client_sdk/src/examples/tutorial/debugging/example.js index 7fdc4ba..6f637f8 100644 --- a/native_client_sdk/src/examples/tutorial/debugging/example.js +++ b/native_client_sdk/src/examples/tutorial/debugging/example.js @@ -10,38 +10,112 @@ function domContentLoaded(name, tc, config, width, height) { common.attachDefaultListeners(); updateStatus('Page Loaded'); - heartBeat(); } // Indicate success when the NaCl module has loaded. function moduleDidLoad() { updateStatus('LOADED'); - setTimeout(boom, 4000); + setTimeout(boom, 2000); +} + +function findAddress(addr, map) { + if (map.length < 1) { + return 'MAP Unavailable'; + } + if (addr < map[0].offs) { + return 'Invalid Address'; + } + + for (var i=1; i < map.length; i++) { + if (addr < map[i].offs) { + var offs = addr - map[i-1].offs; + var filename = map[i-1].file; + + // Force filename to 50 chars + if (filename) { + if (filename.length > 50) { + filename = '...' + filename.substr(filename.length - 47); + } + } else { + filename = 'Unknown'; + } + while (filename.length < 50) { + filename = ' ' + filename + } + return filename + ' ' + map[i-1].name + ' + 0x' + offs.toString(16); + } + } + + var last = map.length - 1; + return filename + ' ' + map[last].name + ' + 0x' + offs.toString(16); +} + +function buildTextMap(map) { + map = map.split('\n'); + var orderedMap = []; + for (var i=0; i < map.length; i++) { + var line = map[i].replace('\t', ' '); + var vals = line.split(' '); + var obj = { + offs: parseInt(vals[0], 16), + name: vals[2], + file: vals[3] + } + if (vals[1] && vals[1].toUpperCase() == 'T') { + orderedMap.push(obj); + } + } + orderedMap.sort(function(a,b) { return a.offs - b.offs; }); + return orderedMap; +} + +function updateStack(traceinfo, map) { + map = buildTextMap(map); + var text = 'Stack Trace\n'; + for (var i=0; i < traceinfo.frames.length; i++) { + var frame = traceinfo.frames[i]; + var addr = findAddress(frame.prog_ctr, map) + text += '[' + i.toString(10) + '] ' + addr + '\n'; + } + document.getElementById('trace').value = text; +} + +function fetchMap(url, traceinfo) { + var xmlhttp = new XMLHttpRequest(); + xmlhttp.open('GET', url, true); + xmlhttp.onload = function() { + updateStack(traceinfo, this.responseText); + } + xmlhttp.traceinfo = traceinfo; + xmlhttp.send(); } // Handle a message coming from the NaCl module. function handleMessage(message_event) { msg_type = message_event.data.substring(0, 4); msg_data = message_event.data.substring(5, message_event.data.length); - if (msg_type == 'POP:') { - alert(message_event.data); - return; - } if (msg_type == 'LOG:') { document.getElementById('log').value += msg_data + '\n'; return; } + if (msg_type == 'STS:') { + updateStatus(msg_data); + } if (msg_type == 'TRC:') { crashed = true; + document.getElementById('json').value = msg_data; + crash_info = JSON.parse(msg_data); updateStatus('Crash Reported'); - xmlhttp = new XMLHttpRequest(); - xmlhttp.open('POST', common.naclModule.src, false); - xmlhttp.send(msg_data); - document.getElementById('trace').value = xmlhttp.responseText + '\n'; + src = common.naclModule.getAttribute('path'); + fetchMap(src + '/debugging_' + crash_info['arch'] + '.map', crash_info); return; } } +function handleCrash(message) { + updateStatus(message); +} + function updateStatus(message) { common.updateStatus(message); @@ -53,18 +127,5 @@ function boom() { if (!crashed) { updateStatus('Send BOOM'); common.naclModule.postMessage('BOOM'); - setTimeout(boom, 1000); - } -} - -function heartBeat() { - if (common.naclModule && common.naclModule.lastError) { - if (lastModuleError != common.naclModule.lastError) { - lastModuleError = common.naclModule.lastError; - updateStatus('Missed heartbeat: ' + common.naclModule.lastError); - crashed = true; - } - } else { - setTimeout(heartBeat, 1000); } } diff --git a/native_client_sdk/src/examples/tutorial/debugging/handler.py b/native_client_sdk/src/examples/tutorial/debugging/handler.py deleted file mode 100644 index 4593223..0000000 --- a/native_client_sdk/src/examples/tutorial/debugging/handler.py +++ /dev/null @@ -1,124 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Wrap the standard run.py to handle additional POST messages needed for -debugging. - -See <NACL_SDK_ROOT>/tools/run.py for more information. -""" - -import os -import SimpleHTTPServer # pylint: disable=W0611 -import sys -import urlparse - -SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) -NACL_SDK_ROOT = os.path.dirname(os.path.dirname(SCRIPT_DIR)) -TOOLS_DIR = os.path.join(NACL_SDK_ROOT, 'tools') - -sys.path.append(TOOLS_DIR) -import decode_dump -import getos - - -last_nexe = None -last_nmf = None - - -# "Safely" split a string at |sep| into a [key, value] pair. If |sep| does not -# exist in |pair|, then the entire |pair| is the key and the value is set to an -# empty string. -def KeyValuePair(pair, sep='='): - if sep in pair: - return pair.split(sep) - else: - return [pair, ''] - - -# $(NACL_SDK_ROOT)/tools/run.py looks for a file named handler.py in any -# directory serving a file, then uses the class name HTTPRequestHandlerDelegate -# to handle GET and POST requests for that directory. -class HTTPRequestHandlerDelegate(object): - def send_head(self, handler): - """Common code for GET and HEAD commands. - - This sends the response code and MIME headers. - - Return value is either a file object (which has to be copied - to the outputfile by the caller unless the command was HEAD, - and must be closed by the caller under all circumstances), or - None, in which case the caller has nothing further to do. - - """ - path = handler.translate_path(handler.path) - f = None - if os.path.isdir(path): - if not handler.path.endswith('/'): - # redirect browser - doing basically what apache does - handler.send_response(301) - handler.send_header("Location", handler.path + "/") - handler.end_headers() - return None - for index in "index.html", "index.htm": - index = os.path.join(path, index) - if os.path.exists(index): - path = index - break - else: - return handler.list_directory(path) - ctype = handler.guess_type(path) - try: - # Always read in binary mode. Opening files in text mode may cause - # newline translations, making the actual size of the content - # transmitted *less* than the content-length! - f = open(path, 'rb') - except IOError: - handler.send_error(404, "File not found") - return None - handler.send_response(200) - handler.send_header("Content-type", ctype) - fs = os.fstat(f.fileno()) - handler.send_header("Content-Length", str(fs[6])) - handler.send_header("Last-Modified", handler.date_time_string(fs.st_mtime)) - handler.send_header('Cache-Control','no-cache, must-revalidate') - handler.send_header('Expires','-1') - handler.end_headers() - return f - - def do_GET(self, handler): - global last_nexe, last_nmf - (_, _, path, query, _) = urlparse.urlsplit(handler.path) - url_params = dict([KeyValuePair(key_value) - for key_value in query.split('&')]) - if 'quit' in url_params and '1' in url_params['quit']: - handler.send_response(200, 'OK') - handler.send_header('Content-type', 'text/html') - handler.send_header('Content-length', '0') - handler.end_headers() - handler.server.shutdown() - return - - if path.endswith('.nexe'): - last_nexe = path - if path.endswith('.nmf'): - last_nmf = path - - handler.base_do_GET() - - def do_POST(self, handler): - if 'Content-Length' in handler.headers: - if not NACL_SDK_ROOT: - handler.wfile('Could not find NACL_SDK_ROOT to decode trace.') - return - data = handler.rfile.read(int(handler.headers['Content-Length'])) - nexe = '.' + last_nexe - nmf = '.' + last_nmf - addr = os.path.join(NACL_SDK_ROOT, 'toolchain', - getos.GetPlatform() + '_x86_newlib', - 'bin', 'x86_64-nacl-addr2line') - decoder = decode_dump.CoreDecoder(nexe, nmf, addr, None, None) - info = decoder.Decode(data) - trace = decoder.StackTrace(info) - decoder.PrintTrace(trace, sys.stdout) - decoder.PrintTrace(trace, handler.wfile) diff --git a/native_client_sdk/src/examples/tutorial/debugging/hello_world.c b/native_client_sdk/src/examples/tutorial/debugging/hello_world.c index a5dd333..8cb9fdb3 100644 --- a/native_client_sdk/src/examples/tutorial/debugging/hello_world.c +++ b/native_client_sdk/src/examples/tutorial/debugging/hello_world.c @@ -25,8 +25,7 @@ #include <pthread.h> -#include "string_stream.h" -#include "untrusted_crash_dump.h" +#include "error_handling/error_handling.h" PPB_Messaging* ppb_messaging_interface = NULL; PPB_Var* ppb_var_interface = NULL; @@ -122,6 +121,14 @@ void PostMessage(const char *str) { ppb_core_interface->CallOnMainThread(0, cb, 0); } +void DumpJson(const char* json) { + char* out = (char*) malloc(strlen(json) + 5); + strcpy(out, "TRC: "); + strcat(out, json); + + PostMessage(out); + free(out); +} /** * Called when the NaCl module is instantiated on the web page. The identifier @@ -153,11 +160,16 @@ static PP_Bool Instance_DidCreate(PP_Instance instance, g_PPAPIThread = pthread_self(); PostMessage("LOG: DidCreate"); - if (!NaClCrashDumpInit()) { - PostMessage("LOG: Failed to set up crash dump."); + + /* Request exception callbacks with JSON. */ + EHRequestExceptionsJson(DumpJson); + + /* Report back if the request was honored. */ + if (!EHHanderInstalled()) { + PostMessage("LOG: Stack traces not available, so don't expect them.\n"); } else { - PostMessage("LOG: Crash Dump On"); + PostMessage("LOG: Stack traces are on."); } pthread_create(&g_NexeThread, NULL, NexeMain, NULL); return PP_TRUE; diff --git a/native_client_sdk/src/examples/tutorial/debugging/index.html b/native_client_sdk/src/examples/tutorial/debugging/index.html index d5d518b..da90b01 100644 --- a/native_client_sdk/src/examples/tutorial/debugging/index.html +++ b/native_client_sdk/src/examples/tutorial/debugging/index.html @@ -8,49 +8,73 @@ <head> <meta http-equiv="Pragma" content="no-cache" /> <meta http-equiv="Expires" content="-1" /> - <title>Logging and Stack Trace</title> + <title>Logging and Crash Handling</title> <script type="text/javascript" src="common.js"></script> <script type="text/javascript" src="example.js"></script> </head> -<body data-width="100" data-height="100" data-custom-load="true" {{attrs}}> - <h1>Native Client Debugging Example: Generating a Stack Trace.</h1> - <h2>How the example works</h2> - <p>This example shows how to trap an untrusted exception (such as a illegal - memory reference in the NEXE). This debugging technique can only be used - for development since it requires several command-line switches, and - environment variables. The test works by loading the module and communicating - with it through PostMessage. Messages from the module are sent to the Status - line and/or the Log window in the page. Four seconds after the module is - loaded, the JavaScript on this page sends a 'BOOM' message to the module that - causes the module to dereference an illegal location in memory.</p> - - <p>If your setup is correct (you launched Chrome with the appropriate - command-line arguments and environment variables), the Log window below - should show that the crash dump facilities are turned on. When the crash data - arrives from the module, the data is forwarded to the HTTP server, which - drives a decoder and sends back a stack trace to the web page.</p> - - <p>If setup incorrectly, the NaCl module may or may not load. If the module - loads, it sends a "LOADED" message to the log and crash after four seconds. - Since the stack trace facilities are not enables, the script will determine - that the module has crashed by detecting a missed heartbeat which the - application would normally send. +<body data-width="0" data-height="0" data-custom-load="true" {{attrs}}> + <h1>Logging and Crash Handling</h1> + <p> This example illustrates techniques for tracking the state of a NaCl + module via PostMessage and status of the module's lastError attribute. + Messages from the modules are in the form of + <li>"LOG: <data>" which adds the message to the log.</li> + <li>"STS: <data>" which updates the status string.</li> + <li>"TRC: <data>" which provides a JSON string defining an exception.</li> + <h2> Exception API </h2> + <p> As of Chrome 28, NativeClient exception handling is possible without + requiring special command-line flags. This feature is not always available + so developers should avoid requring it under normal operation. However it + can be a very useful tool for diagnosing crashes, especially in the field. + NativeClient provides a library called "error_handling" for registering + the exception handler, as well as unwinding the exception context. + <br><b>NOTE: The library requires '-fno-omit-frame-pointer' to facilitate + unwinding the stack.</b></p> + <h2> Trace Walkthrough </h2> + <p> First we request the exception handler interface, and use it to register + both a handler and an exception stack. We use a separate stack since we + do not know the state of stack for the thread handling the exception. + Next, we create a worker thread which will take the exception. It is + recommended that modules do as much work as possible off the main thread. + Failure to do so can block the browser, making the page unresponsive and/or + preventing communication with JavaScript. In addition blocking calls, + which can greatly simplify code, are only allowed off the main thread.</p> + <p> After two seconds, JavaScript sends a message to the module which will + cause it take an exception on the worker thread. The exception handler + unwinds the stack while creating a stringified JSON object containing the + stack frame information. Once unwound, or the buffer is exhausted, the + JSON object is sent to JavaScript for processing. + <p> The message handler in JavaScript takes the JSON object and uses the + arch key to load the appropriate MAP file using an XMLHttpRequest. It + then processes the MAP file and prints out a stack trace using the exception + data in the JSON object.</p> + <h2> Exception Handling in the Field </h2> + <p> + For real world applications, it's important to get the crash information + back the developer. In this case, the JSON object could be sent via + XMLHttpRequest. The JSON object can the be processed by the developers + QA team to manage bugs in the field. The handler.py script provided in + the example sources shows how the JSON object can be used with the tools + to provide a better stack trace. Simply cut and paste the JSON object + to a text file and run the handler.py script on it. </p> - <h2>Running the example</h2> - In a terminal window, to automatically start Chrome with the correct - environment variables and command-line switches: - <ul> - <li>Set the CHROME_PATH environment variable to the fully qualified path of - your Chrome executable.</li> - <li>From the debugging directory type: <b>make RUN</b></li> - </ul> - <div id="listener"></div> <hr> <h2>Status: <code id="statusField">NO-STATUS</code></h2> - <h2>Log</h2> - <textarea id="log" rows="10" cols="130" readonly="readonly"></textarea> + <table> + <tr> + <td><h2>Log</h2></td> + <td><h2>JSON</h2></td> + </tr> + <tr> + <td> + <textarea id="log" rows="10" cols="60" readonly="readonly"></textarea> + </td> + <td> + <textarea id="json" rows="10" cols="60" readonly="readonly"></textarea> + </td> + </tr> + </table> <br> <h2>Stack Trace</h2> <textarea id="trace" rows="10" cols="130" readonly="readonly"></textarea> diff --git a/native_client_sdk/src/examples/tutorial/debugging/untrusted_crash_dump.c b/native_client_sdk/src/examples/tutorial/debugging/untrusted_crash_dump.c deleted file mode 100644 index 959ad9e..0000000 --- a/native_client_sdk/src/examples/tutorial/debugging/untrusted_crash_dump.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (c) 2012 The Native Client Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#include <assert.h> -#include <inttypes.h> -#include <pthread.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> - -#ifdef __GLIBC__ -#include <elf.h> -#include <link.h> -#endif /* __GLIBC__ */ - -#include "untrusted_crash_dump.h" -#include "string_stream.h" -#include "irt.h" - -struct NaClExceptionContext { - uint32_t prog_ctr; - uint32_t stack_ptr; - uint32_t frame_ptr; - /* - * Pad this up to an even multiple of 8 bytes so this struct will add - * a predictable amount of space to the various ExceptionFrame structures - * used on each platform. This allows us to verify stack layout with dead - * reckoning, without access to the ExceptionFrame structure used to set up - * the call stack. - */ - uint32_t pad; -}; - -#define CRASH_PAGE_CHUNK (64 * 1024) -#define CRASH_STACK_SIZE (CRASH_PAGE_CHUNK * 4) -#define CRASH_STACK_GUARD_SIZE CRASH_PAGE_CHUNK -#define CRASH_STACK_COMPLETE_SIZE (CRASH_STACK_GUARD_SIZE + CRASH_STACK_SIZE) - - -static pthread_key_t g_CrashStackKey; -static struct nacl_irt_dev_exception_handling g_ExceptionHandling; -static int g_ExceptionHandlingEnabled = 0; - - -#ifdef __GLIBC__ - -struct ProgramTableData { - sstream_t *core; - int first; -}; - -static void WriteJsonString(const char *str, sstream_t *core) { - char ch; - - ssprintf(core, "\""); - for (;;) { - ch = *str++; - if (ch == '\0') { - break; - } else if (ch == '"') { - ssprintf(core, "\\\""); - } else if (ch == '\\') { - ssprintf(core, "\\\\"); - } else if (ch < 32 || ch > 126) { - ssprintf(core, "\\x%02x", (uint8_t)ch); - } else { - ssprintf(core, "%c", ch); - } - } - ssprintf(core, "\""); -} - -static int PrintSegmentsOne( - struct dl_phdr_info *info, size_t size, void *data) { - int i; - struct ProgramTableData *ptd = (struct ProgramTableData*) data; - - if (ptd->first) { - ptd->first = 0; - } else { - fprintf(ptd->core, ",\n"); - } - fprintf(ptd->core, "{\n"); - fprintf(ptd->core, "\"dlpi_name\": "); - WriteJsonString(info->dlpi_name, ptd->core); - fprintf(ptd->core, ",\n"); - fprintf(ptd->core, "\"dlpi_addr\": %u,\n", - (uintptr_t) info->dlpi_addr); - fprintf(ptd->core, "\"dlpi_phdr\": [\n"); - for (i = 0; i < info->dlpi_phnum; i++) { - /* Skip non-LOAD type segments. */ - if (info->dlpi_phdr[i].p_type != PT_LOAD) { - continue; - } - if (i != 0) { - fprintf(ptd->core, ",\n"); - } - fprintf(ptd->core, "{\n"); - fprintf(ptd->core, "\"p_vaddr\": %u,\n", - (uintptr_t) info->dlpi_phdr[i].p_vaddr); - fprintf(ptd->core, "\"p_memsz\": %u\n", - (uintptr_t) info->dlpi_phdr[i].p_memsz); - fprintf(ptd->core, "}\n"); - } - fprintf(ptd->core, "]\n"); - fprintf(ptd->core, "}\n"); - return 0; -} - -static void PrintSegments(sstream_t *core) { - struct ProgramTableData data; - data.core = core; - data.first = 1; - dl_iterate_phdr(PrintSegmentsOne, &data); -} - -#else /* __GLIBC__ */ - -static void PrintSegments(sstream_t *core) { -} - -#endif /* __GLIBC__ */ - - -void PostMessage(const char *str); - - -uintptr_t SafeRead(uintptr_t a) { - /* TODO(bradnelson): use exception handling to recover from reads. */ - return *(uintptr_t*)a; -} - -static void StackWalk(sstream_t *core, struct NaClExceptionContext *context) { - uintptr_t next; - uintptr_t i; - int first = 1; - uintptr_t prog_ctr = context->prog_ctr; - uintptr_t frame_ptr = context->frame_ptr; - uintptr_t args_start; - - ssprintf(core, "\"frames\": [\n"); - for (;;) { - next = SafeRead(frame_ptr); - if (next <= frame_ptr || next == 0) { - break; - } - if (first) { - first = 0; - } else { - ssprintf(core, ","); - } - ssprintf(core, "{\n"); - ssprintf(core, "\"frame_ptr\": %u,\n", frame_ptr); - ssprintf(core, "\"prog_ctr\": %u,\n", prog_ctr); - ssprintf(core, "\"data\": [\n"); -#if defined(__x86_64__) - args_start = frame_ptr + 8; -#else - args_start = frame_ptr + 16; -#endif - for (i = args_start; i < next && i - args_start < 100; i += 4) { - if (i != args_start) { - ssprintf(core, ","); - } - ssprintf(core, "%u\n", SafeRead(i)); - } - ssprintf(core, "]\n"); - ssprintf(core, "}\n"); - - if (next - frame_ptr > 10000) break; -#if defined(__x86_64__) - prog_ctr = SafeRead(frame_ptr + 8); -#else - prog_ctr = SafeRead(frame_ptr + 4); -#endif - frame_ptr = next; - } - - ssprintf(core, "]\n"); -} - -void CrashHandler(struct NaClExceptionContext *context) { - sstream_t ss; - FILE* handle = fopen("naclcorejson", "wb"); - - ssinit(&ss); - ssprintf(&ss, "TRC: {\n"); - - ssprintf(&ss, "\"segments\": ["); - PrintSegments(&ss); - ssprintf(&ss, "],\n"); - - ssprintf(&ss, "\"handler\": {\n"); - ssprintf(&ss, "\"prog_ctr\": %u,\n", context->prog_ctr); - ssprintf(&ss, "\"stack_ptr\": %u,\n", context->stack_ptr); - ssprintf(&ss, "\"frame_ptr\": %u\n", context->frame_ptr); - ssprintf(&ss, "},\n"); - - StackWalk(&ss, context); - - ssprintf(&ss, "}\n"); - - if (handle != NULL) { - fprintf(handle, "%s", &ss.data[5]); - fclose(handle); - } - - PostMessage(ss.data); - - while(1); -} - -void NaClCrashDumpThreadDestructor(void *arg) { - munmap(arg, CRASH_STACK_COMPLETE_SIZE); -} - -int NaClCrashDumpInit(void) { - int result; - - assert(g_ExceptionHandlingEnabled == 0); - if (nacl_interface_query(NACL_IRT_DEV_EXCEPTION_HANDLING_v0_1, - &g_ExceptionHandling, - sizeof(g_ExceptionHandling)) == 0) { - fprintf(stderr, "ERROR: failed nacl_interface_query\n"); - return 0; - } - result = pthread_key_create(&g_CrashStackKey, NaClCrashDumpThreadDestructor); - assert(result == 0); - if (g_ExceptionHandling.exception_handler(CrashHandler, NULL) != 0) { - fprintf(stderr, "ERROR: failed exception_handler\n"); - return 0; - } - g_ExceptionHandlingEnabled = 1; - if (!NaClCrashDumpInitThread()) { - g_ExceptionHandlingEnabled = 0; - fprintf(stderr, "ERROR: failed InitThread\n"); - return 0; - } - return 1; -} - -int NaClCrashDumpInitThread(void) { - void *stack; - void *guard; - int result; - - if (!g_ExceptionHandlingEnabled) { - return 0; - } - /* - * NOTE: Setting up a per thread stack is only particularly interesting - * for stack overflow. - */ - stack = mmap(NULL, CRASH_STACK_COMPLETE_SIZE, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - assert(stack != MAP_FAILED); - guard = mmap(stack, CRASH_STACK_GUARD_SIZE, - PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - assert(guard == stack); - pthread_setspecific(g_CrashStackKey, stack); - result = g_ExceptionHandling.exception_stack( - stack, CRASH_STACK_COMPLETE_SIZE); - return result == 0; -} diff --git a/native_client_sdk/src/examples/tutorial/debugging/untrusted_crash_dump.h b/native_client_sdk/src/examples/tutorial/debugging/untrusted_crash_dump.h deleted file mode 100644 index 8cd973a..0000000 --- a/native_client_sdk/src/examples/tutorial/debugging/untrusted_crash_dump.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2012 The Native Client Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -/* - * Untrusted crash dumper. - */ - -#ifndef NATIVE_CLIENT_SRC_UNTRUSTED_CRASH_DUMP_UNTRUSTED_CRASH_DUMP_H__ -#define NATIVE_CLIENT_SRC_UNTRUSTED_CRASH_DUMP_UNTRUSTED_CRASH_DUMP_H__ 1 - - -#ifdef __cplusplus -extern "C" { -#endif - -/* These return non-zero on success. */ -int NaClCrashDumpInit(void); -int NaClCrashDumpInitThread(void); - -#ifdef __cplusplus -} -#endif - -#endif /* NATIVE_CLIENT_SRC_UNTRUSTED_CRASH_DUMP_UNTRUSTED_CRASH_DUMP_H__ */ diff --git a/native_client_sdk/src/libraries/error_handling/error_handling.c b/native_client_sdk/src/libraries/error_handling/error_handling.c new file mode 100644 index 0000000..e2fec20 --- /dev/null +++ b/native_client_sdk/src/libraries/error_handling/error_handling.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#include <assert.h> +#include <inttypes.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <unistd.h> + +#ifdef __GLIBC__ +#include <elf.h> +#include <link.h> +#endif /* __GLIBC__ */ + +#include "irt.h" +#include "nacl/nacl_exception.h" + +#include "error_handling/error_handling.h" +#include "error_handling/string_stream.h" + + +#define PAGE_CHUNK_SIZE (64 * 1024) +#define PAGE_CHUNK_MASK (PAGE_CHUNK_SIZE - 1) +#define STACK_SIZE_MIN (PAGE_CHUNK_SIZE * 4) + +#define MAX_FRAME_SIZE (10 * 1024) +#define MAX_FRAME_CAP 128 + +static pthread_key_t s_eh_stack_info_key; +static EHJsonHandler s_eh_json_callback = NULL; +static int s_eh_exception_enabled = 0; +static struct nacl_irt_exception_handling s_exception_handling; + + +typedef struct { + void* stack; + size_t size; +} EHStackInfo; + +static uintptr_t EHReadPointer(uintptr_t offset) { + return *((uintptr_t*) offset); +} + +static void EHPrintArch(sstream_t* ss, struct NaClExceptionContext* context) { +#if defined(__x86_64__) + ssprintf(ss, "\"arch\": \"x86_64\",\n"); +#elif defined(__i386__) + ssprintf(ss, "\"arch\": \"x86_32\",\n"); +#elif defined(__arm__) + ssprintf(ss, "\"arch\": \"arm\",\n"); +#elif defined(__mips__) + ssprintf(ss, "\"arch\": \"mips\",\n"); +#else +#error Unknown ARCH +#endif +} + +static void EHPrintSegments(sstream_t* ss, + struct NaClExceptionContext* context) { + ssprintf(ss, "\"segments\": ["); + ssprintf(ss, "],\n"); +} + +static void EHPrintFrame(sstream_t* ss, EHFrame* frame) { + uintptr_t start; + uintptr_t i; + + ssprintf(ss, "{\n"); + ssprintf(ss, "\"frame_ptr\": %u,\n", frame->frame_ptr); + ssprintf(ss, "\"prog_ctr\": %u,\n", frame->prog_ctr); + ssprintf(ss, "\"data\": [\n"); + +#if defined(__x86_64__) + start = frame->frame_ptr + 8; +#else + start = frame->frame_ptr + 16; +#endif + /* Capture the stack, no mare than 128 bytes to keep the size sane. */ + for (i = start; i < frame->next_ptr && i - start < MAX_FRAME_CAP; i += 4) { + if (i != start) { + ssprintf(ss, ","); + } + ssprintf(ss, "%u\n", EHReadPointer(i)); + } + ssprintf(ss, "]\n}\n"); +} + + +static void EHPrintMainContext(sstream_t* ss, + struct NaClExceptionContext* context) { + struct NaClExceptionPortableContext* portable_context = + nacl_exception_context_get_portable(context); + ssprintf(ss, "\"handler\": {\n"); + ssprintf(ss, "\"prog_ctr\": %u,\n", portable_context->prog_ctr); + ssprintf(ss, "\"stack_ptr\": %u,\n", portable_context->stack_ptr); + ssprintf(ss, "\"frame_ptr\": %u\n", portable_context->frame_ptr); + ssprintf(ss, "},\n"); +} + + +int EHGetTopFrame(sstream_t* ss, struct NaClExceptionContext* context, + EHFrame* frame) { + struct NaClExceptionPortableContext* portable_context = + nacl_exception_context_get_portable(context); + + frame->prog_ctr = portable_context->prog_ctr; + frame->frame_ptr = portable_context->frame_ptr; + frame->next_ptr = EHReadPointer(portable_context->frame_ptr); + return 1; +} + + +int EHUnwindFrame(EHFrame* frame) { + uintptr_t frame_ptr; + uintptr_t next; + + // Verify the current frame + if (NULL == frame) return 0; + + frame_ptr = frame->frame_ptr; + next = frame->next_ptr; + + // Abort if done or unwind moves us in the wrong direction + if (next <= frame_ptr || next == 0) return 0; + + // Abort if frame is > 10K + if (next - frame_ptr > MAX_FRAME_SIZE) return 0; + + // Unwind the frame + frame->frame_ptr = next; + frame->next_ptr = EHReadPointer(frame->frame_ptr); +#if defined(__x86_64__) + frame->prog_ctr = EHReadPointer(frame_ptr + 8); +#else + frame->prog_ctr = EHReadPointer(frame_ptr + 4); +#endif + return 1; +} + + +static void EHStackInfoDestructor(void *arg) { + EHStackInfo* info = (EHStackInfo*) arg; + + if (info) { + munmap(info->stack, info->size); + } + free(info); +} + + +void EHDefaultJsonHandler(struct NaClExceptionContext* context) { + if (s_eh_json_callback) { + EHFrame frame; + sstream_t ss; + ssinit(&ss); + + ssprintf(&ss, "{\n"); + EHPrintArch(&ss, context); + EHPrintSegments(&ss, context); + EHPrintMainContext(&ss, context); + + EHGetTopFrame(&ss, context, &frame); + int first = 1; + + ssprintf(&ss, "\"frames\": [\n"); + do { + if (!first) ssprintf(&ss, ","); + EHPrintFrame(&ss, &frame); + first = 0; + } while (EHUnwindFrame(&frame)); + + /* End frame LIST and context DICT */ + ssprintf(&ss, "]\n}\n"); + s_eh_json_callback(ss.data); + + ssfree(&ss); + while(1) sleep(9999); + } +} + + +void EHRequestExceptionsRaw(EHRawHandler callback) { + size_t interface_size = sizeof(s_exception_handling); + if (s_eh_exception_enabled) { + fprintf(stderr, "ERROR: EHInit already initialized.\n"); + return; + } + if (NULL == callback) { + fprintf(stderr, "ERROR: EHInit called with NULL callback.\n"); + return; + } + + /* Expect an exact match on the interface structure size. */ + if (nacl_interface_query(NACL_IRT_EXCEPTION_HANDLING_v0_1, + &s_exception_handling, + interface_size) != interface_size) { + fprintf(stderr, "ERROR: EHInit failed nacl_interface_query\n"); + return; + } + + if (s_exception_handling.exception_handler(callback, NULL) != 0) { + fprintf(stderr, "ERROR: EHInit failed to install exception_handler\n"); + return; + } + + s_eh_exception_enabled = 1; + + // Create a TLS key for storing per thread stack info + pthread_key_create(&s_eh_stack_info_key, EHStackInfoDestructor); +} + + +void *EHRequestExceptionStackOnThread(size_t stack_size) { + void* stack; + void* guard; + EHStackInfo* stack_info; + + // Set the stack size + stack_size = (stack_size + PAGE_CHUNK_MASK) & PAGE_CHUNK_MASK; + if (stack_size < STACK_SIZE_MIN) stack_size = STACK_SIZE_MIN; + + // Allocate stack + guard page + stack = mmap(NULL, stack_size + PAGE_CHUNK_SIZE, + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (MAP_FAILED == stack) return MAP_FAILED; + + // Remap to mprotect which may not be available + guard = mmap(stack, PAGE_CHUNK_SIZE, + PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (guard != stack) { + fprintf(stderr, "WARNING: Failed to add guard page for alt stack.\n"); + } + + if (!s_exception_handling.exception_stack(stack, stack_size)) { + fprintf(stderr, "ERROR: Failed to assign stack.\n"); + munmap(stack, stack_size); + return MAP_FAILED; + } + + // Allocate stack tracking information for this thread + stack_info = (EHStackInfo*) malloc(sizeof(EHStackInfo)); + stack_info->stack = stack; + stack_info->size = stack_size + PAGE_CHUNK_SIZE; + pthread_setspecific(s_eh_stack_info_key, stack_info); + return stack; +} + + +void EHRequestExceptionsJson(EHJsonHandler callback) { + if (NULL == callback) return; + + EHRequestExceptionsRaw(EHDefaultJsonHandler); + if (s_eh_exception_enabled) s_eh_json_callback = callback; +} + + +int EHHanderInstalled() { + return s_eh_exception_enabled; +}
\ No newline at end of file diff --git a/native_client_sdk/src/libraries/error_handling/error_handling.h b/native_client_sdk/src/libraries/error_handling/error_handling.h new file mode 100644 index 0000000..29b4aca --- /dev/null +++ b/native_client_sdk/src/libraries/error_handling/error_handling.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef ERROR_HANDLING_ERROR_HANDLING_H_ +#define ERROR_HANDLING_ERROR_HANDLING_H_ + +#include "error_handling/string_stream.h" +#include "utils/macros.h" + +EXTERN_C_BEGIN + +struct NaClExceptionContext; + +typedef void (*EHRawHandler)(struct NaClExceptionContext* context); +typedef void (*EHJsonHandler)(const char* str); + +typedef struct { + uint32_t prog_ctr; + uint32_t frame_ptr; + uint32_t next_ptr; +} EHFrame; + + +/** Initialize error handling. + * + * Initializes the error handling to catch untrusted exceptions. The init + * functions will install an untrusted exception handler. + * + * The raw form will install the provided handler directly to the exception + * system. + * + * The json form will install a default handler which will walk the stack + * creating a valid JSON string which is passed to the provided handler. + * + * NOTE: Exception handling is enabled process wide. + * NOTE: Exception handling is not guaranteed to be available so it should + * not be considered an error if the request fails. + */ +void EHRequestExceptionsRaw(EHRawHandler callback); +void EHRequestExceptionsJson(EHJsonHandler callback); + + +/** Request an alternate signal handling stack for this thread. + * + * Specifies an alternate stack which will be used when this thread enters + * the exception handler. This is useful in cases when the threads original + * stack may have overflowed or may be too small to handler the exception. + * + * Returns the allocated stack or MAP_FAILED. + * + * NOTE: Unlike the handler, this is a per thread call. + * NOTE: If the allocation fails, the exception will still take place on the + * thread's original stack. + */ +void *EHRequestExceptionStackOnThread(size_t stack_size); + + +/** Determine if NaCl will forward exceptions. + * + * Returns non-zero if a hander has been installed and exceptions will + * be forwarded. + * + * NOTE: Exception handling is not guarenteed to be available so it should + * not be considered an error if the request fails. + */ +int EHHanderInstalled(); + + +/** Fill an exception frame from an exception context. */ +int EHGetTopFrame(sstream_t* ss, struct NaClExceptionContext* context, + EHFrame* frame); + + +/** Unwind the stack by one frame. + * + * Returns zero once it failes to unwind. + */ +int EHUnwindFrame(EHFrame* frame); + +EXTERN_C_END + +#endif // ERROR_HANDLING_ERROR_HANDLING_H_
\ No newline at end of file diff --git a/native_client_sdk/src/libraries/error_handling/library.dsc b/native_client_sdk/src/libraries/error_handling/library.dsc new file mode 100644 index 0000000..f8ff7b6 --- /dev/null +++ b/native_client_sdk/src/libraries/error_handling/library.dsc @@ -0,0 +1,24 @@ +{ + 'TOOLS': ['newlib'], + 'TARGETS': [ + { + 'NAME' : 'error_handling', + 'TYPE' : 'lib', + 'SOURCES' : [ + "error_handling.c", + "string_stream.c" + ], + } + ], + 'HEADERS': [ + { + 'FILES': [ + "error_handling.h", + "string_stream.h", + ], + 'DEST': 'include/error_handling', + }, + ], + 'DEST': 'src', + 'NAME': 'error_handling', +} diff --git a/native_client_sdk/src/examples/tutorial/debugging/string_stream.c b/native_client_sdk/src/libraries/error_handling/string_stream.c index 08783cc..4c1f7ed 100644 --- a/native_client_sdk/src/examples/tutorial/debugging/string_stream.c +++ b/native_client_sdk/src/libraries/error_handling/string_stream.c @@ -1,8 +1,14 @@ +/* + * Copyright (c) 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + #include <stdlib.h> #include <stdio.h> #include <string.h> -#include "string_stream.h" +#include "error_handling/string_stream.h" void ssinit(sstream_t *stream) { stream->data = NULL; @@ -32,6 +38,7 @@ int ssvprintf(sstream_t *stream, const char *format, va_list args) { stream->data = outstr; vsprintf(&stream->data[stream->length], format, hold); stream->length += len; + return len; } diff --git a/native_client_sdk/src/examples/tutorial/debugging/string_stream.h b/native_client_sdk/src/libraries/error_handling/string_stream.h index 9bb67e0..3c68dd6 100644 --- a/native_client_sdk/src/examples/tutorial/debugging/string_stream.h +++ b/native_client_sdk/src/libraries/error_handling/string_stream.h @@ -1,11 +1,11 @@ /* - * Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Copyright (c) 2013 The Chromium Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ -#ifndef STRING_STREAM_H -#define STRING_STREAM_H +#ifndef ERROR_HANDLING_STRING_STREAM_H +#define ERROR_HANDLING_STRING_STREAM_H /* * Support for a stream stream in 'C', which is appended to via an sprintf-like @@ -28,4 +28,4 @@ int ssvprintf(sstream_t *sstream, const char *format, va_list args); int ssprintf(sstream_t *sstream, const char *format, ...); -#endif +#endif /* ERROR_HANDLING_STRING_STREAM_H */ diff --git a/native_client_sdk/src/tools/nacl_gcc.mk b/native_client_sdk/src/tools/nacl_gcc.mk index bdbe33d..ea55765 100644 --- a/native_client_sdk/src/tools/nacl_gcc.mk +++ b/native_client_sdk/src/tools/nacl_gcc.mk @@ -29,18 +29,21 @@ X86_32_CXX?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/i686-nacl-g++ X86_32_LINK?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/i686-nacl-g++ X86_32_LIB?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/i686-nacl-ar X86_32_STRIP?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/i686-nacl-strip +X86_32_NM?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/i686-nacl-nm X86_64_CC?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/x86_64-nacl-gcc X86_64_CXX?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/x86_64-nacl-g++ X86_64_LINK?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/x86_64-nacl-g++ X86_64_LIB?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/x86_64-nacl-ar X86_64_STRIP?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/x86_64-nacl-strip +X86_64_NM?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/x86_64-nacl-nm ARM_CC?=$(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin/arm-nacl-gcc ARM_CXX?=$(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin/arm-nacl-g++ ARM_LINK?=$(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin/arm-nacl-g++ ARM_LIB?=$(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin/arm-nacl-ar ARM_STRIP?=$(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin/arm-nacl-strip +ARM_NM?=$(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin/arm-nacl-nm # Architecture-specific flags @@ -236,6 +239,37 @@ endef # +# Strip Macro for each arch (e.g., each arch supported by MAP_RULE). +# +# $1 = Target Name +# $2 = Source Name +# +define MAP_ALL_RULE +$(OUTDIR)/$(1)_x86_32.map : $(OUTDIR)/$(2)_x86_32.nexe + $(call LOG,MAP,$$@,$(X86_32_NM) -l $$^ > $$@) + +$(OUTDIR)/$(1)_x86_64.map : $(OUTDIR)/$(2)_x86_64.nexe + $(call LOG,MAP,$$@,$(X86_64_NM) -l $$^ > $$@) + +$(OUTDIR)/$(1)_arm.map : $(OUTDIR)/$(2)_arm.nexe + $(call LOG,MAP,$$@,$(ARM_NM) -l $$^ > $$@ ) + +all: $(OUTDIR)/$(1)_x86_32.map $(OUTDIR)/$(1)_x86_64.map $(OUTDIR)/$(1)_arm.map +endef + + +# +# Top-level MAP Generation Macro +# +# $1 = Target Basename +# $2 = Source Basename +# +define MAP_RULE +$(call MAP_ALL_RULE,$(1),$(2)) +endef + + +# # Determine which architectures to build for. The user can set NACL_ARCH or # ARCHES in the environment to control this. # |