* Added vm_debug_copy_page_memory() which copies memory from a potentially not
mapped page.
* debug_{mem,strl}cpy():
- Added "team" parameter for specifying the address space the address are
to be interpreted in.
- When the standard memcpy() (with fault handler) fails, fall back to
vm_debug_copy_page_memory().
* Added debug_is_debugged_team(): Predicate returning true, if the supplied
team_id refers to the same team debug_get_debugged_thread() belongs to.
* Added DebuggedThreadSetter class for scope-based debug_set_debugged_thread().
Made use of it in several debugger functions.
* print_demangled_call() (x86): Fixed unsafe memory access.
Allows KDL stack traces to work correctly again, even if the page daemon has
already unmapped the concerned pages.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@36230 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -132,8 +132,10 @@ extern bool debug_is_kernel_memory_accessible(addr_t address, size_t size,
|
|||||||
uint32 protection);
|
uint32 protection);
|
||||||
extern int debug_call_with_fault_handler(jmp_buf jumpBuffer,
|
extern int debug_call_with_fault_handler(jmp_buf jumpBuffer,
|
||||||
void (*function)(void*), void* parameter);
|
void (*function)(void*), void* parameter);
|
||||||
extern status_t debug_memcpy(void* to, const void* from, size_t size);
|
extern status_t debug_memcpy(team_id teamID, void* to, const void* from,
|
||||||
extern ssize_t debug_strlcpy(char* to, const char* from, size_t size);
|
size_t size);
|
||||||
|
extern ssize_t debug_strlcpy(team_id teamID, char* to, const char* from,
|
||||||
|
size_t size);
|
||||||
|
|
||||||
extern char kgetc(void);
|
extern char kgetc(void);
|
||||||
extern void kputs(const char *string);
|
extern void kputs(const char *string);
|
||||||
@@ -172,8 +174,9 @@ extern status_t debug_get_next_demangled_argument(uint32* _cookie,
|
|||||||
|
|
||||||
extern struct thread* debug_set_debugged_thread(struct thread* thread);
|
extern struct thread* debug_set_debugged_thread(struct thread* thread);
|
||||||
extern struct thread* debug_get_debugged_thread();
|
extern struct thread* debug_get_debugged_thread();
|
||||||
extern struct arch_debug_registers* debug_get_debug_registers(int32 cpu);
|
extern bool debug_is_debugged_team(team_id teamID);
|
||||||
|
|
||||||
|
extern struct arch_debug_registers* debug_get_debug_registers(int32 cpu);
|
||||||
|
|
||||||
extern status_t _user_kernel_debugger(const char *message);
|
extern status_t _user_kernel_debugger(const char *message);
|
||||||
extern void _user_debug_output(const char *userString);
|
extern void _user_debug_output(const char *userString);
|
||||||
@@ -182,4 +185,27 @@ extern void _user_debug_output(const char *userString);
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
|
||||||
|
struct DebuggedThreadSetter {
|
||||||
|
DebuggedThreadSetter(struct thread* thread)
|
||||||
|
:
|
||||||
|
fPreviousThread(debug_set_debugged_thread(thread))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~DebuggedThreadSetter()
|
||||||
|
{
|
||||||
|
debug_set_debugged_thread(fPreviousThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct thread* fPreviousThread;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
|
||||||
#endif /* _KERNEL_DEBUG_H */
|
#endif /* _KERNEL_DEBUG_H */
|
||||||
|
|||||||
@@ -140,6 +140,9 @@ status_t vm_memcpy_to_physical(addr_t to, const void* from, size_t length,
|
|||||||
bool user);
|
bool user);
|
||||||
void vm_memcpy_physical_page(addr_t to, addr_t from);
|
void vm_memcpy_physical_page(addr_t to, addr_t from);
|
||||||
|
|
||||||
|
status_t vm_debug_copy_page_memory(team_id teamID, void* unsafeMemory,
|
||||||
|
void* buffer, size_t size, bool copyToUnsafe);
|
||||||
|
|
||||||
// user syscalls
|
// user syscalls
|
||||||
area_id _user_create_area(const char *name, void **address, uint32 addressSpec,
|
area_id _user_create_area(const char *name, void **address, uint32 addressSpec,
|
||||||
size_t size, uint32 lock, uint32 protection);
|
size_t size, uint32 lock, uint32 protection);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2009, Haiku Inc. All rights reserved.
|
* Copyright 2003-2010, Haiku Inc. All rights reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
@@ -66,8 +66,10 @@ static status_t
|
|||||||
get_next_frame(addr_t framePointer, addr_t *next, addr_t *ip)
|
get_next_frame(addr_t framePointer, addr_t *next, addr_t *ip)
|
||||||
{
|
{
|
||||||
stack_frame frame;
|
stack_frame frame;
|
||||||
if (debug_memcpy(&frame, (void*)framePointer, sizeof(frame)) != B_OK)
|
if (debug_memcpy(B_CURRENT_TEAM, &frame, (void*)framePointer, sizeof(frame))
|
||||||
|
!= B_OK) {
|
||||||
return B_BAD_ADDRESS;
|
return B_BAD_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
*ip = frame.return_address;
|
*ip = frame.return_address;
|
||||||
*next = (addr_t)frame.previous;
|
*next = (addr_t)frame.previous;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2009, Haiku Inc. All rights reserved.
|
* Copyright 2003-2010, Haiku Inc. All rights reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
@@ -65,8 +65,10 @@ static status_t
|
|||||||
get_next_frame(addr_t framePointer, addr_t *next, addr_t *ip)
|
get_next_frame(addr_t framePointer, addr_t *next, addr_t *ip)
|
||||||
{
|
{
|
||||||
stack_frame frame;
|
stack_frame frame;
|
||||||
if (debug_memcpy(&frame, (void*)framePointer, sizeof(frame)) != B_OK)
|
if (debug_memcpy(B_CURRENT_TEAM, &frame, (void*)framePointer, sizeof(frame))
|
||||||
|
!= B_OK) {
|
||||||
return B_BAD_ADDRESS;
|
return B_BAD_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
*ip = frame.return_address;
|
*ip = frame.return_address;
|
||||||
*next = (addr_t)frame.previous;
|
*next = (addr_t)frame.previous;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2009, Ingo Weinhold, [email protected].
|
* Copyright 2009-2010, Ingo Weinhold, [email protected].
|
||||||
* Copyright 2002-2008, Axel Dörfler, [email protected].
|
* Copyright 2002-2008, Axel Dörfler, [email protected].
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
@@ -81,7 +81,7 @@ static status_t
|
|||||||
get_next_frame_debugger(addr_t ebp, addr_t *_next, addr_t *_eip)
|
get_next_frame_debugger(addr_t ebp, addr_t *_next, addr_t *_eip)
|
||||||
{
|
{
|
||||||
stack_frame frame;
|
stack_frame frame;
|
||||||
if (debug_memcpy(&frame, (void*)ebp, sizeof(frame)) != B_OK)
|
if (debug_memcpy(B_CURRENT_TEAM, &frame, (void*)ebp, sizeof(frame)) != B_OK)
|
||||||
return B_BAD_ADDRESS;
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
*_eip = frame.return_address;
|
*_eip = frame.return_address;
|
||||||
@@ -126,6 +126,21 @@ set_debug_argument_variable(int32 index, uint64 value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
static Type
|
||||||
|
read_function_argument_value(void* argument, bool& _valueKnown)
|
||||||
|
{
|
||||||
|
Type value;
|
||||||
|
if (debug_memcpy(B_CURRENT_TEAM, &value, argument, sizeof(Type)) == B_OK) {
|
||||||
|
_valueKnown = true;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_valueKnown = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
print_demangled_call(const char* image, const char* symbol, addr_t args,
|
print_demangled_call(const char* image, const char* symbol, addr_t args,
|
||||||
bool noObjectMethod, bool addDebugVariables)
|
bool noObjectMethod, bool addDebugVariables)
|
||||||
@@ -151,10 +166,15 @@ print_demangled_call(const char* image, const char* symbol, addr_t args,
|
|||||||
const char* lastName = strrchr(name, ':') - 1;
|
const char* lastName = strrchr(name, ':') - 1;
|
||||||
int namespaceLength = lastName - name;
|
int namespaceLength = lastName - name;
|
||||||
|
|
||||||
kprintf("<%s> %.*s<\33[32m%p\33[0m>%s", image, namespaceLength, name,
|
uint32 argValue = 0;
|
||||||
*(uint32 **)arg, lastName);
|
if (debug_memcpy(B_CURRENT_TEAM, &argValue, arg, 4) == B_OK) {
|
||||||
|
kprintf("<%s> %.*s<\33[32m%#" B_PRIx32 "\33[0m>%s", image,
|
||||||
|
namespaceLength, name, argValue, lastName);
|
||||||
|
} else
|
||||||
|
kprintf("<%s> %.*s<???>%s", image, namespaceLength, name, lastName);
|
||||||
|
|
||||||
if (addDebugVariables)
|
if (addDebugVariables)
|
||||||
set_debug_variable("_this", *(uint32 *)arg);
|
set_debug_variable("_this", argValue);
|
||||||
arg++;
|
arg++;
|
||||||
} else
|
} else
|
||||||
kprintf("<%s> %s", image, name);
|
kprintf("<%s> %s", image, name);
|
||||||
@@ -172,78 +192,103 @@ print_demangled_call(const char* image, const char* symbol, addr_t args,
|
|||||||
// retrieve value and type identifier
|
// retrieve value and type identifier
|
||||||
|
|
||||||
uint64 value;
|
uint64 value;
|
||||||
|
bool valueKnown = false;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case B_INT64_TYPE:
|
case B_INT64_TYPE:
|
||||||
value = *(int64*)arg;
|
value = read_function_argument_value<int64>(arg, valueKnown);
|
||||||
kprintf("int64: \33[34m%Ld\33[0m", value);
|
if (valueKnown)
|
||||||
|
kprintf("int64: \33[34m%Ld\33[0m", value);
|
||||||
break;
|
break;
|
||||||
case B_INT32_TYPE:
|
case B_INT32_TYPE:
|
||||||
value = *(int32*)arg;
|
value = read_function_argument_value<int32>(arg, valueKnown);
|
||||||
kprintf("int32: \33[34m%ld\33[0m", (int32)value);
|
if (valueKnown)
|
||||||
|
kprintf("int32: \33[34m%ld\33[0m", (int32)value);
|
||||||
break;
|
break;
|
||||||
case B_INT16_TYPE:
|
case B_INT16_TYPE:
|
||||||
value = *(int16*)arg;
|
value = read_function_argument_value<int16>(arg, valueKnown);
|
||||||
kprintf("int16: \33[34m%d\33[0m", (int16)value);
|
if (valueKnown)
|
||||||
|
kprintf("int16: \33[34m%d\33[0m", (int16)value);
|
||||||
break;
|
break;
|
||||||
case B_INT8_TYPE:
|
case B_INT8_TYPE:
|
||||||
value = *(int8*)arg;
|
value = read_function_argument_value<int8>(arg, valueKnown);
|
||||||
kprintf("int8: \33[34m%d\33[0m", (int8)value);
|
if (valueKnown)
|
||||||
|
kprintf("int8: \33[34m%d\33[0m", (int8)value);
|
||||||
break;
|
break;
|
||||||
case B_UINT64_TYPE:
|
case B_UINT64_TYPE:
|
||||||
value = *(uint64*)arg;
|
value = read_function_argument_value<uint64>(arg, valueKnown);
|
||||||
kprintf("uint64: \33[34m%#Lx\33[0m", value);
|
if (valueKnown) {
|
||||||
if (value < 0x100000)
|
kprintf("uint64: \33[34m%#Lx\33[0m", value);
|
||||||
kprintf(" (\33[34m%Lu\33[0m)", value);
|
if (value < 0x100000)
|
||||||
|
kprintf(" (\33[34m%Lu\33[0m)", value);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case B_UINT32_TYPE:
|
case B_UINT32_TYPE:
|
||||||
value = *(uint32*)arg;
|
value = read_function_argument_value<uint32>(arg, valueKnown);
|
||||||
kprintf("uint32: \33[34m%#lx\33[0m", (uint32)value);
|
if (valueKnown) {
|
||||||
if (value < 0x100000)
|
kprintf("uint32: \33[34m%#lx\33[0m", (uint32)value);
|
||||||
kprintf(" (\33[34m%lu\33[0m)", (uint32)value);
|
if (value < 0x100000)
|
||||||
|
kprintf(" (\33[34m%lu\33[0m)", (uint32)value);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case B_UINT16_TYPE:
|
case B_UINT16_TYPE:
|
||||||
value = *(uint16*)arg;
|
value = read_function_argument_value<uint16>(arg, valueKnown);
|
||||||
kprintf("uint16: \33[34m%#x\33[0m (\33[34m%u\33[0m)",
|
if (valueKnown) {
|
||||||
(uint16)value, (uint16)value);
|
kprintf("uint16: \33[34m%#x\33[0m (\33[34m%u\33[0m)",
|
||||||
|
(uint16)value, (uint16)value);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case B_UINT8_TYPE:
|
case B_UINT8_TYPE:
|
||||||
value = *(uint8*)arg;
|
value = read_function_argument_value<uint8>(arg, valueKnown);
|
||||||
kprintf("uint8: \33[34m%#x\33[0m (\33[34m%u\33[0m)",
|
if (valueKnown) {
|
||||||
(uint8)value, (uint8)value);
|
kprintf("uint8: \33[34m%#x\33[0m (\33[34m%u\33[0m)",
|
||||||
|
(uint8)value, (uint8)value);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case B_BOOL_TYPE:
|
case B_BOOL_TYPE:
|
||||||
value = *(uint8*)arg;
|
value = read_function_argument_value<uint8>(arg, valueKnown);
|
||||||
kprintf("\33[34m%s\33[0m", value ? "true" : "false");
|
if (valueKnown)
|
||||||
|
kprintf("\33[34m%s\33[0m", value ? "true" : "false");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (buffer[0])
|
if (buffer[0])
|
||||||
kprintf("%s: ", buffer);
|
kprintf("%s: ", buffer);
|
||||||
|
|
||||||
if (length == 4) {
|
if (length == 4) {
|
||||||
value = *(uint32*)arg;
|
value = read_function_argument_value<uint32>(arg,
|
||||||
if (value == 0
|
valueKnown);
|
||||||
&& (type == B_POINTER_TYPE || type == B_REF_TYPE))
|
if (valueKnown) {
|
||||||
kprintf("NULL");
|
if (value == 0
|
||||||
else
|
&& (type == B_POINTER_TYPE || type == B_REF_TYPE))
|
||||||
kprintf("\33[34m%#lx\33[0m", (uint32)value);
|
kprintf("NULL");
|
||||||
|
else
|
||||||
|
kprintf("\33[34m%#lx\33[0m", (uint32)value);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length == 8)
|
|
||||||
value = *(uint64*)arg;
|
if (length == 8) {
|
||||||
else
|
value = read_function_argument_value<uint64>(arg,
|
||||||
|
valueKnown);
|
||||||
|
} else
|
||||||
value = (uint64)arg;
|
value = (uint64)arg;
|
||||||
kprintf("\33[34m%#Lx\33[0m", value);
|
|
||||||
|
if (valueKnown)
|
||||||
|
kprintf("\33[34m%#Lx\33[0m", value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == B_STRING_TYPE) {
|
if (!valueKnown)
|
||||||
|
kprintf("???");
|
||||||
|
|
||||||
|
if (valueKnown && type == B_STRING_TYPE) {
|
||||||
if (value == 0)
|
if (value == 0)
|
||||||
kprintf(" \33[31m\"<NULL>\"\33[0m");
|
kprintf(" \33[31m\"<NULL>\"\33[0m");
|
||||||
else if (debug_strlcpy(buffer, (char*)value, kBufferSize) < B_OK)
|
else if (debug_strlcpy(B_CURRENT_TEAM, buffer, (char*)value,
|
||||||
|
kBufferSize) < B_OK) {
|
||||||
kprintf(" \33[31m\"<???>\"\33[0m");
|
kprintf(" \33[31m\"<???>\"\33[0m");
|
||||||
else
|
} else
|
||||||
kprintf(" \33[36m\"%s\"\33[0m", buffer);
|
kprintf(" \33[36m\"%s\"\33[0m", buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -564,6 +609,8 @@ stack_trace(int argc, char **argv)
|
|||||||
&thread, &ebp, &oldPageDirectory))
|
&thread, &ebp, &oldPageDirectory))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
DebuggedThreadSetter threadSetter(thread);
|
||||||
|
|
||||||
if (thread != NULL) {
|
if (thread != NULL) {
|
||||||
kprintf("stack trace for thread %ld \"%s\"\n", thread->id,
|
kprintf("stack trace for thread %ld \"%s\"\n", thread->id,
|
||||||
thread->name);
|
thread->name);
|
||||||
@@ -736,6 +783,8 @@ show_call(int argc, char **argv)
|
|||||||
&oldPageDirectory))
|
&oldPageDirectory))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
DebuggedThreadSetter threadSetter(thread);
|
||||||
|
|
||||||
int32 callIndex = strtoul(argv[argc == 3 ? 2 : 1], NULL, 0);
|
int32 callIndex = strtoul(argv[argc == 3 ? 2 : 1], NULL, 0);
|
||||||
|
|
||||||
if (thread != NULL)
|
if (thread != NULL)
|
||||||
@@ -816,6 +865,8 @@ dump_iframes(int argc, char **argv)
|
|||||||
if (thread != NULL)
|
if (thread != NULL)
|
||||||
kprintf("iframes for thread %ld \"%s\"\n", thread->id, thread->name);
|
kprintf("iframes for thread %ld \"%s\"\n", thread->id, thread->name);
|
||||||
|
|
||||||
|
DebuggedThreadSetter threadSetter(thread);
|
||||||
|
|
||||||
struct iframe* frame = find_previous_iframe(thread, x86_read_ebp());
|
struct iframe* frame = find_previous_iframe(thread, x86_read_ebp());
|
||||||
while (frame != NULL) {
|
while (frame != NULL) {
|
||||||
print_iframe(frame);
|
print_iframe(frame);
|
||||||
@@ -889,12 +940,11 @@ cmd_in_context(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct thread* previousThread = debug_set_debugged_thread(thread);
|
|
||||||
|
|
||||||
// execute the command
|
// execute the command
|
||||||
evaluate_debug_command(commandLine);
|
{
|
||||||
|
DebuggedThreadSetter threadSetter(thread);
|
||||||
debug_set_debugged_thread(previousThread);
|
evaluate_debug_command(commandLine);
|
||||||
|
}
|
||||||
|
|
||||||
// reset the page directory
|
// reset the page directory
|
||||||
if (oldPageDirectory)
|
if (oldPageDirectory)
|
||||||
@@ -927,6 +977,8 @@ bool
|
|||||||
arch_debug_contains_call(struct thread *thread, const char *symbol,
|
arch_debug_contains_call(struct thread *thread, const char *symbol,
|
||||||
addr_t start, addr_t end)
|
addr_t start, addr_t end)
|
||||||
{
|
{
|
||||||
|
DebuggedThreadSetter threadSetter(thread);
|
||||||
|
|
||||||
addr_t ebp;
|
addr_t ebp;
|
||||||
if (thread == thread_get_current_thread())
|
if (thread == thread_get_current_thread())
|
||||||
ebp = x86_read_ebp();
|
ebp = x86_read_ebp();
|
||||||
|
|||||||
@@ -1852,33 +1852,69 @@ debug_call_with_fault_handler(jmp_buf jumpBuffer, void (*function)(void*),
|
|||||||
|
|
||||||
/*! Similar to user_memcpy(), but can only be invoked from within the kernel
|
/*! Similar to user_memcpy(), but can only be invoked from within the kernel
|
||||||
debugger (and must not be used outside).
|
debugger (and must not be used outside).
|
||||||
|
The supplied \a teamID specifies the address space in which to interpret
|
||||||
|
the addresses. It can be \c B_CURRENT_TEAM for debug_get_debugged_thread(),
|
||||||
|
or any valid team ID. If the addresses are both kernel addresses, the
|
||||||
|
argument is ignored and the current address space is used.
|
||||||
*/
|
*/
|
||||||
status_t
|
status_t
|
||||||
debug_memcpy(void* to, const void* from, size_t size)
|
debug_memcpy(team_id teamID, void* to, const void* from, size_t size)
|
||||||
{
|
{
|
||||||
// don't allow address overflows
|
// don't allow address overflows
|
||||||
if ((addr_t)from + size < (addr_t)from || (addr_t)to + size < (addr_t)to)
|
if ((addr_t)from + size < (addr_t)from || (addr_t)to + size < (addr_t)to)
|
||||||
return B_BAD_ADDRESS;
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
debug_memcpy_parameters parameters = {to, from, size};
|
// Try standard memcpy() with fault handler, if the addresses can be
|
||||||
|
// interpreted in the current address space.
|
||||||
|
if ((IS_KERNEL_ADDRESS(from) && IS_KERNEL_ADDRESS(to))
|
||||||
|
|| debug_is_debugged_team(teamID)) {
|
||||||
|
debug_memcpy_parameters parameters = {to, from, size};
|
||||||
|
|
||||||
if (debug_call_with_fault_handler(gCPU[sDebuggerOnCPU].fault_jump_buffer,
|
if (debug_call_with_fault_handler(gCPU[sDebuggerOnCPU].fault_jump_buffer,
|
||||||
&debug_memcpy_trampoline, ¶meters) != 0) {
|
&debug_memcpy_trampoline, ¶meters) == 0) {
|
||||||
return B_BAD_ADDRESS;
|
return B_OK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try harder. The pages of the respective memory could be unmapped but
|
||||||
|
// still exist in a cache (the page daemon does that with inactive pages).
|
||||||
|
while (size > 0) {
|
||||||
|
uint8 buffer[32];
|
||||||
|
size_t toCopy = std::min(size, sizeof(buffer));
|
||||||
|
|
||||||
|
// restrict the size so we don't cross page boundaries
|
||||||
|
if (((addr_t)from + toCopy) % B_PAGE_SIZE < toCopy)
|
||||||
|
toCopy -= ((addr_t)from + toCopy) % B_PAGE_SIZE;
|
||||||
|
if (((addr_t)to + toCopy) % B_PAGE_SIZE < toCopy)
|
||||||
|
toCopy -= ((addr_t)to + toCopy) % B_PAGE_SIZE;
|
||||||
|
|
||||||
|
if (vm_debug_copy_page_memory(teamID, (void*)from, buffer, toCopy,
|
||||||
|
false) != B_OK
|
||||||
|
|| vm_debug_copy_page_memory(teamID, to, buffer, toCopy, true)
|
||||||
|
!= B_OK) {
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
from = (const uint8*)from + toCopy;
|
||||||
|
to = (uint8*)to + toCopy;
|
||||||
|
size -= toCopy;
|
||||||
|
}
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Similar to user_strlcpy(), but can only be invoked from within the kernel
|
/*! Similar to user_strlcpy(), but can only be invoked from within the kernel
|
||||||
debugger (and must not be used outside).
|
debugger (and must not be used outside).
|
||||||
|
The supplied \a teamID specifies the address space in which to interpret
|
||||||
|
the addresses. It can be \c B_CURRENT_TEAM for debug_get_debugged_thread(),
|
||||||
|
or any valid team ID. If the addresses are both kernel addresses, the
|
||||||
|
argument is ignored and the current address space is used.
|
||||||
*/
|
*/
|
||||||
ssize_t
|
ssize_t
|
||||||
debug_strlcpy(char* to, const char* from, size_t size)
|
debug_strlcpy(team_id teamID, char* to, const char* from, size_t size)
|
||||||
{
|
{
|
||||||
if (size == 0)
|
if (from == NULL || (to == NULL && size > 0))
|
||||||
return 0;
|
|
||||||
if (from == NULL || to == NULL)
|
|
||||||
return B_BAD_ADDRESS;
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
// limit size to avoid address overflows
|
// limit size to avoid address overflows
|
||||||
@@ -1887,18 +1923,80 @@ debug_strlcpy(char* to, const char* from, size_t size)
|
|||||||
// NOTE: Since strlcpy() determines the length of \a from, the source
|
// NOTE: Since strlcpy() determines the length of \a from, the source
|
||||||
// address might still overflow.
|
// address might still overflow.
|
||||||
|
|
||||||
debug_strlcpy_parameters parameters = {to, from, maxSize};
|
// Try standard strlcpy() with fault handler, if the addresses can be
|
||||||
|
// interpreted in the current address space.
|
||||||
|
if ((IS_KERNEL_ADDRESS(from) && IS_KERNEL_ADDRESS(to))
|
||||||
|
|| debug_is_debugged_team(teamID)) {
|
||||||
|
debug_strlcpy_parameters parameters = {to, from, maxSize};
|
||||||
|
|
||||||
if (debug_call_with_fault_handler(gCPU[sDebuggerOnCPU].fault_jump_buffer,
|
if (debug_call_with_fault_handler(
|
||||||
&debug_strlcpy_trampoline, ¶meters) != 0) {
|
gCPU[sDebuggerOnCPU].fault_jump_buffer,
|
||||||
return B_BAD_ADDRESS;
|
&debug_strlcpy_trampoline, ¶meters) == 0) {
|
||||||
|
// If we hit the address overflow boundary, fail.
|
||||||
|
if (parameters.result >= maxSize && maxSize < size)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
return parameters.result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we hit the address overflow boundary, fail.
|
// Try harder. The pages of the respective memory could be unmapped but
|
||||||
if (parameters.result >= maxSize && maxSize < size)
|
// still exist in a cache (the page daemon does that with inactive pages).
|
||||||
return B_BAD_ADDRESS;
|
size_t totalLength = 0;
|
||||||
|
while (maxSize > 0) {
|
||||||
|
char buffer[32];
|
||||||
|
size_t toCopy = std::min(maxSize, sizeof(buffer));
|
||||||
|
|
||||||
return parameters.result;
|
// restrict the size so we don't cross page boundaries
|
||||||
|
if (((addr_t)from + toCopy) % B_PAGE_SIZE < toCopy)
|
||||||
|
toCopy -= ((addr_t)from + toCopy) % B_PAGE_SIZE;
|
||||||
|
if (((addr_t)to + toCopy) % B_PAGE_SIZE < toCopy)
|
||||||
|
toCopy -= ((addr_t)to + toCopy) % B_PAGE_SIZE;
|
||||||
|
|
||||||
|
// copy the next part of the string from the source
|
||||||
|
if (vm_debug_copy_page_memory(teamID, (void*)from, buffer, toCopy,
|
||||||
|
false) != B_OK) {
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine the length of the part and whether we've reached the end
|
||||||
|
// of the string
|
||||||
|
size_t length = strnlen(buffer, toCopy);
|
||||||
|
bool endOfString = length < toCopy;
|
||||||
|
|
||||||
|
from = (const char*)from + toCopy;
|
||||||
|
totalLength += length;
|
||||||
|
maxSize -= length;
|
||||||
|
|
||||||
|
if (endOfString) {
|
||||||
|
// only copy the actual string, including the terminating null
|
||||||
|
toCopy = length + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size > 0) {
|
||||||
|
// We still have space left in the target buffer.
|
||||||
|
if (size <= length) {
|
||||||
|
// Not enough space for the complete part. Null-terminate it and
|
||||||
|
// copy what we can.
|
||||||
|
buffer[size - 1] = '\0';
|
||||||
|
totalLength += length - size;
|
||||||
|
toCopy = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm_debug_copy_page_memory(teamID, to, buffer, toCopy, true)
|
||||||
|
!= B_OK) {
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
to = (char*)to + toCopy;
|
||||||
|
size -= toCopy;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endOfString)
|
||||||
|
return totalLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2060,6 +2158,22 @@ debug_get_debugged_thread()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Returns whether the supplied team ID refers to the same team the currently
|
||||||
|
debugged thread (debug_get_debugged_thread()) belongs to.
|
||||||
|
Always returns \c true, if \c B_CURRENT_TEAM is given.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
debug_is_debugged_team(team_id teamID)
|
||||||
|
{
|
||||||
|
if (teamID == B_CURRENT_TEAM)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
struct thread* thread = debug_get_debugged_thread();
|
||||||
|
return thread != NULL && thread->team != NULL
|
||||||
|
&& thread->team->id == teamID;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
// userland syscalls
|
// userland syscalls
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de
|
* Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de
|
||||||
* Copyright 2006, Stephan Aßmus, superstippi@gmx.de
|
* Copyright 2006, Stephan Aßmus, superstippi@gmx.de
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
@@ -723,7 +723,7 @@ ExpressionParser::_ParseExpression(bool expectAssignment)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug_memcpy(address, &buffer, size) != B_OK) {
|
if (debug_memcpy(B_CURRENT_TEAM, address, &buffer, size) != B_OK) {
|
||||||
snprintf(sTempBuffer, sizeof(sTempBuffer),
|
snprintf(sTempBuffer, sizeof(sTempBuffer),
|
||||||
"failed to write to address %p", address);
|
"failed to write to address %p", address);
|
||||||
parse_exception(sTempBuffer, position);
|
parse_exception(sTempBuffer, position);
|
||||||
@@ -1062,7 +1062,7 @@ ExpressionParser::_ParseDereference(void** _address, uint32* _size)
|
|||||||
|
|
||||||
// read bytes from address into a tempory buffer
|
// read bytes from address into a tempory buffer
|
||||||
uint64 buffer;
|
uint64 buffer;
|
||||||
if (debug_memcpy(&buffer, address, size) != B_OK) {
|
if (debug_memcpy(B_CURRENT_TEAM, &buffer, address, size) != B_OK) {
|
||||||
snprintf(sTempBuffer, sizeof(sTempBuffer),
|
snprintf(sTempBuffer, sizeof(sTempBuffer),
|
||||||
"failed to dereference address %p", address);
|
"failed to dereference address %p", address);
|
||||||
parse_exception(sTempBuffer, starPosition);
|
parse_exception(sTempBuffer, starPosition);
|
||||||
|
|||||||
@@ -296,9 +296,10 @@ gdb_parse_command(void)
|
|||||||
// for gdb may be trying to access an stray pointer
|
// for gdb may be trying to access an stray pointer
|
||||||
// We copy the memory to a safe buffer using
|
// We copy the memory to a safe buffer using
|
||||||
// the bulletproof debug_memcpy().
|
// the bulletproof debug_memcpy().
|
||||||
if (debug_memcpy(sSafeMemory, (char*)address, len) < 0)
|
if (debug_memcpy(B_CURRENT_TEAM, sSafeMemory, (char*)address, len)
|
||||||
|
< 0) {
|
||||||
gdb_reply("E02");
|
gdb_reply("E02");
|
||||||
else
|
} else
|
||||||
gdb_memreply(sSafeMemory, len);
|
gdb_memreply(sSafeMemory, len);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
|
* Copyright 2009-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||||
* Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de.
|
* Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
@@ -1467,8 +1467,10 @@ public:
|
|||||||
if (!IS_USER_ADDRESS(address))
|
if (!IS_USER_ADDRESS(address))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (debug_debugger_running())
|
if (debug_debugger_running()) {
|
||||||
return debug_strlcpy(buffer, address, bufferSize) >= 0;
|
return debug_strlcpy(B_CURRENT_TEAM, buffer, address, bufferSize)
|
||||||
|
>= 0;
|
||||||
|
}
|
||||||
return user_strlcpy(buffer, address, bufferSize) >= 0;
|
return user_strlcpy(buffer, address, bufferSize) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1491,7 +1493,7 @@ UserSymbolLookup::_Read(const T* address, T& data)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (debug_debugger_running())
|
if (debug_debugger_running())
|
||||||
return debug_memcpy(&data, address, sizeof(T)) == B_OK;
|
return debug_memcpy(B_CURRENT_TEAM, &data, address, sizeof(T)) == B_OK;
|
||||||
return user_memcpy(&data, address, sizeof(T)) == B_OK;
|
return user_memcpy(&data, address, sizeof(T)) == B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2640,9 +2640,11 @@ display_mem(int argc, char** argv)
|
|||||||
// string mode
|
// string mode
|
||||||
for (i = 0; true; i++) {
|
for (i = 0; true; i++) {
|
||||||
char c;
|
char c;
|
||||||
if (debug_memcpy(&c, (char*)copyAddress + i, 1) != B_OK
|
if (debug_memcpy(B_CURRENT_TEAM, &c, (char*)copyAddress + i, 1)
|
||||||
|| c == '\0')
|
!= B_OK
|
||||||
|
|| c == '\0') {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (c == '\n')
|
if (c == '\n')
|
||||||
kprintf("\\n");
|
kprintf("\\n");
|
||||||
@@ -2671,8 +2673,8 @@ display_mem(int argc, char** argv)
|
|||||||
|
|
||||||
for (j = 0; j < displayed; j++) {
|
for (j = 0; j < displayed; j++) {
|
||||||
char c;
|
char c;
|
||||||
if (debug_memcpy(&c, (char*)copyAddress + i * itemSize + j,
|
if (debug_memcpy(B_CURRENT_TEAM, &c,
|
||||||
1) != B_OK) {
|
(char*)copyAddress + i * itemSize + j, 1) != B_OK) {
|
||||||
displayed = j;
|
displayed = j;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2689,8 +2691,8 @@ display_mem(int argc, char** argv)
|
|||||||
kprintf(" ");
|
kprintf(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug_memcpy(&value, (uint8*)copyAddress + i * itemSize,
|
if (debug_memcpy(B_CURRENT_TEAM, &value,
|
||||||
itemSize) != B_OK) {
|
(uint8*)copyAddress + i * itemSize, itemSize) != B_OK) {
|
||||||
kprintf("read fault");
|
kprintf("read fault");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -4658,6 +4660,93 @@ vm_memcpy_physical_page(addr_t to, addr_t from)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Copies a range of memory directly from/to a page that might not be mapped
|
||||||
|
at the moment.
|
||||||
|
|
||||||
|
For \a unsafeMemory the current mapping (if any is ignored). The function
|
||||||
|
walks through the respective area's cache chain to find the physical page
|
||||||
|
and copies from/to it directly.
|
||||||
|
The memory range starting at \a unsafeMemory with a length of \a size bytes
|
||||||
|
must not cross a page boundary.
|
||||||
|
|
||||||
|
\param teamID The team ID identifying the address space \a unsafeMemory is
|
||||||
|
to be interpreted in. Ignored, if \a unsafeMemory is a kernel address
|
||||||
|
(the kernel address space is assumed in this case). If \c B_CURRENT_TEAM
|
||||||
|
is passed, the address space of the thread returned by
|
||||||
|
debug_get_debugged_thread() is used.
|
||||||
|
\param unsafeMemory The start of the unsafe memory range to be copied
|
||||||
|
from/to.
|
||||||
|
\param buffer A safely accessible kernel buffer to be copied from/to.
|
||||||
|
\param size The number of bytes to be copied.
|
||||||
|
\param copyToUnsafe If \c true, memory is copied from \a buffer to
|
||||||
|
\a unsafeMemory, the other way around otherwise.
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
vm_debug_copy_page_memory(team_id teamID, void* unsafeMemory, void* buffer,
|
||||||
|
size_t size, bool copyToUnsafe)
|
||||||
|
{
|
||||||
|
if (size > B_PAGE_SIZE
|
||||||
|
|| ((addr_t)unsafeMemory + size) % B_PAGE_SIZE < size) {
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the address space for the debugged thread
|
||||||
|
VMAddressSpace* addressSpace;
|
||||||
|
if (IS_KERNEL_ADDRESS(unsafeMemory)) {
|
||||||
|
addressSpace = VMAddressSpace::Kernel();
|
||||||
|
} else if (teamID == B_CURRENT_TEAM) {
|
||||||
|
struct thread* thread = debug_get_debugged_thread();
|
||||||
|
if (thread == NULL || thread->team == NULL)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
addressSpace = thread->team->address_space;
|
||||||
|
} else
|
||||||
|
addressSpace = VMAddressSpace::DebugGet(teamID);
|
||||||
|
|
||||||
|
if (addressSpace == NULL)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
// get the area
|
||||||
|
VMArea* area = addressSpace->LookupArea((addr_t)unsafeMemory);
|
||||||
|
if (area == NULL)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
// search the page
|
||||||
|
off_t cacheOffset = (addr_t)unsafeMemory - area->Base()
|
||||||
|
+ area->cache_offset;
|
||||||
|
VMCache* cache = area->cache;
|
||||||
|
vm_page* page = NULL;
|
||||||
|
while (cache != NULL) {
|
||||||
|
page = cache->DebugLookupPage(cacheOffset);
|
||||||
|
if (page != NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Page not found in this cache -- if it is paged out, we must not try
|
||||||
|
// to get it from lower caches.
|
||||||
|
if (cache->DebugHasPage(cacheOffset))
|
||||||
|
break;
|
||||||
|
|
||||||
|
cache = cache->source;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page == NULL)
|
||||||
|
return B_UNSUPPORTED;
|
||||||
|
|
||||||
|
// copy from/to physical memory
|
||||||
|
addr_t physicalAddress = page->physical_page_number * B_PAGE_SIZE
|
||||||
|
+ (addr_t)unsafeMemory % B_PAGE_SIZE;
|
||||||
|
|
||||||
|
if (copyToUnsafe) {
|
||||||
|
if (page->Cache() != area->cache)
|
||||||
|
return B_UNSUPPORTED;
|
||||||
|
|
||||||
|
return vm_memcpy_to_physical(physicalAddress, buffer, size, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return vm_memcpy_from_physical(buffer, physicalAddress, size, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - kernel public API
|
// #pragma mark - kernel public API
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user