I'm implementing a printk
function for my toy OS kernel targeting x86 platform. If I call printk
like this:
uint64_t x = 0xdead;
uint64_t z = 0xbeef;
printk("%p %s\n", x & z, "yes");
that is, passing a 64-bit integer (x & z
) to it, then the generated assembly code would be:
c01000bd: c7 45 f0 ad de 00 00 movl $0xdead,-0x10(%ebp)
c01000c4: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%ebp)
c01000cb: c7 45 e8 ef be 00 00 movl $0xbeef,-0x18(%ebp)
c01000d2: c7 45 ec 00 00 00 00 movl $0x0,-0x14(%ebp)
c01000d9: 8b 45 f0 mov -0x10(%ebp),%eax
c01000dc: 23 45 e8 and -0x18(%ebp),%eax
c01000df: 89 c3 mov %eax,%ebx
c01000e1: 8b 45 f4 mov -0xc(%ebp),%eax
c01000e4: 23 45 ec and -0x14(%ebp),%eax
c01000e7: 89 c6 mov %eax,%esi
c01000e9: 68 e4 17 10 c0 push $0xc01017e4
c01000ee: 56 push %esi
c01000ef: 53 push %ebx
c01000f0: 68 e8 17 10 c0 push $0xc01017e8
c01000f5: e8 27 0a 00 00 call c0100b21 <printk>
You can see here gcc uses two 32-bit registers (%esi
and %ebx
) to store the 64-bit value. The result of this is the number of arguments pushed to the stack becomes 4 instead of 3. If the code inside printk
cannot figure out the size of the argument and consume it properly, the stack access would be messed up.
So my question is, when implementing a variadic function, how can I know the size of the next argument when I use va_arg
macro? Or specifically, how to solve this 32-bit-vs-64-bit printk
problem?