aboutsummaryrefslogtreecommitdiff
path: root/goupx.go
diff options
context:
space:
mode:
Diffstat (limited to 'goupx.go')
-rw-r--r--goupx.go205
1 files changed, 205 insertions, 0 deletions
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)
+ }
+ }
+
+}