aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Waller <[email protected]>2012-09-08 15:08:54 +0100
committerPeter Waller <[email protected]>2012-09-08 15:41:50 +0100
commite88a53121cc12151cad9e0af158181babd725235 (patch)
tree3760e171da58882c336ec75e4c84fff485bb5b43
downloadgoupx-e88a53121cc12151cad9e0af158181babd725235.tar.xz
goupx-e88a53121cc12151cad9e0af158181babd725235.zip
Version 1.0v1.0
-rw-r--r--.gitignore1
-rw-r--r--README.md33
-rw-r--r--goupx.go205
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)
+ }
+ }
+
+}