为什么``printf'printf'printf'%hu%```''''in stack而不是堆栈2?
我正在研究组装基础知识,并且经常使用printf
(我认为我从C/C ++经验中足够了解)。我遇到了带有2个字节(16位)值的奇怪的事情:
在32位模式下,使用printf(“%hu”,(unsigned short)123)
,我认为它会脱离6个字节从堆栈(4用于格式字符串的地址,为值2)。同时,现实是,地址为4,价值为4。
为什么?是%hu
等于%u
?还是只是剥离值的高2个字节?
一些代码(从C到ASM编译,-O0
): https://godbolt.org/z/x167zdon1
mov eax, 123
push eax
push format ; "%hu"
call _printf
add esp, 4 + 4 ; works
mov ax, 123
push eax
push format ; "%hu"
call _printf
add esp, 4 + 2 ; doesn't work: print gibberish, as it takes 2 bytes more to display the value....
I am studying assembly basics and I use printf
quite often (which I thought I know good enough from C/C++ experience). I came across weird thing with 2 bytes (16 bits) values:
In 32 bit mode, when using printf("%hu", (unsigned short)123)
, I thought it would take off 6 bytes from stack (4 for address of format string, and 2 for value). Meanwhile reality is, it 4 for address and 4 for value.
Why is that? Is %hu
equal to just %u
? Or is it just stripping high 2 bytes of the value?
Some code (from C to ASM compiling, -O0
):
https://godbolt.org/z/x167zdon1
This works:
mov eax, 123
push eax
push format ; "%hu"
call _printf
add esp, 4 + 4 ; works
But I thought this should:
mov ax, 123
push eax
push format ; "%hu"
call _printf
add esp, 4 + 2 ; doesn't work: print gibberish, as it takes 2 bytes more to display the value....
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
当然:您执行了
推动EAX
,该在堆栈上推动4个字节。因此,您必须再次从堆栈中删除4个字节。在x86-32代码中,堆栈通常应对齐4。这意味着
esp
寄存器的值应为4的倍数。因此,大多数调用约定都需要推动32位值如果函数参数仅为16个字节值。
这取决于呼叫约定和库实现:
使用C编译器必须
push
的32位数字时,如果参数为16位值,则code> push 一个32位数,库(printf
函数)可以以%hu
与%u
相同的方式实现 。当使用该数字的上部16位可能具有任何值的情况下,使用呼叫约定时,
printf
函数当然必须剥离上部2个字节。编辑
实际上,您在
printf
示例中具有两个效果:char
或简短
参数是预期的。printf
)中,必须将类型char> char
或简短
的“附加”参数施加到int
由编译器。假设我们在16位CPU上工作,但是数据类型
int
长32位。我们有一些功能void Test(简短x,...);
。如果我们调用
test(A,A);
在这种情况下,编译器将将第一个参数作为16位值传递(因为该参数具有数据类型简短
)第二个参数为32位值(因为这是一个附加参数)。假设
int
是32位数据类型,printf()
的其他参数也是如此。Sure: You performed a
push eax
, which pushes 4 bytes on the stack. For this reason, you have to remove the 4 bytes from the stack again.In x86-32 code, the stack should typically be aligned to 4. This means that the
esp
register's value shall be a multiple of 4.For this reason, most calling conventions require pushing a 32-bit value if the function argument is only a 16- or even an 8-byte value.
It depends on the calling convention and the library implementation:
When using a calling convention where the C compiler has to
push
a 32-bit number whose upper 16 bits are 0 if the argument is a 16-bit value, the library (theprintf
function) may be implemented in a way that%hu
is identical to%u
.When a calling convention is used where the upper 16 bits of such a number may have any value, the
printf
function must strip the upper 2 bytes, of course.Edit
Actually, you have two effects in the
printf
example:char
orshort
argument is expected.printf
), the "additional" arguments of the typechar
orshort
must be cast toint
by the compiler.Let's assume we work on a 16-bit CPU but the data type
int
is 32 bits long. And we have some functionvoid test(short x, ...);
.If we call
test(a, a);
in this situation, the compiler will pass the first argument as 16-bit value (because the argument has the data typeshort
) and the second argument as 32-bit value (because it is an additional argument).The same is the case for the additional arguments of
printf()
- assuming thatint
is a 32-bit data type.