Outils pour utilisateurs

Outils du site


prog:gcc

Ceci est une ancienne révision du document !


Nightly pour Windows 64

Diagnostic

Information et taille des classes et tables virtuelles

class VirtualBase
{
  public :
    virtual int tick(int n) = 0;
};
 
class VirtualDerived : public VirtualBase
{
  public :
    int m_counter;
  public :
    VirtualDerived() : m_counter(0) {}
    int tick(int n) { m_counter += n; return m_counter; }
};
  • Jusqu'à gcc 7 : g++ -fdump-class-hierarchy -c main.cc
  • A partir de gcc 8 : g++ -fdump-lang-class -c main.cc
Vtable for VirtualBase
VirtualBase::_ZTV11VirtualBase: 3 entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI11VirtualBase)
16    (int (*)(...))__cxa_pure_virtual

Class VirtualBase
   size=8 align=8
   base size=8 base align=8
VirtualBase (0x0x6ffffbe0480) 0 nearly-empty
    vptr=((& VirtualBase::_ZTV11VirtualBase) + 16)

Vtable for VirtualDerived
VirtualDerived::_ZTV14VirtualDerived: 3 entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI14VirtualDerived)
16    (int (*)(...))VirtualDerived::tick

Class VirtualDerived
   size=16 align=8
   base size=12 base align=8
VirtualDerived (0x0x6ffffc301a0) 0
    vptr=((& VirtualDerived::_ZTV14VirtualDerived) + 16)
  VirtualBase (0x0x6ffffbe04e0) 0 nearly-empty
      primary-for VirtualDerived (0x0x6ffffc301a0)
  • clang : clang -cc1 -emit-llvm -fdump-record-layouts-simple main.cc
; ModuleID = 'main.cc'
source_filename = "main.cc"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

%class.VirtualDerived = type <{ %class.VirtualBase, i32, [4 x i8] }>
%class.VirtualBase = type { i32 (...)** }

$_ZN14VirtualDerivedC1Ev = comdat any

$_ZN14VirtualDerivedC2Ev = comdat any

$_ZN11VirtualBaseC2Ev = comdat any

$_ZN14VirtualDerived4tickEi = comdat any

$_ZTV14VirtualDerived = comdat any

$_ZTS14VirtualDerived = comdat any

$_ZTS11VirtualBase = comdat any

$_ZTI11VirtualBase = comdat any

$_ZTI14VirtualDerived = comdat any

$_ZTV11VirtualBase = comdat any

@_ZTV14VirtualDerived = linkonce_odr unnamed_addr constant { [3 x i8*] } { [3 x i8*] [i8* null, i8* bitcast ({ i8*, i8*, i8* }* @_ZTI14VirtualDerived to i8*), i8* bitcast (i32 (%class.VirtualDerived*, i32)* @_ZN14VirtualDerived4tickEi to i8*)] }, comdat, align 8
@_ZTVN10__cxxabiv120__si_class_type_infoE = external global i8*
@_ZTS14VirtualDerived = linkonce_odr constant [17 x i8] c"14VirtualDerived\00", comdat, align 1
@_ZTVN10__cxxabiv117__class_type_infoE = external global i8*
@_ZTS11VirtualBase = linkonce_odr constant [14 x i8] c"11VirtualBase\00", comdat, align 1
@_ZTI11VirtualBase = linkonce_odr constant { i8*, i8* } { i8* bitcast (i8** getelementptr inbounds (i8*, i8** @_ZTVN10__cxxabiv117__class_type_infoE, i64 2) to i8*), i8* getelementptr inbounds ([14 x i8], [14 x i8]* @_ZTS11VirtualBase, i32 0, i32 0) }, comdat, align 8
@_ZTI14VirtualDerived = linkonce_odr constant { i8*, i8*, i8* } { i8* bitcast (i8** getelementptr inbounds (i8*, i8** @_ZTVN10__cxxabiv120__si_class_type_infoE, i64 2) to i8*), i8* getelementptr inbounds ([17 x i8], [17 x i8]* @_ZTS14VirtualDerived, i32 0, i32 0), i8* bitcast ({ i8*, i8* }* @_ZTI11VirtualBase to i8*) }, comdat, align 8
@_ZTV11VirtualBase = linkonce_odr unnamed_addr constant { [3 x i8*] } { [3 x i8*] [i8* null, i8* bitcast ({ i8*, i8* }* @_ZTI11VirtualBase to i8*), i8* bitcast (void ()* @__cxa_pure_virtual to i8*)] }, comdat, align 8

; Function Attrs: noinline nounwind optnone
define void @_Z1fv() #0 {
entry:
  %v = alloca %class.VirtualDerived, align 8
  call void @_ZN14VirtualDerivedC1Ev(%class.VirtualDerived* %v)
  ret void
}

; Function Attrs: noinline nounwind optnone
define linkonce_odr void @_ZN14VirtualDerivedC1Ev(%class.VirtualDerived* %this) unnamed_addr #0 comdat align 2 {
entry:
  %this.addr = alloca %class.VirtualDerived*, align 8
  store %class.VirtualDerived* %this, %class.VirtualDerived** %this.addr, align 8
  %this1 = load %class.VirtualDerived*, %class.VirtualDerived** %this.addr, align 8
  call void @_ZN14VirtualDerivedC2Ev(%class.VirtualDerived* %this1)
  ret void
}

; Function Attrs: noinline nounwind optnone
define linkonce_odr void @_ZN14VirtualDerivedC2Ev(%class.VirtualDerived* %this) unnamed_addr #0 comdat align 2 {
entry:
  %this.addr = alloca %class.VirtualDerived*, align 8
  store %class.VirtualDerived* %this, %class.VirtualDerived** %this.addr, align 8
  %this1 = load %class.VirtualDerived*, %class.VirtualDerived** %this.addr, align 8
  %0 = bitcast %class.VirtualDerived* %this1 to %class.VirtualBase*
  call void @_ZN11VirtualBaseC2Ev(%class.VirtualBase* %0) #1
  %1 = bitcast %class.VirtualDerived* %this1 to i32 (...)***
  store i32 (...)** bitcast (i8** getelementptr inbounds ({ [3 x i8*] }, { [3 x i8*] }* @_ZTV14VirtualDerived, i32 0, inrange i32 0, i32 2) to i32 (...)**), i32 (...)*** %1, align 8
  %m_counter = getelementptr inbounds %class.VirtualDerived, %class.VirtualDerived* %this1, i32 0, i32 1
  store i32 0, i32* %m_counter, align 8
  ret void
}

; Function Attrs: noinline nounwind optnone
define linkonce_odr void @_ZN11VirtualBaseC2Ev(%class.VirtualBase* %this) unnamed_addr #0 comdat align 2 {
entry:
  %this.addr = alloca %class.VirtualBase*, align 8
  store %class.VirtualBase* %this, %class.VirtualBase** %this.addr, align 8
  %this1 = load %class.VirtualBase*, %class.VirtualBase** %this.addr, align 8
  %0 = bitcast %class.VirtualBase* %this1 to i32 (...)***
  store i32 (...)** bitcast (i8** getelementptr inbounds ({ [3 x i8*] }, { [3 x i8*] }* @_ZTV11VirtualBase, i32 0, inrange i32 0, i32 2) to i32 (...)**), i32 (...)*** %0, align 8
  ret void
}

; Function Attrs: noinline nounwind optnone
define linkonce_odr i32 @_ZN14VirtualDerived4tickEi(%class.VirtualDerived* %this, i32 %n) unnamed_addr #0 comdat align 2 {
entry:
  %this.addr = alloca %class.VirtualDerived*, align 8
  %n.addr = alloca i32, align 4
  store %class.VirtualDerived* %this, %class.VirtualDerived** %this.addr, align 8
  store i32 %n, i32* %n.addr, align 4
  %this1 = load %class.VirtualDerived*, %class.VirtualDerived** %this.addr, align 8
  %0 = load i32, i32* %n.addr, align 4
  %m_counter = getelementptr inbounds %class.VirtualDerived, %class.VirtualDerived* %this1, i32 0, i32 1
  %1 = load i32, i32* %m_counter, align 8
  %add = add nsw i32 %1, %0
  store i32 %add, i32* %m_counter, align 8
  %m_counter2 = getelementptr inbounds %class.VirtualDerived, %class.VirtualDerived* %this1, i32 0, i32 1
  %2 = load i32, i32* %m_counter2, align 8
  ret i32 %2
}

declare void @__cxa_pure_virtual() unnamed_addr

attributes #0 = { noinline nounwind optnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang version 11.0.0 "}
  • Table virtuelle uniquement : clang -cc1 -emit-llvm -fdump-vtable-layouts main.cc
Vtable for 'VirtualDerived' (3 entries).
   0 | offset_to_top (0)
   1 | VirtualDerived RTTI
       -- (VirtualBase, 0) vtable address --
       -- (VirtualDerived, 0) vtable address --
   2 | int VirtualDerived::tick(int)

VTable indices for 'VirtualDerived' (1 entries).
   0 | int VirtualDerived::tick(int)

Vtable for 'VirtualBase' (3 entries).
   0 | offset_to_top (0)
   1 | VirtualBase RTTI
       -- (VirtualBase, 0) vtable address --
   2 | int VirtualBase::tick(int) [pure]

VTable indices for 'VirtualBase' (1 entries).
   0 | int VirtualBase::tick(int)

gprof

Génère un graphe de l'utilisation des fonctions d'un programme (nécessite l'option de compilation -pg).

GPROF Tutorial – How to use Linux GNU GCC Profiling Tool Archive du 20/08/2012 le 24/02/2020

Commun avec clang

Sanitizer

Il existe -fsanitize=address,thread,undefined mais address et thread ne sont pas compatible simultanément.

address

Il détecte des erreurs de type global-buffer-overflow, heap-use-after-free. CppCon 2015: Kostya Serebryany “Beyond Sanitizers, Fuzzing and Hardening your C++ apps for Security and Reliability”

  • global-buffer-overflow
main.c
int global_array[100] = {-1};
 
int main(int argc, char **argv) {
  return global_array[argc+100];
}
gcc main.c -g -fsanitize=address -o main && ./main
=================================================================
==2500==ERROR: AddressSanitizer: global-buffer-overflow on address 0x000000740cf4 at pc 0x00000050d9bf bp 0x7ffeabb89b70 sp 0x7ffeabb89b68
READ of size 4 at 0x000000740cf4 thread T0
    #0 0x50d9be in main /tmp/main.c:4:10
    #1 0x7f1208faa461 in __libc_start_main /home/legarrec/info/portage/sys-libs/glibc-2.25-r9/work/glibc-2.25/csu/../csu/libc-start.c:295
    #2 0x419709 in _start (/tmp/main+0x419709)

0x000000740cf4 is located 4 bytes to the right of global variable 'global_array' defined in 'main.c:1:5' (0x740b60) of size 400
SUMMARY: AddressSanitizer: global-buffer-overflow /tmp/main.c:4:10 in main
Shadow bytes around the buggy address:
  0x0000800e0140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800e0150: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800e0160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800e0170: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800e0180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0000800e0190: 00 00 00 00 00 00 00 00 00 00 00 00 00 00[f9]f9
  0x0000800e01a0: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 00 00 00 00
  0x0000800e01b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800e01c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800e01d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800e01e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==2500==ABORTING

valgrind ne détecte pas l'erreur.

  • heap-use-after-free
main2.cc
int main(int argc, char **argv) {
  int *array = new int[100];
  delete [] array;
  return array[argc];
}
g++ main2.c -g -fsanitize=address -o main2 && ./main2
=================================================================
==2765==ERROR: AddressSanitizer: heap-use-after-free on address 0x614000000044 at pc 0x000000512445 bp 0x7ffd51684f60 sp 0x7ffd51684f58
READ of size 4 at 0x614000000044 thread T0
    #0 0x512444 in main /tmp/main2.c:4:10
    #1 0x7fd8d0086461 in __libc_start_main /home/legarrec/info/portage/sys-libs/glibc-2.25-r9/work/glibc-2.25/csu/../csu/libc-start.c:295
    #2 0x419d29 in _start (/tmp/main2+0x419d29)

0x614000000044 is located 4 bytes inside of 400-byte region [0x614000000040,0x6140000001d0)
freed by thread T0 here:
    #0 0x50ef30 in operator delete[](void*) /home/legarrec/info/portage/sys-libs/compiler-rt-sanitizers-5.0.0/work/compiler-rt-5.0.0.src/lib/asan/asan_new_delete.cc:141
    #1 0x5123f6 in main /tmp/main2.c:3:3
    #2 0x7fd8d0086461 in __libc_start_main /home/legarrec/info/portage/sys-libs/glibc-2.25-r9/work/glibc-2.25/csu/../csu/libc-start.c:295
    #3 0x419d29 in _start (/tmp/main2+0x419d29)
  
previously allocated by thread T0 here:
    #0 0x50e1c8 in operator new[](unsigned long) /home/legarrec/info/portage/sys-libs/compiler-rt-  sanitizers-5.0.0/work/compiler-rt-5.0.0.src/lib/asan/asan_new_delete.cc:95
    #1 0x5123d4 in main /tmp/main2.c:2:16
    #2 0x7fd8d0086461 in __libc_start_main /home/legarrec/info/portage/sys-libs/glibc-2.25-r9/work/glibc-2.25/csu/../csu/libc-start.c:295
    #3 0x419d29 in _start (/tmp/main2+0x419d29)

SUMMARY: AddressSanitizer: heap-use-after-free /tmp/main2.c:4:10 in main
Shadow bytes around the buggy address:
  0x0c287fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c287fff8000: fa fa fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd
  0x0c287fff8010: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c287fff8020: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c287fff8030: fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa fa
  0x0c287fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa

valgrind détecte l'erreur.

==2787== Command: ./main2
==2787== 
==2787== Invalid read of size 4
==2787==    at 0x4005AF: main (main2.c:4)
==2787==  Address 0x5b25c84 is 4 bytes inside a block of size 400 free'd
==2787==    at 0x4C2CAA5: operator delete[](void*) (vg_replace_malloc.c:621)
==2787==    by 0x4005A6: main (main2.c:3)
==2787==  Block was alloc'd at
==2787==    at 0x4C2BAC8: operator new[](unsigned long) (vg_replace_malloc.c:423)
==2787==    by 0x400584: main (main2.c:2)
  • stack-use-after-return
main3.c
int *g;
 
void LeakLocal() {
  int local;
  g = &local;
}
 
int main(){
  LeakLocal();
  return *g;
}
gcc main3.c -g -o main3 -fsanitize=address
ASAN_OPTIONS=detect_stack_use_after_return=1 ./main3

L'utilisation de ASAN_OPTIONS=detect_stack_use_after_return=1 est nécessaire car cette option peut créer des faux positifs.

=================================================================
==2907==ERROR: AddressSanitizer: stack-use-after-return on address 0x7f57b2a00020 at pc 0x00000050db25 bp 0x7ffe8dfc7330 sp 0x7ffe8dfc7328
READ of size 4 at 0x7f57b2a00020 thread T0
    #0 0x50db24 in main /tmp/main3.c:10:10
    #1 0x7f57b604d461 in __libc_start_main /home/legarrec/info/portage/sys-libs/glibc-2.25-r9/work/glibc-2.25/csu/../csu/libc-start.c:295
    #2 0x419709 in _start (/tmp/main3+0x419709)

Address 0x7f57b2a00020 is located in stack of thread T0 at offset 32 in frame
    #0 0x50d95f in LeakLocal /tmp/main3.c:3

  This frame has 1 object(s):
    [32, 36) 'local' (line 4) <== Memory access at offset 32 is inside this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-use-after-return /tmp/main3.c:10:10 in main
Shadow bytes around the buggy address:
  0x0feb76537fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0feb76537fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0feb76537fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0feb76537fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0feb76537ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0feb76538000: f5 f5 f5 f5[f5]f5 f5 f5 00 00 00 00 00 00 00 00
  0x0feb76538010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0feb76538020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0feb76538030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0feb76538040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0feb76538050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

valgrind détecte l'erreur.

valgrind --track-origins=yes ./main3
==2961== Syscall param exit_group(status) contains uninitialised byte(s)
==2961==    at 0x4F002B8: _Exit (_exit.c:31)
==2961==    by 0x4E70423: __run_exit_handlers (exit.c:98)
==2961==    by 0x4E704DC: exit (exit.c:105)
==2961==    by 0x4E58468: (below main) (libc-start.c:329)
==2961==  Uninitialised value was created by a stack allocation
==2961==    at 0x4004A4: main (main3.c:10)

thread

main4.cc
#include <thread>
 
int main() {
  int x;
  std::thread t([&]{x=42;});
  x = 43;
  t.join();
 
  return 0;
}
g++ -std=c++11 main4.c -g -o main4 -fsanitize=thread

<note important>Le plantage reste aléatoire et il est nécessaire de lancer l'application plusieurs fois. </note>

==================
WARNING: ThreadSanitizer: data race (pid=3226)
  Write of size 4 at 0x7ffd04d5b144 by thread T1:
    #0 operator() /tmp/main4.c:5 (main4+0x000000400d0b)
    #1 __invoke_impl<void, main()::<lambda()> > /usr/lib/gcc/x86_64-pc-linux-gnu/7.2.0/include/g++-v7/bits/invoke.h:60 (main4+0x000000401171)
    #2 __invoke<main()::<lambda()> > /usr/lib/gcc/x86_64-pc-linux-gnu/7.2.0/include/g++-v7/bits/invoke.h:95 (main4+0x000000400e30)
    #3 _M_invoke<0> /usr/lib/gcc/x86_64-pc-linux-gnu/7.2.0/include/g++-v7/thread:234 (main4+0x0000004014b5)
    #4 operator() /usr/lib/gcc/x86_64-pc-linux-gnu/7.2.0/include/g++-v7/thread:243 (main4+0x000000401448)
    #5 _M_run /usr/lib/gcc/x86_64-pc-linux-gnu/7.2.0/include/g++-v7/thread:186 (main4+0x0000004013ee)
    #6 <null> <null> (libstdc++.so.6+0x0000000e251e)

  Previous write of size 4 at 0x7ffd04d5b144 by main thread:
    #0 main /tmp/main4.c:6 (main4+0x000000400d75)

  Location is stack of main thread.

  Thread T1 (tid=3228, running) created by main thread at:
    #0 pthread_create <null> (libtsan.so.0+0x00000002917a)
    #1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) <null> (libstdc++.so.6+0x0000000e288c)
    #2 main /tmp/main4.c:5 (main4+0x000000400d69)

SUMMARY: ThreadSanitizer: data race /tmp/main4.c:5 in operator()
==================
ThreadSanitizer: reported 1 warnings

valgrind ne détecte pas l'erreur.

undefined

main5.c
int main(int argc, char **argv) {
  int t = argc << 16;
  return t*t;
}
gcc -fsanitize=undefined main5.c -g -o main5
main6.c:3:11: runtime error: signed integer overflow: 65536 * 65536 cannot be represented in type 'int'

valgrind ne détecte pas l'erreur.

prog/gcc.1582585906.txt.gz · Dernière modification : 2020/02/25 00:11 de root