github.com/decomp/exp@v0.0.0-20210624183419-6d058f5e1da6/cmd/bin2asm/dump.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "fmt" 6 "os" 7 "path/filepath" 8 "strings" 9 "text/tabwriter" 10 "text/template" 11 "unicode" 12 13 "github.com/mewrev/pe" 14 "github.com/pkg/errors" 15 ) 16 17 // dumpMainAsm dumps the main.asm file of the executable. 18 func dumpMainAsm(file *pe.File, hasOverlay bool) error { 19 t, err := parseTemplate("main.asm.tmpl") 20 if err != nil { 21 return errors.WithStack(err) 22 } 23 sectHdrs, err := file.SectHeaders() 24 if err != nil { 25 return errors.WithStack(err) 26 } 27 var data []string 28 for _, sectHdr := range sectHdrs { 29 sectName := underline(sectHdr.Name) 30 data = append(data, sectName) 31 } 32 if hasOverlay { 33 data = append(data, "overlay") 34 } 35 // Store output. 36 if err := writeFile(t, "main.asm", data); err != nil { 37 return errors.WithStack(err) 38 } 39 return nil 40 } 41 42 // dumpPEHeaderAsm dumps the pe-hdr.asm file of the executable. 43 func dumpPEHeaderAsm(file *pe.File) error { 44 t, err := parseTemplate("pe-hdr.asm.tmpl") 45 if err != nil { 46 return errors.WithStack(err) 47 } 48 optHdr, err := file.OptHeader() 49 if err != nil { 50 return errors.WithStack(err) 51 } 52 dosHdr, err := file.DOSHeader() 53 if err != nil { 54 return errors.WithStack(err) 55 } 56 dosStub, err := file.DOSStub() 57 if err != nil { 58 return errors.WithStack(err) 59 } 60 fileHdr, err := file.FileHeader() 61 if err != nil { 62 return errors.WithStack(err) 63 } 64 sectAlignKB := optHdr.SectAlign / 1024 65 sectHdrs, err := file.SectHeaders() 66 if err != nil { 67 return errors.WithStack(err) 68 } 69 // Calculate size of sections containing data. 70 var dataSizes []string 71 for _, sectHdr := range sectHdrs { 72 if sectHdr.Flags&pe.SectFlagData == 0 { 73 // Ignore non-data sections. 74 continue 75 } 76 if sectHdr.Size < sectHdr.VirtSize { 77 // Section contains uninitialized data. 78 dataSize := fmt.Sprintf("%s_vsize", underline(sectHdr.Name)) 79 dataSizes = append(dataSizes, dataSize) 80 } else { 81 // Section is padded. 82 dataSize := fmt.Sprintf("%s_size", underline(sectHdr.Name)) 83 dataSizes = append(dataSizes, dataSize) 84 } 85 } 86 // Store output. 87 data := map[string]interface{}{ 88 "OptHdr": optHdr, 89 "DosHdr": dosHdr, 90 "DOSStub": dosStub, 91 "FileHdr": fileHdr, 92 "SectAlignKB": sectAlignKB, 93 "SectHdrs": sectHdrs, 94 "DataSizes": strings.Join(dataSizes, " + "), 95 "DataDirs": optHdr.DataDirs, 96 } 97 if err := writeFile(t, "pe-hdr.asm", data); err != nil { 98 return errors.WithStack(err) 99 } 100 return nil 101 } 102 103 // ### [ Helper functions ] #################################################### 104 105 // writeFile applies a parsed template to the specified data object, writing the 106 // output to the specified file. 107 func writeFile(t *template.Template, filename string, data interface{}) error { 108 path := filepath.Join(outDir, filename) 109 dbg.Printf("creating %q\n", path) 110 f, err := os.Create(path) 111 if err != nil { 112 return errors.WithStack(err) 113 } 114 defer f.Close() 115 bw := bufio.NewWriter(f) 116 defer bw.Flush() 117 tw := tabwriter.NewWriter(bw, 1, 8, 1, ' ', 0) 118 if err := t.Execute(tw, data); err != nil { 119 return errors.WithStack(err) 120 } 121 return nil 122 } 123 124 // parseTemplate parses and returns the given template. 125 func parseTemplate(filename string) (*template.Template, error) { 126 funcMap := map[string]interface{}{ 127 "isprint": isPrint, 128 "underline": underline, 129 "nameArray": nameArray, 130 "ui16": ui16, 131 "ui32": ui32, 132 } 133 path := filepath.Join(bin2asmDir, filename) 134 t, err := template.New(filename).Funcs(funcMap).ParseFiles(path) 135 if err != nil { 136 return nil, errors.WithStack(err) 137 } 138 return t, nil 139 } 140 141 // isPrint reports if the given byte is printable. 142 func isPrint(b byte) bool { 143 if b >= 0x7F { 144 return false 145 } 146 return unicode.IsPrint(rune(b)) 147 } 148 149 // underline replaces dot characters in the given string with underscore 150 // characters. 151 func underline(s string) string { 152 return strings.Replace(s, ".", "_", -1) 153 } 154 155 // nameArray converts the given string to a byte array of 8 characters, pretty- 156 // printed in NASM syntax. 157 func nameArray(s string) string { 158 out := fmt.Sprintf("%q", s) 159 for i := len(s); i < 8; i++ { 160 out += ", 0x00" 161 } 162 return out 163 } 164 165 // ui16 converts x to a 16-bit unsigned integer. 166 func ui16(x interface{}) uint16 { 167 switch x := x.(type) { 168 case pe.Arch: 169 return uint16(x) 170 case pe.DLLFlag: 171 return uint16(x) 172 case pe.Flag: 173 return uint16(x) 174 case pe.OptState: 175 return uint16(x) 176 case pe.Subsystem: 177 return uint16(x) 178 default: 179 panic(fmt.Errorf("main.ui16: support for type %T not yet implemented", x)) 180 } 181 } 182 183 // ui32 converts x to a 32-bit unsigned integer. 184 func ui32(x interface{}) uint32 { 185 switch x := x.(type) { 186 case pe.SectFlag: 187 return uint32(x) 188 case pe.Time: 189 return uint32(x) 190 default: 191 panic(fmt.Errorf("main.ui32: support for type %T not yet implemented", x)) 192 } 193 }