Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0fe1ef24 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

vsprintf: add support for '%pS' and '%pF' pointer formats



They print out a pointer in symbolic format, if possible (ie using
symbolic KALLSYMS information).  The '%pS' format is for regular direct
pointers (which can point to data or code and that you find on the stack
during backtraces etc), while '%pF' is for C function pointer types.

On most architectures, the two mean exactly the same thing, but some
architectures use an indirect pointer for C function pointers, where the
function pointer points to a function descriptor (which in turn contains
the actual pointer to the code).  The '%pF' code automatically does the
appropriate function descriptor dereference on such architectures.

Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 4d8a743c
Loading
Loading
Loading
Loading
+40 −1
Original line number Original line Diff line number Diff line
@@ -22,6 +22,8 @@
#include <linux/string.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/kallsyms.h>
#include <linux/uaccess.h>


#include <asm/page.h>		/* for PAGE_SIZE */
#include <asm/page.h>		/* for PAGE_SIZE */
#include <asm/div64.h>
#include <asm/div64.h>
@@ -511,15 +513,52 @@ static char *string(char *buf, char *end, char *s, int field_width, int precisio
	return buf;
	return buf;
}
}


static inline void *dereference_function_descriptor(void *ptr)
{
#if defined(CONFIG_IA64) || defined(CONFIG_PPC64)
	void *p;
	if (!probe_kernel_address(ptr, p))
		ptr = p;
#endif
	return ptr;
}

static char *symbol_string(char *buf, char *end, void *ptr, int field_width, int precision, int flags)
{
	unsigned long value = (unsigned long) ptr;
#ifdef CONFIG_KALLSYMS
	char sym[KSYM_SYMBOL_LEN];
	sprint_symbol(sym, value);
	return string(buf, end, sym, field_width, precision, flags);
#else
	field_width = 2*sizeof(void *);
	flags |= SPECIAL | SMALL | ZEROPAD;
	return number(buf, end, value, 16, field_width, precision, flags);
#endif
}

/*
/*
 * Show a '%p' thing.  A kernel extension is that the '%p' is followed
 * Show a '%p' thing.  A kernel extension is that the '%p' is followed
 * by an extra set of alphanumeric characters that are extended format
 * by an extra set of alphanumeric characters that are extended format
 * specifiers.
 * specifiers.
 *
 *
 * Right now don't actually handle any such, but we will..
 * Right now we just handle 'F' (for symbolic Function descriptor pointers)
 * and 'S' (for Symbolic direct pointers), but this can easily be
 * extended in the future (network address types etc).
 *
 * The difference between 'S' and 'F' is that on ia64 and ppc64 function
 * pointers are really function descriptors, which contain a pointer the
 * real address. 
 */
 */
static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field_width, int precision, int flags)
static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field_width, int precision, int flags)
{
{
	switch (*fmt) {
	case 'F':
		ptr = dereference_function_descriptor(ptr);
		/* Fallthrough */
	case 'S':
		return symbol_string(buf, end, ptr, field_width, precision, flags);
	}
	flags |= SMALL;
	flags |= SMALL;
	if (field_width == -1) {
	if (field_width == -1) {
		field_width = 2*sizeof(void *);
		field_width = 2*sizeof(void *);