From: "Cohen, Eugene" <eugene@hp.com>
To: "edk2-devel@lists.01.org" <edk2-devel@lists.01.org>,
"Kinney, Michael D" <michael.d.kinney@intel.com>
Subject: What is the right way to print a UINTN?
Date: Mon, 26 Sep 2016 13:46:21 +0000 [thread overview]
Message-ID: <AT5PR84MB0291232000D2209AD7B53D52B4CD0@AT5PR84MB0291.NAMPRD84.PROD.OUTLOOK.COM> (raw)
Get ready for a potentially stupid question (or at least a question I probably should know the answer to by now)...
The implementation of BasePrintLib treats types like %d and %x as the size of the compiler's "int" type. On many 64-bit architectures the size of int is 32-bits. On these same architectures we declare a "UINTN" type as 64-bits.
The handling for integral types (%d, %x, %u, etc) in BasePrintLib is as follows, note the use of 'int':
if (BaseListMarker == NULL) {
Value = VA_ARG (VaListMarker, int);
} else {
Value = BASE_ARG (BaseListMarker, int);
}
So it would seem to be improper to try to Print/DEBUG a UINTN value with %d/%u/%x since the size will be mismatched on some architecture (INTN 64 bits, int 32 bits). But it also would be improper to try to print this as %ld/%lx because the size will be mismatched on 32-bit architectures (INTN 32 bits and print will use a INT64 with the 'l' prefix). It's not obvious then how to create a portable format specifier that works for UINTN.
I did some research in how this is handled in edk2 for modules we know to be portable and I see multiple conflicting techniques being used. The predominant pattern for this is to try to print a UINTN parameter with the %d/%x format specifier anyways like this:
DEBUG ((EFI_D_ERROR, "FATAL ERROR - RaiseTpl with OldTpl(0x%x) > NewTpl(0x%x)\n", OldTpl, NewTpl));
in this case the parameter if EFI_TPL which was typedef'ed as a UINTN. In this case the compiler does the variadic thing and on a 64-bit architecture puts a 64-bit UINTN on the variadic stack (using the term stack in the abstract sense here).
When the print code tries to pop it off the stack using VA_ARG (VaListMarker, int) the 'int' gets upgraded in size to UINTN due to this construct in Base.h (I'm using GCC):
#define VA_ARG(Marker, TYPE) ((sizeof (TYPE) < sizeof (UINTN)) ? (TYPE)(__builtin_va_arg (Marker, UINTN)) : (TYPE)(__builtin_va_arg (Marker, TYPE)))
since sizeof(int) is 4 and this is less than sizeof(UINTN) at 8 the result will be (TYPE)(__builtin_va_arg (Marker, UINTN)) where TYPE is 'int'. So we end up popping 64-bits off the varidic stack but immediately typecast it to an int (4 byte) resulting in loss of the upper-64 bits. If I'm reading this right it means we can only print UINTNs whose value is below 2^32 (confirmed with a debugger on AArch64 using GCC). Maybe this seems benign for the simple EFI_TPL enumeration but if for some reason you add a new value that exceeds 2^32-1 then the print code is broken. Is every user of Print/DEBUG expected understand this range limitation?
What is the preferred pattern for printing a INTN/UINTN then? Two yucky hacks I can think of: upcasting to UINT64/%ld when you "know" the value may exceed 2^32-1 or using %p for non-pointer types since this happens to get the right size treatment although we lose formatting options.
Eugene
next reply other threads:[~2016-09-26 13:46 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-09-26 13:46 Cohen, Eugene [this message]
2016-09-26 14:39 ` What is the right way to print a UINTN? Alexei Fedorov
2016-09-26 15:31 ` Laszlo Ersek
2016-09-27 12:29 ` Cohen, Eugene
2016-09-27 14:30 ` Laszlo Ersek
2016-09-27 16:03 ` Cohen, Eugene
2016-09-27 16:31 ` Laszlo Ersek
2016-09-27 16:47 ` Andrew Fish
2016-09-27 17:14 ` Brian J. Johnson
2016-09-27 18:31 ` Laszlo Ersek
2016-09-27 20:27 ` Kinney, Michael D
2016-09-27 17:27 ` Kinney, Michael D
2016-09-27 17:46 ` Andrew Fish
2016-09-27 18:20 ` Kinney, Michael D
2016-09-27 19:28 ` Cohen, Eugene
2016-09-27 20:10 ` Kinney, Michael D
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=AT5PR84MB0291232000D2209AD7B53D52B4CD0@AT5PR84MB0291.NAMPRD84.PROD.OUTLOOK.COM \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox