Re: [RFC] convert a host address to a string

Joel Brobecker
2009-01-09T13:12:51Z

[-- Attachment #1: Type: text/plain, Size: 1317 bytes --]

> > 3. Work through uintptr_t.
> >
> > #ifdef PRINTF_HAS_LONG_LONG
> > fprintf (buf, "0x%llx", (unsigned long long) (uintptr_t) address);
> > #else
> > fprintf (buf, "0x%lx", (unsigned long) (uintptr_t) address);
> > #endif
>
> This wouldn't be the first place where we'd use a double cast in
> connection with intptr_t/uintptr_t. So I'd say that while this is a
> bit ugly, it's certainly acceptable. It's by far the simplest way to
> fix things.

Here is a new patch implementing this approach. As I told Kai,
it's probably not going to work for x86_64/XP, but it works for
x86_64/Vista (I think Kai agreed to take on the job of improving
this approach to work on XP as well :-).

2009-01-09 Joel Brobecker <brobecker@adacore.com>

* utils.c (host_address_to_string): Cast the address to uintptr_t
first to avoid a possible compilation warning about casting to
an integer of the wrong size. Then format the address using
unsigned long long if supported by printf. Otherwise, fallback
on using usingned long, hoping that long is large enough to hold
an address.

Tested on x86-linux. I also verified that it still builds on
x86_64 Vista, as well as x86/Windows XP (on XP, PRINTF_HAS_LONG_LONG
is undefined).

--
Joel

[-- Attachment #2: host_addr_to_str.diff --]
[-- Type: text/plain, Size: 2332 bytes --]

diff --git a/gdb/utils.c b/gdb/utils.c
index 9e2dfd7..dd606a2 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -3071,10 +3071,44 @@ host_address_to_string (const void *addr)
{
char *str = get_cell ();

- /* We could use the %p conversion specifier to sprintf if we had any
- way of knowing whether this host supports it. But the following
- should work on the Alpha and on 32 bit machines. */
- sprintf (str, "0x%lx", (unsigned long) addr);
+ /* We do not use the %p conversion specifier, because the resulting
+ image can vary from implementation to implementation. For instance,
+ some implementations format the pointer value with a leading "0x"
+ whereas others don't (Solaris, for instance). Also, it is unspecified
+ whether the alphabetical digits are printed using uppercase letters
+ or not (in GDB, we want lowercase).
+
+ So we use the %x type instead. This, however, introduces
+ a couple of issues:
+
+ 1. The %x type expects an integer value, not a pointer.
+ So we first need to cast our pointer to an integer type
+ whose size is identical to the size of our pointer.
+ We use uintptr_t for that.
+
+ 2. The %x type alone expects and int, which is not always
+ large enough to hold an address. Usually, type "long"
+ has the same size as pointers, but certain ABIs define
+ the size of pointers to be larger than the size of long
+ (64bit Windows is one such example).
+
+ So, to be certain to have a type that's large enough
+ to hold an address, we need to use "long long". But
+ the trick is that not all printf implementations support
+ the "ll" modifier. On those platforms where the "ll"
+ modifier is not available, we'll assume that type "long"
+ is large enough to contain an address.
+
+ To make sure that the type we pass to sprintf matches
+ the type we specified in our expression, we perform
+ a second cast to "unsigned long long" if we used "%llx",
+ or "unsigned long" if we used "%lx". */
+
+#ifdef PRINTF_HAS_LONG_LONG
+ sprintf (str, "0x%llx", (unsigned long long) (uintptr_t) addr);
+#else
+ sprintf (str, "0x%lx", (unsigned long) (uintptr_t) addr);
+#endif
return str;
}