| // Copyright 2017 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // linux/mkall.go - Generates all Linux zsysnum, zsyscall, zerror, and ztype |
| // files for all Linux architectures supported by the go compiler. See |
| // README.md for more information about the build system. |
| |
| // To run it you must have a git checkout of the Linux kernel and glibc. Once |
| // the appropriate sources are ready, the program is run as: |
| // go run linux/mkall.go <linux_dir> <glibc_dir> |
| |
| // +build ignore |
| |
| package main |
| |
| import ( |
| "bufio" |
| "bytes" |
| "debug/elf" |
| "encoding/binary" |
| "errors" |
| "fmt" |
| "io" |
| "io/ioutil" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "runtime" |
| "strings" |
| "unicode" |
| ) |
| |
| // These will be paths to the appropriate source directories. |
| var LinuxDir string |
| var GlibcDir string |
| |
| const TempDir = "/tmp" |
| const IncludeDir = TempDir + "/include" // To hold our C headers |
| const BuildDir = TempDir + "/build" // To hold intermediate build files |
| |
| const GOOS = "linux" // Only for Linux targets |
| const BuildArch = "amd64" // Must be built on this architecture |
| const MinKernel = "2.6.23" // https://golang.org/doc/install#requirements |
| |
| type target struct { |
| GoArch string // Architecture name according to Go |
| LinuxArch string // Architecture name according to the Linux Kernel |
| GNUArch string // Architecture name according to GNU tools (https://wiki.debian.org/Multiarch/Tuples) |
| BigEndian bool // Default Little Endian |
| SignedChar bool // Is -fsigned-char needed (default no) |
| Bits int |
| } |
| |
| // List of all Linux targets supported by the go compiler. Currently, riscv64 |
| // and sparc64 are not fully supported, but there is enough support already to |
| // generate Go type and error definitions. |
| var targets = []target{ |
| { |
| GoArch: "386", |
| LinuxArch: "x86", |
| GNUArch: "i686-linux-gnu", // Note "i686" not "i386" |
| Bits: 32, |
| }, |
| { |
| GoArch: "amd64", |
| LinuxArch: "x86", |
| GNUArch: "x86_64-linux-gnu", |
| Bits: 64, |
| }, |
| { |
| GoArch: "arm64", |
| LinuxArch: "arm64", |
| GNUArch: "aarch64-linux-gnu", |
| SignedChar: true, |
| Bits: 64, |
| }, |
| { |
| GoArch: "arm", |
| LinuxArch: "arm", |
| GNUArch: "arm-linux-gnueabi", |
| Bits: 32, |
| }, |
| { |
| GoArch: "mips", |
| LinuxArch: "mips", |
| GNUArch: "mips-linux-gnu", |
| BigEndian: true, |
| Bits: 32, |
| }, |
| { |
| GoArch: "mipsle", |
| LinuxArch: "mips", |
| GNUArch: "mipsel-linux-gnu", |
| Bits: 32, |
| }, |
| { |
| GoArch: "mips64", |
| LinuxArch: "mips", |
| GNUArch: "mips64-linux-gnuabi64", |
| BigEndian: true, |
| Bits: 64, |
| }, |
| { |
| GoArch: "mips64le", |
| LinuxArch: "mips", |
| GNUArch: "mips64el-linux-gnuabi64", |
| Bits: 64, |
| }, |
| { |
| GoArch: "ppc64", |
| LinuxArch: "powerpc", |
| GNUArch: "powerpc64-linux-gnu", |
| BigEndian: true, |
| Bits: 64, |
| }, |
| { |
| GoArch: "ppc64le", |
| LinuxArch: "powerpc", |
| GNUArch: "powerpc64le-linux-gnu", |
| Bits: 64, |
| }, |
| { |
| GoArch: "riscv64", |
| LinuxArch: "riscv", |
| GNUArch: "riscv64-linux-gnu", |
| Bits: 64, |
| }, |
| { |
| GoArch: "s390x", |
| LinuxArch: "s390", |
| GNUArch: "s390x-linux-gnu", |
| BigEndian: true, |
| SignedChar: true, |
| Bits: 64, |
| }, |
| { |
| GoArch: "sparc64", |
| LinuxArch: "sparc", |
| GNUArch: "sparc64-linux-gnu", |
| BigEndian: true, |
| Bits: 64, |
| }, |
| } |
| |
| // ptracePairs is a list of pairs of targets that can, in some cases, |
| // run each other's binaries. |
| var ptracePairs = []struct{ a1, a2 string }{ |
| {"386", "amd64"}, |
| {"arm", "arm64"}, |
| {"mips", "mips64"}, |
| {"mipsle", "mips64le"}, |
| } |
| |
| func main() { |
| if runtime.GOOS != GOOS || runtime.GOARCH != BuildArch { |
| fmt.Printf("Build system has GOOS_GOARCH = %s_%s, need %s_%s\n", |
| runtime.GOOS, runtime.GOARCH, GOOS, BuildArch) |
| return |
| } |
| |
| // Check that we are using the new build system if we should |
| if os.Getenv("GOLANG_SYS_BUILD") != "docker" { |
| fmt.Println("In the new build system, mkall.go should not be called directly.") |
| fmt.Println("See README.md") |
| return |
| } |
| |
| // Parse the command line options |
| if len(os.Args) != 3 { |
| fmt.Println("USAGE: go run linux/mkall.go <linux_dir> <glibc_dir>") |
| return |
| } |
| LinuxDir = os.Args[1] |
| GlibcDir = os.Args[2] |
| |
| for _, t := range targets { |
| fmt.Printf("----- GENERATING: %s -----\n", t.GoArch) |
| if err := t.generateFiles(); err != nil { |
| fmt.Printf("%v\n***** FAILURE: %s *****\n\n", err, t.GoArch) |
| } else { |
| fmt.Printf("----- SUCCESS: %s -----\n\n", t.GoArch) |
| } |
| } |
| |
| fmt.Printf("----- GENERATING ptrace pairs -----\n") |
| ok := true |
| for _, p := range ptracePairs { |
| if err := generatePtracePair(p.a1, p.a2); err != nil { |
| fmt.Printf("%v\n***** FAILURE: %s/%s *****\n\n", err, p.a1, p.a2) |
| ok = false |
| } |
| } |
| if ok { |
| fmt.Printf("----- SUCCESS ptrace pairs -----\n\n") |
| } |
| } |
| |
| // Makes an exec.Cmd with Stderr attached to os.Stderr |
| func makeCommand(name string, args ...string) *exec.Cmd { |
| cmd := exec.Command(name, args...) |
| cmd.Stderr = os.Stderr |
| return cmd |
| } |
| |
| // Set GOARCH for target and build environments. |
| func (t *target) setTargetBuildArch(cmd *exec.Cmd) { |
| // Set GOARCH_TARGET so command knows what GOARCH is.. |
| cmd.Env = append(os.Environ(), "GOARCH_TARGET="+t.GoArch) |
| // Set GOARCH to host arch for command, so it can run natively. |
| for i, s := range cmd.Env { |
| if strings.HasPrefix(s, "GOARCH=") { |
| cmd.Env[i] = "GOARCH=" + BuildArch |
| } |
| } |
| } |
| |
| // Runs the command, pipes output to a formatter, pipes that to an output file. |
| func (t *target) commandFormatOutput(formatter string, outputFile string, |
| name string, args ...string) (err error) { |
| mainCmd := makeCommand(name, args...) |
| if name == "mksyscall" { |
| args = append([]string{"run", "mksyscall.go"}, args...) |
| mainCmd = makeCommand("go", args...) |
| t.setTargetBuildArch(mainCmd) |
| } else if name == "mksysnum" { |
| args = append([]string{"run", "linux/mksysnum.go"}, args...) |
| mainCmd = makeCommand("go", args...) |
| t.setTargetBuildArch(mainCmd) |
| } |
| |
| fmtCmd := makeCommand(formatter) |
| if formatter == "mkpost" { |
| fmtCmd = makeCommand("go", "run", "mkpost.go") |
| t.setTargetBuildArch(fmtCmd) |
| } |
| |
| // mainCmd | fmtCmd > outputFile |
| if fmtCmd.Stdin, err = mainCmd.StdoutPipe(); err != nil { |
| return |
| } |
| if fmtCmd.Stdout, err = os.Create(outputFile); err != nil { |
| return |
| } |
| |
| // Make sure the formatter eventually closes |
| if err = fmtCmd.Start(); err != nil { |
| return |
| } |
| defer func() { |
| fmtErr := fmtCmd.Wait() |
| if err == nil { |
| err = fmtErr |
| } |
| }() |
| |
| return mainCmd.Run() |
| } |
| |
| // Generates all the files for a Linux target |
| func (t *target) generateFiles() error { |
| // Setup environment variables |
| os.Setenv("GOOS", GOOS) |
| os.Setenv("GOARCH", t.GoArch) |
| |
| // Get appropriate compiler and emulator (unless on x86) |
| if t.LinuxArch != "x86" { |
| // Check/Setup cross compiler |
| compiler := t.GNUArch + "-gcc" |
| if _, err := exec.LookPath(compiler); err != nil { |
| return err |
| } |
| os.Setenv("CC", compiler) |
| |
| // Check/Setup emulator (usually first component of GNUArch) |
| qemuArchName := t.GNUArch[:strings.Index(t.GNUArch, "-")] |
| if t.LinuxArch == "powerpc" { |
| qemuArchName = t.GoArch |
| } |
| // Fake uname for QEMU to allow running on Host kernel version < 4.15 |
| if t.LinuxArch == "riscv" { |
| os.Setenv("QEMU_UNAME", "4.15") |
| } |
| os.Setenv("GORUN", "qemu-"+qemuArchName) |
| } else { |
| os.Setenv("CC", "gcc") |
| } |
| |
| // Make the include directory and fill it with headers |
| if err := os.MkdirAll(IncludeDir, os.ModePerm); err != nil { |
| return err |
| } |
| defer os.RemoveAll(IncludeDir) |
| if err := t.makeHeaders(); err != nil { |
| return fmt.Errorf("could not make header files: %v", err) |
| } |
| fmt.Println("header files generated") |
| |
| // Make each of the four files |
| if err := t.makeZSysnumFile(); err != nil { |
| return fmt.Errorf("could not make zsysnum file: %v", err) |
| } |
| fmt.Println("zsysnum file generated") |
| |
| if err := t.makeZSyscallFile(); err != nil { |
| return fmt.Errorf("could not make zsyscall file: %v", err) |
| } |
| fmt.Println("zsyscall file generated") |
| |
| if err := t.makeZTypesFile(); err != nil { |
| return fmt.Errorf("could not make ztypes file: %v", err) |
| } |
| fmt.Println("ztypes file generated") |
| |
| if err := t.makeZErrorsFile(); err != nil { |
| return fmt.Errorf("could not make zerrors file: %v", err) |
| } |
| fmt.Println("zerrors file generated") |
| |
| return nil |
| } |
| |
| // Create the Linux, glibc and ABI (C compiler convention) headers in the include directory. |
| func (t *target) makeHeaders() error { |
| // Make the Linux headers we need for this architecture |
| linuxMake := makeCommand("make", "headers_install", "ARCH="+t.LinuxArch, "INSTALL_HDR_PATH="+TempDir) |
| linuxMake.Dir = LinuxDir |
| if err := linuxMake.Run(); err != nil { |
| return err |
| } |
| |
| // A Temporary build directory for glibc |
| if err := os.MkdirAll(BuildDir, os.ModePerm); err != nil { |
| return err |
| } |
| defer os.RemoveAll(BuildDir) |
| |
| // Make the glibc headers we need for this architecture |
| confScript := filepath.Join(GlibcDir, "configure") |
| glibcConf := makeCommand(confScript, "--prefix="+TempDir, "--host="+t.GNUArch, "--enable-kernel="+MinKernel) |
| glibcConf.Dir = BuildDir |
| if err := glibcConf.Run(); err != nil { |
| return err |
| } |
| glibcMake := makeCommand("make", "install-headers") |
| glibcMake.Dir = BuildDir |
| if err := glibcMake.Run(); err != nil { |
| return err |
| } |
| // We only need an empty stubs file |
| stubsFile := filepath.Join(IncludeDir, "gnu/stubs.h") |
| if file, err := os.Create(stubsFile); err != nil { |
| return err |
| } else { |
| file.Close() |
| } |
| |
| // ABI headers will specify C compiler behavior for the target platform. |
| return t.makeABIHeaders() |
| } |
| |
| // makeABIHeaders generates C header files based on the platform's calling convention. |
| // While many platforms have formal Application Binary Interfaces, in practice, whatever the |
| // dominant C compilers generate is the de-facto calling convention. |
| // |
| // We generate C headers instead of a Go file, so as to enable references to the ABI from Cgo. |
| func (t *target) makeABIHeaders() (err error) { |
| abiDir := filepath.Join(IncludeDir, "abi") |
| if err = os.Mkdir(abiDir, os.ModePerm); err != nil { |
| return err |
| } |
| |
| cc := os.Getenv("CC") |
| if cc == "" { |
| return errors.New("CC (compiler) env var not set") |
| } |
| |
| // Build a sacrificial ELF file, to mine for C compiler behavior. |
| binPath := filepath.Join(TempDir, "tmp_abi.o") |
| bin, err := t.buildELF(cc, cCode, binPath) |
| if err != nil { |
| return fmt.Errorf("cannot build ELF to analyze: %v", err) |
| } |
| defer bin.Close() |
| defer os.Remove(binPath) |
| |
| // Right now, we put everything in abi.h, but we may change this later. |
| abiFile, err := os.Create(filepath.Join(abiDir, "abi.h")) |
| if err != nil { |
| return err |
| } |
| defer func() { |
| if cerr := abiFile.Close(); cerr != nil && err == nil { |
| err = cerr |
| } |
| }() |
| |
| if err = t.writeBitFieldMasks(bin, abiFile); err != nil { |
| return fmt.Errorf("cannot write bitfield masks: %v", err) |
| } |
| |
| return nil |
| } |
| |
| func (t *target) buildELF(cc, src, path string) (*elf.File, error) { |
| // Compile the cCode source using the set compiler - we will need its .data section. |
| // Do not link the binary, so that we can find .data section offsets from the symbol values. |
| ccCmd := makeCommand(cc, "-o", path, "-gdwarf", "-x", "c", "-c", "-") |
| ccCmd.Stdin = strings.NewReader(src) |
| ccCmd.Stdout = os.Stdout |
| if err := ccCmd.Run(); err != nil { |
| return nil, fmt.Errorf("compiler error: %v", err) |
| } |
| |
| bin, err := elf.Open(path) |
| if err != nil { |
| return nil, fmt.Errorf("cannot read ELF file %s: %v", path, err) |
| } |
| |
| return bin, nil |
| } |
| |
| func (t *target) writeBitFieldMasks(bin *elf.File, out io.Writer) error { |
| symbols, err := bin.Symbols() |
| if err != nil { |
| return fmt.Errorf("getting ELF symbols: %v", err) |
| } |
| var masksSym *elf.Symbol |
| |
| for _, sym := range symbols { |
| if sym.Name == "masks" { |
| masksSym = &sym |
| } |
| } |
| |
| if masksSym == nil { |
| return errors.New("could not find the 'masks' symbol in ELF symtab") |
| } |
| |
| dataSection := bin.Section(".data") |
| if dataSection == nil { |
| return errors.New("ELF file has no .data section") |
| } |
| |
| data, err := dataSection.Data() |
| if err != nil { |
| return fmt.Errorf("could not read .data section: %v\n", err) |
| } |
| |
| var bo binary.ByteOrder |
| if t.BigEndian { |
| bo = binary.BigEndian |
| } else { |
| bo = binary.LittleEndian |
| } |
| |
| // 64 bit masks of type uint64 are stored in the data section starting at masks.Value. |
| // Here we are running on AMD64, but these values may be big endian or little endian, |
| // depending on target architecture. |
| for i := uint64(0); i < 64; i++ { |
| off := masksSym.Value + i*8 |
| // Define each mask in native by order, so as to match target endian. |
| fmt.Fprintf(out, "#define BITFIELD_MASK_%d %dULL\n", i, bo.Uint64(data[off:off+8])) |
| } |
| |
| return nil |
| } |
| |
| // makes the zsysnum_linux_$GOARCH.go file |
| func (t *target) makeZSysnumFile() error { |
| zsysnumFile := fmt.Sprintf("zsysnum_linux_%s.go", t.GoArch) |
| unistdFile := filepath.Join(IncludeDir, "asm/unistd.h") |
| |
| args := append(t.cFlags(), unistdFile) |
| return t.commandFormatOutput("gofmt", zsysnumFile, "mksysnum", args...) |
| } |
| |
| // makes the zsyscall_linux_$GOARCH.go file |
| func (t *target) makeZSyscallFile() error { |
| zsyscallFile := fmt.Sprintf("zsyscall_linux_%s.go", t.GoArch) |
| // Find the correct architecture syscall file (might end with x.go) |
| archSyscallFile := fmt.Sprintf("syscall_linux_%s.go", t.GoArch) |
| if _, err := os.Stat(archSyscallFile); os.IsNotExist(err) { |
| shortArch := strings.TrimSuffix(t.GoArch, "le") |
| archSyscallFile = fmt.Sprintf("syscall_linux_%sx.go", shortArch) |
| } |
| |
| args := append(t.mksyscallFlags(), "-tags", "linux,"+t.GoArch, |
| "syscall_linux.go", archSyscallFile) |
| return t.commandFormatOutput("gofmt", zsyscallFile, "mksyscall", args...) |
| } |
| |
| // makes the zerrors_linux_$GOARCH.go file |
| func (t *target) makeZErrorsFile() error { |
| zerrorsFile := fmt.Sprintf("zerrors_linux_%s.go", t.GoArch) |
| |
| return t.commandFormatOutput("gofmt", zerrorsFile, "./mkerrors.sh", t.cFlags()...) |
| } |
| |
| // makes the ztypes_linux_$GOARCH.go file |
| func (t *target) makeZTypesFile() error { |
| ztypesFile := fmt.Sprintf("ztypes_linux_%s.go", t.GoArch) |
| |
| args := []string{"tool", "cgo", "-godefs", "--"} |
| args = append(args, t.cFlags()...) |
| args = append(args, "linux/types.go") |
| return t.commandFormatOutput("mkpost", ztypesFile, "go", args...) |
| } |
| |
| // Flags that should be given to gcc and cgo for this target |
| func (t *target) cFlags() []string { |
| // Compile statically to avoid cross-architecture dynamic linking. |
| flags := []string{"-Wall", "-Werror", "-static", "-I" + IncludeDir} |
| |
| // Architecture-specific flags |
| if t.SignedChar { |
| flags = append(flags, "-fsigned-char") |
| } |
| if t.LinuxArch == "x86" { |
| flags = append(flags, fmt.Sprintf("-m%d", t.Bits)) |
| } |
| |
| return flags |
| } |
| |
| // Flags that should be given to mksyscall for this target |
| func (t *target) mksyscallFlags() (flags []string) { |
| if t.Bits == 32 { |
| if t.BigEndian { |
| flags = append(flags, "-b32") |
| } else { |
| flags = append(flags, "-l32") |
| } |
| } |
| |
| // This flag means a 64-bit value should use (even, odd)-pair. |
| if t.GoArch == "arm" || (t.LinuxArch == "mips" && t.Bits == 32) { |
| flags = append(flags, "-arm") |
| } |
| return |
| } |
| |
| // generatePtracePair takes a pair of GOARCH values that can run each |
| // other's binaries, such as 386 and amd64. It extracts the PtraceRegs |
| // type for each one. It writes a new file defining the types |
| // PtraceRegsArch1 and PtraceRegsArch2 and the corresponding functions |
| // Ptrace{Get,Set}Regs{arch1,arch2}. This permits debugging the other |
| // binary on a native system. |
| func generatePtracePair(arch1, arch2 string) error { |
| def1, err := ptraceDef(arch1) |
| if err != nil { |
| return err |
| } |
| def2, err := ptraceDef(arch2) |
| if err != nil { |
| return err |
| } |
| f, err := os.Create(fmt.Sprintf("zptrace%s_linux.go", arch1)) |
| if err != nil { |
| return err |
| } |
| buf := bufio.NewWriter(f) |
| fmt.Fprintf(buf, "// Code generated by linux/mkall.go generatePtracePair(%s, %s). DO NOT EDIT.\n", arch1, arch2) |
| fmt.Fprintf(buf, "\n") |
| fmt.Fprintf(buf, "// +build linux\n") |
| fmt.Fprintf(buf, "// +build %s %s\n", arch1, arch2) |
| fmt.Fprintf(buf, "\n") |
| fmt.Fprintf(buf, "package unix\n") |
| fmt.Fprintf(buf, "\n") |
| fmt.Fprintf(buf, "%s\n", `import "unsafe"`) |
| fmt.Fprintf(buf, "\n") |
| writeOnePtrace(buf, arch1, def1) |
| fmt.Fprintf(buf, "\n") |
| writeOnePtrace(buf, arch2, def2) |
| if err := buf.Flush(); err != nil { |
| return err |
| } |
| if err := f.Close(); err != nil { |
| return err |
| } |
| return nil |
| } |
| |
| // ptraceDef returns the definition of PtraceRegs for arch. |
| func ptraceDef(arch string) (string, error) { |
| filename := fmt.Sprintf("ztypes_linux_%s.go", arch) |
| data, err := ioutil.ReadFile(filename) |
| if err != nil { |
| return "", fmt.Errorf("reading %s: %v", filename, err) |
| } |
| start := bytes.Index(data, []byte("type PtraceRegs struct")) |
| if start < 0 { |
| return "", fmt.Errorf("%s: no definition of PtraceRegs", filename) |
| } |
| data = data[start:] |
| end := bytes.Index(data, []byte("\n}\n")) |
| if end < 0 { |
| return "", fmt.Errorf("%s: can't find end of PtraceRegs definition", filename) |
| } |
| return string(data[:end+2]), nil |
| } |
| |
| // writeOnePtrace writes out the ptrace definitions for arch. |
| func writeOnePtrace(w io.Writer, arch, def string) { |
| uarch := string(unicode.ToUpper(rune(arch[0]))) + arch[1:] |
| fmt.Fprintf(w, "// PtraceRegs%s is the registers used by %s binaries.\n", uarch, arch) |
| fmt.Fprintf(w, "%s\n", strings.Replace(def, "PtraceRegs", "PtraceRegs"+uarch, 1)) |
| fmt.Fprintf(w, "\n") |
| fmt.Fprintf(w, "// PtraceGetRegs%s fetches the registers used by %s binaries.\n", uarch, arch) |
| fmt.Fprintf(w, "func PtraceGetRegs%s(pid int, regsout *PtraceRegs%s) error {\n", uarch, uarch) |
| fmt.Fprintf(w, "\treturn ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout)))\n") |
| fmt.Fprintf(w, "}\n") |
| fmt.Fprintf(w, "\n") |
| fmt.Fprintf(w, "// PtraceSetRegs%s sets the registers used by %s binaries.\n", uarch, arch) |
| fmt.Fprintf(w, "func PtraceSetRegs%s(pid int, regs *PtraceRegs%s) error {\n", uarch, uarch) |
| fmt.Fprintf(w, "\treturn ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs)))\n") |
| fmt.Fprintf(w, "}\n") |
| } |
| |
| // cCode is compiled for the target architecture, and the resulting data section is carved for |
| // the statically initialized bit masks. |
| const cCode = ` |
| // Bit fields are used in some system calls and other ABIs, but their memory layout is |
| // implementation-defined [1]. Even with formal ABIs, bit fields are a source of subtle bugs [2]. |
| // Here we generate the offsets for all 64 bits in an uint64. |
| // 1: http://en.cppreference.com/w/c/language/bit_field |
| // 2: https://lwn.net/Articles/478657/ |
| |
| #include <stdint.h> |
| |
| struct bitfield { |
| union { |
| uint64_t val; |
| struct { |
| uint64_t u64_bit_0 : 1; |
| uint64_t u64_bit_1 : 1; |
| uint64_t u64_bit_2 : 1; |
| uint64_t u64_bit_3 : 1; |
| uint64_t u64_bit_4 : 1; |
| uint64_t u64_bit_5 : 1; |
| uint64_t u64_bit_6 : 1; |
| uint64_t u64_bit_7 : 1; |
| uint64_t u64_bit_8 : 1; |
| uint64_t u64_bit_9 : 1; |
| uint64_t u64_bit_10 : 1; |
| uint64_t u64_bit_11 : 1; |
| uint64_t u64_bit_12 : 1; |
| uint64_t u64_bit_13 : 1; |
| uint64_t u64_bit_14 : 1; |
| uint64_t u64_bit_15 : 1; |
| uint64_t u64_bit_16 : 1; |
| uint64_t u64_bit_17 : 1; |
| uint64_t u64_bit_18 : 1; |
| uint64_t u64_bit_19 : 1; |
| uint64_t u64_bit_20 : 1; |
| uint64_t u64_bit_21 : 1; |
| uint64_t u64_bit_22 : 1; |
| uint64_t u64_bit_23 : 1; |
| uint64_t u64_bit_24 : 1; |
| uint64_t u64_bit_25 : 1; |
| uint64_t u64_bit_26 : 1; |
| uint64_t u64_bit_27 : 1; |
| uint64_t u64_bit_28 : 1; |
| uint64_t u64_bit_29 : 1; |
| uint64_t u64_bit_30 : 1; |
| uint64_t u64_bit_31 : 1; |
| uint64_t u64_bit_32 : 1; |
| uint64_t u64_bit_33 : 1; |
| uint64_t u64_bit_34 : 1; |
| uint64_t u64_bit_35 : 1; |
| uint64_t u64_bit_36 : 1; |
| uint64_t u64_bit_37 : 1; |
| uint64_t u64_bit_38 : 1; |
| uint64_t u64_bit_39 : 1; |
| uint64_t u64_bit_40 : 1; |
| uint64_t u64_bit_41 : 1; |
| uint64_t u64_bit_42 : 1; |
| uint64_t u64_bit_43 : 1; |
| uint64_t u64_bit_44 : 1; |
| uint64_t u64_bit_45 : 1; |
| uint64_t u64_bit_46 : 1; |
| uint64_t u64_bit_47 : 1; |
| uint64_t u64_bit_48 : 1; |
| uint64_t u64_bit_49 : 1; |
| uint64_t u64_bit_50 : 1; |
| uint64_t u64_bit_51 : 1; |
| uint64_t u64_bit_52 : 1; |
| uint64_t u64_bit_53 : 1; |
| uint64_t u64_bit_54 : 1; |
| uint64_t u64_bit_55 : 1; |
| uint64_t u64_bit_56 : 1; |
| uint64_t u64_bit_57 : 1; |
| uint64_t u64_bit_58 : 1; |
| uint64_t u64_bit_59 : 1; |
| uint64_t u64_bit_60 : 1; |
| uint64_t u64_bit_61 : 1; |
| uint64_t u64_bit_62 : 1; |
| uint64_t u64_bit_63 : 1; |
| }; |
| }; |
| }; |
| |
| struct bitfield masks[] = { |
| {.u64_bit_0 = 1}, |
| {.u64_bit_1 = 1}, |
| {.u64_bit_2 = 1}, |
| {.u64_bit_3 = 1}, |
| {.u64_bit_4 = 1}, |
| {.u64_bit_5 = 1}, |
| {.u64_bit_6 = 1}, |
| {.u64_bit_7 = 1}, |
| {.u64_bit_8 = 1}, |
| {.u64_bit_9 = 1}, |
| {.u64_bit_10 = 1}, |
| {.u64_bit_11 = 1}, |
| {.u64_bit_12 = 1}, |
| {.u64_bit_13 = 1}, |
| {.u64_bit_14 = 1}, |
| {.u64_bit_15 = 1}, |
| {.u64_bit_16 = 1}, |
| {.u64_bit_17 = 1}, |
| {.u64_bit_18 = 1}, |
| {.u64_bit_19 = 1}, |
| {.u64_bit_20 = 1}, |
| {.u64_bit_21 = 1}, |
| {.u64_bit_22 = 1}, |
| {.u64_bit_23 = 1}, |
| {.u64_bit_24 = 1}, |
| {.u64_bit_25 = 1}, |
| {.u64_bit_26 = 1}, |
| {.u64_bit_27 = 1}, |
| {.u64_bit_28 = 1}, |
| {.u64_bit_29 = 1}, |
| {.u64_bit_30 = 1}, |
| {.u64_bit_31 = 1}, |
| {.u64_bit_32 = 1}, |
| {.u64_bit_33 = 1}, |
| {.u64_bit_34 = 1}, |
| {.u64_bit_35 = 1}, |
| {.u64_bit_36 = 1}, |
| {.u64_bit_37 = 1}, |
| {.u64_bit_38 = 1}, |
| {.u64_bit_39 = 1}, |
| {.u64_bit_40 = 1}, |
| {.u64_bit_41 = 1}, |
| {.u64_bit_42 = 1}, |
| {.u64_bit_43 = 1}, |
| {.u64_bit_44 = 1}, |
| {.u64_bit_45 = 1}, |
| {.u64_bit_46 = 1}, |
| {.u64_bit_47 = 1}, |
| {.u64_bit_48 = 1}, |
| {.u64_bit_49 = 1}, |
| {.u64_bit_50 = 1}, |
| {.u64_bit_51 = 1}, |
| {.u64_bit_52 = 1}, |
| {.u64_bit_53 = 1}, |
| {.u64_bit_54 = 1}, |
| {.u64_bit_55 = 1}, |
| {.u64_bit_56 = 1}, |
| {.u64_bit_57 = 1}, |
| {.u64_bit_58 = 1}, |
| {.u64_bit_59 = 1}, |
| {.u64_bit_60 = 1}, |
| {.u64_bit_61 = 1}, |
| {.u64_bit_62 = 1}, |
| {.u64_bit_63 = 1} |
| }; |
| |
| int main(int argc, char **argv) { |
| struct bitfield *mask_ptr = &masks[0]; |
| return mask_ptr->val; |
| } |
| |
| ` |