How can I calculate the size of an ELF executable in Go?
I am looking for something along the lines of the following C code:
Currently I am using Cgo but I would prefer a pure-Go solution.
How can I calculate the size of an ELF executable in Go?
I am looking for something along the lines of the following C code:
Currently I am using Cgo but I would prefer a pure-Go solution.
Welcome to Go Forum!
Don’t get what you mean, ls -l
should be able to show the size of the binary right? Or do you mean the breakdown from the total size?
There is an elf
package from standard library. Will that help? elf package - debug/elf - Go Packages
Thanks for the warm welcome @hollowaykeanho.
What I didn’t mention is that I need to calculate the size of the ELF binary using the information from the ELF headers, not by looking at the file size on disk, because the file on disk has random data appended to the ELF.
Code examples in other languages:
Since I am really just starting out with Go I don’t quite know yet how to translate this into Go.
Well, one can start off by reading through the documentation (elf package - debug/elf - Go Packages) and try to understand what is going with it. Then read up the source code (- The Go Programming Language) espcially Line 240 by searching the Header32
and Header64
strcuture keywords pointed by @rot13.
This is one code example for reproduce the shoff
, shentsize
, and shnum
through analyzing the NewFile
.
// Authors:
// 1. Holloway, Chew Kean Ho <kean.ho.chew@zoralab.com>
// main is a demostration for elf data reader.
package main
import (
"debug/elf"
"encoding/binary"
"fmt"
"io"
"os"
)
func ioReader(file string) (io.ReaderAt, int64) {
f, err := os.Open(file)
if err != nil {
panic(err)
}
fi, err := f.Stat()
if err != nil {
panic(err)
}
return f, fi.Size()
}
func main() {
if len(os.Args) < 2 {
fmt.Println(`Usage: ./elftester /path/to/elf/file`)
os.Exit(1)
}
// Open given elf file
f, size := ioReader(os.Args[1])
e, err := elf.NewFile(f)
if err != nil {
panic(err)
}
// Read identifier
var ident [16]uint8
_, err = f.ReadAt(ident[0:], 0)
if err != nil {
panic(err)
}
// Decode identifier
if ident[0] != '\x7f' ||
ident[1] != 'E' ||
ident[2] != 'L' ||
ident[3] != 'F' {
fmt.Printf("Bad magic number at %d\n", ident[0:4])
os.Exit(1)
}
// Process by architecture
sr := io.NewSectionReader(f, 0, 1<<63-1)
var shoff, shentsize, shnum int64
switch e.Class.String() {
case "ELFCLASS64":
hdr := new(elf.Header64)
sr.Seek(0, 0)
err = binary.Read(sr, e.ByteOrder, hdr)
if err != nil {
panic(err)
}
shoff = int64(hdr.Shoff)
shnum = int64(hdr.Shnum)
shentsize = int64(hdr.Shentsize)
case "ELFCLASS32":
hdr := new(elf.Header32)
sr.Seek(0, 0)
err = binary.Read(sr, e.ByteOrder, hdr)
if err != nil {
panic(err)
}
shoff = int64(hdr.Shoff)
shnum = int64(hdr.Shnum)
shentsize = int64(hdr.Shentsize)
default:
panic("unsupported elf architecture")
}
// calculate
x := shoff + (shentsize * shnum)
// Printout
fmt.Printf("File Size (ls -l) : %d\n", size)
fmt.Printf("Start of section header (shoff) : %d\n", shoff)
fmt.Printf("Size of section header (shentsize): %d\n", shentsize)
fmt.Printf("Number of section header (shnum) : %d\n", shnum)
fmt.Printf("shoff + (shentsize * shnum) : %d\n", x)
}
// Output:
// File Size (ls -l) : 17384
// Start of section header (shoff) : 15464
// Size of section header (shentsize): 64
// Number of section header (shnum) : 30
// shoff + (shentsize * shnum) : 17384
Note that there is no clear example in the package so I did a reverse engineering against NewFile(...)
. I do expect an easier way to get those values back since NewFile(...)
by symbol query but again, limited by scarce documentation.
Thank you very much @hollowaykeanho. This is a very useful example and explanation. I owe you big time.
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.