/* * CMSIS Debug Adapter * Copyright (c) 2017-2019 Marcel Ball * Copyright (c) 2019 Arm Limited * * The MIT License (MIT) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ import { spawnSync } from 'child_process'; import { EOL } from 'os'; import { dirname, normalize, basename } from 'path'; export enum SymbolType { Function, File, Object, Normal } export enum SymbolScope { Local, Global, Neither, Both } export interface SymbolInformation { address: number; length: number; name: string; section: string; type: SymbolType; scope: SymbolScope; file?: string; hidden: boolean; } const SYMBOL_REGEX = /^([0-9a-f]{8})\s([lg\ !])([w\ ])([C\ ])([W\ ])([I\ ])([dD\ ])([FfO\ ])\s([^\s]+)\s([0-9a-f]+)\s(.*)\r?$/; const TYPE_MAP: { [id: string]: SymbolType } = { 'F': SymbolType.Function, 'f': SymbolType.File, 'O': SymbolType.Object, ' ': SymbolType.Normal }; const SCOPE_MAP: { [id: string]: SymbolScope } = { 'l': SymbolScope.Local, 'g': SymbolScope.Global, ' ': SymbolScope.Neither, '!': SymbolScope.Both }; export class SymbolTable { private symbols: SymbolInformation[] = []; constructor(private program: string, private objdump?: string) { const varRegexp = /\$\{.*\}/; if (varRegexp.test(program)) { throw new Error(`Unresolved variable: ${program}`); } if (objdump && varRegexp.test(objdump)) { throw new Error(`Unresolved variable: ${objdump}`); } } public async loadSymbols(): Promise { const results = await this.execute(); const output = results.toString(); const lines = output.split(EOL); let currentFile: string | undefined; for (const line of lines) { const match = line.match(SYMBOL_REGEX); if (match) { if (match[7] === 'd' && match[8] === 'f') { currentFile = match[11].trim(); } const type = TYPE_MAP[match[8]]; const scope = SCOPE_MAP[match[2]]; let name = match[11].trim(); let hidden = false; if (name.startsWith('.hidden')) { name = name.substring(7).trim(); hidden = true; } this.symbols.push({ address: parseInt(match[1], 16), type: type, scope: scope, section: match[9].trim(), length: parseInt(match[10], 16), name: name, file: scope === SymbolScope.Local ? currentFile : undefined, hidden: hidden }); } } } public getGlobalVariables(): SymbolInformation[] { const matches = this.symbols.filter(s => s.type === SymbolType.Object && s.scope === SymbolScope.Global); return matches; } public getStaticVariables(file: string): SymbolInformation[] { return this.symbols.filter(s => s.type === SymbolType.Object // Only load objects && s.scope === SymbolScope.Local // Scoped to this file && !s.name.startsWith('.') // Ignore names beginning with '.' && (normalize(s.file || '') === normalize(file) || normalize(s.file || '') === basename(file))); // Match full path or file name } private execute(): Promise { return new Promise((resolve, reject) => { if (!this.objdump) { return reject(new Error('Missing parameter: objdump')); } try { const { stdout, stderr } = spawnSync(this.objdump, [ '--syms', this.program ], { cwd: dirname(this.objdump), windowsHide: true }); const error = stderr.toString('utf8'); if (error) { return reject(new Error(error)); } resolve(stdout.toString('utf8')); } catch (error) { return reject(new Error(error)); } }); } }