/*******************************************************************************
* 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 <tcf/config.h>
#if SERVICE_LineNumbers && (!ENABLE_LineNumbersProxy || ENABLE_LineNumbersMux) && ENABLE_PE
#include <errno.h>
#include <assert.h>
#include <stdio.h>
#include <tcf/framework/json.h>
#include <tcf/framework/protocol.h>
#include <tcf/framework/context.h>
#include <tcf/framework/exceptions.h>
#include <tcf/framework/cache.h>
#include <tcf/services/pathmap.h>
#include <tcf/services/linenumbers.h>
#include <system/Windows/tcf/windbgcache.h>
#include <system/Windows/tcf/context-win32.h>
#if ENABLE_LineNumbersMux
#define LINENUMBERS_READER_PREFIX win32_reader_
#include <tcf/services/linenumbers_mux.h>
#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_LINE64 img_line;
Channel * chnl = cache_channel();
memset(&area, 0, sizeof(area));
memset(&img_line, 0, sizeof(img_line));
img_line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
file = canonic_path_map_file_name(file);
if (!SymGetLineFromName64(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_LINE64 img_next;
memcpy(&img_next, &img_line, sizeof(img_next));
img_next.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (!SymGetLineNext64(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 = (ContextAddress)img_line.Address;
area.end_line = img_next.LineNumber;
area.end_address = (ContextAddress)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_LINE64 line;
IMAGEHLP_LINE64 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_LINE64);
if (addr0 >= addr1) not_found = 1;
while (err == 0 && not_found == 0 && !SymGetLineFromAddr64(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 <addr0> */
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 && !SymGetLineNext64(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 = (ContextAddress)line.Address;
area.start_line = line.LineNumber;
area.end_address = (ContextAddress)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 (!SymGetLineNext64(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 */