Add -fsanitize=<sanitizers> argument to driver and frontend, and add

-fno-sanitize=<sanitizers> argument to driver. These allow ASan, TSan, and the
various UBSan checks to be enabled and disabled separately. Right now, the
different modes can't be combined, but the intention is that combining UBSan
and the other sanitizers will be permitted in the near future.

Currently, the UBSan checks will all be enabled if any of them is; that will be
fixed by the next patch.

llvm-svn: 167411
This commit is contained in:
Richard Smith 2012-11-05 22:04:41 +00:00
parent 18d2762048
commit 52be619c84
10 changed files with 308 additions and 23 deletions

View File

@ -127,8 +127,6 @@ BENIGN_LANGOPT(ParseUnknownAnytype, 1, 0, "__unknown_anytype")
BENIGN_LANGOPT(DebuggerSupport , 1, 0, "debugger support")
BENIGN_LANGOPT(DebuggerCastResultToId, 1, 0, "for 'po' in the debugger, cast the result to id if it is of unknown type")
BENIGN_LANGOPT(DebuggerObjCLiteral , 1, 0, "debugger Objective-C literals and subscripting support")
BENIGN_LANGOPT(SanitizeAddress , 1, 0, "AddressSanitizer enabled")
BENIGN_LANGOPT(SanitizeThread , 1, 0, "ThreadSanitizer enabled")
BENIGN_LANGOPT(SpellChecking , 1, 1, "spell-checking")
LANGOPT(SinglePrecisionConstants , 1, 0, "treating double-precision floating point constants as single precision constants")
@ -170,6 +168,11 @@ BENIGN_LANGOPT(EmitMicrosoftInlineAsm , 1, 0,
BENIGN_LANGOPT(RetainCommentsFromSystemHeaders, 1, 0, "retain documentation comments from system headers in the AST")
/// Runtime sanitizers.
#define SANITIZER(NAME, ID) \
BENIGN_LANGOPT(Sanitize##ID, 1, 0, NAME " sanitizer")
#include "clang/Basic/Sanitizers.def"
#undef LANGOPT
#undef VALUE_LANGOPT
#undef BENIGN_LANGOPT

View File

@ -0,0 +1,69 @@
//===--- Sanitizers.def - Runtime sanitizer options -------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the options for specifying which runtime sanitizers to
// enable. Users of this file must define the SANITIZER macro to make use of
// this information. Users of this file can also define the SANITIZER_GROUP
// macro to get information on options which refer to sets of sanitizers.
//
//===----------------------------------------------------------------------===//
#ifndef SANITIZER
#error "Define SANITIZER prior to including this file!"
#endif
// SANITIZER(NAME, ID)
// The first value is the name of the sanitizer as a string. The sanitizer can
// be enabled by specifying -fsanitize=NAME.
// The second value is an identifier which can be used to refer to the
// sanitizer.
// SANITIZER_GROUP(NAME, ID, ALIAS)
// The first two values have the same semantics as the corresponding SANITIZER
// values. The third value is an expression ORing together the IDs of individual
// sanitizers in this group.
#ifndef SANITIZER_GROUP
#define SANITIZER_GROUP(NAME, ID, ALIAS)
#endif
// AddressSanitizer
SANITIZER("address", Address)
// ThreadSanitizer
SANITIZER("thread", Thread)
// UndefinedBehaviorSanitizer
SANITIZER("signed-integer-overflow", SignedIntegerOverflow)
SANITIZER("divide-by-zero", DivideByZero)
SANITIZER("shift", Shift)
SANITIZER("unreachable", Unreachable)
SANITIZER("return", Return)
SANITIZER("vla-bound", VLABound)
SANITIZER("alignment", Alignment)
SANITIZER("null", Null)
SANITIZER("vptr", Vptr)
SANITIZER("object-size", ObjectSize)
SANITIZER("float-cast-overflow", FloatCastOverflow)
// -fsanitize=undefined (and its alias -fcatch-undefined-behavior). This should
// include all the sanitizers which have low overhead, no ABI or address space
// layout implications, and only catch undefined behavior.
SANITIZER_GROUP("undefined", Undefined,
SignedIntegerOverflow | DivideByZero | Shift | Unreachable |
Return | VLABound | Alignment | Null | Vptr | ObjectSize |
FloatCastOverflow)
#undef SANITIZER
#undef SANITIZER_GROUP

View File

@ -387,6 +387,9 @@ def fmath_errno : Flag<["-"], "fmath-errno">, Group<f_Group>, Flags<[CC1Option]>
def fno_math_errno : Flag<["-"], "fno-math-errno">, Group<f_Group>;
def fsignaling_math : Flag<["-"], "fsignaling-math">, Group<f_Group>;
def fno_signaling_math : Flag<["-"], "fno-signaling-math">, Group<f_Group>;
def fsanitize_EQ : CommaJoined<["-"], "fsanitize=">, Group<f_clang_Group>,
Flags<[CC1Option]>;
def fno_sanitize_EQ : CommaJoined<["-"], "fno-sanitize=">, Group<f_clang_Group>;
def funsafe_math_optimizations : Flag<["-"], "funsafe-math-optimizations">,
Group<f_Group>;
def fno_unsafe_math_optimizations : Flag<["-"], "fno-unsafe-math-optimizations">,

View File

@ -1452,13 +1452,149 @@ static bool UseRelaxAll(Compilation &C, const ArgList &Args) {
RelaxDefault);
}
namespace {
struct SanitizerArgs {
/// Assign ordinals to sanitizer flags. We'll use the ordinal values as
/// bit positions within \c Kind.
enum SanitizeOrdinal {
#define SANITIZER(NAME, ID) SO_##ID,
#include "clang/Basic/Sanitizers.def"
SO_Count
};
/// Bugs to catch at runtime.
enum SanitizeKind {
#define SANITIZER(NAME, ID) ID = 1 << SO_##ID,
#define SANITIZER_GROUP(NAME, ID, ALIAS) ID = ALIAS,
#include "clang/Basic/Sanitizers.def"
NeedsAsanRt = Address,
NeedsTsanRt = Thread,
NeedsUbsanRt = Undefined
};
unsigned Kind;
SanitizerArgs() : Kind(0) {}
bool needsAsanRt() const { return Kind & NeedsAsanRt; }
bool needsTsanRt() const { return Kind & NeedsTsanRt; }
bool needsUbsanRt() const { return Kind & NeedsUbsanRt; }
/// Parse a single value from a -fsanitize= or -fno-sanitize= value list.
/// Returns a member of the \c SanitizeKind enumeration, or \c 0 if \p Value
/// is not known.
static unsigned parse(const char *Value) {
return llvm::StringSwitch<SanitizeKind>(Value)
#define SANITIZER(NAME, ID) .Case(NAME, ID)
#define SANITIZER_GROUP(NAME, ID, ALIAS) .Case(NAME, ID)
#include "clang/Basic/Sanitizers.def"
.Default(SanitizeKind());
}
/// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
/// invalid components.
static unsigned parse(const Driver &D, const Arg *A) {
unsigned Kind = 0;
for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) {
if (unsigned K = parse(A->getValue(I)))
Kind |= K;
else
D.Diag(diag::err_drv_unsupported_option_argument)
<< A->getOption().getName() << A->getValue(I);
}
return Kind;
}
void addArgs(const ArgList &Args, ArgStringList &CmdArgs) const {
if (Kind & Address)
CmdArgs.push_back("-faddress-sanitizer");
if (Kind & Thread)
CmdArgs.push_back("-fthread-sanitizer");
if (Kind & Undefined)
CmdArgs.push_back("-fcatch-undefined-behavior");
if (!Kind)
return;
llvm::SmallString<256> SanitizeOpt("-fsanitize=");
#define SANITIZER(NAME, ID) \
if (Kind & ID) \
SanitizeOpt += NAME ",";
#include "clang/Basic/Sanitizers.def"
SanitizeOpt.pop_back();
CmdArgs.push_back(Args.MakeArgString(SanitizeOpt));
}
};
}
/// Produce an argument string from argument \p A, which shows how it provides a
/// value in \p Mask. For instance, the argument "-fsanitize=address,alignment"
/// with mask \c NeedsUbsanRt would produce "-fsanitize=alignment".
static std::string describeSanitizeArg(const Arg *A, unsigned Mask) {
if (!A->getOption().matches(options::OPT_fsanitize_EQ))
return A->getOption().getName();
for (unsigned I = 0, N = A->getNumValues(); I != N; ++I)
if (SanitizerArgs::parse(A->getValue(I)) & Mask)
return std::string("-fsanitize=") + A->getValue(I);
llvm_unreachable("arg didn't provide expected value");
}
/// Parse the sanitizer arguments from an argument list.
static SanitizerArgs getSanitizerArgs(const Driver &D, const ArgList &Args) {
SanitizerArgs Sanitize;
const Arg *AsanArg, *TsanArg, *UbsanArg;
for (ArgList::const_iterator I = Args.begin(), E = Args.end(); I != E; ++I) {
unsigned Add = 0, Remove = 0;
if ((*I)->getOption().matches(options::OPT_faddress_sanitizer))
Add = SanitizerArgs::Address;
else if ((*I)->getOption().matches(options::OPT_fno_address_sanitizer))
Remove = SanitizerArgs::Address;
else if ((*I)->getOption().matches(options::OPT_fthread_sanitizer))
Add = SanitizerArgs::Thread;
else if ((*I)->getOption().matches(options::OPT_fno_thread_sanitizer))
Remove = SanitizerArgs::Thread;
else if ((*I)->getOption().matches(options::OPT_fcatch_undefined_behavior))
Add = SanitizerArgs::Undefined;
else if ((*I)->getOption().matches(options::OPT_fsanitize_EQ))
Add = SanitizerArgs::parse(D, *I);
else if ((*I)->getOption().matches(options::OPT_fno_sanitize_EQ))
Remove = SanitizerArgs::parse(D, *I);
else
continue;
(*I)->claim();
Sanitize.Kind |= Add;
Sanitize.Kind &= ~Remove;
if (Add & SanitizerArgs::NeedsAsanRt) AsanArg = *I;
if (Add & SanitizerArgs::NeedsTsanRt) TsanArg = *I;
if (Add & SanitizerArgs::NeedsUbsanRt) UbsanArg = *I;
}
// Only one runtime library can be used at once.
// FIXME: Allow Ubsan to be combined with the other two.
bool NeedsAsan = Sanitize.needsAsanRt();
bool NeedsTsan = Sanitize.needsTsanRt();
bool NeedsUbsan = Sanitize.needsUbsanRt();
if (NeedsAsan + NeedsTsan + NeedsUbsan > 1)
D.Diag(diag::err_drv_argument_not_allowed_with)
<< describeSanitizeArg(NeedsAsan ? AsanArg : TsanArg,
NeedsAsan ? SanitizerArgs::NeedsAsanRt
: SanitizerArgs::NeedsTsanRt)
<< describeSanitizeArg(NeedsUbsan ? UbsanArg : TsanArg,
NeedsUbsan ? SanitizerArgs::NeedsUbsanRt
: SanitizerArgs::NeedsTsanRt);
return Sanitize;
}
/// If AddressSanitizer is enabled, add appropriate linker flags (Linux).
/// This needs to be called before we add the C run-time (malloc, etc).
static void addAsanRTLinux(const ToolChain &TC, const ArgList &Args,
ArgStringList &CmdArgs) {
if (!Args.hasFlag(options::OPT_faddress_sanitizer,
options::OPT_fno_address_sanitizer, false))
return;
if(TC.getTriple().getEnvironment() == llvm::Triple::Android) {
if (!Args.hasArg(options::OPT_shared)) {
if (!Args.hasArg(options::OPT_pie))
@ -1490,9 +1626,6 @@ static void addAsanRTLinux(const ToolChain &TC, const ArgList &Args,
/// This needs to be called before we add the C run-time (malloc, etc).
static void addTsanRTLinux(const ToolChain &TC, const ArgList &Args,
ArgStringList &CmdArgs) {
if (!Args.hasFlag(options::OPT_fthread_sanitizer,
options::OPT_fno_thread_sanitizer, false))
return;
if (!Args.hasArg(options::OPT_shared)) {
// LibTsan is "libclang_rt.tsan-<ArchName>.a" in the Linux library
// resource directory.
@ -1511,8 +1644,6 @@ static void addTsanRTLinux(const ToolChain &TC, const ArgList &Args,
/// (Linux).
static void addUbsanRTLinux(const ToolChain &TC, const ArgList &Args,
ArgStringList &CmdArgs) {
if (!Args.hasArg(options::OPT_fcatch_undefined_behavior))
return;
if (!Args.hasArg(options::OPT_shared)) {
// LibUbsan is "libclang_rt.ubsan-<ArchName>.a" in the Linux library
// resource directory.
@ -2370,7 +2501,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-ffreestanding");
// Forward -f (flag) options which we can pass directly.
Args.AddLastArg(CmdArgs, options::OPT_fcatch_undefined_behavior);
Args.AddLastArg(CmdArgs, options::OPT_femit_all_decls);
Args.AddLastArg(CmdArgs, options::OPT_fheinous_gnu_extensions);
Args.AddLastArg(CmdArgs, options::OPT_flimit_debug_info);
@ -2380,6 +2510,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_show_template_tree);
Args.AddLastArg(CmdArgs, options::OPT_fno_elide_type);
SanitizerArgs Sanitize = getSanitizerArgs(D, Args);
Sanitize.addArgs(Args, CmdArgs);
// Report and error for -faltivec on anything other then PowerPC.
if (const Arg *A = Args.getLastArg(options::OPT_faltivec))
if (!(getToolChain().getTriple().getArch() == llvm::Triple::ppc ||
@ -2390,14 +2523,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
if (getToolChain().SupportsProfiling())
Args.AddLastArg(CmdArgs, options::OPT_pg);
if (Args.hasFlag(options::OPT_faddress_sanitizer,
options::OPT_fno_address_sanitizer, false))
CmdArgs.push_back("-faddress-sanitizer");
if (Args.hasFlag(options::OPT_fthread_sanitizer,
options::OPT_fno_thread_sanitizer, false))
CmdArgs.push_back("-fthread-sanitizer");
// -flax-vector-conversions is default.
if (!Args.hasFlag(options::OPT_flax_vector_conversions,
options::OPT_fno_lax_vector_conversions))
@ -2544,9 +2669,20 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
// -frtti is default.
if (!Args.hasFlag(options::OPT_frtti, options::OPT_fno_rtti) ||
KernelOrKext)
KernelOrKext) {
CmdArgs.push_back("-fno-rtti");
// -fno-rtti cannot usefully be combined with -fsanitize=vptr.
if (Sanitize.Kind & SanitizerArgs::Vptr) {
llvm::StringRef NoRttiArg =
Args.getLastArg(options::OPT_mkernel,
options::OPT_fapple_kext,
options::OPT_fno_rtti)->getOption().getName();
D.Diag(diag::err_drv_argument_not_allowed_with)
<< "-fsanitize=vptr" << NoRttiArg;
}
}
// -fshort-enums=0 is default for all architectures except Hexagon.
if (Args.hasFlag(options::OPT_fshort_enums,
options::OPT_fno_short_enums,
@ -5972,8 +6108,11 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA,
AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs);
SanitizerArgs Sanitize = getSanitizerArgs(D, Args);
// Call this before we add the C++ ABI library.
addUbsanRTLinux(getToolChain(), Args, CmdArgs);
if (Sanitize.needsUbsanRt())
addUbsanRTLinux(getToolChain(), Args, CmdArgs);
if (D.CCCIsCXX &&
!Args.hasArg(options::OPT_nostdlib) &&
@ -5989,8 +6128,10 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA,
}
// Call this before we add the C run-time.
addAsanRTLinux(getToolChain(), Args, CmdArgs);
addTsanRTLinux(getToolChain(), Args, CmdArgs);
if (Sanitize.needsAsanRt())
addAsanRTLinux(getToolChain(), Args, CmdArgs);
if (Sanitize.needsTsanRt())
addTsanRTLinux(getToolChain(), Args, CmdArgs);
if (!Args.hasArg(options::OPT_nostdlib)) {
if (!Args.hasArg(options::OPT_nodefaultlibs)) {

View File

@ -1273,6 +1273,37 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
case 1: Opts.setStackProtector(LangOptions::SSPOn); break;
case 2: Opts.setStackProtector(LangOptions::SSPReq); break;
}
// Parse -fsanitize= arguments.
std::vector<std::string> Sanitizers = Args.getAllArgValues(OPT_fsanitize_EQ);
for (unsigned I = 0, N = Sanitizers.size(); I != N; ++I) {
// Since the Opts.Sanitize* values are bitfields, it's a little tricky to
// efficiently map string values to them. Perform the mapping indirectly:
// convert strings to enumerated values, then switch over the enum to set
// the right bitfield value.
enum Sanitizer {
#define SANITIZER(NAME, ID) \
ID,
#include "clang/Basic/Sanitizers.def"
Unknown
};
switch (llvm::StringSwitch<unsigned>(Sanitizers[I])
#define SANITIZER(NAME, ID) \
.Case(NAME, ID)
#include "clang/Basic/Sanitizers.def"
.Default(Unknown)) {
#define SANITIZER(NAME, ID) \
case ID: \
Opts.Sanitize##ID = true; \
break;
#include "clang/Basic/Sanitizers.def"
case Unknown:
Diags.Report(diag::err_drv_invalid_value)
<< "-fsanitize=" << Sanitizers[I];
break;
}
}
}
static void ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args,

View File

@ -4,6 +4,12 @@
// RUN: -target i386-unknown-linux -faddress-sanitizer \
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
// RUN: | FileCheck --check-prefix=CHECK-LINUX %s
//
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
// RUN: -target i386-unknown-linux -fsanitize=address \
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
// RUN: | FileCheck --check-prefix=CHECK-LINUX %s
//
// CHECK-LINUX: "{{.*}}ld{{(.exe)?}}"
// CHECK-LINUX-NOT: "-lc"
// CHECK-LINUX: libclang_rt.asan-i386.a"
@ -15,6 +21,12 @@
// RUN: -target arm-linux-androideabi -faddress-sanitizer \
// RUN: --sysroot=%S/Inputs/basic_android_tree/sysroot \
// RUN: | FileCheck --check-prefix=CHECK-ANDROID %s
//
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
// RUN: -target arm-linux-androideabi -fsanitize=address \
// RUN: --sysroot=%S/Inputs/basic_android_tree/sysroot \
// RUN: | FileCheck --check-prefix=CHECK-ANDROID %s
//
// CHECK-ANDROID: "{{.*}}ld{{(.exe)?}}"
// CHECK-ANDROID-NOT: "-lc"
// CHECK-ANDROID: libclang_rt.asan-arm-android.so"
@ -25,6 +37,13 @@
// RUN: --sysroot=%S/Inputs/basic_android_tree/sysroot \
// RUN: -shared \
// RUN: | FileCheck --check-prefix=CHECK-ANDROID-SHARED %s
//
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
// RUN: -target arm-linux-androideabi -fsanitize=address \
// RUN: --sysroot=%S/Inputs/basic_android_tree/sysroot \
// RUN: -shared \
// RUN: | FileCheck --check-prefix=CHECK-ANDROID-SHARED %s
//
// CHECK-ANDROID-SHARED: "{{.*}}ld{{(.exe)?}}"
// CHECK-ANDROID-SHARED-NOT: "-lc"
// CHECK-ANDROID-SHARED: libclang_rt.asan-arm-android.so"

View File

@ -2,6 +2,7 @@
// RUN: %clang -O1 -target i386-unknown-unknown -faddress-sanitizer %s -S -emit-llvm -o - | FileCheck %s
// RUN: %clang -O2 -target i386-unknown-unknown -faddress-sanitizer %s -S -emit-llvm -o - | FileCheck %s
// RUN: %clang -O3 -target i386-unknown-unknown -faddress-sanitizer %s -S -emit-llvm -o - | FileCheck %s
// RUN: %clang -target i386-unknown-unknown -fsanitize=address %s -S -emit-llvm -o - | FileCheck %s
// Verify that -faddress-sanitizer invokes asan instrumentation.
int foo(int *a) { return *a; }

View File

@ -1,5 +1,6 @@
// Make sure AddressSanitizer disables _FORTIFY_SOURCE on Darwin.
// RUN: %clang -faddress-sanitizer %s -E -dM -target x86_64-darwin - | FileCheck %s
// RUN: %clang -fsanitize=address %s -E -dM -target x86_64-darwin - | FileCheck %s
// CHECK: #define _FORTIFY_SOURCE 0

View File

@ -0,0 +1,16 @@
// RUN: %clang -target x86_64-linux-gnu -fcatch-undefined-behavior %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED
// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED
// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|divide-by-zero|shift|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow),?){11}"}}
// RUN: %clang -target x86_64-linux-gnu -fsanitize=thread,undefined -fno-thread-sanitizer -fno-sanitize=float-cast-overflow,vptr %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-PARTIAL-UNDEFINED
// CHECK-PARTIAL-UNDEFINED: "-fsanitize={{((signed-integer-overflow|divide-by-zero|shift|unreachable|return|vla-bound|alignment|null|object-size),?){9}"}}
// RUN: %clang -target x86_64-linux-gnu -fsanitize=vptr -fno-rtti %s -c -o - 2>&1 | FileCheck %s --check-prefix=CHECK-VPTR-NO-RTTI
// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fno-rtti %s -c -o - 2>&1 | FileCheck %s --check-prefix=CHECK-VPTR-NO-RTTI
// CHECK-VPTR-NO-RTTI: '-fsanitize=vptr' not allowed with 'fno-rtti'
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address,thread -fno-rtti %s -c -o - 2>&1 | FileCheck %s --check-prefix=CHECK-SANA-SANT
// CHECK-SANA-SANT: '-fsanitize=address' not allowed with '-fsanitize=thread'
// RUN: %clang -target x86_64-linux-gnu -faddress-sanitizer -fthread-sanitizer -fno-rtti %s -c -o - 2>&1 | FileCheck %s --check-prefix=CHECK-ASAN-TSAN
// CHECK-ASAN-TSAN: 'faddress-sanitizer' not allowed with 'fthread-sanitizer'

View File

@ -2,6 +2,7 @@
// RUN: %clang -O1 -target i386-unknown-unknown -fthread-sanitizer %s -S -emit-llvm -o - | FileCheck %s
// RUN: %clang -O2 -target i386-unknown-unknown -fthread-sanitizer %s -S -emit-llvm -o - | FileCheck %s
// RUN: %clang -O3 -target i386-unknown-unknown -fthread-sanitizer %s -S -emit-llvm -o - | FileCheck %s
// RUN: %clang -target i386-unknown-unknown -fsanitize=thread %s -S -emit-llvm -o - | FileCheck %s
// Verify that -fthread-sanitizer invokes tsan instrumentation.
int foo(int *a) { return *a; }