Repository: pwaller/goupx Branch: master Commit: 1d58e01d5ce2 Files: 5 Total size: 8.7 KB Directory structure: gitextract_f5pnkk7l/ ├── .gitignore ├── LICENSE ├── README.md ├── hemfix/ │ └── hemfix.go └── main.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ /goupx ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2012 Peter Waller Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ goupx - Fix golang Linux ELF executables to work with upx --------------------------------------------------- ## Update: 2016/03/10 As far as I (pwaller) know, goupx is no longer necessary for Linux binaries since it was fixed in go1.6. ## About Installation: `go get github.com/pwaller/goupx` (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`). Usage: `goupx [filename]` Fixes the `PT_LOAD` offset of [filename] and then runs `upx`. This is only necessary for Linux ELF executables (not Mach-O executables or windows binaries, for example). Based on [code found on the upx bugtracker](http://sourceforge.net/tracker/?func=detail&atid=102331&aid=3408066&group_id=2331). MIT licensed. Fixes the following issue ========================= $ upx [linux ELF go binary] Ultimate Packer for eXecutables Copyright (C) 1996 - 2011 UPX 3.08 Markus Oberhumer, Laszlo Molnar & John Reiser Dec 12th 2011 File size Ratio Format Name -------------------- ------ ----------- ----------- upx: goupx: EOFException: premature end of file Packed 1 file: 0 ok, 1 error. Typical compression ratio ========================= Resulting filesizes are typically 25% of the original go executable. Your mileage my vary. ================================================ FILE: hemfix/hemfix.go ================================================ package hemfix /* goupx: Fix compiled go binaries so that they can be packed by the universal packer for executables (upx) Copyright (c) 2012 Peter Waller All rights reserved. Based on code found at http://sourceforge.net/tracker/?func=detail&atid=102331&aid=3408066&group_id=2331 Based on hemfix.c Copyright (C) 2012 John Reiser, BitWagon Software LLC This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import ( ELF "debug/elf" "encoding/binary" "errors" "io" "log" "os" ) // The functions gethdr and writephdr are heavily influenced by code found at // http://golang.org/src/pkg/debug/elf/file.go // Returns the Prog header offset and size func gethdr(f *ELF.File, sr io.ReadSeeker) (int64, int, error) { sr.Seek(0, os.SEEK_SET) switch f.Class { case ELF.ELFCLASS32: hdr := new(ELF.Header32) if err := binary.Read(sr, f.ByteOrder, hdr); err != nil { return 0, 0, err } return int64(hdr.Phoff), int(hdr.Phentsize), nil case ELF.ELFCLASS64: hdr := new(ELF.Header64) if err := binary.Read(sr, f.ByteOrder, hdr); err != nil { return 0, 0, err } return int64(hdr.Phoff), int(hdr.Phentsize), nil } return 0, 0, errors.New("Unexpected ELF class") } // Write out a Prog header to an elf with a given destination // Writes out `p` to `sw` at `dst` using information from `f` func writephdr(f *ELF.File, dst int64, sw io.WriteSeeker, p *ELF.Prog) error { sw.Seek(dst, os.SEEK_SET) switch f.Class { case ELF.ELFCLASS32: hdr := ELF.Prog32{ Type: uint32(p.Type), Flags: uint32(p.Flags), Off: uint32(p.Off), Vaddr: uint32(p.Vaddr), Paddr: uint32(p.Paddr), Filesz: uint32(p.Filesz), Memsz: uint32(p.Memsz), Align: uint32(p.Align), } if err := binary.Write(sw, f.ByteOrder, hdr); err != nil { return err } case ELF.ELFCLASS64: hdr := ELF.Prog64{ Type: uint32(p.Type), Flags: uint32(p.Flags), Off: p.Off, Vaddr: p.Vaddr, Paddr: p.Paddr, Filesz: p.Filesz, Memsz: p.Memsz, Align: p.Align, } if err := binary.Write(sw, f.ByteOrder, hdr); err != nil { return err } } return nil } func fixelf(elf *ELF.File, fd io.ReadWriteSeeker) error { // Determine where to write header (need off, sz, err := gethdr(elf, fd) if err != nil { return err } for i := range elf.Progs { p := elf.Progs[i] if p.ProgHeader.Type != ELF.PT_LOAD { // Only consider PT_LOAD sections continue } if p.Flags&ELF.PF_X != ELF.PF_X { continue } mask := -p.Align if ^mask&p.Vaddr != 0 && (^mask&(p.Vaddr-p.Off)) == 0 { log.Printf("Hemming PT_LOAD section") hem := ^mask & p.Off p.Off -= hem p.Vaddr -= hem if p.Paddr != 0 { p.Paddr -= hem } p.Filesz += hem p.Memsz += hem dst := off + int64(sz*i) writephdr(elf, dst, fd, p) break } } return nil } func FixFile(filename string) error { fd, err := os.OpenFile(filename, os.O_RDWR, 0) if err != nil { return err } defer fd.Close() elf, err := ELF.NewFile(fd) if err != nil { log.Print("Failed to parse ELF. This can happen if the binary is already packed.") return err } defer elf.Close() log.Printf("%+v", elf.FileHeader) err = fixelf(elf, fd) if err != nil { log.Fatal("Failure to read ELF header") return err } return nil } ================================================ FILE: main.go ================================================ package main import ( "log" "os" "os/exec" "github.com/pwaller/goupx/hemfix" ) const usageText = `usage: goupx [args...] files... --no-upx: Disables UPX from running. --strip-binary: Strips binaries before compressing them. See UPX's documentation (man upx) for information on UPX's flags. ` var run_strip = false var run_upx = true var upxPath string // usage prints some nice output instead of panic stacktrace when an user calls // goupx without arguments func usage() { os.Stderr.WriteString(usageText) } // findUpxBinary searches for the upx binary in PATH. func findUpxBinary() { var err error upxPath, err = exec.LookPath("upx") if err != nil { log.Fatal("Couldn't find upx binary in PATH") } } // parseArguments parses arguments from os.Args and separates the goupx flags // from the UPX flags, as well as separating the files from the arguments. func parseArguments() (args []string, files []string) { if len(os.Args) == 1 { usage() } args = append(args, upxPath) for _, arg := range os.Args[1:] { switch { case arg == "-h" || arg == "--help": usage() case arg == "--no-upx": run_upx = false case arg == "--strip-binary": run_strip = true case arg[0] != '-': files = append(files, arg) default: args = append(args, arg) } } return } // compressBinary attempts to compress the binary with UPX. func compressBinary(input_file string, arguments []string) { if run_upx { cmd := &exec.Cmd{ Path: upxPath, Args: append(arguments, input_file), } cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { log.Panic("upx failed: ", err) } } } // stripBinary attempts to strip the binary. func stripBinary(input_file string) { if run_strip { cmd := exec.Command("strip", "-s", input_file) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { log.Panic("strip failed: ", err) } } } // runHemfix will attempt to fix the current input file. func runHemfix(input_file string) { if err := hemfix.FixFile(input_file); err != nil { log.Panicf("Failed to fix '%s': %v", input_file, err) } log.Print("File fixed!") } func main() { findUpxBinary() arguments, files := parseArguments() for _, file := range files { runHemfix(file) stripBinary(file) compressBinary(file, arguments) } if err := recover(); err != nil { log.Print("Panicked. Giving up.") panic(err) return } }