diff --git a/Makefile b/Makefile index f64adc4..f2a12fc 100644 --- a/Makefile +++ b/Makefile @@ -30,4 +30,4 @@ lint: golint ./shared/ ./tests/ ./cmd/... clean: - rm -f data/hello data/hello.bc [td]*/.helloworld.c.o [td]*/.helloworld.c.o.bc + rm -f data/*hello data/*.bc [td]*/.*.c.o [td]*/*.o [td]*/.*.c.o.bc data/*.notanextensionthatwerecognize diff --git a/README.md b/README.md index 3a5a0f4..95bffcc 100644 --- a/README.md +++ b/README.md @@ -250,7 +250,12 @@ such as *link time optimization* (indicated by the presence of compiler flag `-f your build is unlikely to produce anything that `get-bc` will work on. This is to be expected. +## Developer tools +Debugging usually boils down to looking in the logs, maybe adding a print statement or two. +There is an additional executable, not mentioned above, called `gparse` that gets installed +along with `gclang`, `gclang++`, `get-bc` and `gsanity-check`. `gparse` takes the command line +arguments to the compiler, and outputs how it parsed them. This can sometimes be helpful. ## License diff --git a/shared/compiler.go b/shared/compiler.go index 5d7df99..cd01162 100644 --- a/shared/compiler.go +++ b/shared/compiler.go @@ -153,6 +153,7 @@ func attachBitcodePathToObject(bcFile, objFile string) (success bool) { default: //OK we have to work harder here ok, err := injectableViaFileType(objFile) + LogDebug("attachBitcodePathToObject: injectableViaFileType returned ok=%v err=%v", ok, err) if ok { success = injectPath(extension, bcFile, objFile) return @@ -161,6 +162,7 @@ func attachBitcodePathToObject(bcFile, objFile string) (success bool) { // OK we have to work EVEN harder here (the file utility is not installed - probably) // N.B. this will probably fail if we are cross compiling. ok, err = injectableViaDebug(objFile) + LogDebug("attachBitcodePathToObject: injectableViaDebug returned ok=%v err=%v", ok, err) if ok { success = injectPath(extension, bcFile, objFile) return @@ -269,6 +271,7 @@ func compileTimeLinkFiles(compilerExecName string, pr ParserResult, objFiles []s func buildObjectFile(compilerExecName string, pr ParserResult, srcFile string, objFile string) (success bool) { args := pr.CompileArgs[:] args = append(args, srcFile, "-c", "-o", objFile) + LogDebug("buildObjectFile: %v", args) success, err := execCmd(compilerExecName, args, "") if !success { LogError("Failed to build object file for %s because: %v\n", srcFile, err) diff --git a/shared/filetypes.go b/shared/filetypes.go index 41df092..ef9167e 100644 --- a/shared/filetypes.go +++ b/shared/filetypes.go @@ -42,7 +42,7 @@ import ( "strings" ) -//BinaryType is the 'intersection' of elf.Type and macho.Type and partitions +// BinaryType is the 'intersection' of elf.Type and macho.Type and partitions // the binary world into categories we are most interested in. Missing is // ARCHIVE but that is because it is not an elf format, so we cannot entirely // eliminate the use of the 'file' utility (cf getFileType below). @@ -59,6 +59,49 @@ const ( BinaryShared BinaryType = 3 ) +func (bt BinaryType) String() string { + switch bt { + case BinaryUnknown: + return "Unknown" + case BinaryObject: + return "Object" + case BinaryExecutable: + return "Executable" + case BinaryShared: + return "Library" + default: + return "Error" + } +} + +// GetBinaryType gets the binary type of the given path +func GetBinaryType(path string) (bt BinaryType) { + bt = BinaryUnknown + plain := IsPlainFile(path) + if !plain { + return + } + // try the format that suits the platform first + operatingSys := runtime.GOOS + switch operatingSys { + case "linux", "freebsd": + bt, _ = ElfFileType(path) + case "darwin": + bt, _ = MachoFileType(path) + } + if bt != BinaryUnknown { + return + } + // try the other format instead + switch operatingSys { + case "linux", "freebsd": + bt, _ = MachoFileType(path) + case "darwin": + bt, _ = ElfFileType(path) + } + return +} + func elfType2BinaryType(et elf.Type) (bt BinaryType) { bt = BinaryUnknown switch et { @@ -95,21 +138,24 @@ func machoType2BinaryType(mt macho.Type) (bt BinaryType) { return } -// isPlainFile returns true if the file is stat-able (i.e. exists etc), and is not a directory, else it returns false. -func isPlainFile(objectFile string) (ok bool) { +// IsPlainFile returns true if the file is stat-able (i.e. exists etc), and is not a directory, else it returns false. +func IsPlainFile(objectFile string) (ok bool) { info, err := os.Stat(objectFile) - if os.IsNotExist(err) || info.IsDir() { + if os.IsNotExist(err) { return } if err != nil { return } + if info.IsDir() { + return + } ok = true return } func injectableViaFileType(objectFile string) (ok bool, err error) { - plain := isPlainFile(objectFile) + plain := IsPlainFile(objectFile) if !plain { return } @@ -117,7 +163,7 @@ func injectableViaFileType(objectFile string) (ok bool, err error) { if err != nil { return } - ok = (fileType == fileTypeELFOBJECT) || (fileType == fileTypeELFOBJECT) + ok = (fileType == fileTypeELFOBJECT) || (fileType == fileTypeMACHOBJECT) return } @@ -151,7 +197,7 @@ func MachoFileType(objectFile string) (code BinaryType, err error) { //IsObjectFileForOS returns true if the given file is an object file for the given OS, using the debug/elf and debug/macho packages. func IsObjectFileForOS(objectFile string, operatingSys string) (ok bool, err error) { - plain := isPlainFile(objectFile) + plain := IsPlainFile(objectFile) if !plain { return } diff --git a/shared/parser.go b/shared/parser.go index 0ca66bd..e1383a6 100644 --- a/shared/parser.go +++ b/shared/parser.go @@ -177,8 +177,8 @@ func Parse(argList []string) ParserResult { "--version": {0, pr.compileOnlyCallback}, "-v": {0, pr.compileOnlyCallback}, - "-w": {0, pr.compileOnlyCallback}, - "-W": {0, pr.compileOnlyCallback}, + "-w": {0, pr.compileUnaryCallback}, + "-W": {0, pr.compileUnaryCallback}, "-emit-llvm": {0, pr.emitLLVMCallback}, "-flto": {0, pr.linkTimeOptimizationCallback}, diff --git a/tests/entry_test.go b/tests/entry_test.go index 5eca10a..0abd9c3 100644 --- a/tests/entry_test.go +++ b/tests/entry_test.go @@ -7,19 +7,23 @@ import ( "testing" ) +const ( + DEBUG bool = false +) + func Test_basic_functionality(t *testing.T) { args := []string{"../data/helloworld.c", "-o", "../data/hello"} exitCode := shared.Compile(args, "clang") if exitCode != 0 { t.Errorf("Compile of %v returned %v\n", args, exitCode) - } else { + } else if DEBUG { fmt.Println("Compiled OK") } args = []string{"get-bc", "-v", "../data/hello"} exitCode = shared.Extract(args) if exitCode != 0 { t.Errorf("Extraction of %v returned %v\n", args, exitCode) - } else { + } else if DEBUG { fmt.Println("Extraction OK") } } @@ -30,27 +34,27 @@ func Test_more_functionality(t *testing.T) { exitCode := shared.Compile(args, "clang") if exitCode != 0 { t.Errorf("Compile of %v returned %v\n", args, exitCode) - } else { + } else if DEBUG { fmt.Println("Compiled OK") } ok, err := shared.IsObjectFileForOS(objectFile, runtime.GOOS) if !ok { t.Errorf("isObjectFileForOS(%v, %v) = %v (err = %v)\n", objectFile, runtime.GOOS, ok, err) - } else { + } else if DEBUG { fmt.Printf("isObjectFileForOS(%v, %v) = %v\n", objectFile, runtime.GOOS, ok) } args = []string{objectFile, "-o", "../data/bhello"} exitCode = shared.Compile(args, "clang") if exitCode != 0 { t.Errorf("Compile of %v returned %v\n", args, exitCode) - } else { + } else if DEBUG { fmt.Println("Compiled OK") } args = []string{"get-bc", "-v", "../data/bhello"} exitCode = shared.Extract(args) if exitCode != 0 { t.Errorf("Extraction of %v returned %v\n", args, exitCode) - } else { + } else if DEBUG { fmt.Println("Extraction OK") } } @@ -64,39 +68,123 @@ func Test_obscure_functionality(t *testing.T) { exitCode := shared.Compile(args, "clang") if exitCode != 0 { t.Errorf("Compile of %v returned %v\n", args, exitCode) - } else { + } else if DEBUG { fmt.Println("Compiled OK") } ok, err := shared.IsObjectFileForOS(sourceFile, opSys) if ok { t.Errorf("isObjectFileForOS(%v, %v) = %v\n", sourceFile, opSys, ok) - } else { + } else if DEBUG { fmt.Printf("isObjectFileForOS(%v, %v) = %v (err = %v)\n", sourceFile, opSys, ok, err) } ok, err = shared.IsObjectFileForOS(objectFile, opSys) if !ok { t.Errorf("isObjectFileForOS(%v, %v) = %v (err = %v)\n", objectFile, opSys, ok, err) - } else { + } else if DEBUG { fmt.Printf("isObjectFileForOS(%v, %v) = %v\n", objectFile, opSys, ok) } args = []string{objectFile, "-o", exeFile} exitCode = shared.Compile(args, "clang") if exitCode != 0 { t.Errorf("Compile of %v returned %v\n", args, exitCode) - } else { + } else if DEBUG { fmt.Println("Compiled OK") } ok, err = shared.IsObjectFileForOS(exeFile, opSys) if ok { t.Errorf("isObjectFileForOS(%v, %v) = %v\n", exeFile, opSys, ok) - } else { + } else if DEBUG { fmt.Printf("isObjectFileForOS(%v, %v) = %v (err = %v)\n", exeFile, opSys, ok, err) } args = []string{"get-bc", "-v", exeFile} exitCode = shared.Extract(args) if exitCode != 0 { t.Errorf("Extraction of %v returned %v\n", args, exitCode) - } else { + } else if DEBUG { fmt.Println("Extraction OK") } } + +func Test_file_type(t *testing.T) { + fictionalFile := "HopefullyThereIsNotAFileCalledThisNearBy.txt" + dataDir := "../data" + sourceFile := "../data/helloworld.c" + objectFile := "../data/bhello.notanextensionthatwerecognize" + exeFile := "../data/bhello" + + var binaryFileType shared.BinaryType + binaryFileType = shared.GetBinaryType(fictionalFile) + + if binaryFileType != shared.BinaryUnknown { + t.Errorf("GetBinaryType(%v) = %v\n", fictionalFile, binaryFileType) + } else if DEBUG { + fmt.Printf("GetBinaryType(%v) = %v\n", fictionalFile, binaryFileType) + } + + binaryFileType = shared.GetBinaryType(dataDir) + + if binaryFileType != shared.BinaryUnknown { + t.Errorf("GetBinaryType(%v) = %v\n", dataDir, binaryFileType) + } else if DEBUG { + fmt.Printf("GetBinaryType(%v) = %v\n", dataDir, binaryFileType) + } + + binaryFileType = shared.GetBinaryType(sourceFile) + if binaryFileType != shared.BinaryUnknown { + t.Errorf("GetBinaryType(%v) = %v\n", sourceFile, binaryFileType) + } else if DEBUG { + fmt.Printf("GetBinaryType(%v) = %v\n", sourceFile, binaryFileType) + } + + binaryFileType = shared.GetBinaryType(objectFile) + if binaryFileType != shared.BinaryObject { + t.Errorf("GetBinaryType(%v) = %v\n", objectFile, binaryFileType) + } else if DEBUG { + fmt.Printf("GetBinaryType(%v) = %v\n", objectFile, binaryFileType) + } + + binaryFileType = shared.GetBinaryType(exeFile) + if binaryFileType != shared.BinaryExecutable { + t.Errorf("GetBinaryType(%v) = %v\n", exeFile, binaryFileType) + } else if DEBUG { + fmt.Printf("GetBinaryType(%v) = %v\n", exeFile, binaryFileType) + } + + var plain bool + plain = shared.IsPlainFile(fictionalFile) + + if plain { + t.Errorf("shared.IsPlainFile(%v) returned %v\n", fictionalFile, plain) + } else if DEBUG { + fmt.Printf("shared.IsPlainFile(%v) returned %v\n", fictionalFile, plain) + } + + plain = shared.IsPlainFile(dataDir) + if plain { + t.Errorf("shared.IsPlainFile(%v) returned %v\n", dataDir, plain) + } else if DEBUG { + fmt.Printf("shared.IsPlainFile(%v) returned %v\n", dataDir, plain) + } + + plain = shared.IsPlainFile(sourceFile) + if !plain { + t.Errorf("shared.IsPlainFile(%v) returned %v\n", sourceFile, plain) + } else if DEBUG { + fmt.Printf("shared.IsPlainFile(%v) returned %v\n", sourceFile, plain) + } + + plain = shared.IsPlainFile(objectFile) + if !plain { + t.Errorf("shared.IsPlainFile(%v) returned %v\n", objectFile, plain) + } else if DEBUG { + fmt.Printf("shared.IsPlainFile(%v) returned %v\n", objectFile, plain) + } + + plain = shared.IsPlainFile(exeFile) + if !plain { + t.Errorf("shared.IsPlainFile(%v) returned %v\n", exeFile, plain) + } else if DEBUG { + fmt.Printf("shared.IsPlainFile(%v) returned %v\n", exeFile, plain) + } + +}