[
  {
    "path": ".gitignore",
    "content": "/goupx\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2012 Peter Waller\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "goupx - Fix golang Linux ELF executables to work with upx\n---------------------------------------------------\n\n## Update: 2016/03/10\n\nAs far as I (pwaller) know, goupx is no longer necessary for Linux binaries since it was fixed in\ngo1.6.\n\n## About\n\nInstallation: `go get github.com/pwaller/goupx`\n\n(or if you don't want to do it with root, `GOPATH=${HOME}/.local go get github.com/pwaller/goupx` will install it to `${HOME}/.local/bin/goupx`).\n\nUsage: `goupx [filename]`\n\nFixes the `PT_LOAD` offset of [filename] and then runs `upx`.\n\nThis is only necessary for Linux ELF executables (not Mach-O executables or windows binaries, for example).\n\nBased on [code found on the upx bugtracker](http://sourceforge.net/tracker/?func=detail&atid=102331&aid=3408066&group_id=2331).\n\nMIT licensed.\n\nFixes the following issue\n=========================\n\n    $ upx [linux ELF go binary]\n                           Ultimate Packer for eXecutables\n                              Copyright (C) 1996 - 2011\n    UPX 3.08        Markus Oberhumer, Laszlo Molnar & John Reiser   Dec 12th 2011\n\n            File size         Ratio      Format      Name\n       --------------------   ------   -----------   -----------\n    upx: goupx: EOFException: premature end of file                                \n\n    Packed 1 file: 0 ok, 1 error.\n\nTypical compression ratio\n=========================\n\nResulting filesizes are typically 25% of the original go executable. Your mileage my vary.\n"
  },
  {
    "path": "hemfix/hemfix.go",
    "content": "package hemfix\n\n/*\n\ngoupx: Fix compiled go binaries so that they can be packed by\n       the universal packer for executables (upx)\n\nCopyright (c) 2012 Peter Waller <peter@pwaller.net>\nAll rights reserved.\n\nBased on code found at http://sourceforge.net/tracker/?func=detail&atid=102331&aid=3408066&group_id=2331\n\nBased on hemfix.c Copyright (C) 2012 John Reiser, BitWagon Software LLC\n\n  This program is free software; you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation; either version 3, or (at your option)\n  any later version.\n\n  This program is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with this program; if not, write to the Free Software Foundation,\n  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n*/\n\nimport (\n\tELF \"debug/elf\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n)\n\n// The functions gethdr and writephdr are heavily influenced by code found at\n// http://golang.org/src/pkg/debug/elf/file.go\n\n// Returns the Prog header offset and size\nfunc gethdr(f *ELF.File, sr io.ReadSeeker) (int64, int, error) {\n\tsr.Seek(0, os.SEEK_SET)\n\n\tswitch f.Class {\n\tcase ELF.ELFCLASS32:\n\t\thdr := new(ELF.Header32)\n\t\tif err := binary.Read(sr, f.ByteOrder, hdr); err != nil {\n\t\t\treturn 0, 0, err\n\t\t}\n\t\treturn int64(hdr.Phoff), int(hdr.Phentsize), nil\n\n\tcase ELF.ELFCLASS64:\n\t\thdr := new(ELF.Header64)\n\t\tif err := binary.Read(sr, f.ByteOrder, hdr); err != nil {\n\t\t\treturn 0, 0, err\n\t\t}\n\t\treturn int64(hdr.Phoff), int(hdr.Phentsize), nil\n\t}\n\treturn 0, 0, errors.New(\"Unexpected ELF class\")\n}\n\n// Write out a Prog header to an elf with a given destination\n// Writes out `p` to `sw` at `dst` using information from `f`\nfunc writephdr(f *ELF.File, dst int64, sw io.WriteSeeker, p *ELF.Prog) error {\n\tsw.Seek(dst, os.SEEK_SET)\n\n\tswitch f.Class {\n\tcase ELF.ELFCLASS32:\n\t\thdr := ELF.Prog32{\n\t\t\tType:   uint32(p.Type),\n\t\t\tFlags:  uint32(p.Flags),\n\t\t\tOff:    uint32(p.Off),\n\t\t\tVaddr:  uint32(p.Vaddr),\n\t\t\tPaddr:  uint32(p.Paddr),\n\t\t\tFilesz: uint32(p.Filesz),\n\t\t\tMemsz:  uint32(p.Memsz),\n\t\t\tAlign:  uint32(p.Align),\n\t\t}\n\t\tif err := binary.Write(sw, f.ByteOrder, hdr); err != nil {\n\t\t\treturn err\n\t\t}\n\n\tcase ELF.ELFCLASS64:\n\t\thdr := ELF.Prog64{\n\t\t\tType:   uint32(p.Type),\n\t\t\tFlags:  uint32(p.Flags),\n\t\t\tOff:    p.Off,\n\t\t\tVaddr:  p.Vaddr,\n\t\t\tPaddr:  p.Paddr,\n\t\t\tFilesz: p.Filesz,\n\t\t\tMemsz:  p.Memsz,\n\t\t\tAlign:  p.Align,\n\t\t}\n\t\tif err := binary.Write(sw, f.ByteOrder, hdr); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc fixelf(elf *ELF.File, fd io.ReadWriteSeeker) error {\n\n\t// Determine where to write header (need\n\toff, sz, err := gethdr(elf, fd)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor i := range elf.Progs {\n\t\tp := elf.Progs[i]\n\n\t\tif p.ProgHeader.Type != ELF.PT_LOAD {\n\t\t\t// Only consider PT_LOAD sections\n\t\t\tcontinue\n\t\t}\n\n\t\tif p.Flags&ELF.PF_X != ELF.PF_X {\n\t\t\tcontinue\n\t\t}\n\n\t\tmask := -p.Align\n\t\tif ^mask&p.Vaddr != 0 && (^mask&(p.Vaddr-p.Off)) == 0 {\n\t\t\tlog.Printf(\"Hemming PT_LOAD section\")\n\t\t\them := ^mask & p.Off\n\t\t\tp.Off -= hem\n\t\t\tp.Vaddr -= hem\n\t\t\tif p.Paddr != 0 {\n\t\t\t\tp.Paddr -= hem\n\t\t\t}\n\t\t\tp.Filesz += hem\n\t\t\tp.Memsz += hem\n\n\t\t\tdst := off + int64(sz*i)\n\t\t\twritephdr(elf, dst, fd, p)\n\t\t\tbreak\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc FixFile(filename string) error {\n\tfd, err := os.OpenFile(filename, os.O_RDWR, 0)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer fd.Close()\n\n\telf, err := ELF.NewFile(fd)\n\tif err != nil {\n\t\tlog.Print(\"Failed to parse ELF. This can happen if the binary is already packed.\")\n\t\treturn err\n\t}\n\tdefer elf.Close()\n\n\tlog.Printf(\"%+v\", elf.FileHeader)\n\terr = fixelf(elf, fd)\n\tif err != nil {\n\t\tlog.Fatal(\"Failure to read ELF header\")\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\n\t\"github.com/pwaller/goupx/hemfix\"\n)\n\nconst usageText = `usage: goupx [args...] files...\n\n    --no-upx: Disables UPX from running.\n    --strip-binary: Strips binaries before compressing them.\n\nSee UPX's documentation (man upx) for information on UPX's flags.\n`\n\nvar run_strip = false\nvar run_upx = true\nvar upxPath string\n\n// usage prints some nice output instead of panic stacktrace when an user calls\n// goupx without arguments\nfunc usage() {\n\tos.Stderr.WriteString(usageText)\n}\n\n// findUpxBinary searches for the upx binary in PATH.\nfunc findUpxBinary() {\n\tvar err error\n\tupxPath, err = exec.LookPath(\"upx\")\n\tif err != nil {\n\t\tlog.Fatal(\"Couldn't find upx binary in PATH\")\n\t}\n}\n\n// parseArguments parses arguments from os.Args and separates the goupx flags\n// from the UPX flags, as well as separating the files from the arguments.\nfunc parseArguments() (args []string, files []string) {\n\tif len(os.Args) == 1 {\n\t\tusage()\n\t}\n\targs = append(args, upxPath)\n\tfor _, arg := range os.Args[1:] {\n\t\tswitch {\n\t\tcase arg == \"-h\" || arg == \"--help\":\n\t\t\tusage()\n\t\tcase arg == \"--no-upx\":\n\t\t\trun_upx = false\n\t\tcase arg == \"--strip-binary\":\n\t\t\trun_strip = true\n\t\tcase arg[0] != '-':\n\t\t\tfiles = append(files, arg)\n\t\tdefault:\n\t\t\targs = append(args, arg)\n\t\t}\n\t}\n\treturn\n}\n\n// compressBinary attempts to compress the binary with UPX.\nfunc compressBinary(input_file string, arguments []string) {\n\tif run_upx {\n\t\tcmd := &exec.Cmd{\n\t\t\tPath: upxPath,\n\t\t\tArgs: append(arguments, input_file),\n\t\t}\n\t\tcmd.Stdout = os.Stdout\n\t\tcmd.Stderr = os.Stderr\n\t\tif err := cmd.Run(); err != nil {\n\t\t\tlog.Panic(\"upx failed: \", err)\n\t\t}\n\t}\n}\n\n// stripBinary attempts to strip the binary.\nfunc stripBinary(input_file string) {\n\tif run_strip {\n\t\tcmd := exec.Command(\"strip\", \"-s\", input_file)\n\t\tcmd.Stdout = os.Stdout\n\t\tcmd.Stderr = os.Stderr\n\t\tif err := cmd.Run(); err != nil {\n\t\t\tlog.Panic(\"strip failed: \", err)\n\t\t}\n\t}\n}\n\n// runHemfix will attempt to fix the current input file.\nfunc runHemfix(input_file string) {\n\tif err := hemfix.FixFile(input_file); err != nil {\n\t\tlog.Panicf(\"Failed to fix '%s': %v\", input_file, err)\n\t}\n\tlog.Print(\"File fixed!\")\n}\n\nfunc main() {\n\tfindUpxBinary()\n\targuments, files := parseArguments()\n\tfor _, file := range files {\n\t\trunHemfix(file)\n\t\tstripBinary(file)\n\t\tcompressBinary(file, arguments)\n\t}\n\tif err := recover(); err != nil {\n\t\tlog.Print(\"Panicked. Giving up.\")\n\t\tpanic(err)\n\t\treturn\n\t}\n}\n"
  }
]