Linux/Unix internals: x86_64 CFLAGS/CXXFLAGS

Published: December 29th, 2024

Updated: January 5th, 2025

As I’ve been using Gentoo, I’ve become curious about what kinds of interesting build flags other systems use to compile their binaries on a global level, as well as what configure-time flags are set on GCC and Clang. This article collects these flags for some distributions to make them easier for me to track down and compare in the future.

Because this is a huge rabbit hole, there are some things I won’t be doing in the interest of time. They’re listed in the Caveats section.

Even though this isn’t perfect and doesn’t list every operating system I’m interested in having these details for, I feel I should share what I have so far.

Some relevant documentation can be found in these places:

If there’s something incorrect here, please let me know, either by contacting me directly or by opening an issue on my website’s GitHub repository (the latter would be preferred, but either is fine).

I’m definitely open to pull requests, especially to fix issues. Pull requests to add missing details to anything listed, or to add new items along with details, are welcome as well. Though it’s unlikely that I’ll add in forks of systems already mentioned unless they differ enough in content to be interesting.

Table of contents

  1. Arch
  2. Alpine
  3. Clear Linux
  4. Debian
  5. Fedora
  6. Gentoo
  7. NixOS
  8. OpenBSD
  9. OpenSUSE
  10. Solus
  11. Ubuntu
  12. Void
  13. Caveats
  14. Areas for improvement
  15. Other resources

Arch

According to this poster, the build flags can be found in the x86_64.conf file in the devtools package.

CFLAGS:

-march=x86-64
-mtune=generic
-O2
-pipe
-fno-plt
-fexceptions
-Wp,-D_FORTIFY_SOURCE=3
-Wformat
-Werror=format-security
-fstack-clash-protection
-fcf-protection
-fno-omit-frame-pointer
-mno-omit-leaf-frame-pointer

CXXFLAGS:

$CFLAGS
-Wp,-D_GLIBCXX_ASSERTIONS

LDFLAGS:

-Wl,-O1
-Wl,--sort-common
-Wl,--as-needed
-Wl,-z,relro
-Wl,-z,now
-Wl,-z,pack-relative-relocs

LTOFLAGS:

-flto=auto

Here are the configure-time GCC flags for Arch.

Here are the configure-time Clang flags for Arch.

Here are the configure-time LLVM flags for Arch.

Alpine

Alpine compiles packages using abuild. The default.conf file has some of the compiler flags used for the distribution.

CFLAGS:

-Os
-fstack-clash-protection
-Wformat
-Werror=format-security

CXXFLAGS:

-Os
-fstack-clash-protection
-Wformat
-Werror=format-security
-D_GLIBCXX_ASSERTIONS=1
-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS=1
-D_LIBCPP_ENABLE_HARDENED_MODE=1

LDFLAGS:

-Wl,--as-needed,-O1,--sort-common

GCC configuration for Alpine.

Clang configuration for Alpine.

LLVM configuration for Alpine.

This discussion has some interesting details.

Clear Linux

The performance section in the documentation lays everything out nicely.

For x86_64, here is what that looks like:

-O2
-g
-feliminate-unused-debug-types
-pipe
-Wall
-Wp,-D_FORTIFY_SOURCE=2
-fexceptions
-fstack-protector
--param=ssp-buffer-size=64
-Wformat
-Wformat-security
-Wl,-z,now,-z,relro,-z,max-page-size=0x4000,-z,separate-code
-Wno-error
-ftree-vectorize
-ftree-slp-vectorize
-Wl,--enable-new-dtags
-Wl,--build-id=sha1
-ftrivial-auto-var-init=zero
-mrelax-cmpxchg-loop
-m64
-march=westmere
-mtune=skylake-avx512
-fasynchronous-unwind-tables
-Wp,-D_REENTRANT

There are some interesting things in here. Two flags in particular I find really interesting: -ftrivial-auto-var-init=zero and -mrelax-cmpxchg-loop.

-ftrivial-auto-var-init is a security-related flag. I’ve heard that both ChromiumOS and Android use that flag in some form. Gentoo may add it to their hardened builds, at least this is what this open bug would suggest.

-mrelax-cmpxchg-loop relaxes spin loops in certain conditions, benefiting thread synchronization. Here is the GCC bug where it’s discussed. Intel discusses it a little here.

The question of compiler configure-time flags for Clear Linux gets complicated, because they have separate GCC builds for AVX2 and AVX512. Here are the configure-time flags for the main GCC build.

Similar situation for Clang. Here are the Clear Linux configuration-time flags for Clang/LLVM.

I’m breaking a rule I made for myself a little bit (no per-package CFLAGS/CXXFLAGS reviewing) to discuss some interesting tweaks Clear Linux does. autospec is at the heart of this. A couple of interesting things about autospec and the packages in clearlinux-pkgs:

There’s a lot to learn from this distribution.

Debian

dpkg-buildflags is a Perl script, provided by the dpkg-dev package. A lot of the heavy lifting is done by the libraries sourced by the script, which are in /usr/share/perl5/Dpkg. Those libraries are provided by the libdpkg-perl package (a dependency of dpkg-dev).

Debian 12 (Bookworm).

$ dpkg-buildflags --query
Vendor: Debian
Environment:

Area: future
Features:
 lfs=no
Builtins:

Area: hardening
Features:
 bindnow=no
 format=yes
 fortify=yes
 pie=yes
 relro=yes
 stackprotector=yes
 stackprotectorstrong=yes
Builtins:
 pie=yes

Area: optimize
Features:
 lto=no
Builtins:

Area: qa
Features:
 bug=no
 canary=no
Builtins:

Area: reproducible
Features:
 fixdebugpath=yes
 fixfilepath=yes
 timeless=yes
Builtins:

Area: sanitize
Features:
 address=no
 leak=no
 thread=no
 undefined=no
Builtins:

Flag: ASFLAGS
Value: 
Origin: vendor

Flag: CFLAGS
Value: -g -O2 -ffile-prefix-map=/home/user=. -fstack-protector-strong -Wformat -Werror=format-security
Origin: vendor

Flag: CPPFLAGS
Value: -Wdate-time -D_FORTIFY_SOURCE=2
Origin: vendor

Flag: CXXFLAGS
Value: -g -O2 -ffile-prefix-map=/home/user=. -fstack-protector-strong -Wformat -Werror=format-security
Origin: vendor

Flag: DFLAGS
Value: -frelease
Origin: vendor

Flag: FCFLAGS
Value: -g -O2 -ffile-prefix-map=/home/user=. -fstack-protector-strong
Origin: vendor

Flag: FFLAGS
Value: -g -O2 -ffile-prefix-map=/home/user=. -fstack-protector-strong
Origin: vendor

Flag: GCJFLAGS
Value: -g -O2 -ffile-prefix-map=/home/user=. -fstack-protector-strong
Origin: vendor

Flag: LDFLAGS
Value: -Wl,-z,relro
Origin: vendor

Flag: OBJCFLAGS
Value: -g -O2 -ffile-prefix-map=/home/user=. -fstack-protector-strong -Wformat -Werror=format-security
Origin: vendor

Flag: OBJCXXFLAGS
Value: -g -O2 -ffile-prefix-map=/home/user=. -fstack-protector-strong -Wformat -Werror=format-security
Origin: vendor

Debian’s GCC configure-time flags are in this file, stored in the CONFARGS variable.

Debian’s Clang/LLVM configure-time flags.

Fedora

Obtained via RPM macros.

Build flags docs here.

These commands were run in a Fedora 40 virtual machine.

$ rpm --eval "%{optflags}" | tr ' ' '\n' | grep -v '^$'
-O2
-flto=auto
-ffat-lto-objects
-fexceptions
-g
-grecord-gcc-switches
-pipe
-Wall
-Wno-complain-wrong-lang
-Werror=format-security
-Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3
-Wp,-D_GLIBCXX_ASSERTIONS
-specs=/usr/lib/rpm/redhat/redhat-hardened-cc1
-fstack-protector-strong
-specs=/usr/lib/rpm/redhat/redhat-annobin-cc1
-m64
-march=x86-64
-mtune=generic
-fasynchronous-unwind-tables
-fstack-clash-protection
-fcf-protection
-fno-omit-frame-pointer
-mno-omit-leaf-frame-pointer
$ rpm --eval "%{build_ldflags}" | tr ' ' '\n' | grep -v '^$'
-Wl,-z,relro
-Wl,--as-needed
-Wl,-z,pack-relative-relocs
-Wl,-z,now
-specs=/usr/lib/rpm/redhat/redhat-hardened-ld
-specs=/usr/lib/rpm/redhat/redhat-annobin-cc1
-Wl,--build-id=sha1

Fedora’s GCC configure-time flags.

Fedora’s Clang configure-time flags.

Gentoo

/etc/portage/make.conf, of course. But there are other flags that are implicitly set as well. The settings depend on things like what USE flags are set, as an example.

The build flags mainly seem to be in toolchain.eclass. Gentoo’s configure-time flags for GCC are in toolchain.eclass as well.

The referenced patches are located here.

Current GCC.

Current Clang.

Current LLVM.

The hardened toolchain changes table in Project:Toolchain is worth mentioning as well.

While on the subject of Gentoo: ChromiumOS is interesting to think about. ChromiumOS is the open source base for ChromeOS on Chromebooks; it uses Portage, Gentoo’s package management system. It probably has some cool ideas and developments. I haven’t looked into it enough to offer insightful commentary on it—I’d have to sift through a lot of code to be able to talk about it halfway intelligently.

NixOS

I’m making a bit of a presumption here: that NixOS doesn’t add CFLAGS/CXXFLAGS beyond what nixpkgs does. I don’t really see why it would, after all, but I felt it was important to note that all the same.

Hardening flags are mentioned here.

Here are the ones used by default at the time of writing (I verified this by copying the .nix file from this guide and setting NIX_DEBUG = 2; in the pkgs.stdenv.mkDerivation section, then built the package):

-fPIC
-Wformat
-Wformat-security
-Werror=format-security
-fstack-protector-strong
--param ssp-buffer-size=4
-O2
-D_FORTIFY_SOURCE=2
-fno-strict-overflow
-Wl,-z,relro
-Wl,-z,now

If -fzero-call-used-regs=used-gpr is used, it wasn’t printed during the test build.

GCC nixpkg.

Clang nixpkg.

LLVM nixpkg.

OpenBSD

Local changes to their integrated (and old) version of GCC are described in gcc-local. The same applies to clang-local, although it’s a current version.

OpenBSD’s GCC port and OpenBSD’s LLVM port.

Here is the source code for OpenBSD’s version of LLVM/Clang. Same for GCC.

OpenSUSE

Line 3581 here should list the options. OpenSUSE:Factory is what they use to build Tumbleweed packages.

-O2
-Wall
-U_FORTIFY_SOURCE
-D_FORTIFY_SOURCE=3
-fstack-protector-strong
-funwind-tables
-fasynchronous-unwind-tables
-fstack-clash-protection
-Werror=return-type
%%{?_lto_cflags}

There is a dummy package for GCC here, and ditto for clang/llvm. At the time of writing, the relevant versions are 14 and 19, respectively.

So, OpenSUSE’s GCC configure-time flags are here. Starts on line 2510.

OpenSUSE’s Clang configure-time flags are here. Starts on line 1092.

Solus

These are sourced from this GitHub issue. From what I can tell, the flags are set by ypkg. Specifically, ypkgcontext.py.

To get the latest flags, I guess one would have to figure out how to get ypkg (or some other piece of the Solus build system) to spit them out somehow. I haven’t done so, but it certainly seems possible.

CFLAGS:

-mtune=generic
-march=x86-64
-g2
-O2
-pipe
-fno-plt
-fPIC
-Wformat
-Wformat-security
-D_FORTIFY_SOURCE=2
-fstack-protector-strong
--param=ssp-buffer-size=32
-fasynchronous-unwind-tables
-ftree-vectorize
-feliminate-unused-debug-types
-Wall
-Wno-error
-Wp,-D_REENTRANT

CXXFLAGS:

-mtune=generic
-march=x86-64
-g2
-O2
-pipe
-fno-plt
-fPIC
-D_FORTIFY_SOURCE=2
-fstack-protector-strong
--param=ssp-buffer-size=32
-fasynchronous-unwind-tables
-ftree-vectorize
-feliminate-unused-debug-types
-Wall
-Wno-error
-Wp,-D_REENTRANT

LDFLAGS:

-Wl,--copy-dt-needed-entries
-Wl,-O1
-Wl,-z,relro
-Wl,-z,now
-Wl,-z,max-page-size=0x1000
-Wl,-Bsymbolic-functions
-Wl,--sort-common

Solus’ GCC configure-time flags are here.

Solus’ Clang/LLVM configure-time flags are here.

Ubuntu

Retrieved the same way as Debian. Ubuntu 24.10 (oracular).

$ dpkg-buildflags --query
Vendor: Ubuntu
Environment:

Area: abi
Features:
 lfs=no
 time64=yes
Builtins:
 lfs=yes
 time64=yes

Area: future
Features:
 lfs=no
Builtins:

Area: hardening
Features:
 bindnow=no
 branch=yes
 format=yes
 fortify=yes
 pie=yes
 relro=yes
 stackclash=yes
 stackprotector=yes
 stackprotectorstrong=yes
Builtins:
 pie=yes

Area: optimize
Features:
 lto=yes
Builtins:

Area: qa
Features:
 bug=no
 bug-implicit-func=yes
 canary=no
 elfpackagemetadata=no
 framepointer=yes
Builtins:

Area: reproducible
Features:
 fixdebugpath=yes
 fixfilepath=yes
 timeless=yes
Builtins:

Area: sanitize
Features:
 address=no
 leak=no
 thread=no
 undefined=no
Builtins:

Flag: ASFLAGS
Value: 
Origin: vendor

Flag: ASFLAGS_FOR_BUILD
Value: 
Origin: vendor

Flag: CFLAGS
Value: -g -O2 -Werror=implicit-function-declaration -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=/home/ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection
Origin: vendor

Flag: CFLAGS_FOR_BUILD
Value: -g -O2 -Werror=implicit-function-declaration -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=/home/ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection
Origin: vendor

Flag: CPPFLAGS
Value: -Wdate-time -D_FORTIFY_SOURCE=3
Origin: vendor

Flag: CPPFLAGS_FOR_BUILD
Value: -Wdate-time -D_FORTIFY_SOURCE=3
Origin: vendor

Flag: CXXFLAGS
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=/home/ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection
Origin: vendor

Flag: CXXFLAGS_FOR_BUILD
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=/home/ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection
Origin: vendor

Flag: DFLAGS
Value: -frelease
Origin: vendor

Flag: DFLAGS_FOR_BUILD
Value: -frelease
Origin: vendor

Flag: FCFLAGS
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=/home/ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -fcf-protection
Origin: vendor

Flag: FCFLAGS_FOR_BUILD
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=/home/ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -fcf-protection
Origin: vendor

Flag: FFLAGS
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=/home/ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -fcf-protection
Origin: vendor

Flag: FFLAGS_FOR_BUILD
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=/home/ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -fcf-protection
Origin: vendor

Flag: LDFLAGS
Value: -Wl,-Bsymbolic-functions -flto=auto -ffat-lto-objects -Wl,-z,relro
Origin: vendor

Flag: LDFLAGS_FOR_BUILD
Value: -flto=auto -ffat-lto-objects -Wl,-z,relro
Origin: vendor

Flag: OBJCFLAGS
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=/home/ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection
Origin: vendor

Flag: OBJCFLAGS_FOR_BUILD
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=/home/ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection
Origin: vendor

Flag: OBJCXXFLAGS
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=/home/ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection
Origin: vendor

Flag: OBJCXXFLAGS_FOR_BUILD
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=/home/ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection
Origin: vendor

Flag: RUSTFLAGS
Value: -Cforce-frame-pointers=yes
Origin: vendor

Flag: RUSTFLAGS_FOR_BUILD
Value: 
Origin: vendor

Ubuntu Oracular GCC version appears to be 14. Here is the relevant Ubuntu GCC file that I figured that out from. Ditto for Clang/LLVM, which appears to be 19.

Ubuntu’s GCC configure-time flags begin here.

Ubuntu’s LLVM configure-time flags are here.

Void

I found these by reasoning out what these files in the void-packages repo set:

CFLAGS:

-mtune=generic
-O2
-pipe
-fstack-clash-protection
-D_FORTIFY_SOURCE=2

CXXFLAGS:

-mtune=generic
-O2
-pipe
-fstack-clash-protection
-D_FORTIFY_SOURCE=2

LDFLAGS:

-Wl,--as-needed
-Wl,-z,relro
-Wl,-z,now

Void’s GCC configure-time flags are here.

Void’s Clang configure-time flags are here. The main version of clang used by the distro can be found in the llvm package template.

Caveats

Here are things I haven’t done, and why I haven’t done them.

My approach favored coverage, not extreme precision. I mostly gathered compilation flags that I could find in a configuration file (the equivalent of /etc/portage/make.conf for other distributions), through some light source code reading, or by executing some utility. I had to limit myself in some ways because otherwise it was unlikely that I’d be able to release this article anytime soon.

So, please don’t take what I’ve written here as gospel. Something being absent doesn’t necessarily prove anything, as the distribution may have enabled it in a place that I didn’t look. However, something being present and reflected in the linked source code does prove that a distribution uses it in that context, or at least used it at the time of writing.

Areas for improvement

Other resources