/******************************************************************************* * Copyright (c) 2007, 2014 Wind River Systems, Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * You may elect to redistribute this code under either of these licenses. * * Contributors: * Wind River Systems - initial API and implementation *******************************************************************************/ /* * TCF service line Numbers * The service associates locations in the source files with the corresponding * machine instruction addresses in the executable object. */ #include #if SERVICE_LineNumbers && (!ENABLE_LineNumbersProxy || ENABLE_LineNumbersMux) && ENABLE_PE #include #include #include #include #include #include #include #include #include #include #include #include #if ENABLE_LineNumbersMux #define LINENUMBERS_READER_PREFIX win32_reader_ #include #endif static int compare_path(Channel * chnl, Context * ctx, const char * file, const char * name) { int i, j; char * full_name = NULL; if (file == NULL) return 0; if (name == NULL) return 0; while (file[0] == '.') { if (file[1] == '.' && file[2] == '/') file += 3; else if (file[1] == '/') file += 2; else break; } i = strlen(file); full_name = canonic_path_map_file_name(name); j = strlen(full_name); if (i <= j && strcmp(file, full_name + j - i) == 0) return 1; #if SERVICE_PathMap { char * s = apply_path_map(chnl, ctx, full_name, PATH_MAP_TO_CLIENT); if (s != full_name) { full_name = canonic_path_map_file_name(s); j = strlen(full_name); if (i <= j && strcmp(file, full_name + j - i) == 0) return 1; } } #endif return 0; } int line_to_address(Context * ctx, const char * file, int line, int column, LineNumbersCallBack * callback, void * user_args) { int err = 0; if (ctx == NULL) err = ERR_INV_CONTEXT; else if (ctx->exited) err = ERR_ALREADY_EXITED; if (err == 0 && ctx->parent != NULL) ctx = ctx->parent; if (err == 0) { CodeArea area; LONG offset = 0; IMAGEHLP_LINE img_line; Channel * chnl = cache_channel(); memset(&area, 0, sizeof(area)); memset(&img_line, 0, sizeof(img_line)); img_line.SizeOfStruct = sizeof(IMAGEHLP_LINE); file = canonic_path_map_file_name(file); if (!SymGetLineFromName(get_context_handle(ctx), NULL, file, line, &offset, &img_line)) { DWORD win_err = GetLastError(); if (win_err != ERROR_NOT_FOUND) { err = set_win32_errno(win_err); } } else { IMAGEHLP_LINE img_next; memcpy(&img_next, &img_line, sizeof(img_next)); img_next.SizeOfStruct = sizeof(IMAGEHLP_LINE); if (!SymGetLineNext(get_context_handle(ctx), &img_next)) { err = set_win32_errno(GetLastError()); } else if (compare_path(chnl, ctx, file, img_line.FileName)) { area.file = img_line.FileName; area.start_line = img_line.LineNumber; area.start_address = img_line.Address; area.end_line = img_next.LineNumber; area.end_address = img_next.Address; callback(&area, user_args); } } } if (err != 0) { errno = err; return -1; } return 0; } #define JMPD08 0xeb #define JMPD32 0xe9 #define GRP5 0xff #define JMPN 0x25 int address_to_line(Context * ctx, ContextAddress addr0, ContextAddress addr1, LineNumbersCallBack * callback, void * user_args) { int err = 0; int not_found = 0; DWORD offset = 0; IMAGEHLP_LINE line; IMAGEHLP_LINE next; ContextAddress org_addr0 = addr0; ContextAddress org_addr1 = addr1; if (ctx == NULL) err = ERR_INV_CONTEXT; else if (ctx->exited) err = ERR_ALREADY_EXITED; if (err == 0 && ctx->parent != NULL) ctx = ctx->parent; memset(&line, 0, sizeof(line)); line.SizeOfStruct = sizeof(IMAGEHLP_LINE); if (addr0 >= addr1) not_found = 1; while (err == 0 && not_found == 0 && !SymGetLineFromAddr(get_context_handle(ctx), addr0, &offset, &line)) { DWORD w = GetLastError(); if (w == ERROR_MOD_NOT_FOUND) { not_found = 1; } else if (w == ERROR_INVALID_ADDRESS) { /* Check if the address points to a jump instruction (e.g. inside a jump table) * and try to get line info for jump destination address. */ unsigned char instr; /* instruction opcode at */ ContextAddress dest = 0; /* Jump destination address */ if (context_read_mem(ctx, addr0, &instr, 1) == 0) { /* If instruction is a JMP, get destination adrs */ if (instr == JMPD08) { signed char disp08; if (context_read_mem(ctx, addr0 + 1, &disp08, 1) == 0) { dest = addr0 + 2 + disp08; org_addr1 = addr0 + 2; } } else if (instr == JMPD32) { int disp32; assert(sizeof(disp32) == 4); if (context_read_mem(ctx, addr0 + 1, &disp32, 4) == 0) { dest = addr0 + 5 + disp32; org_addr1 = addr0 + 5; } } else if (instr == GRP5) { if (context_read_mem(ctx, addr0 + 1, &instr, 1) == 0 && instr == JMPN) { ContextAddress ptr = 0; if (context_read_mem(ctx, addr0 + 2, &ptr, 4) == 0) { context_read_mem(ctx, ptr, &dest, 4); org_addr1 = addr0 + 6; } } } } if (dest != 0) { addr0 = dest; addr1 = dest + 1; } else { not_found = 1; } } else { err = set_win32_errno(w); } } memcpy(&next, &line, sizeof(next)); if (err == 0 && !not_found && !SymGetLineNext(get_context_handle(ctx), &next)) { DWORD w = GetLastError(); if (w == ERROR_NOT_FOUND) { /* Last line in the source file */ ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)]; SYMBOL_INFO * info = (SYMBOL_INFO *)buffer; info->SizeOfStruct = sizeof(SYMBOL_INFO); info->MaxNameLen = MAX_SYM_NAME; if (SymFromAddr(get_context_handle(ctx), next.Address, NULL, info)) { next.Address = (ULONG_PTR)info->Address + info->Size; next.LineNumber++; } } else { err = set_win32_errno(GetLastError()); } } if (err == 0 && !not_found) { while (line.Address < next.Address && line.Address < addr1 && next.Address > addr0) { CodeArea area; memset(&area, 0, sizeof(area)); area.file = line.FileName; area.start_address = line.Address; area.start_line = line.LineNumber; area.end_address = next.Address; area.end_line = next.LineNumber; if (org_addr0 != addr0) { area.start_address = org_addr0; area.end_address = org_addr1; } callback(&area, user_args); memcpy(&line, &next, sizeof(line)); if (!SymGetLineNext(get_context_handle(ctx), &next)) break; } } if (err != 0) { errno = err; return -1; } return 0; } void ini_line_numbers_lib(void) { SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS); #if ENABLE_LineNumbersMux add_line_numbers_reader(&line_numbers_reader); #endif } #endif /* SERVICE_LineNumbers && (!ENABLE_LineNumbersProxy || ENABLE_LineNumbersMux) && ENABLE_PE */