aboutsummaryrefslogtreecommitdiffstats
path: root/sysgen
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2015-10-12 10:16:57 +0200
committerDmitry Vyukov <dvyukov@google.com>2015-10-12 10:16:57 +0200
commit874c5754bb22dbf77d6b600ff91f0f4f1fc5073a (patch)
tree0075fbd088046ad5c86e6e972235701d68b3ce7c /sysgen
initial commit
Diffstat (limited to 'sysgen')
-rw-r--r--sysgen/parser.go92
-rw-r--r--sysgen/sysgen.go649
2 files changed, 741 insertions, 0 deletions
diff --git a/sysgen/parser.go b/sysgen/parser.go
new file mode 100644
index 000000000..92c27c58a
--- /dev/null
+++ b/sysgen/parser.go
@@ -0,0 +1,92 @@
+// Copyright 2015 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "os"
+)
+
+type Parser struct {
+ r *bufio.Scanner
+ s string
+ i int
+ l int
+}
+
+func NewParser(r io.Reader) *Parser {
+ return &Parser{r: bufio.NewScanner(r)}
+}
+
+func (p *Parser) Scan() bool {
+ if !p.r.Scan() {
+ if err := p.r.Err(); err != nil {
+ failf("failed to read input file: %v", err)
+ }
+ return false
+ }
+ p.s = p.r.Text()
+ p.i = 0
+ p.l++
+ return true
+}
+
+func (p *Parser) Str() string {
+ return p.s
+}
+
+func (p *Parser) EOF() bool {
+ return p.i == len(p.s)
+}
+
+func (p *Parser) Char() byte {
+ if p.EOF() {
+ p.failf("unexpected eof")
+ }
+ return p.s[p.i]
+}
+
+func (p *Parser) Parse(ch byte) {
+ if p.EOF() {
+ p.failf("want %s, got EOF", string(ch))
+ }
+ if p.s[p.i] != ch {
+ p.failf("want '%v', got '%v'", string(ch), string(p.s[p.i]))
+ }
+ p.i++
+ p.SkipWs()
+}
+
+func (p *Parser) SkipWs() {
+ for p.i < len(p.s) && (p.s[p.i] == ' ' || p.s[p.i] == '\t') {
+ p.i++
+ }
+}
+
+func (p *Parser) Ident() string {
+ i := p.i
+ for p.i < len(p.s) &&
+ (p.s[p.i] >= 'a' && p.s[p.i] <= 'z' ||
+ p.s[p.i] >= 'A' && p.s[p.i] <= 'Z' ||
+ p.s[p.i] >= '0' && p.s[p.i] <= '9' ||
+ p.s[p.i] == '_' || p.s[p.i] == '$') { // $ is for n-way syscalls (like ptrace$peek)
+ p.i++
+ }
+ if i == p.i {
+ p.failf("failed to parse identifier at pos %v", i)
+ }
+ if ch := p.s[i]; ch >= '0' && ch <= '9' {
+ // p.failf("identifier starts with a digit at pos %v", i)
+ }
+ s := p.s[i:p.i]
+ p.SkipWs()
+ return s
+}
+
+func (p *Parser) failf(msg string, args ...interface{}) {
+ fmt.Fprintf(os.Stderr, "line #%v: %v\n", p.l, p.s)
+ failf(msg, args...)
+}
diff --git a/sysgen/sysgen.go b/sysgen/sysgen.go
new file mode 100644
index 000000000..e8a3c62a1
--- /dev/null
+++ b/sysgen/sysgen.go
@@ -0,0 +1,649 @@
+// Copyright 2015 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "go/format"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+func main() {
+ if len(os.Args) != 2 {
+ failf("usage: gen input_file")
+ }
+ inf, err := os.Open(os.Args[1])
+ if err != nil {
+ failf("failed to open input file: %v", err)
+ }
+ defer inf.Close()
+
+ includes, defines, syscalls, structs, unnamed, flags := parse(bufio.NewReader(inf))
+ intFlags, flagVals := compileFlags(includes, defines, flags)
+
+ out := new(bytes.Buffer)
+ generate(syscalls, structs, unnamed, intFlags, out)
+ writeSource("sys.go", out.Bytes())
+
+ out = new(bytes.Buffer)
+ generateConsts(flagVals, out)
+ writeSource("../prog/consts.go", out.Bytes())
+
+ out = new(bytes.Buffer)
+ generateHeader(syscalls, out)
+ writeFile("../executor/syscalls.h", out.Bytes())
+}
+
+type Syscall struct {
+ Name string
+ CallName string
+ Args [][]string
+ Ret []string
+}
+
+type Struct struct {
+ Name string
+ Flds [][]string
+}
+
+type Flag struct {
+ Name string
+ Values []string
+}
+
+func generate(syscalls []Syscall, structs map[string]Struct, unnamed map[string][]string, flags map[string][]string, out io.Writer) {
+ fmt.Fprintf(out, "// AUTOGENERATED FILE\n")
+ fmt.Fprintf(out, "package sys\n\n")
+
+ fmt.Fprintf(out, "var Calls = []*Call {\n")
+ for i, s := range syscalls {
+ fmt.Fprintf(out, "&Call{ID: %v, Name: \"%v\", CallName: \"%v\"", i, s.Name, s.CallName)
+ if len(s.Ret) != 0 {
+ fmt.Fprintf(out, ", Ret: ")
+ generateArg("ret", s.Ret[0], s.Ret[1:], structs, unnamed, flags, false, out)
+ }
+ fmt.Fprintf(out, ", Args: []Type{")
+ for i, a := range s.Args {
+ if i != 0 {
+ fmt.Fprintf(out, ", ")
+ }
+ generateArg(a[0], a[1], a[2:], structs, unnamed, flags, false, out)
+ }
+ fmt.Fprintf(out, "}},\n")
+ }
+ fmt.Fprintf(out, "}\n")
+}
+
+func generateArg(name, typ string, a []string, structs map[string]Struct, unnamed map[string][]string, flags map[string][]string, isField bool, out io.Writer) {
+ name = "\"" + name + "\""
+ opt := false
+ for i, v := range a {
+ if v == "opt" {
+ opt = true
+ copy(a[i:], a[i+1:])
+ a = a[:len(a)-1]
+ break
+ }
+ }
+ common := func() string {
+ return fmt.Sprintf("TypeCommon: TypeCommon{TypeName: %v, IsOptional: %v}", name, opt)
+ }
+ switch typ {
+ case "fd":
+ if len(a) == 0 {
+ a = append(a, "")
+ }
+ if want := 1; len(a) != want {
+ failf("wrong number of arguments for %v arg %v want %v, got %v", typ, name, want, len(a))
+ }
+ fmt.Fprintf(out, "ResourceType{%v, Kind: ResFD, Subkind: %v}", common(), fmtFdKind(a[0]))
+ case "io_ctx":
+ if want := 0; len(a) != want {
+ failf("wrong number of arguments for %v arg %v want %v, got %v", typ, name, want, len(a))
+ }
+ fmt.Fprintf(out, "ResourceType{%v, Kind: ResIOCtx}", common())
+ case "ipc":
+ if want := 1; len(a) != want {
+ failf("wrong number of arguments for %v arg %v want %v, got %v", typ, name, want, len(a))
+ }
+ fmt.Fprintf(out, "ResourceType{%v, Kind: ResIPC, Subkind: %v}", common(), fmtIPCKind(a[0]))
+ case "key":
+ if want := 0; len(a) != want {
+ failf("wrong number of arguments for %v arg %v want %v, got %v", typ, name, want, len(a))
+ }
+ fmt.Fprintf(out, "ResourceType{%v, Kind: ResKey}", common())
+ case "inotifydesc":
+ if want := 0; len(a) != want {
+ failf("wrong number of arguments for %v arg %v want %v, got %v", typ, name, want, len(a))
+ }
+ fmt.Fprintf(out, "ResourceType{%v, Kind: ResInotifyDesc}", common())
+ case "timerid":
+ if want := 0; len(a) != want {
+ failf("wrong number of arguments for %v arg %v want %v, got %v", typ, name, want, len(a))
+ }
+ fmt.Fprintf(out, "ResourceType{%v, Kind: ResTimerid}", common())
+ case "pid":
+ if want := 0; len(a) != want {
+ failf("wrong number of arguments for %v arg %v want %v, got %v", typ, name, want, len(a))
+ }
+ fmt.Fprintf(out, "ResourceType{%v, Kind: ResPid}", common())
+ case "uid":
+ if want := 0; len(a) != want {
+ failf("wrong number of arguments for %v arg %v want %v, got %v", typ, name, want, len(a))
+ }
+ fmt.Fprintf(out, "ResourceType{%v, Kind: ResUid}", common())
+ case "gid":
+ if want := 0; len(a) != want {
+ failf("wrong number of arguments for %v arg %v want %v, got %v", typ, name, want, len(a))
+ }
+ fmt.Fprintf(out, "ResourceType{%v, Kind: ResGid}", common())
+ case "fileoff":
+ var size uint64
+ if isField {
+ if want := 2; len(a) != want {
+ failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a))
+ }
+ size = typeToSize(a[1])
+ } else {
+ if want := 1; len(a) != want {
+ failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a))
+ }
+ }
+ fmt.Fprintf(out, "FileoffType{%v, File: \"%v\", TypeSize: %v}", common(), a[0], size)
+ case "buffer":
+ if want := 1; len(a) != want {
+ failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a))
+ }
+ commonHdr := common()
+ opt = false
+ fmt.Fprintf(out, "PtrType{%v, Dir: %v, Type: BufferType{%v, Kind: BufferBlob}}", commonHdr, fmtDir(a[0]), common())
+ case "string":
+ if want := 0; len(a) != want {
+ failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a))
+ }
+ commonHdr := common()
+ opt = false
+ fmt.Fprintf(out, "PtrType{%v, Dir: %v, Type: BufferType{%v, Kind: BufferString}}", commonHdr, fmtDir("in"), common())
+ case "sockaddr":
+ if want := 0; len(a) != want {
+ failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a))
+ }
+ fmt.Fprintf(out, "BufferType{%v, Kind: BufferSockaddr}", common())
+ case "vma":
+ if want := 0; len(a) != want {
+ failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a))
+ }
+ fmt.Fprintf(out, "VmaType{%v}", common())
+ case "len":
+ var size uint64
+ if isField {
+ if want := 2; len(a) != want {
+ failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a))
+ }
+ size = typeToSize(a[1])
+ } else {
+ if want := 1; len(a) != want {
+ failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a))
+ }
+ }
+ fmt.Fprintf(out, "LenType{%v, Buf: \"%v\", TypeSize: %v}", common(), a[0], size)
+ case "flags":
+ var size uint64
+ if isField {
+ if want := 2; len(a) != want {
+ failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a))
+ }
+ size = typeToSize(a[1])
+ } else {
+ if want := 1; len(a) != want {
+ failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a))
+ }
+ }
+ vals := flags[a[0]]
+ if len(vals) == 0 {
+ failf("unknown flag %v", a[0])
+ }
+ fmt.Fprintf(out, "FlagsType{%v, TypeSize: %v, Vals: []uintptr{%v}}", common(), size, strings.Join(vals, ","))
+ case "int8", "int16", "int32", "int64", "intptr":
+ if want := 0; len(a) != want {
+ failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a))
+ }
+ fmt.Fprintf(out, "IntType{%v, TypeSize: %v}", common(), typeToSize(typ))
+ case "signalno":
+ if want := 0; len(a) != want {
+ failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a))
+ }
+ fmt.Fprintf(out, "IntType{%v, TypeSize: 4, Limit: 130}", common())
+ case "filename":
+ if want := 0; len(a) != want {
+ failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a))
+ }
+ commonHdr := common()
+ opt = false
+ fmt.Fprintf(out, "PtrType{%v, Dir: DirIn, Type: FilenameType{%v}}", commonHdr, common())
+ case "array":
+ if want := 1; len(a) != want {
+ failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a))
+ }
+ fmt.Fprintf(out, "ArrayType{%v, Type: %v}", common(), generateType(a[0], structs, unnamed, flags))
+ case "ptr":
+ if want := 2; len(a) != want {
+ failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a))
+ }
+ fmt.Fprintf(out, "PtrType{%v, Type: %v, Dir: %v}", common(), generateType(a[1], structs, unnamed, flags), fmtDir(a[0]))
+ default:
+ if strings.HasPrefix(typ, "unnamed") {
+ if inner, ok := unnamed[typ]; ok {
+ generateArg("", inner[0], inner[1:], structs, unnamed, flags, isField, out)
+ return
+ }
+ failf("unknown unnamed type '%v'", typ)
+ }
+ if str, ok := structs[typ]; ok {
+ fmt.Fprintf(out, "StructType{TypeCommon: TypeCommon{TypeName: \"%v\", IsOptional: %v}, Fields: []Type{", str.Name, false)
+ for i, a := range str.Flds {
+ if i != 0 {
+ fmt.Fprintf(out, ", ")
+ }
+ generateArg(a[0], a[1], a[2:], structs, unnamed, flags, true, out)
+ }
+ fmt.Fprintf(out, "}}")
+ return
+ }
+ failf("unknown arg type \"%v\" for %v", typ, name)
+ }
+}
+
+func generateType(typ string, structs map[string]Struct, unnamed map[string][]string, flags map[string][]string) string {
+ buf := new(bytes.Buffer)
+ generateArg("", typ, nil, structs, unnamed, flags, true, buf)
+ return buf.String()
+}
+
+func fmtFdKind(s string) string {
+ switch s {
+ case "":
+ return "ResAny"
+ case "file":
+ return "FdFile"
+ case "sock":
+ return "FdSock"
+ case "pipe":
+ return "FdPipe"
+ case "signal":
+ return "FdSignal"
+ case "event":
+ return "FdEvent"
+ case "timer":
+ return "FdTimer"
+ case "epoll":
+ return "FdEpoll"
+ case "dir":
+ return "FdDir"
+ case "mq":
+ return "FdMq"
+ case "inotify":
+ return "FdInotify"
+ case "fanotify":
+ return "FdFanotify"
+ default:
+ failf("bad fd type %v", s)
+ return ""
+ }
+}
+
+func fmtIPCKind(s string) string {
+ switch s {
+ case "msq":
+ return "IPCMsq"
+ case "sem":
+ return "IPCSem"
+ case "shm":
+ return "IPCShm"
+ default:
+ failf("bad ipc type %v", s)
+ return ""
+ }
+}
+
+func fmtDir(s string) string {
+ switch s {
+ case "in":
+ return "DirIn"
+ case "out":
+ return "DirOut"
+ case "inout":
+ return "DirInOut"
+ default:
+ failf("bad direction %v", s)
+ return ""
+ }
+}
+
+func typeToSize(typ string) uint64 {
+ switch typ {
+ case "int8", "int16", "int32", "int64", "intptr":
+ default:
+ failf("unknown type %v", typ)
+ }
+ sz := int64(64) // TODO: assume that pointer is 8 bytes for now
+ if typ != "intptr" {
+ sz, _ = strconv.ParseInt(typ[3:], 10, 64)
+ }
+ return uint64(sz / 8)
+}
+
+type F struct {
+ name string
+ val string
+}
+
+type FlagArray []F
+
+func (a FlagArray) Len() int { return len(a) }
+func (a FlagArray) Less(i, j int) bool { return a[i].name < a[j].name }
+func (a FlagArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+func generateConsts(flags map[string]string, out io.Writer) {
+ var ff []F
+ for k, v := range flags {
+ ff = append(ff, F{k, v})
+ }
+ sort.Sort(FlagArray(ff))
+
+ fmt.Fprintf(out, "// AUTOGENERATED FILE\n")
+ fmt.Fprintf(out, "package prog\n\n")
+ fmt.Fprintf(out, "const (\n")
+ for _, f := range ff {
+ fmt.Fprintf(out, " %v = %v\n", f.name, f.val)
+ }
+ fmt.Fprintf(out, ")\n")
+}
+
+func generateHeader(syscalls []Syscall, out io.Writer) {
+ fmt.Fprint(out, `// AUTOGENERATED FILE
+
+struct call_t {
+ const char* name;
+ int sys_nr;
+};
+
+call_t syscalls[] = {
+`)
+ for _, s := range syscalls {
+ fmt.Fprintf(out, "\t{\"%v\", __NR_%v},\n", s.Name, s.CallName)
+ }
+ fmt.Fprintf(out, "};\n")
+}
+
+func compileFlags(includes []string, defines map[string]string, flags []Flag) (map[string][]string, map[string]string) {
+ vals := make(map[string]string)
+ for _, f := range flags {
+ for _, v := range f.Values {
+ vals[v] = ""
+ }
+ }
+ for k := range defines {
+ vals[k] = ""
+ }
+ valArray := make([]string, 0, len(vals))
+ for k := range vals {
+ valArray = append(valArray, k)
+ }
+ flagVals := fetchValues(valArray, includes, defines)
+ for i, f := range valArray {
+ vals[f] = flagVals[i]
+ }
+ res := make(map[string][]string)
+ for _, f := range flags {
+ var arr []string
+ for _, v := range f.Values {
+ arr = append(arr, vals[v])
+ }
+ if res[f.Name] != nil {
+ failf("flag %v is defined multiple times", f.Name)
+ }
+ res[f.Name] = arr
+ }
+ ids := make(map[string]string)
+ for k, v := range vals {
+ if isIdentifier(k) {
+ ids[k] = v
+ }
+ }
+ return res, ids
+}
+
+func fetchValues(vals []string, includes []string, defines map[string]string) []string {
+ includeText := ""
+ for _, inc := range includes {
+ includeText += fmt.Sprintf("#include <%v>\n", inc)
+ }
+ definesText := ""
+ for k, v := range defines {
+ definesText += fmt.Sprintf("#ifndef %v\n#define %v %v\n#endif\n", k, k, v)
+ }
+ src := strings.Replace(fetchSrc, "[[INCLUDES]]", includeText, 1)
+ src = strings.Replace(src, "[[DEFAULTS]]", definesText, 1)
+ src = strings.Replace(src, "[[VALS]]", strings.Join(vals, ","), 1)
+ bin, err := ioutil.TempFile("", "")
+ if err != nil {
+ failf("failed to create temp file: %v", err)
+ }
+ bin.Close()
+ defer os.Remove(bin.Name())
+
+ cmd := exec.Command("gcc", "-x", "c", "-", "-o", bin.Name())
+ cmd.Stdin = strings.NewReader(src)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ failf("failed to run gcc: %v\n%v", err, string(out))
+ }
+
+ out, err = exec.Command(bin.Name()).CombinedOutput()
+ if err != nil {
+ failf("failed to flags binary: %v\n%v", err, string(out))
+ }
+
+ flagVals := strings.Split(string(out), " ")
+ if len(flagVals) != len(vals) {
+ failf("fetched wrong number of values")
+ }
+ for _, v := range flagVals {
+ _, err := strconv.ParseUint(v, 10, 64)
+ if err != nil {
+ failf("failed to parse value: %v (%v)", err, v)
+ }
+ }
+ return flagVals
+}
+
+func isIdentifier(s string) bool {
+ for i, c := range s {
+ if c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || i > 0 && (c >= '0' && c <= '9') {
+ continue
+ }
+ return false
+ }
+ return true
+}
+
+func parse(in io.Reader) (includes []string, defines map[string]string, syscalls []Syscall, structs map[string]Struct, unnamed map[string][]string, flags []Flag) {
+ p := NewParser(in)
+ defines = make(map[string]string)
+ structs = make(map[string]Struct)
+ unnamed = make(map[string][]string)
+ var str *Struct
+ for p.Scan() {
+ if p.EOF() || p.Char() == '#' {
+ continue
+ }
+ if str != nil {
+ // Parsing a struct.
+ if p.Char() == '}' {
+ p.Parse('}')
+ if _, ok := structs[str.Name]; ok {
+ failf("%v struct is defined multiple times", str.Name)
+ }
+ structs[str.Name] = *str
+ str = nil
+ } else {
+ p.SkipWs()
+ fld := []string{p.Ident()}
+ fld = append(fld, parseType(p, unnamed)...)
+ str.Flds = append(str.Flds, fld)
+ }
+ } else {
+ name := p.Ident()
+ if name == "include" {
+ p.Parse('<')
+ var include []byte
+ for {
+ ch := p.Char()
+ if ch == '>' {
+ break
+ }
+ p.Parse(ch)
+ include = append(include, ch)
+ }
+ p.Parse('>')
+ includes = append(includes, string(include))
+ } else if name == "define" {
+ key := p.Ident()
+ var val []byte
+ for !p.EOF() {
+ ch := p.Char()
+ p.Parse(ch)
+ val = append(val, ch)
+ }
+ if defines[key] != "" {
+ failf("%v define is defined multiple times", key)
+ }
+ defines[key] = string(val)
+ } else {
+ switch p.Char() {
+ case '(':
+ // syscall
+ p.Parse('(')
+ var args [][]string
+ for p.Char() != ')' {
+ arg := []string{p.Ident()}
+ arg = append(arg, parseType(p, unnamed)...)
+ args = append(args, arg)
+ if p.Char() != ')' {
+ p.Parse(',')
+ }
+ }
+ p.Parse(')')
+ var ret []string
+ if !p.EOF() {
+ ret = parseType(p, unnamed)
+ }
+ callName := name
+ if idx := strings.IndexByte(callName, '$'); idx != -1 {
+ callName = callName[:idx]
+ }
+ syscalls = append(syscalls, Syscall{name, callName, args, ret})
+ case '=':
+ // flag
+ p.Parse('=')
+ vals := []string{p.Ident()}
+ for !p.EOF() {
+ p.Parse(',')
+ vals = append(vals, p.Ident())
+ }
+ flags = append(flags, Flag{name, vals})
+ case '{':
+ p.Parse('{')
+ str = &Struct{Name: name}
+ default:
+ failf("bad line (%v)", p.Str())
+ }
+ }
+ }
+ if !p.EOF() {
+ failf("trailing data (%v)", p.Str())
+ }
+ }
+ return
+}
+
+func parseType(p *Parser, unnamed map[string][]string) []string {
+ return parseType1(p, unnamed, p.Ident())
+}
+
+var unnamedSeq int
+
+func parseType1(p *Parser, unnamed map[string][]string, name string) []string {
+ typ := []string{name}
+ if !p.EOF() && p.Char() == '[' {
+ p.Parse('[')
+ for {
+ id := p.Ident()
+ if p.Char() == '[' {
+ inner := parseType1(p, unnamed, id)
+ id = fmt.Sprintf("unnamed%v", unnamedSeq)
+ unnamedSeq++
+ unnamed[id] = inner
+ }
+ typ = append(typ, id)
+ if p.Char() == ']' {
+ break
+ }
+ p.Parse(',')
+ }
+ p.Parse(']')
+ }
+ return typ
+}
+
+func writeSource(file string, data []byte) {
+ src, err := format.Source(data)
+ if err != nil {
+ fmt.Printf("%s\n", data)
+ failf("failed to format output: %v", err)
+ }
+ writeFile(file, src)
+}
+
+func writeFile(file string, data []byte) {
+ outf, err := os.Create(file)
+ if err != nil {
+ failf("failed to create output file: %v", err)
+ }
+ defer outf.Close()
+ outf.Write(data)
+}
+
+func failf(msg string, args ...interface{}) {
+ fmt.Fprintf(os.Stderr, msg+"\n", args...)
+ os.Exit(1)
+}
+
+var fetchSrc = `
+#define _GNU_SOURCE
+#include <stdio.h>
+[[INCLUDES]]
+
+[[DEFAULTS]]
+
+int main() {
+ int i;
+ unsigned long vals[] = {[[VALS]]};
+ for (i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) {
+ if (i != 0)
+ printf(" ");
+ printf("%lu", vals[i]);
+ }
+ return 0;
+}
+`