一键导入
per-function-optimize-attribute-abi-mismatch
`__attribute__((optimize("O0")))` on one function inside an `-O2` TU on GCC can crash on first call.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
`__attribute__((optimize("O0")))` on one function inside an `-O2` TU on GCC can crash on first call.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
A TRefCountPtr/TSharedPtr member in a UE class needs the pointee's full definition, not a forward decl.
When WSL's mirrored networking fails and falls back to "None", plus /etc/wsl.conf has generateResolvConf=false, the distro has no DNS; fix both layers.
When a Windows shell (PowerShell/cmd) feeds a bash script into WSL, CRLF line endings can corrupt the first shell builtin; force LF or pipe via a temp file.
Before "fixing" a recurring error, check git log to see if it was already fixed upstream — the working tree may just be stale.
Always add a space after URL brackets in Markdown to prevent 404 errors with special characters.
Plan a Python 3 modernization sweep (f-strings, super(), type hints) as a series of mechanical PRs, not one mega-PR.
| name | per-function-optimize-attribute-abi-mismatch |
| description | `__attribute__((optimize("O0")))` on one function inside an `-O2` TU on GCC can crash on first call. |
__attribute__((optimize("O0"))) silently breaks the ABI on GCCYou're trying to isolate a miscompile in a single helper function by
forcing it to lower optimization, using one of these forms inside a
translation unit that is otherwise built at -O2 or higher:
__attribute__((optimize("O0")))
static void Helper(uint8_t* Dst, const uint8_t* Src, int N) { ... }
or equivalently:
#pragma GCC push_options
#pragma GCC optimize("O0")
static void Helper(...) { ... }
#pragma GCC pop_options
and the program now crashes — usually SIGSEGV — on the first call
into Helper, before any of its body runs, even though the same code
worked at -O2.
GCC's per-function optimize attribute changes the optimization
level of the callee, but it does not re-align the ABI between
that callee and its -O2 caller. Concretely:
-O0 GCC emits a full stack frame with a frame pointer, spills all
arguments to memory, and does not use the x86-64 SysV red zone.-O2 GCC elides the frame pointer, keeps arguments in registers,
and may use the red zone.-O2, passes arguments and return address
under -O2 assumptions. The callee reads them under -O0
assumptions. The mismatch shows up as reading 0 or garbage from a
pointer argument, or a segfault on return (frame pointer mismatch).Crucially, there is no compiler warning. The attribute silently produces a working-looking binary that crashes at runtime.
On Clang, __attribute__((optnone)) exists and is better-behaved
about this, but it's also more restrictive (disables all opts, not
just some) and is not a fix-by-dropping-level tool.
If you really need one function at a lower optimization level, pick one of these, in order of preference:
Don't. If you reached for optimize("O0") to diagnose a
miscompile, see
stop-chasing-the-optimizer-reduce-instead
— reduce the repro and fix the real bug (usually UB) instead.
Isolate the function in its own TU, compiled at -O1. Move
Helper into helper.cpp and add a per-file flag in the build
system:
# CMake
set_source_files_properties(helper.cpp PROPERTIES COMPILE_FLAGS "-O1")
# Unreal Build Tool, in a Module's Build.cs
// equivalent: exclude just this file from unity builds, then use a
// per-file override. UBT doesn't expose per-file -O1 directly, so
// the usual escape is a separate Module or a .cpp-level #pragma.
-O1 is enough to keep the ABI in sync with -O2 callers while
disabling the handful of aggressive passes that tend to miscompile.
Whole-TU pragma, not per-function. If you can't split the TU, at least widen the scope:
// Top of the file, before any function definition.
#pragma GCC optimize("O1")
This degrades every function in the file equally, so the callees
within the file are ABI-consistent with each other. Callers in
other TUs still see the file's functions at -O1, which is fine
— -O1 is ABI-compatible with -O2 in all the ways that matter.
Never mix optimize("O0") with a helper that's inlined. Even
without ABI issues, the inlining decision can flip and silently
undo your intent. Pair the attribute with noinline:
__attribute__((noinline, optimize("O1"))) // O1, not O0
Minimal repro of the crash:
// build with: g++ -std=c++17 -O2 repro.cpp -o repro
#include <cstdio>
#include <cstdint>
__attribute__((noinline, optimize("O0")))
static void Store(uint8_t* Dst, uint64_t V)
{
Dst[0] = (uint8_t)(V >> 0);
Dst[1] = (uint8_t)(V >> 8);
}
int main()
{
uint8_t Buf[16] = {};
Store(Buf, 0x11223344); // segfault here on some GCC versions
std::printf("%02X %02X\n", Buf[0], Buf[1]);
}
Replace optimize("O0") with optimize("O1") and the crash goes
away. That's the smallest fix; the real fix is to remove the attribute
entirely and address whatever miscompile made you add it.
Diagnostic tells:
printf as the first line of the function,
the message never appears.gdb shows a corrupt return address or a bogus rbp immediately
on function entry.g++ -O2 -S repro.cpp shows the caller storing args in registers
only; the annotated callee's assembly spills them to stack offsets
the caller never wrote to.#pragma GCC optimize("O0") has the same problem as the
attribute form. The "pragma vs attribute" choice doesn't rescue
you; the ABI mismatch is about the optimization level, not the
spelling.optnone does
not crash in the same way — but assuming Clang behavior on GCC is
how you get here in the first place.#pragma optimize("", off)
is whole-file, applied between function definitions, not per
function. Code that relies on per-function optimize("O0") is
automatically non-portable.-flto, the "caller" and "callee"
may be re-optimized together post-attribute, and the level you
asked for silently doesn't stick.