Bug 177706

Summary: Constant argument of a function with variable length args is pushed as a 4-byte value instead of 8-byte
Product: Red Hat Enterprise Linux 3 Reporter: Lego Haryanto <legoharyanto>
Component: gccAssignee: Jakub Jelinek <jakub>
Status: CLOSED NOTABUG QA Contact:
Severity: medium Docs Contact:
Priority: medium    
Version: 3.0   
Target Milestone: ---   
Target Release: ---   
Hardware: ia64   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2006-01-13 07:33:03 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Attachments:
Description Flags
C test code
none
Assembly output of the same test.c none

Description Lego Haryanto 2006-01-13 02:29:42 UTC
Description of problem:
The problem happens when dealing with a variable-arguments function, e.g.: int 
foo(int numArgs, ...);

When I call this foo function like the following (please note that we pass 
constants on all arguments):

foo(13, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);

On the later arguments (e.g.: 12 and 13), the constants are pushed as a 4-byte 
integer.  Since the elements in the stack are of 8-bytes, then there will be 4-
bytes with unknown value for those arguments.

When the callee (in this case, foo) treats the argument as a size_t (which is 
an 8-byte data type) via something like:

size_t value;

value = va_arg(args, size_t);

We'll see the problem that the value we got is possibly different with the one 
pushed to the stack earlier.

I'm attaching the test code (test.c) and the assembly output.


Version-Release number of selected component (if applicable):
gcc-3.2.3-42

How reproducible:
Everytime.

Steps to Reproduce:
1. Compile the attached code (I called it test.c) with: "gcc test.c" to 
produce a.out
2. Run "./a.out" and observe the output

  
Actual results:
sizeof size_t = 8
sizeof long = 8
col: 0 -> arg: 1
col: 1 -> arg: 2
col: 2 -> arg: 3
col: 3 -> arg: 4
col: 4 -> arg: 5
col: 5 -> arg: 6
col: 6 -> arg: 7
col: 7 -> arg: 8
col: 8 -> arg: 9
col: 9 -> arg: a
col: 10 -> arg: b
col: 11 -> arg: 200000000000000c
col: 12 -> arg: 200000000000000d

(please note that the extra 200000000000000 on 11 and 12 doesn't necessarily 
have to be the same value).


Expected results:
sizeof size_t = 8
sizeof long = 8
col: 0 -> arg: 1
col: 1 -> arg: 2
col: 2 -> arg: 3
col: 3 -> arg: 4
col: 4 -> arg: 5
col: 5 -> arg: 6
col: 6 -> arg: 7
col: 7 -> arg: 8
col: 8 -> arg: 9
col: 9 -> arg: a
col: 10 -> arg: b
col: 11 -> arg: c
col: 12 -> arg: d


Additional info:
One can argue that the behavior is expected, but if we look closely at the 
assembly output, it's obvious that the compiler sometimes treats it as a 4-
byte push, and other times it treats it as an 8-byte (via general purpose 
registers).
Please also take a look at the produce assembly output.

Comment 1 Lego Haryanto 2006-01-13 02:29:42 UTC
Created attachment 123147 [details]
C test code

Comment 2 Lego Haryanto 2006-01-13 02:31:04 UTC
Created attachment 123148 [details]
Assembly output of the same test.c

Comment 3 Jakub Jelinek 2006-01-13 07:33:03 UTC
GCC behavior matches the IA-64 ABI, your testcase is buggy.
The constants you are passing to the function have int type, not size_t
or long, so if you use va_arg (ap, size_t), the behaviour is undefined.
If you want to read the values as size_t arguments, you need to make sure they
are passed with those types, i.e. either
foo (2, (size_t) 1, (size_t) 34);
or e.g.
foo (2, 1L, 34L);
(the former is portable, the latter will work on IA-64 where you know
size_t and long are the same size, and will work e.g. on all ILP32 and LP64
Linux targets, but there might be some weirdo targets where sizeof (size_t) !=
sizeof (long); still, sizeof (size_t) == sizeof (long) is pretty common, while
sizeof (size_t) != sizeof (int) is true on many targets).