gitee.com/quant1x/num@v0.3.2/asm/c2goasm/c2goasm.go (about)

     1  /*
     2   * Minio Cloud Storage, (C) 2017 Minio, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package main
    18  
    19  import (
    20  	"bufio"
    21  	"encoding/hex"
    22  	"flag"
    23  	"fmt"
    24  	"log"
    25  	"os"
    26  	"os/exec"
    27  	"regexp"
    28  	"strings"
    29  )
    30  
    31  var (
    32  	assembleFlag = flag.Bool("a", false, "Immediately invoke asm2plan9s")
    33  	stripFlag    = flag.Bool("s", false, "Strip comments")
    34  	compactFlag  = flag.Bool("c", false, "Compact byte codes")
    35  	formatFlag   = flag.Bool("f", false, "Format using asmfmt")
    36  	targetFlag   = flag.String("t", "x86", "Target machine of input code")
    37  )
    38  
    39  // readLines reads a whole file into memory
    40  // and returns a slice of its lines.
    41  func readLines(path string) ([]string, error) {
    42  	file, err := os.Open(path)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  	defer file.Close()
    47  
    48  	var lines []string
    49  	scanner := bufio.NewScanner(file)
    50  	for scanner.Scan() {
    51  		lines = append(lines, scanner.Text())
    52  	}
    53  	return lines, scanner.Err()
    54  }
    55  
    56  // writeLines writes the lines to the given file.
    57  func writeLines(lines []string, path string, header bool) error {
    58  	file, err := os.Create(path)
    59  	if err != nil {
    60  		return err
    61  	}
    62  	defer file.Close()
    63  
    64  	w := bufio.NewWriter(file)
    65  	if header {
    66  		fmt.Fprintln(w, "//+build !noasm !appengine")
    67  		fmt.Fprintln(w, "// AUTO-GENERATED BY C2GOASM -- DO NOT EDIT")
    68  		fmt.Fprintln(w, "")
    69  	}
    70  	for _, line := range lines {
    71  		fmt.Fprintln(w, line)
    72  	}
    73  	return w.Flush()
    74  }
    75  
    76  func process(assembly []string, goCompanionFile string) ([]string, error) {
    77  
    78  	// Split out the assembly source into subroutines
    79  	subroutines := segmentSource(assembly)
    80  	tables := segmentConstTables(assembly)
    81  
    82  	var result []string
    83  
    84  	// Iterate over all subroutines
    85  	for isubroutine, sub := range subroutines {
    86  
    87  		golangArgs, golangReturns := parseCompanionFile(goCompanionFile, sub.name)
    88  		stackArgs := argumentsOnStack(sub.body)
    89  		if len(golangArgs) > 6 && len(golangArgs)-6 < stackArgs.Number {
    90  			panic(fmt.Sprintf("Found too few arguments on stack (%d) but needed %d", len(golangArgs)-6, stackArgs.Number))
    91  		}
    92  
    93  		// Check for constants table
    94  		if table := getCorrespondingTable(sub.body, tables); table.isPresent() {
    95  
    96  			// Output constants table
    97  			result = append(result, strings.Split(table.Constants, "\n")...)
    98  			result = append(result, "") // append empty line
    99  
   100  			sub.table = table
   101  		}
   102  
   103  		// Create object to get offsets for stack pointer
   104  		stack := NewStack(sub.epilogue, len(golangArgs), scanBodyForCalls(sub))
   105  
   106  		// Write header for subroutine in go assembly
   107  		result = append(result, writeGoasmPrologue(sub, stack, golangArgs, golangReturns)...)
   108  
   109  		// Write body of code
   110  		assembly, err := writeGoasmBody(sub, stack, stackArgs, golangArgs, golangReturns)
   111  		if err != nil {
   112  			panic(fmt.Sprintf("writeGoasmBody: %v", err))
   113  		}
   114  		result = append(result, assembly...)
   115  
   116  		if isubroutine < len(subroutines)-1 {
   117  			// Empty lines before next subroutine
   118  			result = append(result, "\n", "\n")
   119  		}
   120  	}
   121  
   122  	return result, nil
   123  }
   124  
   125  func stripGoasmComments(file string) {
   126  
   127  	lines, err := readLines(file)
   128  	if err != nil {
   129  		log.Fatalf("readLines: %s", err)
   130  	}
   131  
   132  	for i, l := range lines {
   133  		if strings.Contains(l, "LONG") || strings.Contains(l, "WORD") || strings.Contains(l, "BYTE") {
   134  			opcode := strings.TrimSpace(strings.SplitN(l, "//", 2)[0])
   135  			lines[i] = strings.SplitN(l, opcode, 2)[0] + opcode
   136  		}
   137  	}
   138  
   139  	err = writeLines(lines, file, false)
   140  	if err != nil {
   141  		log.Fatalf("writeLines: %s", err)
   142  	}
   143  }
   144  
   145  func reverseBytes(hex string) string {
   146  
   147  	result := ""
   148  	for i := len(hex) - 2; i >= 0; i -= 2 {
   149  		result = result + hex[i:i+2]
   150  	}
   151  	return result
   152  }
   153  
   154  func compactArray(opcodes []byte) []string {
   155  
   156  	var result []string
   157  
   158  	dst := make([]byte, hex.EncodedLen(len(opcodes)))
   159  	hex.Encode(dst, opcodes)
   160  
   161  	q := 0
   162  	for ; q+31 < len(dst); q += 32 {
   163  		result = append(result, fmt.Sprintf("    QUAD $0x%s; QUAD $0x%s", reverseBytes(string(dst[q:q+16])), reverseBytes(string(dst[q+16:q+32]))))
   164  	}
   165  	for ; q+15 < len(dst); q += 16 {
   166  		result = append(result, fmt.Sprintf("    QUAD $0x%s", reverseBytes(string(dst[q:q+16]))))
   167  	}
   168  	if q < len(dst) {
   169  		last := ""
   170  		l := 0
   171  		if q+7 < len(dst) {
   172  			last += fmt.Sprintf("LONG $0x%s", reverseBytes(string(dst[q:q+8])))
   173  			l = 8
   174  		}
   175  		w := 0
   176  		if q+l+3 < len(dst) {
   177  			if len(last) > 0 {
   178  				last = last + "; "
   179  			}
   180  			last += fmt.Sprintf("WORD $0x%s", reverseBytes(string(dst[q+l:q+l+4])))
   181  			w = 4
   182  		}
   183  		if q+l+w+1 < len(dst) {
   184  			if len(last) > 0 {
   185  				last = last + "; "
   186  			}
   187  			last += fmt.Sprintf("BYTE $0x%s", dst[q+l+w:q+l+w+2])
   188  		}
   189  		result = append(result, "    "+last)
   190  	}
   191  
   192  	return result
   193  }
   194  
   195  func compactOpcodes(file string) {
   196  
   197  	lines, err := readLines(file)
   198  	if err != nil {
   199  		log.Fatalf("readLines: %s", err)
   200  	}
   201  
   202  	var result []string
   203  
   204  	opcodes := make([]byte, 0, 1000)
   205  
   206  	hexMatch := regexp.MustCompile(`(\$0x[0-9a-f]+)`)
   207  
   208  	for _, l := range lines {
   209  		if strings.Contains(l, "LONG") || strings.Contains(l, "WORD") || strings.Contains(l, "BYTE") {
   210  			match := hexMatch.FindAllStringSubmatch(l, -1)
   211  			for _, m := range match {
   212  				dst := make([]byte, hex.DecodedLen(len(m[0][3:])))
   213  				_, err := hex.Decode(dst, []byte(m[0][3:]))
   214  				if err != nil {
   215  					log.Fatal(err)
   216  				}
   217  				for i := len(dst) - 1; i >= 0; i -= 1 { // append starting with lowest byte first
   218  					opcodes = append(opcodes, dst[i:i+1]...)
   219  				}
   220  			}
   221  		} else {
   222  
   223  			if len(opcodes) != 0 {
   224  				result = append(result, compactArray(opcodes)...)
   225  				opcodes = opcodes[:0]
   226  			}
   227  
   228  			result = append(result, l)
   229  		}
   230  	}
   231  
   232  	err = writeLines(result, file, false)
   233  	if err != nil {
   234  		log.Fatalf("writeLines: %s", err)
   235  	}
   236  }
   237  
   238  func main() {
   239  
   240  	flag.Parse()
   241  
   242  	if flag.NArg() < 2 {
   243  		fmt.Printf("error: not enough input files specified\n\n")
   244  		fmt.Println("usage: c2goasm /path/to/c-project/build/SomeGreatCode.cpp.s SomeGreatCode_amd64.s")
   245  		return
   246  	}
   247  	assemblyFile := flag.Arg(1)
   248  	if !strings.HasSuffix(assemblyFile, ".s") {
   249  		fmt.Printf("error: second parameter must have '.s' extension\n")
   250  		return
   251  	}
   252  
   253  	goCompanion := assemblyFile[:len(assemblyFile)-2] + ".go"
   254  	if _, err := os.Stat(goCompanion); os.IsNotExist(err) {
   255  		fmt.Printf("error: companion '.go' file is missing for %s\n", flag.Arg(1))
   256  		return
   257  	}
   258  
   259  	fmt.Println("Processing", flag.Arg(0))
   260  	lines, err := readLines(flag.Arg(0))
   261  	if err != nil {
   262  		log.Fatalf("readLines: %s", err)
   263  	}
   264  
   265  	result, err := process(lines, goCompanion)
   266  	if err != nil {
   267  		fmt.Print(err)
   268  		os.Exit(-1)
   269  	}
   270  
   271  	err = writeLines(result, assemblyFile, true)
   272  	if err != nil {
   273  		log.Fatalf("writeLines: %s", err)
   274  	}
   275  
   276  	if *assembleFlag {
   277  		fmt.Println("Invoking asm2plan9s on", assemblyFile)
   278  		cmd := exec.Command("asm2plan9s", assemblyFile)
   279  		_, err := cmd.CombinedOutput()
   280  		if err != nil {
   281  			log.Fatalf("asm2plan9s: %v", err)
   282  		}
   283  	}
   284  
   285  	if *stripFlag {
   286  		stripGoasmComments(assemblyFile)
   287  	}
   288  
   289  	if *compactFlag {
   290  		compactOpcodes(assemblyFile)
   291  	}
   292  
   293  	if *formatFlag {
   294  		cmd := exec.Command("asmfmt", "-w", assemblyFile)
   295  		_, err := cmd.CombinedOutput()
   296  		if err != nil {
   297  			log.Fatalf("asmfmt: %v", err)
   298  		}
   299  	}
   300  }