[libvirt] [PATCH v2 5/5] Prevent more compiler optimization of mockable functions

Daniel P. Berrange berrange at redhat.com
Wed Jul 5 11:58:51 UTC 2017


Currently all mockable functions are annotated with the 'noinline'
attribute. This is insufficient to guarantee that a function can
be reliably mocked with an LD_PRELOAD. The C language spec allows
the compiler to assume there is only a single implementation of
each function. It can thus do things like propagating constant
return values into the caller at compile time, or creating
multiple specialized copies of the function body each optimized
for a different caller. To prevent these optimizations we must
also set the 'noclone' and 'weak' attributes.

This fixes the test suite when libvirt.so is built with CLang
with optimization enabled.

Signed-off-by: Daniel P. Berrange <berrange at redhat.com>
---
 build-aux/mock-noinline.pl      |  2 +-
 src/check-symfile.pl            |  2 +-
 src/internal.h                  | 23 ++++++++++++++++++-----
 src/qemu/qemu_capspriv.h        |  2 +-
 src/rpc/virnetsocket.h          |  4 ++--
 src/util/vircommand.h           |  2 +-
 src/util/vircrypto.h            |  2 +-
 src/util/virfile.h              |  2 +-
 src/util/virhostcpu.h           |  4 ++--
 src/util/virmacaddr.h           |  2 +-
 src/util/virnetdev.h            |  8 ++++----
 src/util/virnetdevip.h          |  2 +-
 src/util/virnetdevopenvswitch.h |  2 +-
 src/util/virnetdevtap.h         |  6 +++---
 src/util/virnuma.h              | 16 ++++++++--------
 src/util/virrandom.h            |  6 +++---
 src/util/virscsi.h              |  2 +-
 src/util/virscsivhost.h         |  2 +-
 src/util/virtpm.h               |  2 +-
 src/util/virutil.h              | 10 +++++-----
 src/util/viruuid.h              |  2 +-
 21 files changed, 58 insertions(+), 45 deletions(-)

diff --git a/build-aux/mock-noinline.pl b/build-aux/mock-noinline.pl
index eafe20d..2745d4b 100644
--- a/build-aux/mock-noinline.pl
+++ b/build-aux/mock-noinline.pl
@@ -43,7 +43,7 @@ sub scan_annotations {
         } elsif (/^\s*$/) {
             $func = undef;
         }
-        if (/ATTRIBUTE_NOINLINE/) {
+        if (/ATTRIBUTE_MOCKABLE/) {
             if (defined $func) {
                 $noninlined{$func} = 1;
             }
diff --git a/src/check-symfile.pl b/src/check-symfile.pl
index d59a213..3b062d0 100755
--- a/src/check-symfile.pl
+++ b/src/check-symfile.pl
@@ -52,7 +52,7 @@ foreach my $elflib (@elflibs) {
     open NM, "-|", "nm", $elflib or die "cannot run 'nm $elflib': $!";
 
     while (<NM>) {
-        next unless /^\S+\s(?:[TBD])\s(\S+)\s*$/;
+        next unless /^\S+\s(?:[TBDW])\s(\S+)\s*$/;
 
         $gotsyms{$1} = 1;
     }
diff --git a/src/internal.h b/src/internal.h
index c29f20f..00edd4f 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -113,13 +113,26 @@
 # endif
 
 /**
- * ATTRIBUTE_NOINLINE:
+ * ATTRIBUTE_MOCKABLE:
+ *
+ * Ensure that the symbol can be overridden in a mock
+ * library preload. This implies a number of attributes
+ *
+ *  - noinline: prevents the body being inlined to
+ *              callers,
+ *  - noclone: prevents specialized copies of the
+ *             function body being created for different
+ *             callers
+ *  - weak: prevents the compiler making optimizations
+ *          such as constant return value propagation
  *
- * Force compiler not to inline a method. Should be used if
- * the method need to be overridable by test mocks.
  */
-# ifndef ATTRIBUTE_NOINLINE
-#  define ATTRIBUTE_NOINLINE __attribute__((__noinline__))
+# ifndef ATTRIBUTE_MOCKABLE
+#  if __GNUC_PREREQ(4, 5)
+#   define ATTRIBUTE_MOCKABLE __attribute__((__noinline__, __noclone__, __weak__))
+#  else
+#   define ATTRIBUTE_MOCKABLE __attribute__((__noinline__, __weak__))
+#  endif
 # endif
 
 /**
diff --git a/src/qemu/qemu_capspriv.h b/src/qemu/qemu_capspriv.h
index 94fa75b..6cc189e 100644
--- a/src/qemu/qemu_capspriv.h
+++ b/src/qemu/qemu_capspriv.h
@@ -95,7 +95,7 @@ virQEMUCapsSetCPUModelInfo(virQEMUCapsPtr qemuCaps,
 virCPUDefPtr
 virQEMUCapsProbeHostCPUForEmulator(virCapsPtr caps,
                                    virQEMUCapsPtr qemuCaps,
-                                   virDomainVirtType type) ATTRIBUTE_NOINLINE;
+                                   virDomainVirtType type) ATTRIBUTE_MOCKABLE;
 
 void
 virQEMUCapsSetGICCapabilities(virQEMUCapsPtr qemuCaps,
diff --git a/src/rpc/virnetsocket.h b/src/rpc/virnetsocket.h
index de795af..3c2945e 100644
--- a/src/rpc/virnetsocket.h
+++ b/src/rpc/virnetsocket.h
@@ -137,10 +137,10 @@ int virNetSocketGetUNIXIdentity(virNetSocketPtr sock,
                                 gid_t *gid,
                                 pid_t *pid,
                                 unsigned long long *timestamp)
-    ATTRIBUTE_NOINLINE;
+    ATTRIBUTE_MOCKABLE;
 int virNetSocketGetSELinuxContext(virNetSocketPtr sock,
                                   char **context)
-    ATTRIBUTE_NOINLINE;
+    ATTRIBUTE_MOCKABLE;
 
 int virNetSocketSetBlocking(virNetSocketPtr sock,
                             bool blocking);
diff --git a/src/util/vircommand.h b/src/util/vircommand.h
index e7c2e51..c042a53 100644
--- a/src/util/vircommand.h
+++ b/src/util/vircommand.h
@@ -58,7 +58,7 @@ enum {
 
 void virCommandPassFD(virCommandPtr cmd,
                       int fd,
-                      unsigned int flags) ATTRIBUTE_NOINLINE;
+                      unsigned int flags) ATTRIBUTE_MOCKABLE;
 
 void virCommandPassListenFDs(virCommandPtr cmd);
 
diff --git a/src/util/vircrypto.h b/src/util/vircrypto.h
index 068602f..50400c6 100644
--- a/src/util/vircrypto.h
+++ b/src/util/vircrypto.h
@@ -55,6 +55,6 @@ int virCryptoEncryptData(virCryptoCipher algorithm,
     ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(6)
     ATTRIBUTE_NONNULL(8) ATTRIBUTE_NONNULL(9) ATTRIBUTE_RETURN_CHECK;
 
-uint8_t *virCryptoGenerateRandom(size_t nbytes) ATTRIBUTE_NOINLINE;
+uint8_t *virCryptoGenerateRandom(size_t nbytes) ATTRIBUTE_MOCKABLE;
 
 #endif /* __VIR_CRYPTO_H__ */
diff --git a/src/util/virfile.h b/src/util/virfile.h
index 57ceb80..32c9115 100644
--- a/src/util/virfile.h
+++ b/src/util/virfile.h
@@ -188,7 +188,7 @@ void virFileActivateDirOverride(const char *argv0)
 
 off_t virFileLength(const char *path, int fd) ATTRIBUTE_NONNULL(1);
 bool virFileIsDir (const char *file) ATTRIBUTE_NONNULL(1);
-bool virFileExists(const char *file) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NOINLINE;
+bool virFileExists(const char *file) ATTRIBUTE_NONNULL(1) ATTRIBUTE_MOCKABLE;
 bool virFileIsExecutable(const char *file) ATTRIBUTE_NONNULL(1);
 
 enum {
diff --git a/src/util/virhostcpu.h b/src/util/virhostcpu.h
index 67033de..3b30a0d 100644
--- a/src/util/virhostcpu.h
+++ b/src/util/virhostcpu.h
@@ -38,7 +38,7 @@ bool virHostCPUHasBitmap(void);
 virBitmapPtr virHostCPUGetPresentBitmap(void);
 virBitmapPtr virHostCPUGetOnlineBitmap(void);
 int virHostCPUGetCount(void);
-int virHostCPUGetThreadsPerSubcore(virArch arch) ATTRIBUTE_NOINLINE;
+int virHostCPUGetThreadsPerSubcore(virArch arch) ATTRIBUTE_MOCKABLE;
 
 int virHostCPUGetMap(unsigned char **cpumap,
                      unsigned int *online,
@@ -51,7 +51,7 @@ int virHostCPUGetInfo(virArch hostarch,
                       unsigned int *cores,
                       unsigned int *threads);
 
-int virHostCPUGetKVMMaxVCPUs(void) ATTRIBUTE_NOINLINE;
+int virHostCPUGetKVMMaxVCPUs(void) ATTRIBUTE_MOCKABLE;
 
 int virHostCPUStatsAssign(virNodeCPUStatsPtr param,
                           const char *name,
diff --git a/src/util/virmacaddr.h b/src/util/virmacaddr.h
index f4f5e2c..79492cd 100644
--- a/src/util/virmacaddr.h
+++ b/src/util/virmacaddr.h
@@ -48,7 +48,7 @@ void virMacAddrGetRaw(const virMacAddr *src, unsigned char dst[VIR_MAC_BUFLEN]);
 const char *virMacAddrFormat(const virMacAddr *addr,
                              char *str);
 void virMacAddrGenerate(const unsigned char prefix[VIR_MAC_PREFIX_BUFLEN],
-                        virMacAddrPtr addr) ATTRIBUTE_NOINLINE;
+                        virMacAddrPtr addr) ATTRIBUTE_MOCKABLE;
 int virMacAddrParse(const char* str,
                     virMacAddrPtr addr) ATTRIBUTE_RETURN_CHECK;
 int virMacAddrParseHex(const char* str,
diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h
index 51fcae5..2e9a9c4 100644
--- a/src/util/virnetdev.h
+++ b/src/util/virnetdev.h
@@ -156,7 +156,7 @@ int virNetDevExists(const char *brname)
 
 int virNetDevSetOnline(const char *ifname,
                        bool online)
-    ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NOINLINE;
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK ATTRIBUTE_MOCKABLE;
 int virNetDevGetOnline(const char *ifname,
                       bool *online)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
@@ -164,7 +164,7 @@ int virNetDevGetOnline(const char *ifname,
 
 int virNetDevSetMAC(const char *ifname,
                     const virMacAddr *macaddr)
-    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NOINLINE;
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK ATTRIBUTE_MOCKABLE;
 int virNetDevGetMAC(const char *ifname,
                     virMacAddrPtr macaddr)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
@@ -303,8 +303,8 @@ int virNetDevSysfsFile(char **pf_sysfs_device_link,
                        const char *ifname,
                        const char *file)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
-    ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NOINLINE;
+    ATTRIBUTE_RETURN_CHECK ATTRIBUTE_MOCKABLE;
 
 int virNetDevRunEthernetScript(const char *ifname, const char *script)
-    ATTRIBUTE_NOINLINE;
+    ATTRIBUTE_MOCKABLE;
 #endif /* __VIR_NETDEV_H__ */
diff --git a/src/util/virnetdevip.h b/src/util/virnetdevip.h
index 6b509ea..972a49a 100644
--- a/src/util/virnetdevip.h
+++ b/src/util/virnetdevip.h
@@ -67,7 +67,7 @@ int virNetDevIPAddrAdd(const char *ifname,
                        virSocketAddr *addr,
                        virSocketAddr *peer,
                        unsigned int prefix)
-    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NOINLINE;
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK ATTRIBUTE_MOCKABLE;
 int virNetDevIPRouteAdd(const char *ifname,
                         virSocketAddrPtr addr,
                         unsigned int prefix,
diff --git a/src/util/virnetdevopenvswitch.h b/src/util/virnetdevopenvswitch.h
index 51bb1dd..dc677ca 100644
--- a/src/util/virnetdevopenvswitch.h
+++ b/src/util/virnetdevopenvswitch.h
@@ -59,6 +59,6 @@ int virNetDevOpenvswitchInterfaceStats(const char *ifname,
 
 int virNetDevOpenvswitchGetVhostuserIfname(const char *path,
                                            char **ifname)
-    ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NOINLINE;
+    ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK ATTRIBUTE_MOCKABLE;
 
 #endif /* __VIR_NETDEV_OPENVSWITCH_H__ */
diff --git a/src/util/virnetdevtap.h b/src/util/virnetdevtap.h
index 0b17feb..1c4343e 100644
--- a/src/util/virnetdevtap.h
+++ b/src/util/virnetdevtap.h
@@ -39,7 +39,7 @@ int virNetDevTapCreate(char **ifname,
                        int *tapfd,
                        size_t tapfdSize,
                        unsigned int flags)
-    ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NOINLINE;
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK ATTRIBUTE_MOCKABLE;
 
 int virNetDevTapDelete(const char *ifname,
                        const char *tunpath)
@@ -49,7 +49,7 @@ int virNetDevTapGetName(int tapfd, char **ifname)
     ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
 
 char* virNetDevTapGetRealDeviceName(char *ifname)
-      ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NOINLINE;
+      ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK ATTRIBUTE_MOCKABLE;
 
 typedef enum {
    VIR_NETDEV_TAP_CREATE_NONE = 0,
@@ -89,7 +89,7 @@ int virNetDevTapCreateInBridgePort(const char *brname,
                                    unsigned int *actualMTU,
                                    unsigned int flags)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
-    ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NOINLINE;
+    ATTRIBUTE_RETURN_CHECK ATTRIBUTE_MOCKABLE;
 
 int virNetDevTapInterfaceStats(const char *ifname,
                                virDomainInterfaceStatsPtr stats)
diff --git a/src/util/virnuma.h b/src/util/virnuma.h
index e4e1fd0..62b89e9 100644
--- a/src/util/virnuma.h
+++ b/src/util/virnuma.h
@@ -34,20 +34,20 @@ int virNumaSetupMemoryPolicy(virDomainNumatuneMemMode mode,
                              virBitmapPtr nodeset);
 
 virBitmapPtr virNumaGetHostMemoryNodeset(void);
-bool virNumaNodesetIsAvailable(virBitmapPtr nodeset) ATTRIBUTE_NOINLINE;
-bool virNumaIsAvailable(void) ATTRIBUTE_NOINLINE;
-int virNumaGetMaxNode(void) ATTRIBUTE_NOINLINE;
-bool virNumaNodeIsAvailable(int node) ATTRIBUTE_NOINLINE;
+bool virNumaNodesetIsAvailable(virBitmapPtr nodeset) ATTRIBUTE_MOCKABLE;
+bool virNumaIsAvailable(void) ATTRIBUTE_MOCKABLE;
+int virNumaGetMaxNode(void) ATTRIBUTE_MOCKABLE;
+bool virNumaNodeIsAvailable(int node) ATTRIBUTE_MOCKABLE;
 int virNumaGetDistances(int node,
                         int **distances,
-                        int *ndistances) ATTRIBUTE_NOINLINE;
+                        int *ndistances) ATTRIBUTE_MOCKABLE;
 int virNumaGetNodeMemory(int node,
                          unsigned long long *memsize,
-                         unsigned long long *memfree) ATTRIBUTE_NOINLINE;
+                         unsigned long long *memfree) ATTRIBUTE_MOCKABLE;
 
 unsigned int virNumaGetMaxCPUs(void);
 
-int virNumaGetNodeCPUs(int node, virBitmapPtr *cpus) ATTRIBUTE_NOINLINE;
+int virNumaGetNodeCPUs(int node, virBitmapPtr *cpus) ATTRIBUTE_MOCKABLE;
 
 int virNumaGetPageInfo(int node,
                        unsigned int page_size,
@@ -59,7 +59,7 @@ int virNumaGetPages(int node,
                     unsigned int **pages_avail,
                     unsigned int **pages_free,
                     size_t *npages)
-    ATTRIBUTE_NONNULL(5) ATTRIBUTE_NOINLINE;
+    ATTRIBUTE_NONNULL(5) ATTRIBUTE_MOCKABLE;
 int virNumaSetPagePoolSize(int node,
                            unsigned int page_size,
                            unsigned long long page_count,
diff --git a/src/util/virrandom.h b/src/util/virrandom.h
index 7a984ee..990a456 100644
--- a/src/util/virrandom.h
+++ b/src/util/virrandom.h
@@ -24,11 +24,11 @@
 
 # include "internal.h"
 
-uint64_t virRandomBits(int nbits) ATTRIBUTE_NOINLINE;
+uint64_t virRandomBits(int nbits) ATTRIBUTE_MOCKABLE;
 double virRandom(void);
 uint32_t virRandomInt(uint32_t max);
 int virRandomBytes(unsigned char *buf, size_t buflen)
-    ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NOINLINE;
-int virRandomGenerateWWN(char **wwn, const char *virt_type) ATTRIBUTE_NOINLINE;
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK ATTRIBUTE_MOCKABLE;
+int virRandomGenerateWWN(char **wwn, const char *virt_type) ATTRIBUTE_MOCKABLE;
 
 #endif /* __VIR_RANDOM_H__ */
diff --git a/src/util/virscsi.h b/src/util/virscsi.h
index 9f8b3ec..eed563d 100644
--- a/src/util/virscsi.h
+++ b/src/util/virscsi.h
@@ -37,7 +37,7 @@ char *virSCSIDeviceGetSgName(const char *sysfs_prefix,
                              const char *adapter,
                              unsigned int bus,
                              unsigned int target,
-                             unsigned long long unit) ATTRIBUTE_NOINLINE;
+                             unsigned long long unit) ATTRIBUTE_MOCKABLE;
 char *virSCSIDeviceGetDevName(const char *sysfs_prefix,
                               const char *adapter,
                               unsigned int bus,
diff --git a/src/util/virscsivhost.h b/src/util/virscsivhost.h
index 21887dd..f9272a6 100644
--- a/src/util/virscsivhost.h
+++ b/src/util/virscsivhost.h
@@ -61,6 +61,6 @@ void virSCSIVHostDeviceGetUsedBy(virSCSIVHostDevicePtr dev,
                                  const char **drv_name,
                                  const char **dom_name);
 void virSCSIVHostDeviceFree(virSCSIVHostDevicePtr dev);
-int virSCSIVHostOpenVhostSCSI(int *vhostfd) ATTRIBUTE_NOINLINE;
+int virSCSIVHostOpenVhostSCSI(int *vhostfd) ATTRIBUTE_MOCKABLE;
 
 #endif /* __VIR_SCSIHOST_H__ */
diff --git a/src/util/virtpm.h b/src/util/virtpm.h
index b21fc05..7067bb5 100644
--- a/src/util/virtpm.h
+++ b/src/util/virtpm.h
@@ -22,6 +22,6 @@
 #ifndef __VIR_TPM_H__
 # define __VIR_TPM_H__
 
-char *virTPMCreateCancelPath(const char *devpath) ATTRIBUTE_NOINLINE;
+char *virTPMCreateCancelPath(const char *devpath) ATTRIBUTE_MOCKABLE;
 
 #endif /* __VIR_TPM_H__ */
diff --git a/src/util/virutil.h b/src/util/virutil.h
index 4938255..35e5ca3 100644
--- a/src/util/virutil.h
+++ b/src/util/virutil.h
@@ -139,8 +139,8 @@ char *virGetUserConfigDirectory(void);
 char *virGetUserCacheDirectory(void);
 char *virGetUserRuntimeDirectory(void);
 char *virGetUserShell(uid_t uid);
-char *virGetUserName(uid_t uid) ATTRIBUTE_NOINLINE;
-char *virGetGroupName(gid_t gid) ATTRIBUTE_NOINLINE;
+char *virGetUserName(uid_t uid) ATTRIBUTE_MOCKABLE;
+char *virGetGroupName(gid_t gid) ATTRIBUTE_MOCKABLE;
 int virGetGroupList(uid_t uid, gid_t group, gid_t **groups)
     ATTRIBUTE_NONNULL(3);
 int virGetUserID(const char *name,
@@ -201,12 +201,12 @@ verify((int)VIR_TRISTATE_BOOL_ABSENT == (int)VIR_TRISTATE_SWITCH_ABSENT);
 
 unsigned int virGetListenFDs(void);
 
-long virGetSystemPageSize(void) ATTRIBUTE_NOINLINE;
-long virGetSystemPageSizeKB(void) ATTRIBUTE_NOINLINE;
+long virGetSystemPageSize(void) ATTRIBUTE_MOCKABLE;
+long virGetSystemPageSizeKB(void) ATTRIBUTE_MOCKABLE;
 
 unsigned long long virMemoryLimitTruncate(unsigned long long value);
 bool virMemoryLimitIsSet(unsigned long long value);
-unsigned long long virMemoryMaxValue(bool ulong) ATTRIBUTE_NOINLINE;
+unsigned long long virMemoryMaxValue(bool ulong) ATTRIBUTE_MOCKABLE;
 
 /**
  * VIR_ASSIGN_IS_OVERFLOW:
diff --git a/src/util/viruuid.h b/src/util/viruuid.h
index 1d67e9e..3b41b42 100644
--- a/src/util/viruuid.h
+++ b/src/util/viruuid.h
@@ -49,7 +49,7 @@ int virGetHostUUID(unsigned char *host_uuid) ATTRIBUTE_NONNULL(1);
 
 int virUUIDIsValid(unsigned char *uuid);
 
-int virUUIDGenerate(unsigned char *uuid) ATTRIBUTE_NOINLINE;
+int virUUIDGenerate(unsigned char *uuid) ATTRIBUTE_MOCKABLE;
 
 int virUUIDParse(const char *uuidstr,
                  unsigned char *uuid)
-- 
2.9.4




More information about the libvir-list mailing list