// Copyright 2017 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 ( "bytes" "fmt" "os" "regexp" "strconv" "strings" "text/template" "github.com/google/syzkaller/pkg/compiler" "github.com/google/syzkaller/pkg/osutil" ) func extract(info *compiler.ConstInfo, cc string, args []string, addSource string, declarePrintf bool) ( map[string]uint64, map[string]bool, error) { data := &CompileData{ AddSource: addSource, Defines: info.Defines, Includes: info.Includes, Values: info.Consts, DeclarePrintf: declarePrintf, } undeclared := make(map[string]bool) bin, out, err := compile(cc, args, data) if err != nil { // Some consts and syscall numbers are not defined on some archs. // Figure out from compiler output undefined consts, // and try to compile again without them. valMap := make(map[string]bool) for _, val := range info.Consts { valMap[val] = true } for _, errMsg := range []string{ "error: ‘([a-zA-Z0-9_]+)’ undeclared", "error: '([a-zA-Z0-9_]+)' undeclared", "note: in expansion of macro ‘([a-zA-Z0-9_]+)’", "error: use of undeclared identifier '([a-zA-Z0-9_]+)'", } { re := regexp.MustCompile(errMsg) matches := re.FindAllSubmatch(out, -1) for _, match := range matches { val := string(match[1]) if valMap[val] { undeclared[val] = true } } } data.Values = nil for _, v := range info.Consts { if undeclared[v] { continue } data.Values = append(data.Values, v) } bin, out, err = compile(cc, args, data) if err != nil { return nil, nil, fmt.Errorf("failed to run compiler: %v\n%v", err, string(out)) } } defer os.Remove(bin) out, err = osutil.Command(bin).CombinedOutput() if err != nil { return nil, nil, fmt.Errorf("failed to run flags binary: %v\n%v", err, string(out)) } flagVals := strings.Split(string(out), " ") if len(out) == 0 { flagVals = nil } if len(flagVals) != len(data.Values) { return nil, nil, fmt.Errorf("fetched wrong number of values %v, want != %v", len(flagVals), len(data.Values)) } res := make(map[string]uint64) for i, name := range data.Values { val := flagVals[i] n, err := strconv.ParseUint(val, 10, 64) if err != nil { return nil, nil, fmt.Errorf("failed to parse value: %v (%v)", err, val) } res[name] = n } return res, undeclared, nil } type CompileData struct { AddSource string Defines map[string]string Includes []string Values []string DeclarePrintf bool } func compile(cc string, args []string, data *CompileData) (bin string, out []byte, err error) { src := new(bytes.Buffer) if err := srcTemplate.Execute(src, data); err != nil { return "", nil, fmt.Errorf("failed to generate source: %v", err) } binFile, err := osutil.TempFile("syz-extract-bin") if err != nil { return "", nil, err } args = append(args, []string{ "-x", "c", "-", "-o", binFile, "-w", }...) cmd := osutil.Command(cc, args...) cmd.Stdin = src if out, err := cmd.CombinedOutput(); err != nil { os.Remove(binFile) return "", out, err } return binFile, nil, nil } var srcTemplate = template.Must(template.New("").Parse(` #define __asm__(...) {{range $incl := $.Includes}} #include <{{$incl}}> {{end}} {{range $name, $val := $.Defines}} #ifndef {{$name}} # define {{$name}} {{$val}} #endif {{end}} {{.AddSource}} {{if .DeclarePrintf}} int printf(const char *format, ...); {{end}} int main() { int i; unsigned long long vals[] = { {{range $val := $.Values}}(unsigned long long){{$val}}, {{end}} }; for (i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) { if (i != 0) printf(" "); printf("%llu", vals[i]); } return 0; } `))