github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/src/debug/pe/file_test.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package pe
     6  
     7  import (
     8  	"debug/dwarf"
     9  	"internal/testenv"
    10  	"io/ioutil"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"reflect"
    15  	"regexp"
    16  	"runtime"
    17  	"strconv"
    18  	"testing"
    19  	"text/template"
    20  )
    21  
    22  type fileTest struct {
    23  	file           string
    24  	hdr            FileHeader
    25  	opthdr         interface{}
    26  	sections       []*SectionHeader
    27  	symbols        []*Symbol
    28  	hasNoDwarfInfo bool
    29  }
    30  
    31  var fileTests = []fileTest{
    32  	{
    33  		file: "testdata/gcc-386-mingw-obj",
    34  		hdr:  FileHeader{0x014c, 0x000c, 0x0, 0x64a, 0x1e, 0x0, 0x104},
    35  		sections: []*SectionHeader{
    36  			{".text", 0, 0, 36, 500, 1440, 0, 3, 0, 0x60300020},
    37  			{".data", 0, 0, 0, 0, 0, 0, 0, 0, 3224371264},
    38  			{".bss", 0, 0, 0, 0, 0, 0, 0, 0, 3224371328},
    39  			{".debug_abbrev", 0, 0, 137, 536, 0, 0, 0, 0, 0x42100000},
    40  			{".debug_info", 0, 0, 418, 673, 1470, 0, 7, 0, 1108344832},
    41  			{".debug_line", 0, 0, 128, 1091, 1540, 0, 1, 0, 1108344832},
    42  			{".rdata", 0, 0, 16, 1219, 0, 0, 0, 0, 1076887616},
    43  			{".debug_frame", 0, 0, 52, 1235, 1550, 0, 2, 0, 1110441984},
    44  			{".debug_loc", 0, 0, 56, 1287, 0, 0, 0, 0, 1108344832},
    45  			{".debug_pubnames", 0, 0, 27, 1343, 1570, 0, 1, 0, 1108344832},
    46  			{".debug_pubtypes", 0, 0, 38, 1370, 1580, 0, 1, 0, 1108344832},
    47  			{".debug_aranges", 0, 0, 32, 1408, 1590, 0, 2, 0, 1108344832},
    48  		},
    49  		symbols: []*Symbol{
    50  			{".file", 0x0, -2, 0x0, 0x67},
    51  			{"_main", 0x0, 1, 0x20, 0x2},
    52  			{".text", 0x0, 1, 0x0, 0x3},
    53  			{".data", 0x0, 2, 0x0, 0x3},
    54  			{".bss", 0x0, 3, 0x0, 0x3},
    55  			{".debug_abbrev", 0x0, 4, 0x0, 0x3},
    56  			{".debug_info", 0x0, 5, 0x0, 0x3},
    57  			{".debug_line", 0x0, 6, 0x0, 0x3},
    58  			{".rdata", 0x0, 7, 0x0, 0x3},
    59  			{".debug_frame", 0x0, 8, 0x0, 0x3},
    60  			{".debug_loc", 0x0, 9, 0x0, 0x3},
    61  			{".debug_pubnames", 0x0, 10, 0x0, 0x3},
    62  			{".debug_pubtypes", 0x0, 11, 0x0, 0x3},
    63  			{".debug_aranges", 0x0, 12, 0x0, 0x3},
    64  			{"___main", 0x0, 0, 0x20, 0x2},
    65  			{"_puts", 0x0, 0, 0x20, 0x2},
    66  		},
    67  	},
    68  	{
    69  		file: "testdata/gcc-386-mingw-exec",
    70  		hdr:  FileHeader{0x014c, 0x000f, 0x4c6a1b60, 0x3c00, 0x282, 0xe0, 0x107},
    71  		opthdr: &OptionalHeader32{
    72  			0x10b, 0x2, 0x38, 0xe00, 0x1a00, 0x200, 0x1160, 0x1000, 0x2000, 0x400000, 0x1000, 0x200, 0x4, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x10000, 0x400, 0x14abb, 0x3, 0x0, 0x200000, 0x1000, 0x100000, 0x1000, 0x0, 0x10,
    73  			[16]DataDirectory{
    74  				{0x0, 0x0},
    75  				{0x5000, 0x3c8},
    76  				{0x0, 0x0},
    77  				{0x0, 0x0},
    78  				{0x0, 0x0},
    79  				{0x0, 0x0},
    80  				{0x0, 0x0},
    81  				{0x0, 0x0},
    82  				{0x0, 0x0},
    83  				{0x7000, 0x18},
    84  				{0x0, 0x0},
    85  				{0x0, 0x0},
    86  				{0x0, 0x0},
    87  				{0x0, 0x0},
    88  				{0x0, 0x0},
    89  				{0x0, 0x0},
    90  			},
    91  		},
    92  		sections: []*SectionHeader{
    93  			{".text", 0xcd8, 0x1000, 0xe00, 0x400, 0x0, 0x0, 0x0, 0x0, 0x60500060},
    94  			{".data", 0x10, 0x2000, 0x200, 0x1200, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
    95  			{".rdata", 0x120, 0x3000, 0x200, 0x1400, 0x0, 0x0, 0x0, 0x0, 0x40300040},
    96  			{".bss", 0xdc, 0x4000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0400080},
    97  			{".idata", 0x3c8, 0x5000, 0x400, 0x1600, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
    98  			{".CRT", 0x18, 0x6000, 0x200, 0x1a00, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
    99  			{".tls", 0x20, 0x7000, 0x200, 0x1c00, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
   100  			{".debug_aranges", 0x20, 0x8000, 0x200, 0x1e00, 0x0, 0x0, 0x0, 0x0, 0x42100000},
   101  			{".debug_pubnames", 0x51, 0x9000, 0x200, 0x2000, 0x0, 0x0, 0x0, 0x0, 0x42100000},
   102  			{".debug_pubtypes", 0x91, 0xa000, 0x200, 0x2200, 0x0, 0x0, 0x0, 0x0, 0x42100000},
   103  			{".debug_info", 0xe22, 0xb000, 0x1000, 0x2400, 0x0, 0x0, 0x0, 0x0, 0x42100000},
   104  			{".debug_abbrev", 0x157, 0xc000, 0x200, 0x3400, 0x0, 0x0, 0x0, 0x0, 0x42100000},
   105  			{".debug_line", 0x144, 0xd000, 0x200, 0x3600, 0x0, 0x0, 0x0, 0x0, 0x42100000},
   106  			{".debug_frame", 0x34, 0xe000, 0x200, 0x3800, 0x0, 0x0, 0x0, 0x0, 0x42300000},
   107  			{".debug_loc", 0x38, 0xf000, 0x200, 0x3a00, 0x0, 0x0, 0x0, 0x0, 0x42100000},
   108  		},
   109  	},
   110  	{
   111  		file: "testdata/gcc-386-mingw-no-symbols-exec",
   112  		hdr:  FileHeader{0x14c, 0x8, 0x69676572, 0x0, 0x0, 0xe0, 0x30f},
   113  		opthdr: &OptionalHeader32{0x10b, 0x2, 0x18, 0xe00, 0x1e00, 0x200, 0x1280, 0x1000, 0x2000, 0x400000, 0x1000, 0x200, 0x4, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x9000, 0x400, 0x5306, 0x3, 0x0, 0x200000, 0x1000, 0x100000, 0x1000, 0x0, 0x10,
   114  			[16]DataDirectory{
   115  				{0x0, 0x0},
   116  				{0x6000, 0x378},
   117  				{0x0, 0x0},
   118  				{0x0, 0x0},
   119  				{0x0, 0x0},
   120  				{0x0, 0x0},
   121  				{0x0, 0x0},
   122  				{0x0, 0x0},
   123  				{0x0, 0x0},
   124  				{0x8004, 0x18},
   125  				{0x0, 0x0},
   126  				{0x0, 0x0},
   127  				{0x60b8, 0x7c},
   128  				{0x0, 0x0},
   129  				{0x0, 0x0},
   130  				{0x0, 0x0},
   131  			},
   132  		},
   133  		sections: []*SectionHeader{
   134  			{".text", 0xc64, 0x1000, 0xe00, 0x400, 0x0, 0x0, 0x0, 0x0, 0x60500060},
   135  			{".data", 0x10, 0x2000, 0x200, 0x1200, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
   136  			{".rdata", 0x134, 0x3000, 0x200, 0x1400, 0x0, 0x0, 0x0, 0x0, 0x40300040},
   137  			{".eh_fram", 0x3a0, 0x4000, 0x400, 0x1600, 0x0, 0x0, 0x0, 0x0, 0x40300040},
   138  			{".bss", 0x60, 0x5000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0300080},
   139  			{".idata", 0x378, 0x6000, 0x400, 0x1a00, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
   140  			{".CRT", 0x18, 0x7000, 0x200, 0x1e00, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
   141  			{".tls", 0x20, 0x8000, 0x200, 0x2000, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
   142  		},
   143  		hasNoDwarfInfo: true,
   144  	},
   145  	{
   146  		file: "testdata/gcc-amd64-mingw-obj",
   147  		hdr:  FileHeader{0x8664, 0x6, 0x0, 0x198, 0x12, 0x0, 0x4},
   148  		sections: []*SectionHeader{
   149  			{".text", 0x0, 0x0, 0x30, 0x104, 0x15c, 0x0, 0x3, 0x0, 0x60500020},
   150  			{".data", 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0500040},
   151  			{".bss", 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0500080},
   152  			{".rdata", 0x0, 0x0, 0x10, 0x134, 0x0, 0x0, 0x0, 0x0, 0x40500040},
   153  			{".xdata", 0x0, 0x0, 0xc, 0x144, 0x0, 0x0, 0x0, 0x0, 0x40300040},
   154  			{".pdata", 0x0, 0x0, 0xc, 0x150, 0x17a, 0x0, 0x3, 0x0, 0x40300040},
   155  		},
   156  		symbols: []*Symbol{
   157  			{".file", 0x0, -2, 0x0, 0x67},
   158  			{"main", 0x0, 1, 0x20, 0x2},
   159  			{".text", 0x0, 1, 0x0, 0x3},
   160  			{".data", 0x0, 2, 0x0, 0x3},
   161  			{".bss", 0x0, 3, 0x0, 0x3},
   162  			{".rdata", 0x0, 4, 0x0, 0x3},
   163  			{".xdata", 0x0, 5, 0x0, 0x3},
   164  			{".pdata", 0x0, 6, 0x0, 0x3},
   165  			{"__main", 0x0, 0, 0x20, 0x2},
   166  			{"puts", 0x0, 0, 0x20, 0x2},
   167  		},
   168  		hasNoDwarfInfo: true,
   169  	},
   170  	{
   171  		file: "testdata/gcc-amd64-mingw-exec",
   172  		hdr:  FileHeader{0x8664, 0x11, 0x53e4364f, 0x39600, 0x6fc, 0xf0, 0x27},
   173  		opthdr: &OptionalHeader64{
   174  			0x20b, 0x2, 0x16, 0x6a00, 0x2400, 0x1600, 0x14e0, 0x1000, 0x400000, 0x1000, 0x200, 0x4, 0x0, 0x0, 0x0, 0x5, 0x2, 0x0, 0x45000, 0x600, 0x46f19, 0x3, 0x0, 0x200000, 0x1000, 0x100000, 0x1000, 0x0, 0x10,
   175  			[16]DataDirectory{
   176  				{0x0, 0x0},
   177  				{0xe000, 0x990},
   178  				{0x0, 0x0},
   179  				{0xa000, 0x498},
   180  				{0x0, 0x0},
   181  				{0x0, 0x0},
   182  				{0x0, 0x0},
   183  				{0x0, 0x0},
   184  				{0x0, 0x0},
   185  				{0x10000, 0x28},
   186  				{0x0, 0x0},
   187  				{0x0, 0x0},
   188  				{0xe254, 0x218},
   189  				{0x0, 0x0},
   190  				{0x0, 0x0},
   191  				{0x0, 0x0},
   192  			}},
   193  		sections: []*SectionHeader{
   194  			{".text", 0x6860, 0x1000, 0x6a00, 0x600, 0x0, 0x0, 0x0, 0x0, 0x60500020},
   195  			{".data", 0xe0, 0x8000, 0x200, 0x7000, 0x0, 0x0, 0x0, 0x0, 0xc0500040},
   196  			{".rdata", 0x6b0, 0x9000, 0x800, 0x7200, 0x0, 0x0, 0x0, 0x0, 0x40600040},
   197  			{".pdata", 0x498, 0xa000, 0x600, 0x7a00, 0x0, 0x0, 0x0, 0x0, 0x40300040},
   198  			{".xdata", 0x488, 0xb000, 0x600, 0x8000, 0x0, 0x0, 0x0, 0x0, 0x40300040},
   199  			{".bss", 0x1410, 0xc000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0600080},
   200  			{".idata", 0x990, 0xe000, 0xa00, 0x8600, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
   201  			{".CRT", 0x68, 0xf000, 0x200, 0x9000, 0x0, 0x0, 0x0, 0x0, 0xc0400040},
   202  			{".tls", 0x48, 0x10000, 0x200, 0x9200, 0x0, 0x0, 0x0, 0x0, 0xc0600040},
   203  			{".debug_aranges", 0x600, 0x11000, 0x600, 0x9400, 0x0, 0x0, 0x0, 0x0, 0x42500040},
   204  			{".debug_info", 0x1316e, 0x12000, 0x13200, 0x9a00, 0x0, 0x0, 0x0, 0x0, 0x42100040},
   205  			{".debug_abbrev", 0x2ccb, 0x26000, 0x2e00, 0x1cc00, 0x0, 0x0, 0x0, 0x0, 0x42100040},
   206  			{".debug_line", 0x3c4d, 0x29000, 0x3e00, 0x1fa00, 0x0, 0x0, 0x0, 0x0, 0x42100040},
   207  			{".debug_frame", 0x18b8, 0x2d000, 0x1a00, 0x23800, 0x0, 0x0, 0x0, 0x0, 0x42400040},
   208  			{".debug_str", 0x396, 0x2f000, 0x400, 0x25200, 0x0, 0x0, 0x0, 0x0, 0x42100040},
   209  			{".debug_loc", 0x13240, 0x30000, 0x13400, 0x25600, 0x0, 0x0, 0x0, 0x0, 0x42100040},
   210  			{".debug_ranges", 0xa70, 0x44000, 0xc00, 0x38a00, 0x0, 0x0, 0x0, 0x0, 0x42100040},
   211  		},
   212  	},
   213  }
   214  
   215  func isOptHdrEq(a, b interface{}) bool {
   216  	switch va := a.(type) {
   217  	case *OptionalHeader32:
   218  		vb, ok := b.(*OptionalHeader32)
   219  		if !ok {
   220  			return false
   221  		}
   222  		return *vb == *va
   223  	case *OptionalHeader64:
   224  		vb, ok := b.(*OptionalHeader64)
   225  		if !ok {
   226  			return false
   227  		}
   228  		return *vb == *va
   229  	case nil:
   230  		return b == nil
   231  	}
   232  	return false
   233  }
   234  
   235  func TestOpen(t *testing.T) {
   236  	for i := range fileTests {
   237  		tt := &fileTests[i]
   238  
   239  		f, err := Open(tt.file)
   240  		if err != nil {
   241  			t.Error(err)
   242  			continue
   243  		}
   244  		if !reflect.DeepEqual(f.FileHeader, tt.hdr) {
   245  			t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.FileHeader, tt.hdr)
   246  			continue
   247  		}
   248  		if !isOptHdrEq(tt.opthdr, f.OptionalHeader) {
   249  			t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.OptionalHeader, tt.opthdr)
   250  			continue
   251  		}
   252  
   253  		for i, sh := range f.Sections {
   254  			if i >= len(tt.sections) {
   255  				break
   256  			}
   257  			have := &sh.SectionHeader
   258  			want := tt.sections[i]
   259  			if !reflect.DeepEqual(have, want) {
   260  				t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want)
   261  			}
   262  		}
   263  		tn := len(tt.sections)
   264  		fn := len(f.Sections)
   265  		if tn != fn {
   266  			t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn)
   267  		}
   268  		for i, have := range f.Symbols {
   269  			if i >= len(tt.symbols) {
   270  				break
   271  			}
   272  			want := tt.symbols[i]
   273  			if !reflect.DeepEqual(have, want) {
   274  				t.Errorf("open %s, symbol %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want)
   275  			}
   276  		}
   277  		if !tt.hasNoDwarfInfo {
   278  			_, err = f.DWARF()
   279  			if err != nil {
   280  				t.Errorf("fetching %s dwarf details failed: %v", tt.file, err)
   281  			}
   282  		}
   283  	}
   284  }
   285  
   286  func TestOpenFailure(t *testing.T) {
   287  	filename := "file.go"    // not a PE file
   288  	_, err := Open(filename) // don't crash
   289  	if err == nil {
   290  		t.Errorf("open %s: succeeded unexpectedly", filename)
   291  	}
   292  }
   293  
   294  const (
   295  	linkNoCgo = iota
   296  	linkCgoDefault
   297  	linkCgoInternal
   298  	linkCgoExternal
   299  )
   300  
   301  func testDWARF(t *testing.T, linktype int) {
   302  	if runtime.GOOS != "windows" {
   303  		t.Skip("skipping windows only test")
   304  	}
   305  	testenv.MustHaveGoRun(t)
   306  
   307  	tmpdir, err := ioutil.TempDir("", "TestDWARF")
   308  	if err != nil {
   309  		t.Fatal(err)
   310  	}
   311  	defer os.RemoveAll(tmpdir)
   312  
   313  	src := filepath.Join(tmpdir, "a.go")
   314  	file, err := os.Create(src)
   315  	if err != nil {
   316  		t.Fatal(err)
   317  	}
   318  	err = template.Must(template.New("main").Parse(testprog)).Execute(file, linktype != linkNoCgo)
   319  	if err != nil {
   320  		if err := file.Close(); err != nil {
   321  			t.Error(err)
   322  		}
   323  		t.Fatal(err)
   324  	}
   325  	if err := file.Close(); err != nil {
   326  		t.Fatal(err)
   327  	}
   328  
   329  	exe := filepath.Join(tmpdir, "a.exe")
   330  	args := []string{"build", "-o", exe}
   331  	switch linktype {
   332  	case linkNoCgo:
   333  	case linkCgoDefault:
   334  	case linkCgoInternal:
   335  		args = append(args, "-ldflags", "-linkmode=internal")
   336  	case linkCgoExternal:
   337  		args = append(args, "-ldflags", "-linkmode=external")
   338  	default:
   339  		t.Fatalf("invalid linktype parameter of %v", linktype)
   340  	}
   341  	args = append(args, src)
   342  	out, err := exec.Command(testenv.GoToolPath(t), args...).CombinedOutput()
   343  	if err != nil {
   344  		t.Fatalf("building test executable failed: %s %s", err, out)
   345  	}
   346  	out, err = exec.Command(exe).CombinedOutput()
   347  	if err != nil {
   348  		t.Fatalf("running test executable failed: %s %s", err, out)
   349  	}
   350  
   351  	matches := regexp.MustCompile("main=(.*)\n").FindStringSubmatch(string(out))
   352  	if len(matches) < 2 {
   353  		t.Fatalf("unexpected program output: %s", out)
   354  	}
   355  	wantaddr, err := strconv.ParseUint(matches[1], 0, 64)
   356  	if err != nil {
   357  		t.Fatalf("unexpected main address %q: %s", matches[1], err)
   358  	}
   359  
   360  	f, err := Open(exe)
   361  	if err != nil {
   362  		t.Fatal(err)
   363  	}
   364  	defer f.Close()
   365  
   366  	d, err := f.DWARF()
   367  	if err != nil {
   368  		t.Fatal(err)
   369  	}
   370  
   371  	// look for main.main
   372  	r := d.Reader()
   373  	for {
   374  		e, err := r.Next()
   375  		if err != nil {
   376  			t.Fatal("r.Next:", err)
   377  		}
   378  		if e == nil {
   379  			break
   380  		}
   381  		if e.Tag == dwarf.TagSubprogram {
   382  			if name, ok := e.Val(dwarf.AttrName).(string); ok && name == "main.main" {
   383  				if addr, ok := e.Val(dwarf.AttrLowpc).(uint64); ok && addr == wantaddr {
   384  					return
   385  				}
   386  			}
   387  		}
   388  	}
   389  	t.Fatal("main.main not found")
   390  }
   391  
   392  func TestBSSHasZeros(t *testing.T) {
   393  	testenv.MustHaveExec(t)
   394  
   395  	if runtime.GOOS != "windows" {
   396  		t.Skip("skipping windows only test")
   397  	}
   398  	gccpath, err := exec.LookPath("gcc")
   399  	if err != nil {
   400  		t.Skip("skipping test: gcc is missing")
   401  	}
   402  
   403  	tmpdir, err := ioutil.TempDir("", "TestBSSHasZeros")
   404  	if err != nil {
   405  		t.Fatal(err)
   406  	}
   407  	defer os.RemoveAll(tmpdir)
   408  
   409  	srcpath := filepath.Join(tmpdir, "a.c")
   410  	src := `
   411  #include <stdio.h>
   412  
   413  int zero = 0;
   414  
   415  int
   416  main(void)
   417  {
   418  	printf("%d\n", zero);
   419  	return 0;
   420  }
   421  `
   422  	err = ioutil.WriteFile(srcpath, []byte(src), 0644)
   423  	if err != nil {
   424  		t.Fatal(err)
   425  	}
   426  
   427  	objpath := filepath.Join(tmpdir, "a.obj")
   428  	cmd := exec.Command(gccpath, "-c", srcpath, "-o", objpath)
   429  	out, err := cmd.CombinedOutput()
   430  	if err != nil {
   431  		t.Fatalf("failed to build object file: %v - %v", err, string(out))
   432  	}
   433  
   434  	f, err := Open(objpath)
   435  	if err != nil {
   436  		t.Fatal(err)
   437  	}
   438  	defer f.Close()
   439  
   440  	var bss *Section
   441  	for _, sect := range f.Sections {
   442  		if sect.Name == ".bss" {
   443  			bss = sect
   444  			break
   445  		}
   446  	}
   447  	if bss == nil {
   448  		t.Fatal("could not find .bss section")
   449  	}
   450  	data, err := bss.Data()
   451  	if err != nil {
   452  		t.Fatal(err)
   453  	}
   454  	if len(data) == 0 {
   455  		t.Fatalf("%s file .bss section cannot be empty", objpath)
   456  	}
   457  	for _, b := range data {
   458  		if b != 0 {
   459  			t.Fatalf(".bss section has non zero bytes: %v", data)
   460  		}
   461  	}
   462  }
   463  
   464  func TestDWARF(t *testing.T) {
   465  	testDWARF(t, linkNoCgo)
   466  }
   467  
   468  const testprog = `
   469  package main
   470  
   471  import "fmt"
   472  {{if .}}import "C"
   473  {{end}}
   474  
   475  func main() {
   476  	fmt.Printf("main=%p\n", main)
   477  }
   478  `