Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/sys/windows: wrong Sizeof result on struct windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION on ARCH 386 #41001

Closed
dbellavista opened this issue Aug 24, 2020 · 8 comments

Comments

@dbellavista
Copy link

@dbellavista dbellavista commented Aug 24, 2020

Hi! I'm trying to call the windows.SetInformationJobObject function which requires the size of the input structure of type windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION. To my understanding I can use unsafe.Sizeof to achieve the same result of the C sizeof operator.

When compiling on amd64 arch, the sizeof is correct, but on 386 there is a 4 bytes difference.

What version of Go are you using (go version)?

$ go version
go version go1.14.6 windows/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
set GO111MODULE=
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\danie\AppData\Local\go-build
set GOENV=C:\Users\danie\AppData\Roaming\go\env
set GOEXE=.exe
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=C:\Users\danie\go
set GOPRIVATE=
set GOPROXY=https://proxy.golang.org,direct
set GOROOT=c:\go
set GOSUMDB=sum.golang.org
set GOTMPDIR=
set GOTOOLDIR=c:\go\pkg\tool\windows_amd64
set GCCGO=gccgo
set AR=ar
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=Y:\Agent\windows-service-wrapper\go.mod
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -fmessage-length=0 -fdebug-prefix-map=C:\Users\danie\AppData\Local\Temp\go-build389674314=/tmp/go-build -gno-record-gcc-switches

What did you do?

package main

// #include <windows.h>
//
// int SizeOfJob() {
//     JOBOBJECT_EXTENDED_LIMIT_INFORMATION test;
//     test.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
//     return sizeof(test);
// }
import "C"

import (
	"fmt"
	"unsafe"

	"golang.org/x/sys/windows"
)

func main() {
	info := windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION{
		BasicLimitInformation: windows.JOBOBJECT_BASIC_LIMIT_INFORMATION{
			LimitFlags: windows.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
		},
	}

	fmt.Printf("Go Size %d\nC Size %d", unsafe.Sizeof(info), C.SizeOfJob())
}

What did you expect to see?

Compiled on windows with environment GOARCH=386

Go Size 112
C Size 112

Compiled on windows with environment GOARCH="amd64"

Go Size 144
C Size 144

What did you see instead?

Compiled on windows with environment GOARCH=386

Go Size 108
C Size 112
@networkimprov
Copy link

@networkimprov networkimprov commented Aug 25, 2020

cc @alexbrainman

@gopherbot add OS-Windows

@cagedmantis cagedmantis changed the title Wrong Sizeof result on struct windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION on ARCH 386 cmd/cgo: wrong Sizeof result on struct windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION on ARCH 386 Aug 26, 2020
@cagedmantis cagedmantis added this to the Backlog milestone Aug 26, 2020
@cagedmantis
Copy link
Contributor

@cagedmantis cagedmantis commented Aug 26, 2020

@ianlancetaylor ianlancetaylor changed the title cmd/cgo: wrong Sizeof result on struct windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION on ARCH 386 x/sys/windows: wrong Sizeof result on struct windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION on ARCH 386 Aug 26, 2020
@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Aug 26, 2020

My first guess would be an alignment difference. Can you print the C offsetof and the Go unsafe.Offsetof for each field in the struct? So that we can find out exactly where the difference happens? Thanks.

@ianlancetaylor ianlancetaylor modified the milestones: Backlog, Unreleased Aug 26, 2020
@dbellavista
Copy link
Author

@dbellavista dbellavista commented Aug 27, 2020

@ianlancetaylor You are right. It seems to be a size difference in JOBOBJECT_BASIC_LIMIT_INFORMATION. The C size is greater (48 bytes) than the sum of the field sizes.

Offset and sizes of JOBOBJECT_EXTENDED_LIMIT_INFORMATION

Field GO Offset C Offset GO Size C Size
BasicLimitInformation 0 0 44 48
IoInfo 44 48 48 48
ProcessMemoryLimit 92 96 4 4
JobMemoryLimit 96 100 4 4
PeakProcessMemoryUsed 100 104 4 4
PeakJobMemoryUsed 104 108 4 4

Offset and sizes of JOBOBJECT_BASIC_LIMIT_INFORMATION (type of BasicLimitInformation)

Field GO Offset C Offset GO Size C Size
PerProcessUserTimeLimit 0 0 8 8
PerJobUserTimeLimit 8 8 8 8
LimitFlags 16 16 4 4
MinimumWorkingSetSize 20 20 4 4
MaximumWorkingSetSize 24 24 4 4
ActiveProcessLimit 28 28 4 4
Affinity 32 32 4 4
PriorityClass 36 36 4 4
SchedulingClass 40 40 4 4
Code

package main

// #include <windows.h>
//
// int SizeOfJob() {
//     JOBOBJECT_EXTENDED_LIMIT_INFORMATION test;
//     return sizeof(test);
// }
// int SizeOfBasicJob() {
//     JOBOBJECT_EXTENDED_LIMIT_INFORMATION test;
//     return sizeof(test.BasicLimitInformation);
// }
// int SizeOfBasicStructJob() {
//     JOBOBJECT_BASIC_LIMIT_INFORMATION test;
//     return sizeof(test);
// }
// int SizeOfBasicLimitInformation() { JOBOBJECT_EXTENDED_LIMIT_INFORMATION test; return sizeof(test.BasicLimitInformation); };
// int SizeOfIoInfo() { JOBOBJECT_EXTENDED_LIMIT_INFORMATION test; return sizeof(test.IoInfo); };
// int SizeOfProcessMemoryLimit() { JOBOBJECT_EXTENDED_LIMIT_INFORMATION test; return sizeof(test.ProcessMemoryLimit); };
// int SizeOfJobMemoryLimit() { JOBOBJECT_EXTENDED_LIMIT_INFORMATION test; return sizeof(test.JobMemoryLimit); };
// int SizeOfPeakProcessMemoryUsed() { JOBOBJECT_EXTENDED_LIMIT_INFORMATION test; return sizeof(test.PeakProcessMemoryUsed); };
// int SizeOfPeakJobMemoryUsed() { JOBOBJECT_EXTENDED_LIMIT_INFORMATION test; return sizeof(test.PeakJobMemoryUsed); };
//
// int OffsetOfBasicLimitInformation() { return offsetof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION, BasicLimitInformation); };
// int OffsetOfIoInfo() { return offsetof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION, IoInfo); };
// int OffsetOfProcessMemoryLimit() { return offsetof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION, ProcessMemoryLimit); };
// int OffsetOfJobMemoryLimit() { return offsetof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION, JobMemoryLimit); };
// int OffsetOfPeakProcessMemoryUsed() { return offsetof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION, PeakProcessMemoryUsed); };
// int OffsetOfPeakJobMemoryUsed() { return offsetof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION, PeakJobMemoryUsed); };
//
// int SizeOfPerProcessUserTimeLimit() { JOBOBJECT_BASIC_LIMIT_INFORMATION test; return sizeof(test.PerProcessUserTimeLimit); }
// int SizeOfPerJobUserTimeLimit() { JOBOBJECT_BASIC_LIMIT_INFORMATION test; return sizeof(test.PerJobUserTimeLimit); }
// int SizeOfLimitFlags() { JOBOBJECT_BASIC_LIMIT_INFORMATION test; return sizeof(test.LimitFlags); }
// int SizeOfMinimumWorkingSetSize() { JOBOBJECT_BASIC_LIMIT_INFORMATION test; return sizeof(test.MinimumWorkingSetSize); }
// int SizeOfMaximumWorkingSetSize() { JOBOBJECT_BASIC_LIMIT_INFORMATION test; return sizeof(test.MaximumWorkingSetSize); }
// int SizeOfActiveProcessLimit() { JOBOBJECT_BASIC_LIMIT_INFORMATION test; return sizeof(test.ActiveProcessLimit); }
// int SizeOfAffinity() { JOBOBJECT_BASIC_LIMIT_INFORMATION test; return sizeof(test.Affinity); }
// int SizeOfPriorityClass() { JOBOBJECT_BASIC_LIMIT_INFORMATION test; return sizeof(test.PriorityClass); }
// int SizeOfSchedulingClass() { JOBOBJECT_BASIC_LIMIT_INFORMATION test; return sizeof(test.SchedulingClass); }
//
// int OffsetOfPerProcessUserTimeLimit() { return offsetof(JOBOBJECT_BASIC_LIMIT_INFORMATION, PerProcessUserTimeLimit); }
// int OffsetOfPerJobUserTimeLimit() { return offsetof(JOBOBJECT_BASIC_LIMIT_INFORMATION, PerJobUserTimeLimit); }
// int OffsetOfLimitFlags() { return offsetof(JOBOBJECT_BASIC_LIMIT_INFORMATION, LimitFlags); }
// int OffsetOfMinimumWorkingSetSize() { return offsetof(JOBOBJECT_BASIC_LIMIT_INFORMATION, MinimumWorkingSetSize); }
// int OffsetOfMaximumWorkingSetSize() { return offsetof(JOBOBJECT_BASIC_LIMIT_INFORMATION, MaximumWorkingSetSize); }
// int OffsetOfActiveProcessLimit() { return offsetof(JOBOBJECT_BASIC_LIMIT_INFORMATION, ActiveProcessLimit); }
// int OffsetOfAffinity() { return offsetof(JOBOBJECT_BASIC_LIMIT_INFORMATION, Affinity); }
// int OffsetOfPriorityClass() { return offsetof(JOBOBJECT_BASIC_LIMIT_INFORMATION, PriorityClass); }
// int OffsetOfSchedulingClass() { return offsetof(JOBOBJECT_BASIC_LIMIT_INFORMATION, SchedulingClass); }
import "C"

import (
	"fmt"
	"unsafe"

	"golang.org/x/sys/windows"
)

func main() {
	info := windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION{}

	fmt.Printf("|Field | GO Offset | C Offset | GO Size | C Size|\n|-|-|-|-|-|\n")

	fmt.Printf("|BasicLimitInformation | %d | %d | %d | %d|\n", unsafe.Offsetof(info.BasicLimitInformation), C.OffsetOfBasicLimitInformation(), unsafe.Sizeof(info.BasicLimitInformation), C.SizeOfBasicLimitInformation())
	fmt.Printf("|IoInfo | %d | %d | %d | %d|\n", unsafe.Offsetof(info.IoInfo), C.OffsetOfIoInfo(), unsafe.Sizeof(info.IoInfo), C.SizeOfIoInfo())
	fmt.Printf("|ProcessMemoryLimit | %d | %d | %d | %d|\n", unsafe.Offsetof(info.ProcessMemoryLimit), C.OffsetOfProcessMemoryLimit(), unsafe.Sizeof(info.ProcessMemoryLimit), C.SizeOfProcessMemoryLimit())
	fmt.Printf("|JobMemoryLimit | %d | %d | %d | %d|\n", unsafe.Offsetof(info.JobMemoryLimit), C.OffsetOfJobMemoryLimit(), unsafe.Sizeof(info.JobMemoryLimit), C.SizeOfJobMemoryLimit())
	fmt.Printf("|PeakProcessMemoryUsed | %d | %d | %d | %d|\n", unsafe.Offsetof(info.PeakProcessMemoryUsed), C.OffsetOfPeakProcessMemoryUsed(), unsafe.Sizeof(info.PeakProcessMemoryUsed), C.SizeOfPeakProcessMemoryUsed())
	fmt.Printf("|PeakJobMemoryUsed | %d | %d | %d | %d|\n", unsafe.Offsetof(info.PeakJobMemoryUsed), C.OffsetOfPeakJobMemoryUsed(), unsafe.Sizeof(info.PeakJobMemoryUsed), C.SizeOfPeakJobMemoryUsed())

	fmt.Printf("\n")
	fmt.Printf("|Field | GO Offset | C Offset | GO Size | C Size|\n|-|-|-|-|-|\n")
	fmt.Printf("|PerProcessUserTimeLimit | %d | %d | %d | %d|\n", unsafe.Offsetof(info.BasicLimitInformation.PerProcessUserTimeLimit), C.OffsetOfPerProcessUserTimeLimit(), unsafe.Sizeof(info.BasicLimitInformation.PerProcessUserTimeLimit), C.SizeOfPerProcessUserTimeLimit())
	fmt.Printf("|PerJobUserTimeLimit | %d | %d | %d | %d|\n", unsafe.Offsetof(info.BasicLimitInformation.PerJobUserTimeLimit), C.OffsetOfPerJobUserTimeLimit(), unsafe.Sizeof(info.BasicLimitInformation.PerJobUserTimeLimit), C.SizeOfPerJobUserTimeLimit())
	fmt.Printf("|LimitFlags | %d | %d | %d | %d|\n", unsafe.Offsetof(info.BasicLimitInformation.LimitFlags), C.OffsetOfLimitFlags(), unsafe.Sizeof(info.BasicLimitInformation.LimitFlags), C.SizeOfLimitFlags())
	fmt.Printf("|MinimumWorkingSetSize | %d | %d | %d | %d|\n", unsafe.Offsetof(info.BasicLimitInformation.MinimumWorkingSetSize), C.OffsetOfMinimumWorkingSetSize(), unsafe.Sizeof(info.BasicLimitInformation.MinimumWorkingSetSize), C.SizeOfMinimumWorkingSetSize())
	fmt.Printf("|MaximumWorkingSetSize | %d | %d | %d | %d|\n", unsafe.Offsetof(info.BasicLimitInformation.MaximumWorkingSetSize), C.OffsetOfMaximumWorkingSetSize(), unsafe.Sizeof(info.BasicLimitInformation.MaximumWorkingSetSize), C.SizeOfMaximumWorkingSetSize())
	fmt.Printf("|ActiveProcessLimit | %d | %d | %d | %d|\n", unsafe.Offsetof(info.BasicLimitInformation.ActiveProcessLimit), C.OffsetOfActiveProcessLimit(), unsafe.Sizeof(info.BasicLimitInformation.ActiveProcessLimit), C.SizeOfActiveProcessLimit())
	fmt.Printf("|Affinity | %d | %d | %d | %d|\n", unsafe.Offsetof(info.BasicLimitInformation.Affinity), C.OffsetOfAffinity(), unsafe.Sizeof(info.BasicLimitInformation.Affinity), C.SizeOfAffinity())
	fmt.Printf("|PriorityClass | %d | %d | %d | %d|\n", unsafe.Offsetof(info.BasicLimitInformation.PriorityClass), C.OffsetOfPriorityClass(), unsafe.Sizeof(info.BasicLimitInformation.PriorityClass), C.SizeOfPriorityClass())
	fmt.Printf("|SchedulingClass | %d | %d | %d | %d|\n", unsafe.Offsetof(info.BasicLimitInformation.SchedulingClass), C.OffsetOfSchedulingClass(), unsafe.Sizeof(info.BasicLimitInformation.SchedulingClass), C.SizeOfSchedulingClass())
	fmt.Printf("\n")
}

@gopherbot
Copy link

@gopherbot gopherbot commented Aug 27, 2020

Change https://golang.org/cl/251197 mentions this issue: windows: use GOARCH-specific JOBOBJECT_BASIC_LIMIT_INFORMATION

@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Aug 27, 2020

@dbellavista Thanks! Looks like for Windows the alignment of JOBOBJECT_BASIC_LIMIT_INFORMATION is 8 bytes, but for Go it is 4 bytes. Sounds like we need to pad the 32-bit versions of the struct.

Can you see whether https://golang.org/cl/251197 fixes the problem? Thanks.

@dbellavista
Copy link
Author

@dbellavista dbellavista commented Aug 28, 2020

@ianlancetaylor thanks! The sizes are now equal

@gopherbot
Copy link

@gopherbot gopherbot commented Sep 4, 2020

Change https://golang.org/cl/253097 mentions this issue: windows: add TestJobObjectInfo

gopherbot pushed a commit to golang/sys that referenced this issue Sep 5, 2020
Add test for CL 251197.

Updates golang/go#41001

Change-Id: I6317678057eb8b18a1f7564842a92682c0c9930f
Reviewed-on: https://go-review.googlesource.com/c/sys/+/253097
Run-TryBot: Alex Brainman <alex.brainman@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
5 participants
You can’t perform that action at this time.