Show binary exports, entrypoint, and imports (#2626)

show binary exports, entrypoint, and imports for macho, elf, and pe formats

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Alex Goodman 2024-03-12 18:04:02 -04:00 committed by GitHub
parent 2e2a9377ea
commit 47fc909700
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 2959 additions and 48 deletions

View file

@ -39,8 +39,14 @@ jobs:
- name: Restore file executable test-fixture cache
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 #v4.0.1
with:
path: syft/file/cataloger/executable/test-fixtures/bin
key: ${{ runner.os }}-unit-file-executable-cache-${{ hashFiles( 'syft/file/cataloger/executable/test-fixtures/cache.fingerprint' ) }}
path: syft/file/cataloger/executable/test-fixtures/elf/bin
key: ${{ runner.os }}-unit-file-executable-elf-cache-${{ hashFiles( 'syft/file/cataloger/executable/test-fixtures/elf/cache.fingerprint' ) }}
- name: Restore file executable shared-info test-fixture cache
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 #v4.0.1
with:
path: syft/file/cataloger/executable/test-fixtures/shared-info/bin
key: ${{ runner.os }}-unit-file-executable-shared-info-cache-${{ hashFiles( 'syft/file/cataloger/executable/test-fixtures/shared-info/cache.fingerprint' ) }}
- name: Restore Java test-fixture cache
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 #v4.0.1

View file

@ -266,7 +266,8 @@ tasks:
desc: Generate test fixture fingerprints
generates:
- cmd/syft/internal/test/integration/test-fixtures/cache.fingerprint
- syft/file/cataloger/executable/test-fixtures/cache.fingerprint
- syft/file/cataloger/executable/test-fixtures/elf/cache.fingerprint
- syft/file/cataloger/executable/test-fixtures/shared-info/cache.fingerprint
- syft/pkg/cataloger/binary/test-fixtures/cache.fingerprint
- syft/pkg/cataloger/java/test-fixtures/java-builds/cache.fingerprint
- syft/pkg/cataloger/golang/test-fixtures/archs/binaries.fingerprint
@ -276,7 +277,8 @@ tasks:
- test/cli/test-fixtures/cache.fingerprint
cmds:
# for EXECUTABLE unit test fixtures
- "cd syft/file/cataloger/executable/test-fixtures && make cache.fingerprint"
- "cd syft/file/cataloger/executable/test-fixtures/elf && make cache.fingerprint"
- "cd syft/file/cataloger/executable/test-fixtures/shared-info && make cache.fingerprint"
# for IMAGE integration test fixtures
- "cd cmd/syft/internal/test/integration/test-fixtures && make cache.fingerprint"
# for BINARY unit test fixtures
@ -297,7 +299,8 @@ tasks:
fixtures:
desc: Generate test fixtures
cmds:
- "cd syft/file/cataloger/executable/test-fixtures && make"
- "cd syft/file/cataloger/executable/test-fixtures/elf && make"
- "cd syft/file/cataloger/executable/test-fixtures/shared-info && make"
- "cd syft/pkg/cataloger/java/test-fixtures/java-builds && make"
- "cd syft/pkg/cataloger/redhat/test-fixtures && make"
- "cd syft/pkg/cataloger/binary/test-fixtures && make"

View file

@ -3,5 +3,5 @@ package internal
const (
// JSONSchemaVersion is the current schema version output by the JSON encoder
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
JSONSchemaVersion = "16.0.4"
JSONSchemaVersion = "16.0.5"
)

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "anchore.io/schema/syft/json/16.0.4/document",
"$id": "anchore.io/schema/syft/json/16.0.5/document",
"$ref": "#/$defs/Document",
"$defs": {
"AlpmDbEntry": {
@ -681,13 +681,28 @@
"format": {
"type": "string"
},
"hasExports": {
"type": "boolean"
},
"hasEntrypoint": {
"type": "boolean"
},
"importedLibraries": {
"items": {
"type": "string"
},
"type": "array"
},
"elfSecurityFeatures": {
"$ref": "#/$defs/ELFSecurityFeatures"
}
},
"type": "object",
"required": [
"format"
"format",
"hasExports",
"hasEntrypoint",
"importedLibraries"
]
},
"File": {

View file

@ -155,13 +155,25 @@ func processExecutable(loc file.Location, reader unionreader.UnionReader) (*file
data.Format = format
securityFeatures, err := findSecurityFeatures(format, reader)
if err != nil {
log.WithFields("error", err).Tracef("unable to determine security features for %q", loc.RealPath)
return nil, nil
switch format {
case file.ELF:
if err := findELFFeatures(&data, reader); err != nil {
log.WithFields("error", err).Tracef("unable to determine ELF features for %q", loc.RealPath)
}
case file.PE:
if err := findPEFeatures(&data, reader); err != nil {
log.WithFields("error", err).Tracef("unable to determine PE features for %q", loc.RealPath)
}
case file.MachO:
if err := findMachoFeatures(&data, reader); err != nil {
log.WithFields("error", err).Tracef("unable to determine Macho features for %q", loc.RealPath)
}
}
data.SecurityFeatures = securityFeatures
// always allocate collections for presentation
if data.ImportedLibraries == nil {
data.ImportedLibraries = []string{}
}
return &data, nil
}
@ -230,18 +242,3 @@ func isPE(by []byte) bool {
func isELF(by []byte) bool {
return bytes.HasPrefix(by, []byte(elf.ELFMAG))
}
func findSecurityFeatures(format file.ExecutableFormat, reader unionreader.UnionReader) (*file.ELFSecurityFeatures, error) {
// TODO: add support for PE and MachO
switch format { //nolint: gocritic
case file.ELF:
return findELFSecurityFeatures(reader) //nolint: gocritic
case file.PE:
// return findPESecurityFeatures(reader)
return nil, nil
case file.MachO:
// return findMachOSecurityFeatures(reader)
return nil, nil
}
return nil, fmt.Errorf("unsupported executable format: %q", format)
}

View file

@ -12,13 +12,33 @@ import (
"github.com/anchore/syft/syft/internal/unionreader"
)
func findELFSecurityFeatures(reader unionreader.UnionReader) (*file.ELFSecurityFeatures, error) {
func findELFFeatures(data *file.Executable, reader unionreader.UnionReader) error {
f, err := elf.NewFile(reader)
if err != nil {
return nil, nil
return err
}
features := file.ELFSecurityFeatures{
libs, err := f.ImportedLibraries()
if err != nil {
// TODO: known-unknowns
log.WithFields("error", err).Trace("unable to read imported libraries from elf file")
libs = nil
}
if libs == nil {
libs = []string{}
}
data.ImportedLibraries = libs
data.ELFSecurityFeatures = findELFSecurityFeatures(f)
data.HasEntrypoint = elfHasEntrypoint(f)
data.HasExports = elfHasExports(f)
return nil
}
func findELFSecurityFeatures(f *elf.File) *file.ELFSecurityFeatures {
return &file.ELFSecurityFeatures{
SymbolTableStripped: isElfSymbolTableStripped(f),
StackCanary: checkElfStackCanary(f),
NoExecutable: checkElfNXProtection(f),
@ -29,8 +49,6 @@ func findELFSecurityFeatures(reader unionreader.UnionReader) (*file.ELFSecurityF
LlvmControlFlowIntegrity: checkLLVMControlFlowIntegrity(f),
ClangFortifySource: checkClangFortifySource(f),
}
return &features, nil
}
func isElfSymbolTableStripped(file *elf.File) bool {
@ -219,3 +237,34 @@ func checkClangFortifySource(file *elf.File) *bool {
}
return boolRef(false)
}
func elfHasEntrypoint(f *elf.File) bool {
// this is akin to
// readelf -h ./path/to/bin | grep "Entry point address"
return f.Entry > 0
}
func elfHasExports(f *elf.File) bool {
// this is akin to:
// nm -D --defined-only ./path/to/bin | grep ' T \| W \| B '
// where:
// T - symbol in the text section
// W - weak symbol that might be overwritten
// B - variable located in the uninitialized data section
// really anything that is not marked with 'U' (undefined) is considered an export.
symbols, err := f.DynamicSymbols()
if err != nil {
// TODO: known-unknowns?
return false
}
for _, s := range symbols {
// check if the section is SHN_UNDEF, which is the "U" output type from "nm -D" meaning that the symbol
// is undefined, meaning it is not an export. Any entry that is not undefined is considered an export.
if s.Section != elf.SHN_UNDEF {
return true
}
}
return false
}

View file

@ -1,11 +1,13 @@
package executable
import (
"debug/elf"
"os"
"path/filepath"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/file"
@ -16,7 +18,7 @@ func Test_findELFSecurityFeatures(t *testing.T) {
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
t.Helper()
f, err := os.Open(filepath.Join("test-fixtures", fixture))
f, err := os.Open(filepath.Join("test-fixtures/elf", fixture))
require.NoError(t, err)
return f
}
@ -26,7 +28,6 @@ func Test_findELFSecurityFeatures(t *testing.T) {
fixture string
want *file.ELFSecurityFeatures
wantStripped bool
wantErr require.ErrorAssertionFunc
}{
{
name: "detect canary",
@ -145,14 +146,10 @@ func Test_findELFSecurityFeatures(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.wantErr == nil {
tt.wantErr = require.NoError
}
got, err := findELFSecurityFeatures(readerForFixture(t, tt.fixture))
tt.wantErr(t, err)
if err != nil {
return
}
f, err := elf.NewFile(readerForFixture(t, tt.fixture))
require.NoError(t, err)
got := findELFSecurityFeatures(f)
if d := cmp.Diff(tt.want, got); d != "" {
t.Errorf("findELFSecurityFeatures() mismatch (-want +got):\n%s", d)
@ -160,3 +157,70 @@ func Test_findELFSecurityFeatures(t *testing.T) {
})
}
}
func Test_elfHasEntrypoint(t *testing.T) {
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
t.Helper()
f, err := os.Open(filepath.Join("test-fixtures/shared-info", fixture))
require.NoError(t, err)
return f
}
tests := []struct {
name string
fixture string
want bool
}{
{
name: "shared lib",
fixture: "bin/libhello.so",
want: false,
},
{
name: "application",
fixture: "bin/hello_linux",
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f, err := elf.NewFile(readerForFixture(t, tt.fixture))
require.NoError(t, err)
assert.Equal(t, tt.want, elfHasEntrypoint(f))
})
}
}
func Test_elfHasExports(t *testing.T) {
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
t.Helper()
f, err := os.Open(filepath.Join("test-fixtures/shared-info", fixture))
require.NoError(t, err)
return f
}
tests := []struct {
name string
fixture string
want bool
}{
{
name: "shared lib",
fixture: "bin/libhello.so",
want: true,
},
{
name: "application",
fixture: "bin/hello_linux",
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f, err := elf.NewFile(readerForFixture(t, tt.fixture))
require.NoError(t, err)
assert.Equal(t, tt.want, elfHasExports(f))
})
}
}

View file

@ -0,0 +1,92 @@
package executable
import (
"debug/macho"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/internal/unionreader"
)
// source http://www.cilinder.be/docs/next/NeXTStep/3.3/nd/DevTools/14_MachO/MachO.htmld/index.html
const (
machoNPExt uint8 = 0x10 /* N_PEXT: private external symbol bit */
machoNExt uint8 = 0x01 /* N_EXT: external symbol bit, set for external symbols */
// > #define LC_REQ_DYLD 0x80000000
// > #define LC_MAIN (0x28|LC_REQ_DYLD) /* replacement for LC_UNIXTHREAD */
lcMain = 0x28 | 0x80000000
)
func findMachoFeatures(data *file.Executable, reader unionreader.UnionReader) error {
// TODO: support security features
// TODO: support multi-architecture binaries
f, err := macho.NewFile(reader)
if err != nil {
return err
}
libs, err := f.ImportedLibraries()
if err != nil {
return err
}
data.ImportedLibraries = libs
data.HasEntrypoint = machoHasEntrypoint(f)
data.HasExports = machoHasExports(f)
return nil
}
func machoHasEntrypoint(f *macho.File) bool {
// derived from struct entry_point_command found from which explicitly calls out LC_MAIN:
// https://opensource.apple.com/source/xnu/xnu-2050.18.24/EXTERNAL_HEADERS/mach-o/loader.h
// we need to look for both LC_MAIN and LC_UNIXTHREAD commands to determine if the file is an executable
//
// this is akin to:
// otool -l ./path/to/bin | grep -A4 LC_MAIN
// otool -l ./path/to/bin | grep -A4 LC_UNIXTHREAD
for _, l := range f.Loads {
data := l.Raw()
cmd := f.ByteOrder.Uint32(data)
if macho.LoadCmd(cmd) == macho.LoadCmdUnixThread || macho.LoadCmd(cmd) == lcMain {
return true
}
}
return false
}
func machoHasExports(f *macho.File) bool {
for _, sym := range f.Symtab.Syms {
// look for symbols that are:
// - not private and are external
// - do not have an N_TYPE value of N_UNDF (undefined symbol)
//
// here's the bit layout for the n_type field:
// 0000 0000
// ─┬─│ ─┬─│
// │ │ │ └─ N_EXT (external symbol)
// │ │ └─ N_TYPE (N_UNDF, N_ABS, N_SECT, N_PBUD, N_INDR)
// │ └─ N_PEXT (private external symbol)
// └─ N_STAB (debugging symbol)
//
isExternal := sym.Type&machoNExt == machoNExt
isPrivate := sym.Type&machoNPExt == machoNPExt
nTypeIsUndefined := sym.Type&0x0e == 0
if isExternal && !isPrivate {
if sym.Name == "_main" || sym.Name == "__mh_execute_header" {
// ...however there are some symbols that are not exported but are still important
// for debugging or as an entrypoint, so we need to explicitly check for them
continue
}
if nTypeIsUndefined {
continue
}
// we have a symbol that is not private and is external
// and is not undefined, so it is an export
return true
}
}
return false
}

View file

@ -0,0 +1,80 @@
package executable
import (
"debug/macho"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/internal/unionreader"
)
func Test_machoHasEntrypoint(t *testing.T) {
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
t.Helper()
f, err := os.Open(filepath.Join("test-fixtures/shared-info", fixture))
require.NoError(t, err)
return f
}
tests := []struct {
name string
fixture string
want bool
}{
{
name: "shared lib",
fixture: "bin/libhello.dylib",
want: false,
},
{
name: "application",
fixture: "bin/hello_mac",
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f, err := macho.NewFile(readerForFixture(t, tt.fixture))
require.NoError(t, err)
assert.Equal(t, tt.want, machoHasEntrypoint(f))
})
}
}
func Test_machoHasExports(t *testing.T) {
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
t.Helper()
f, err := os.Open(filepath.Join("test-fixtures/shared-info", fixture))
require.NoError(t, err)
return f
}
tests := []struct {
name string
fixture string
want bool
}{
{
name: "shared lib",
fixture: "bin/libhello.dylib",
want: true,
},
{
name: "application",
fixture: "bin/hello_mac",
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f, err := macho.NewFile(readerForFixture(t, tt.fixture))
require.NoError(t, err)
assert.Equal(t, tt.want, machoHasExports(f))
})
}
}

View file

@ -0,0 +1,84 @@
package executable
import (
"debug/pe"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/internal/unionreader"
)
func findPEFeatures(data *file.Executable, reader unionreader.UnionReader) error {
// TODO: support security features
f, err := pe.NewFile(reader)
if err != nil {
return err
}
libs, err := f.ImportedLibraries()
if err != nil {
return err
}
data.ImportedLibraries = libs
data.HasEntrypoint = peHasEntrypoint(f)
data.HasExports = peHasExports(f)
return nil
}
var (
windowsExeEntrypoints = strset.New("main", "WinMain", "wWinMain")
windowsDllEntrypoints = strset.New("DllMain", "_DllMainCRTStartup@12", "CRT_INIT")
)
func peHasEntrypoint(f *pe.File) bool {
// DLLs can have entrypoints, but they are not "executables" in the traditional sense,
// but instead point to an initialization function (DLLMain).
// The PE format does not require an entrypoint, so it is possible to not have one, however,
// the microsoft C runtime does: https://learn.microsoft.com/en-US/troubleshoot/developer/visualstudio/cpp/libraries/use-c-run-time
//
// > When building a DLL which uses any of the C Run-time libraries, in order to ensure that the CRT is properly initialized, either
// > 1. the initialization function must be named DllMain() and the entry point must be specified with the linker option -entry:_DllMainCRTStartup@12 - or -
// > 2. the DLL's entry point must explicitly call CRT_INIT() on process attach and process detach
//
// This isn't really helpful from a user perspective when it comes to indicating if there is an entrypoint or not
// since it will always effectively be true for DLLs! All DLLs and Executables (aka "modules") have a single
// entrypoint, _GetPEImageBase, but we're more interested in the logical idea of an entrypoint.
// See https://learn.microsoft.com/en-us/windows/win32/psapi/module-information for more details.
var hasLibEntrypoint, hasExeEntrypoint bool
for _, s := range f.Symbols {
if windowsExeEntrypoints.Has(s.Name) {
hasExeEntrypoint = true
}
if windowsDllEntrypoints.Has(s.Name) {
hasLibEntrypoint = true
}
}
switch v := f.OptionalHeader.(type) {
case *pe.OptionalHeader32:
return v.AddressOfEntryPoint > 0 && !hasLibEntrypoint && hasExeEntrypoint
case *pe.OptionalHeader64:
return v.AddressOfEntryPoint > 0 && !hasLibEntrypoint && hasExeEntrypoint
}
return false
}
func peHasExports(f *pe.File) bool {
if f.OptionalHeader == nil {
return false
}
switch v := f.OptionalHeader.(type) {
case *pe.OptionalHeader32:
return v.DataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].Size > 0
case *pe.OptionalHeader64:
return v.DataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].Size > 0
}
return false
}

View file

@ -0,0 +1,80 @@
package executable
import (
"debug/pe"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/internal/unionreader"
)
func Test_peHasEntrypoint(t *testing.T) {
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
t.Helper()
f, err := os.Open(filepath.Join("test-fixtures/shared-info", fixture))
require.NoError(t, err)
return f
}
tests := []struct {
name string
fixture string
want bool
}{
{
name: "shared lib",
fixture: "bin/hello.dll", // though this is a shared lib, it has an entrypoint (DLLMain)
want: false,
},
{
name: "application",
fixture: "bin/hello.exe",
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f, err := pe.NewFile(readerForFixture(t, tt.fixture))
require.NoError(t, err)
assert.Equal(t, tt.want, peHasEntrypoint(f))
})
}
}
func Test_peHasExports(t *testing.T) {
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
t.Helper()
f, err := os.Open(filepath.Join("test-fixtures/shared-info", fixture))
require.NoError(t, err)
return f
}
tests := []struct {
name string
fixture string
want bool
}{
{
name: "shared lib",
fixture: "bin/hello.dll",
want: true,
},
{
name: "application",
fixture: "bin/hello.exe",
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f, err := pe.NewFile(readerForFixture(t, tt.fixture))
require.NoError(t, err)
assert.Equal(t, tt.want, peHasExports(f))
})
}
}

View file

@ -0,0 +1,3 @@
../bin
actual_verify
Dockerfile.sha256

View file

@ -0,0 +1 @@
bin

View file

@ -0,0 +1,16 @@
# syntax=docker/dockerfile:1
ARG OSXCROSS_VERSION=13.1
ARG BUILDPLATFORM=linux/amd64
FROM --platform=$BUILDPLATFORM crazymax/osxcross:${OSXCROSS_VERSION}-ubuntu AS osxcross
FROM --platform=$BUILDPLATFORM ubuntu:22.04
RUN apt update -y && apt install -y \
curl wget \
make gcc clang \
git mingw-w64
COPY --from=osxcross /osxcross /osxcross
ENV PATH="/osxcross/bin:$PATH"
ENV LD_LIBRARY_PATH="/osxcross/lib:$LD_LIBRARY_PATH"

View file

@ -0,0 +1,25 @@
BIN=./bin
TOOL_IMAGE=localhost/syft-shared-info-build-tools:latest
VERIFY_FILE=actual_verify
all: build
tools-check:
@sha256sum -c Dockerfile.sha256 || (echo "Tools Dockerfile has changed" && exit 1)
tools:
@(docker inspect $(TOOL_IMAGE) > /dev/null && make tools-check) || (docker build -t $(TOOL_IMAGE) . && sha256sum Dockerfile > Dockerfile.sha256)
build: tools
mkdir -p $(BIN)
docker run --platform linux/amd64 -i -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) make
debug:
docker run --platform linux/amd64 -i --rm -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) bash
cache.fingerprint:
@find project Dockerfile Makefile -type f -exec md5sum {} + | awk '{print $1}' | sort | tee cache.fingerprint
clean:
rm -f $(BIN)/*
.PHONY: build verify debug build-image build-bins clean dockerfile-check cache.fingerprint

View file

@ -0,0 +1,10 @@
# invoke all make files in subdirectories
.PHONY: all hello libhello
all: hello libhello
hello:
$(MAKE) -C hello
libhello:
$(MAKE) -C libhello

View file

@ -0,0 +1,23 @@
.PHONY: all linux windows mac
BIN=../../bin
all: $(BIN)/hello_linux $(BIN)/hello.exe $(BIN)/hello_mac
linux: $(BIN)/libhello.so
windows: $(BIN)/libhello.dll
mac: $(BIN)/libhello.dylib
$(BIN)/hello_linux:
gcc hello.c -o $(BIN)/hello_linux
$(BIN)/hello.exe:
x86_64-w64-mingw32-gcc hello.c -o $(BIN)/hello.exe
$(BIN)/hello_mac:
o64-clang hello.c -o $(BIN)/hello_mac
clean:
rm -f $(BIN)/hello_linux $(BIN)/hello.exe $(BIN)/hello_mac

View file

@ -0,0 +1,6 @@
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}

View file

@ -0,0 +1,23 @@
.PHONY: all linux windows mac
BIN=../../bin
all: $(BIN)/libhello.so $(BIN)/libhello.dll $(BIN)/libhello.dylib
linux: $(BIN)/libhello.so
windows: $(BIN)/libhello.dll
mac: $(BIN)/libhello.dylib
$(BIN)/libhello.so:
gcc -shared -fPIC -o $(BIN)/libhello.so hello.c
$(BIN)/libhello.dll:
x86_64-w64-mingw32-gcc -shared -o $(BIN)/hello.dll hello.c -Wl,--out-implib,$(BIN)/libhello.a
$(BIN)/libhello.dylib:
o64-clang -dynamiclib -o $(BIN)/libhello.dylib hello.c
clean:
rm -f $(BIN)/libhello.so $(BIN)/hello.dll $(BIN)/libhello.dylib $(BIN)/libhello.a

View file

@ -0,0 +1,5 @@
#include <stdio.h>
void hello() {
printf("Hello from shared library!\n");
}

View file

@ -0,0 +1,6 @@
#ifndef HELLO_H
#define HELLO_H
void hello();
#endif

View file

@ -19,7 +19,10 @@ type Executable struct {
// Format denotes either ELF, Mach-O, or PE
Format ExecutableFormat `json:"format" yaml:"format" mapstructure:"format"`
SecurityFeatures *ELFSecurityFeatures `json:"elfSecurityFeatures,omitempty" yaml:"elfSecurityFeatures" mapstructure:"elfSecurityFeatures"`
HasExports bool `json:"hasExports" yaml:"hasExports" mapstructure:"hasExports"`
HasEntrypoint bool `json:"hasEntrypoint" yaml:"hasEntrypoint" mapstructure:"hasEntrypoint"`
ImportedLibraries []string `json:"importedLibraries" yaml:"importedLibraries" mapstructure:"importedLibraries"`
ELFSecurityFeatures *ELFSecurityFeatures `json:"elfSecurityFeatures,omitempty" yaml:"elfSecurityFeatures" mapstructure:"elfSecurityFeatures"`
}
type ELFSecurityFeatures struct {

View file

@ -224,7 +224,7 @@ func Test_encodeDecodeFileMetadata(t *testing.T) {
Executables: map[file.Coordinates]file.Executable{
c: {
Format: file.ELF,
SecurityFeatures: &file.ELFSecurityFeatures{
ELFSecurityFeatures: &file.ELFSecurityFeatures{
SymbolTableStripped: false,
StackCanary: boolRef(true),
NoExecutable: false,

View file

@ -288,7 +288,7 @@ func Test_toSyftFiles(t *testing.T) {
},
Executable: &file.Executable{
Format: file.ELF,
SecurityFeatures: &file.ELFSecurityFeatures{
ELFSecurityFeatures: &file.ELFSecurityFeatures{
SymbolTableStripped: false,
StackCanary: boolRef(true),
NoExecutable: false,
@ -329,7 +329,7 @@ func Test_toSyftFiles(t *testing.T) {
Executables: map[file.Coordinates]file.Executable{
coord: {
Format: file.ELF,
SecurityFeatures: &file.ELFSecurityFeatures{
ELFSecurityFeatures: &file.ELFSecurityFeatures{
SymbolTableStripped: false,
StackCanary: boolRef(true),
NoExecutable: false,

View file

@ -1,8 +1,9 @@
package dotnet
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_getDepsJSONFilePrefix(t *testing.T) {