diff options
| author | Peter Waller <[email protected]> | 2012-09-08 15:08:54 +0100 |
|---|---|---|
| committer | Peter Waller <[email protected]> | 2012-09-08 15:41:50 +0100 |
| commit | e88a53121cc12151cad9e0af158181babd725235 (patch) | |
| tree | 3760e171da58882c336ec75e4c84fff485bb5b43 | |
| download | goupx-e88a53121cc12151cad9e0af158181babd725235.tar.xz goupx-e88a53121cc12151cad9e0af158181babd725235.zip | |
Version 1.0v1.0
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | README.md | 33 | ||||
| -rw-r--r-- | goupx.go | 205 |
3 files changed, 239 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e397f51 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/goupx diff --git a/README.md b/README.md new file mode 100644 index 0000000..cce99c5 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +goupx - Fix golang executables to work with upx +------------------------------------------- + +Installation: `go get github.com/pwaller/goupx` + +(or if you don't want to do it with root, `GOPATH=${PWD}/env go get github.com/pwaller/goupx` will install it to `${PWD}/env/bin/goupx`) + +Usage: `goupx [filename]` + +Fixes the `PT_LOAD` offset of [filename] and then runs `upx`. + +Based on [code found on the upx bugtracker](http://sourceforge.net/tracker/?func=detail&atid=102331&aid=3408066&group_id=2331). + +GPLv3 licensed. + +Fixes the following issue +========================= + + $ upx [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. diff --git a/goupx.go b/goupx.go new file mode 100644 index 0000000..05c615f --- /dev/null +++ b/goupx.go @@ -0,0 +1,205 @@ +package main + +/* + +goupx: Fix compiled go binaries so that they can be packed by + the universal packer for executables (upx) + +Copyright (c) 2012 Peter Waller <[email protected]> +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" + "flag" + "io" + "log" + "os" + "os/exec" +) + +// 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 + } + + 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) + } + } + 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 +} + +func main() { + + run_strip := flag.Bool("s", false, "run strip") + run_upx := flag.Bool("u", true, "run upx") + + flag.Parse() + + defer func() { + if err := recover(); err != nil { + log.Print("Panicked. Giving up.") + panic(err) + return + } + }() + + input_file := flag.Arg(0) + err := fixfile(input_file) + if err != nil { + log.Panicf("Failed to fix '%s': %v", input_file, err) + } + log.Print("File fixed!") + + if *run_strip { + cmd := exec.Command("strip", "-s", input_file) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + if err != nil { + log.Panic("strip failed: ", err) + } + } + + if *run_upx { + cmd := exec.Command("upx", input_file) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + if err != nil { + log.Panic("upx failed: ", err) + } + } + +} |