Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: use code generate for moduledata #77

Merged
merged 12 commits into from
Feb 9, 2024
14 changes: 9 additions & 5 deletions elf.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ type elfFile struct {
osFile *os.File
}

func (e *elfFile) GetParsedFile() any {
func (e *elfFile) getParsedFile() any {
Zxilly marked this conversation as resolved.
Show resolved Hide resolved
return e.file
}

func (e *elfFile) GetFile() *os.File {
func (e *elfFile) getFile() *os.File {
return e.osFile
}

Expand Down Expand Up @@ -88,12 +88,16 @@ func (e *elfFile) getRData() ([]byte, error) {
return section.Data()
}

func (e *elfFile) getCodeSection() ([]byte, error) {
func (e *elfFile) getCodeSection() (uint64, []byte, error) {
section := e.file.Section(".text")
if section == nil {
return nil, ErrSectionDoesNotExist
return 0, nil, ErrSectionDoesNotExist
}
return section.Data()
data, err := section.Data()
if err != nil {
return 0, nil, fmt.Errorf("error when getting the code section: %w", err)
}
return section.Addr, data, nil
}

func (e *elfFile) getPCLNTABData() (uint64, []byte, error) {
Expand Down
148 changes: 101 additions & 47 deletions file.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func Open(filePath string) (*GoFile, error) {

buf := make([]byte, maxMagicBufLen)
n, err := f.Read(buf)
f.Close()
_ = f.Close()
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -94,8 +94,9 @@ func Open(filePath string) (*GoFile, error) {

// Try to extract build information.
if bi, err := gofile.extractBuildInfo(); err == nil {
// This error is a minor failure, it just means we don't have
// this information. So if fails we just ignores it.
// This error is a minor failure; it just means we don't have
// this information.
// So if fails, we just ignore it.
gofile.BuildInfo = bi
if bi.Compiler != nil {
gofile.FileInfo.goversion = bi.Compiler
Expand All @@ -107,40 +108,72 @@ func Open(filePath string) (*GoFile, error) {

// GoFile is a structure representing a go binary file.
type GoFile struct {
// BuildInfo holds the data from the buildinf structure. This can be nil
// because it's not always available.
// BuildInfo holds the data from the buildinfo structure.
// This can be a nil because it's not always available.
BuildInfo *BuildInfo
// FileInfo holds information about the file.
FileInfo *FileInfo
// BuildID is the Go build ID hash extracted from the binary.
BuildID string
fh fileHandler
stdPkgs []*Package
generated []*Package
pkgs []*Package
vendors []*Package
unknown []*Package
pclntab *gosym.Table
initPackages sync.Once
BuildID string

fh fileHandler

stdPkgs []*Package
generated []*Package
pkgs []*Package
vendors []*Package
unknown []*Package

pclntab *gosym.Table

initPackagesOnce sync.Once
initPackagesError error

moduledata moduledata

versionError error

initModuleDataOnce sync.Once
initModuleDataError error
}

func (f *GoFile) init() error {
var returnVal error
f.initPackages.Do(func() {
func (f *GoFile) initModuleData() error {
f.initModuleDataOnce.Do(func() {
err := f.ensureCompilerVersion()
if err != nil {
f.initModuleDataError = err
return
}
f.moduledata, f.initModuleDataError = extractModuledata(f.FileInfo, f.fh)
})
return f.initModuleDataError
}

// Moduledata extracts the file's moduledata.
func (f *GoFile) Moduledata() (Moduledata, error) {
err := f.initModuleData()
if err != nil {
return moduledata{}, err
}
return f.moduledata, nil
}

func (f *GoFile) initPackages() error {
f.initPackagesOnce.Do(func() {
tab, err := f.PCLNTab()
if err != nil {
returnVal = err
f.initPackagesError = err
return
}
f.pclntab = tab
returnVal = f.enumPackages()
f.initPackagesError = f.enumPackages()
})
return returnVal
return f.initPackagesError
}

// GetFile returns the raw file opened by the library.
func (f *GoFile) GetFile() *os.File {
return f.fh.GetFile()
return f.fh.getFile()
}

// GetParsedFile returns the parsed file, should be cast based on the file type.
Expand All @@ -151,13 +184,38 @@ func (f *GoFile) GetFile() *os.File {
//
// all from the debug package.
func (f *GoFile) GetParsedFile() any {
return f.fh.GetParsedFile()
return f.fh.getParsedFile()
}

// GetCompilerVersion returns the Go compiler version of the compiler
// that was used to compile the binary.
func (f *GoFile) GetCompilerVersion() (*GoVersion, error) {
return findGoCompilerVersion(f)
err := f.ensureCompilerVersion()
if err != nil {
return nil, err
}
return f.FileInfo.goversion, nil
}

func (f *GoFile) ensureCompilerVersion() error {
if f.FileInfo.goversion == nil {
f.tryExtractCompilerVersion()
}
return f.versionError
}

// tryExtractCompilerVersion tries to extract the compiler version from the binary.
// should only be called if FileInfo.goversion is nil.
func (f *GoFile) tryExtractCompilerVersion() {
if f.FileInfo.goversion != nil {
return
}
v, err := findGoCompilerVersion(f)
if err != nil {
f.versionError = err
} else {
f.FileInfo.goversion = v
}
}

// SourceInfo returns the source code filename, starting line number
Expand All @@ -168,10 +226,9 @@ func (f *GoFile) SourceInfo(fn *Function) (string, int, int) {
return srcFile, start, end
}

// GetGoRoot returns the Go Root path
// that was used to compile the binary.
// GetGoRoot returns the Go Root path used to compile the binary.
func (f *GoFile) GetGoRoot() (string, error) {
err := f.init()
err := f.initPackages()
if err != nil {
return "", err
}
Expand All @@ -181,7 +238,7 @@ func (f *GoFile) GetGoRoot() (string, error) {
// SetGoVersion sets the assumed compiler version that was used. This
// can be used to force a version if gore is not able to determine the
// compiler version used. The version string must match one of the strings
// normally extracted from the binary. For example to set the version to
// normally extracted from the binary. For example, to set the version to
// go 1.12.0, use "go1.12". For 1.7.2, use "go1.7.2".
// If an incorrect version string or version not known to the library,
// ErrInvalidGoVersion is returned.
Expand All @@ -194,35 +251,35 @@ func (f *GoFile) SetGoVersion(version string) error {
return nil
}

// GetPackages returns the go packages that has been classified as part of the main
// GetPackages returns the go packages that have been classified as part of the main
// project.
func (f *GoFile) GetPackages() ([]*Package, error) {
err := f.init()
err := f.initPackages()
return f.pkgs, err
}

// GetVendors returns the 3rd party packages used by the binary.
// GetVendors returns the third party packages used by the binary.
func (f *GoFile) GetVendors() ([]*Package, error) {
err := f.init()
err := f.initPackages()
return f.vendors, err
}

// GetSTDLib returns the standard library packages used by the binary.
func (f *GoFile) GetSTDLib() ([]*Package, error) {
err := f.init()
err := f.initPackages()
return f.stdPkgs, err
}

// GetGeneratedPackages returns the compiler generated packages used by the binary.
func (f *GoFile) GetGeneratedPackages() ([]*Package, error) {
err := f.init()
err := f.initPackages()
return f.generated, err
}

// GetUnknown returns unclassified packages used by the binary. This is a catch all
// category when the classification could not be determined.
// GetUnknown returns unclassified packages used by the binary.
// This is a catch-all category when the classification could not be determined.
func (f *GoFile) GetUnknown() ([]*Package, error) {
err := f.init()
err := f.initPackages()
return f.unknown, err
}

Expand Down Expand Up @@ -325,24 +382,21 @@ func (f *GoFile) PCLNTab() (*gosym.Table, error) {

// GetTypes returns a map of all types found in the binary file.
func (f *GoFile) GetTypes() ([]*GoType, error) {
if f.FileInfo.goversion == nil {
ver, err := f.GetCompilerVersion()
if err != nil {
return nil, err
}
f.FileInfo.goversion = ver
err := f.ensureCompilerVersion()
if err != nil {
return nil, err
}
t, err := getTypes(f.FileInfo, f.fh)
if err != nil {
return nil, err
}
if err = f.init(); err != nil {
if err = f.initPackages(); err != nil {
return nil, err
}
return sortTypes(t), nil
}

// Bytes returns a slice of raw bytes with the length in the file from the address.
// Bytes return a slice of raw bytes with the length in the file from the address.
func (f *GoFile) Bytes(address uint64, length uint64) ([]byte, error) {
base, section, err := f.fh.getSectionDataFromOffset(address)
if err != nil {
Expand Down Expand Up @@ -377,15 +431,15 @@ type fileHandler interface {
io.Closer
getPCLNTab() (*gosym.Table, error)
getRData() ([]byte, error)
getCodeSection() ([]byte, error)
getCodeSection() (uint64, []byte, error)
getSectionDataFromOffset(uint64) (uint64, []byte, error)
getSectionData(string) (uint64, []byte, error)
getFileInfo() *FileInfo
getPCLNTABData() (uint64, []byte, error)
moduledataSection() string
getBuildID() (string, error)
GetFile() *os.File
GetParsedFile() any
getFile() *os.File
getParsedFile() any
}

func fileMagicMatch(buf, magic []byte) bool {
Expand Down
11 changes: 5 additions & 6 deletions file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,8 @@ func TestGoldFiles(t *testing.T) {
// Get info from filename gold-os-arch-goversion
fileInfo := strings.Split(file, "-")

// If patch level is 0, it is dropped. For example. 10.0.0 is 10.0.
// This was changed in 1.21 so if the version is 1.21 or greater, we take
// the whole string.
// If the patch level is 0, it is dropped. For example. 10.0.0 is 10.0
// Up until 1.21.0, if patch level is 0, it is dropped. For example. 10.0.0 is 10.0
var actualVersion string
verArr := strings.Split(fileInfo[3], ".")
if len(verArr) == 3 && verArr[2] == "0" && mustParse(strconv.Atoi(verArr[1])) < 21 {
Expand Down Expand Up @@ -175,11 +174,11 @@ type mockFileHandler struct {
mGetSectionDataFromOffset func(uint64) (uint64, []byte, error)
}

func (m *mockFileHandler) GetFile() *os.File {
func (m *mockFileHandler) getFile() *os.File {
panic("not implemented")
}

func (m *mockFileHandler) GetParsedFile() any {
func (m *mockFileHandler) getParsedFile() any {
panic("not implemented")
}

Expand All @@ -195,7 +194,7 @@ func (m *mockFileHandler) getRData() ([]byte, error) {
panic("not implemented")
}

func (m *mockFileHandler) getCodeSection() ([]byte, error) {
func (m *mockFileHandler) getCodeSection() (uint64, []byte, error) {
panic("not implemented")
}

Expand Down
Loading
Loading