Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion cmd/fyne/internal/commands/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,12 @@ func (b *Builder) applyCAndLDFlags(env *[]string, os string) {
}

arch := targetArch()
cflagsHardening := hardeningCFlagsLookup(ccVersion(), os, arch)
ccVer, err := ccVersionAndDefaultFlags()
if err != nil {
fyne.LogError("failed to get compiler version and default flags", err)
ccVer = ccVersion()
}
cflagsHardening := hardeningCFlagsLookup(ccVer, os, arch)
if cflagsHardening != "" {
cflags = append(cflags, cflagsHardening)
}
Expand Down
84 changes: 80 additions & 4 deletions cmd/fyne/internal/commands/hardening_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package commands
import (
"os"
"os/exec"
"regexp"
"strings"

"golang.org/x/mod/semver"

Expand All @@ -28,12 +30,16 @@ var hardeningFlagsTable = []hardeningFlags{
//revive:enable:add-constant
}

func ccVersion() string {
func ccProg() string {
cc, ok := os.LookupEnv("CC")
if !ok {
cc = "cc"
return "cc"
}
return cc
}

func ccVersion() string {
cc := ccProg()
cmd := exec.Command(cc, "--version")
out, err := cmd.CombinedOutput()
if err != nil {
Expand All @@ -43,11 +49,60 @@ func ccVersion() string {
return string(out)
}

const ccTestFlagsCode = `
int main(int argc, char **argv) {
return 0;
}
`

func ccVersionAndDefaultFlags() (string, error) {
f, err := os.CreateTemp("", "fyne-check-cc-*.c")
if err != nil {
return "", err
}
inFile := f.Name()
outFile := strings.TrimSuffix(inFile, ".c")
defer func() {
_ = os.Remove(inFile)
_ = os.Remove(outFile)
}()

if _, err := f.WriteString(ccTestFlagsCode); err != nil {
return "", err
}
if err := f.Close(); err != nil {
return "", err
}

cmd := exec.Command(ccProg(), "-Q", "-v", "-o", outFile, inFile)
out, err := cmd.CombinedOutput()
if err != nil {
return "", err
}

return string(out), nil
}

func gccDefaultFlags(s string) []string {
re := regexp.MustCompile(`(?ms)^options enabled:((?:\s+\S+\n?)*)`)
m := re.FindStringSubmatch(s)
if len(m) < 2 {
return nil
}
return strings.Fields(m[1])
}

func hardeningCFlagsLookup(out, goos, arch string) string {
info, err := util.DetectCompiler(out, goos)
if err != nil {
return ""
}

var defs []string
if info.Name == "gcc" {
defs = gccDefaultFlags(out)
}

for _, e := range hardeningFlagsTable {
//revive:disable:add-constant
if e.cc != "*" && e.cc != info.Name {
Expand All @@ -65,8 +120,29 @@ func hardeningCFlagsLookup(out, goos, arch string) string {
if e.maxVer != "*" && semver.Compare("v"+info.Version, "v"+e.maxVer) > 0 {
continue
}
return e.cflags
return dedupeFlags(e.cflags, defs)
//revive:enable:add-constant
}
return hardeningCFLAGS
return dedupeFlags(hardeningCFLAGS, defs)
}

func dedupeFlags(s string, defs []string) string {
if len(defs) == 0 {
return s
}

seen := make(map[string]struct{})
for _, flag := range defs {
seen[flag] = struct{}{}
}

r := []string{}
for _, flag := range strings.Fields(s) {
if _, found := seen[flag]; found {
continue
}
seen[flag] = struct{}{}
r = append(r, flag)
}
return util.JoinSpace(r)
}
28 changes: 28 additions & 0 deletions cmd/fyne/internal/commands/hardening_flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,31 @@ func Test_hardeningCFlagsLookup(t *testing.T) {
// darwin/arm64 doesn't do -fcf-protection
assert.Equal(t, "-D_FORTIFY_SOURCE=3 -fstack-protector-strong", hardeningCFlagsLookup("Apple clang version 17.0.0 (clang-1700.0.13.5)", "darwin", "arm64"))
}

func Test_gccDefaultFlags(t *testing.T) {
in := `
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
options passed: -v -auxbase
options enabled: -falign-loops -fargument-alias
-fasynchronous-unwind-tables -fbranch-count-reg -fcommon -fearly-inlining
-feliminate-unused-debug-types -femit-class-debug-always -ffunction-cse
-fgcse-lm -finline-functions-called-once -fivopts -fkeep-static-consts
-fleading-underscore -fmath-errno -fmove-loop-invariants -fpeephole -fpic
-fpie -freg-struct-return -fsched-interblock -fsched-spec
-fsched-stalled-insns-dep -fshow-column -fsplit-ivs-in-unroller
-ftoplevel-reorder -ftrapping-math -ftree-loop-im -ftree-loop-ivcanon
-ftree-loop-optimize -ftree-vect-loop-version -funwind-tables
-fvar-tracking -m128bit-long-double -m64 -m80387
-maccumulate-outgoing-args -malign-stringops -mfancy-math-387
-mfp-ret-in-387 -mieee-fp -mmmx -mpush-args -mred-zone -msse -msse2
Compiler executable checksum: 1e242b9c9b970b8bb2f3765bba204637
`
x := gccDefaultFlags(in)
assert.NotNil(t, x)
}

func Test_ccVersionAndDefaultFlags(t *testing.T) {
s, err := ccVersionAndDefaultFlags()
assert.NoError(t, err)
assert.NotEmpty(t, s)
}
Loading