From 0f9bfa569d46f2346a53a940b2b9e49a38635732 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 6 Jul 2008 16:06:25 -0700 Subject: vsprintf: split out '%s' handling logic The actual code is the same, just split out into a helper function. This makes it easier to read, and allows for future sharing of the string code. Signed-off-by: Linus Torvalds --- lib/vsprintf.c | 57 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 26 deletions(-) (limited to 'lib/vsprintf.c') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 6021757a4496..926c7e00e2dc 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -482,6 +482,35 @@ static char *number(char *buf, char *end, unsigned long long num, int base, int return buf; } +static char *string(char *buf, char *end, char *s, int field_width, int precision, int flags) +{ + int len, i; + + if ((unsigned long)s < PAGE_SIZE) + s = ""; + + len = strnlen(s, precision); + + if (!(flags & LEFT)) { + while (len < field_width--) { + if (buf < end) + *buf = ' '; + ++buf; + } + } + for (i = 0; i < len; ++i) { + if (buf < end) + *buf = *s; + ++buf; ++s; + } + while (len < field_width--) { + if (buf < end) + *buf = ' '; + ++buf; + } + return buf; +} + /** * vsnprintf - Format a string and place it in a buffer * @buf: The buffer to place the result into @@ -502,11 +531,9 @@ static char *number(char *buf, char *end, unsigned long long num, int base, int */ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) { - int len; unsigned long long num; - int i, base; + int base; char *str, *end, c; - const char *s; int flags; /* flags to number() */ @@ -622,29 +649,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) continue; case 's': - s = va_arg(args, char *); - if ((unsigned long)s < PAGE_SIZE) - s = ""; - - len = strnlen(s, precision); - - if (!(flags & LEFT)) { - while (len < field_width--) { - if (str < end) - *str = ' '; - ++str; - } - } - for (i = 0; i < len; ++i) { - if (str < end) - *str = *s; - ++str; ++s; - } - while (len < field_width--) { - if (str < end) - *str = ' '; - ++str; - } + str = string(str, end, va_arg(args, char *), field_width, precision, flags); continue; case 'p': -- cgit v1.2.3 From 78a8bf69b32980879975f7e31d30386c50bfe851 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 6 Jul 2008 16:16:15 -0700 Subject: vsprintf: split out '%p' handling logic The actual code is the same, just split out into a helper function. This makes it easier to read, and allows for simple future extension of %p handling. Signed-off-by: Linus Torvalds --- lib/vsprintf.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'lib/vsprintf.c') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 926c7e00e2dc..f569feb7662e 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -511,6 +511,16 @@ static char *string(char *buf, char *end, char *s, int field_width, int precisio return buf; } +static char *pointer(char *buf, char *end, void *ptr, int field_width, int precision, int flags) +{ + flags |= SMALL; + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + return number(buf, end, (unsigned long) ptr, 16, field_width, precision, flags); +} + /** * vsnprintf - Format a string and place it in a buffer * @buf: The buffer to place the result into @@ -653,17 +663,9 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) continue; case 'p': - flags |= SMALL; - if (field_width == -1) { - field_width = 2*sizeof(void *); - flags |= ZEROPAD; - } - str = number(str, end, - (unsigned long) va_arg(args, void *), - 16, field_width, precision, flags); + str = pointer(str, end, va_arg(args, void *), field_width, precision, flags); continue; - case 'n': /* FIXME: * What does C99 say about the overflow case here? */ -- cgit v1.2.3 From 4d8a743cdd2690c0bc8d1b8cbd02cffb1ead849f Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 6 Jul 2008 16:24:57 -0700 Subject: vsprintf: add infrastructure support for extended '%p' specifiers This expands the kernel '%p' handling with an arbitrary alphanumberic specifier extension string immediately following the '%p'. Right now it's just being ignored, but the next commit will start adding some specific pointer type extensions. NOTE! The reason the extension is appended to the '%p' is to allow minimal gcc type checking: gcc will still see the '%p' and will check that the argument passed in is indeed a pointer, and yet will not complain about the extended information that gcc doesn't understand about (on the other hand, it also won't actually check that the pointer type and the extension are compatible). Alphanumeric characters were chosen because there is no sane existing use for a string format with a hex pointer representation immediately followed by alphanumerics (which is what such a format string would have traditionally resulted in). Signed-off-by: Linus Torvalds --- lib/vsprintf.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'lib/vsprintf.c') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index f569feb7662e..5d6f0718b6d9 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -511,7 +511,14 @@ static char *string(char *buf, char *end, char *s, int field_width, int precisio return buf; } -static char *pointer(char *buf, char *end, void *ptr, int field_width, int precision, int flags) +/* + * Show a '%p' thing. A kernel extension is that the '%p' is followed + * by an extra set of alphanumeric characters that are extended format + * specifiers. + * + * Right now don't actually handle any such, but we will.. + */ +static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field_width, int precision, int flags) { flags |= SMALL; if (field_width == -1) { @@ -663,7 +670,12 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) continue; case 'p': - str = pointer(str, end, va_arg(args, void *), field_width, precision, flags); + str = pointer(fmt+1, str, end, + va_arg(args, void *), + field_width, precision, flags); + /* Skip all alphanumeric pointer suffixes */ + while (isalnum(fmt[1])) + fmt++; continue; case 'n': -- cgit v1.2.3 From 0fe1ef24f7bd0020f29ffe287dfdb9ead33ca0b2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 6 Jul 2008 16:43:12 -0700 Subject: 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: Linus Torvalds --- lib/vsprintf.c | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) (limited to 'lib/vsprintf.c') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 5d6f0718b6d9..1dc2d1d18fa8 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include /* for PAGE_SIZE */ #include @@ -511,15 +513,52 @@ static char *string(char *buf, char *end, char *s, int field_width, int precisio 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 * by an extra set of alphanumeric characters that are extended format * 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) { + switch (*fmt) { + case 'F': + ptr = dereference_function_descriptor(ptr); + /* Fallthrough */ + case 'S': + return symbol_string(buf, end, ptr, field_width, precision, flags); + } flags |= SMALL; if (field_width == -1) { field_width = 2*sizeof(void *); -- cgit v1.2.3