Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
bbguimaraes
bbguimaraes.com
Commits
a44fc412
Commit
a44fc412
authored
Feb 22, 2022
by
bbguimaraes
Browse files
blog: c++ virtual tables
parent
d0b1737a
Changes
30
Expand all
Hide whitespace changes
Inline
Side-by-side
bbguimaraes.com/blog/c++-virtual-tables.html
0 → 100644
View file @
a44fc412
This diff is collapsed.
Click to expand it.
bbguimaraes.com/blog/c++-virtual-tables/adj.cpp
0 → 100644
View file @
a44fc412
struct
A
{
virtual
void
a
(
void
);
long
long
i
;
};
struct
B
{
virtual
void
b
(
void
);
};
struct
D
:
A
,
B
{};
void
f
(
B
*
);
void
g
(
D
*
p
)
{
f
(
p
);
}
bbguimaraes.com/blog/c++-virtual-tables/asm_c.sh
0 → 100755
View file @
a44fc412
#!/bin/bash
set
-euo
pipefail
gcc
\
-std
=
c18
-S
-masm
=
intel
-O2
\
-fno-stack-protector
-fno-asynchronous-unwind-tables
\
-o
-
\
"
$@
"
\
| c++filt
bbguimaraes.com/blog/c++-virtual-tables/asm_cpp.sh
0 → 100755
View file @
a44fc412
#!/bin/bash
set
-euo
pipefail
g++
\
-std
=
c++20
-S
-masm
=
intel
-O2
\
-fno-exceptions
-fno-rtti
-fno-stack-protector
\
-fno-asynchronous-unwind-tables
\
-o
-
\
"
$@
"
\
| c++filt
bbguimaraes.com/blog/c++-virtual-tables/dyn.cpp
0 → 100644
View file @
a44fc412
struct
B
{
virtual
void
b
(
void
);
};
void
*
f
(
B
*
p
)
{
return
dynamic_cast
<
void
*>
(
p
);
}
bbguimaraes.com/blog/c++-virtual-tables/leaf.c
0 → 100644
View file @
a44fc412
#include <stddef.h>
struct
typeinfo
;
struct
vtable_header
{
ptrdiff_t
offset_to_top
;
const
struct
typeinfo
*
typeinfo
;
};
struct
S
;
struct
S_vtable
{
void
(
*
const
f
)(
struct
S
*
);
};
void
S_f
(
struct
S
*
)
{}
const
struct
{
const
struct
vtable_header
header
;
const
struct
S_vtable
vtable
;
}
S_vtable
=
{
.
vtable
.
f
=
S_f
,
};
struct
S
{
const
struct
S_vtable
*
vptr
;
};
struct
S
s
=
{
.
vptr
=
&
S_vtable
.
vtable
,
};
void
f
(
struct
S
*
p
)
{
p
->
vptr
->
f
(
p
);
}
bbguimaraes.com/blog/c++-virtual-tables/leaf.cpp
0 → 100644
View file @
a44fc412
struct
S
{
virtual
void
f
(
void
);
}
s
;
void
f
(
S
*
p
)
{
p
->
f
();
}
bbguimaraes.com/blog/c++-virtual-tables/leaf_fns.cpp
0 → 100644
View file @
a44fc412
struct
S
{
virtual
void
f
(
void
);
virtual
void
g
(
void
);
virtual
void
h
(
void
);
}
s
;
void
f
(
S
*
p
)
{
p
->
f
(),
p
->
g
(),
p
->
h
();
}
bbguimaraes.com/blog/c++-virtual-tables/leaf_fns_inline.cpp
0 → 100644
View file @
a44fc412
struct
S
{
virtual
void
f
(
void
)
{}
virtual
void
g
(
void
)
{}
virtual
void
h
(
void
)
{}
}
s
;
void
f
(
S
*
p
)
{
p
->
f
(),
p
->
g
(),
p
->
h
();
}
bbguimaraes.com/blog/c++-virtual-tables/leaf_inline.c
0 → 100644
View file @
a44fc412
#include <stddef.h>
struct
typeinfo
;
struct
vtable_header
{
ptrdiff_t
offset_to_top
;
const
struct
typeinfo
*
typeinfo
;
};
struct
S
;
struct
S_vtable
{
void
(
*
const
f
)(
struct
S
*
);
};
void
S_f
(
struct
S
*
)
{}
const
struct
{
const
struct
vtable_header
header
;
const
struct
S_vtable
vtable
;
}
S_vtable
=
{
.
vtable
.
f
=
S_f
,
};
struct
S
{
const
struct
S_vtable
*
vptr
;
};
struct
S
s
=
{
.
vptr
=
&
S_vtable
.
vtable
,
};
void
f
(
struct
S
*
p
)
{
void
(
*
f
)(
struct
S
*
)
=
p
->
vptr
->
f
;
__builtin_expect
(
f
==
S_f
,
1
)
?
S_f
(
p
)
:
f
(
p
);
}
bbguimaraes.com/blog/c++-virtual-tables/leaf_inline.cpp
0 → 100644
View file @
a44fc412
struct
S
{
virtual
void
f
(
void
)
{}
}
s
;
void
f
(
S
*
p
)
{
p
->
f
();
}
bbguimaraes.com/blog/c++-virtual-tables/motivation/Makefile
0 → 100644
View file @
a44fc412
CFLAGS
:=
-O2
-g3
CXXFLAGS
:=
$(CFLAGS)
-fno-rtti
all
:
test
test.o
:
test.cpp
test
:
test.o evil.o
$(CXX)
-o
$@
$^
.PHONY
:
check clean
check
:
test
./test | c++filt
clean
:
rm
-f
*
.o
test
bbguimaraes.com/blog/c++-virtual-tables/motivation/evil.c
0 → 100644
View file @
a44fc412
#include <stddef.h>
#include <stdio.h>
#define container_of(p, t, m) ((t*)((char*)(p) - offsetof(t, m)))
struct
typeinfo
;
struct
vtable_header
{
ptrdiff_t
offset_to_top
;
const
struct
typeinfo
*
typeinfo
;
};
struct
S
;
struct
T
;
struct
U
;
struct
S_vtable
{
void
(
*
const
sf
)(
struct
S
*
);
};
struct
T_vtable
{
void
(
*
const
tf
)(
struct
T
*
);
};
struct
U_vtable
{
const
struct
S_vtable
S
;
void
(
*
const
uf
)(
struct
U
*
);
};
// base class definition in c
struct
S
{
const
struct
S_vtable
*
vptr
;
};
// base class definition in c
struct
T
{
const
struct
T_vtable
*
vptr
;
};
// derived class definition in c
struct
U
{
union
{
const
struct
U_vtable
*
vptr
;
struct
S
s
;
};
struct
T
t
;
};
// external references to member functions, defined in c++
// S::sf()
void
_ZN1S2sfEv
(
struct
S
*
p
)
{
printf
(
"%s in %s
\n
"
,
__func__
,
__FILE__
);
}
// T::tf()
void
_ZN1T2tfEv
(
struct
T
*
p
)
{
printf
(
"%s in %s
\n
"
,
__func__
,
__FILE__
);
}
// U::tf()
void
_ZN1U2tfEv
(
struct
U
*
p
)
{
printf
(
"%s in %s
\n
"
,
__func__
,
__FILE__
);
}
// U::uf()
void
_ZN1U2ufEv
(
struct
U
*
p
)
{
printf
(
"%s in %s
\n
"
,
__func__
,
__FILE__
);
}
// pointer-adjusting entrypoint for overidden function
// non-virtual thunk to U::tf()
void
_ZThn8_N1U2tfEv
(
struct
T
*
p
)
{
printf
(
"%s in %s
\n
"
,
__func__
,
__FILE__
);
// U::tf()
return
_ZN1U2tfEv
(
container_of
(
p
,
struct
U
,
t
));
}
// vtable for S
const
struct
{
const
struct
vtable_header
header
;
const
struct
S_vtable
vtable
;
}
_ZTV1S
=
{
// S::sf()
.
vtable
.
sf
=
_ZN1S2sfEv
,
};
// vtable for T
const
struct
{
const
struct
vtable_header
header
;
const
struct
T_vtable
vtable
;
}
_ZTV1T
=
{
// T::tf()
.
vtable
.
tf
=
_ZN1T2tfEv
,
};
// vtable for U
const
struct
{
const
struct
vtable_header
header
;
const
struct
U_vtable
vtable
;
const
struct
vtable_header
T_header
;
const
struct
T_vtable
T_vtable
;
}
_ZTV1U
=
{
// S::sf()
.
vtable
.
S
.
sf
=
_ZN1S2sfEv
,
// U::uf()
.
vtable
.
uf
=
_ZN1U2ufEv
,
.
T_header
.
offset_to_top
=
-
offsetof
(
struct
U
,
t
),
// T::tf()
.
T_vtable
.
tf
=
_ZN1T2tfEv
,
.
T_vtable
.
tf
=
_ZThn8_N1U2tfEv
,
};
struct
S
s
=
{
// vtable for S
.
vptr
=
&
_ZTV1S
.
vtable
,
};
struct
T
t
=
{
// vtable for T
.
vptr
=
&
_ZTV1T
.
vtable
,
};
struct
U
u
=
{
// vtable for U
.
vptr
=
&
_ZTV1U
.
vtable
,
// vtable for U
.
t
.
vptr
=
&
_ZTV1U
.
T_vtable
,
};
// free functions declared in c++
// f(S*)
void
_Z1fP1S
(
struct
S
*
p
)
{
printf
(
"%s in %s
\n
"
,
__func__
,
__FILE__
);
p
->
vptr
->
sf
(
p
);
}
// f(T*)
void
_Z1fP1T
(
struct
T
*
p
)
{
printf
(
"%s in %s
\n
"
,
__func__
,
__FILE__
);
p
->
vptr
->
tf
(
p
);
}
// f(U*)
void
_Z1fP1U
(
struct
U
*
p
)
{
printf
(
"%s in %s
\n
"
,
__func__
,
__FILE__
);
p
->
vptr
->
S
.
sf
(
&
p
->
s
);
p
->
t
.
vptr
->
tf
(
&
p
->
t
);
p
->
vptr
->
uf
(
p
);
}
bbguimaraes.com/blog/c++-virtual-tables/motivation/test.cpp
0 → 100644
View file @
a44fc412
#include <cstdio>
// base and derived classes declared in C++
struct
S
{
// base function
virtual
void
sf
(
void
);
};
struct
T
{
// base overridden function
virtual
void
tf
(
void
);
};
struct
U
:
S
,
T
{
// derived overridden function
void
tf
(
void
)
override
;
// leaf virtual function
virtual
void
uf
(
void
);
};
// derived class defined in C++
struct
V
:
U
{
void
tf
(
void
)
override
{
std
::
printf
(
"V::tf() in %s
\n
"
,
__FILE__
);
}
void
uf
(
void
)
override
{
std
::
puts
(
"
\n
=== C++ obj -> C++ offset -> C base impl ===
\n
"
);
U
::
uf
();
std
::
puts
(
"
\n
=== C++ obj -> C++ offset -> C derived impl ===
\n
"
);
std
::
printf
(
"V::uf() in %s
\n
"
,
__FILE__
);
}
virtual
void
vf
(
void
)
{
std
::
printf
(
"V::vf() in %s
\n
"
,
__FILE__
);
}
};
// C++ objects defined in C
extern
S
s
;
extern
T
t
;
extern
U
u
;
// C++ functions defined in C
void
f
(
S
*
p
);
void
f
(
T
*
p
);
void
f
(
U
*
p
);
int
main
(
void
)
{
extern
S
s
;
extern
T
t
;
std
::
puts
(
"=== C obj -> C impl ===
\n
"
);
f
(
&
s
);
f
(
&
t
);
extern
U
u
;
std
::
puts
(
"
\n
=== C obj -> C++ offset -> C base impl ===
\n
"
);
f
(
static_cast
<
S
*>
(
&
u
));
std
::
puts
(
"
\n
=== C obj -> C++ offset -> C prelude -> C derived impl ===
\n
"
);
f
(
static_cast
<
T
*>
(
&
u
));
std
::
puts
(
"
\n
=== C obj -> C impl -> virtual calls ===
\n
"
);
f
(
&
u
);
V
v
;
std
::
puts
(
"
\n
=== C++ obj -> C++ offset -> C base impl ===
\n
"
);
static_cast
<
S
*>
(
&
v
)
->
sf
();
std
::
puts
(
"
\n
=== C++ obj -> C++ offset -> C++ derived impl ===
\n
"
);
static_cast
<
T
*>
(
&
v
)
->
tf
();
static_cast
<
U
*>
(
&
v
)
->
uf
();
std
::
puts
(
"
\n
=== C++ obj -> C++ impl ===
\n
"
);
(
&
v
)
->
vf
();
}
bbguimaraes.com/blog/c++-virtual-tables/multiple.c
0 → 100644
View file @
a44fc412
#include <stddef.h>
#define container_of(p, t, m) ((t*)((char*)(p) - offsetof(t, m)))
struct
typeinfo
;
struct
vtable_header
{
ptrdiff_t
offset_to_top
;
const
struct
typeinfo
*
typeinfo
;
};
struct
S
;
struct
T
;
struct
U
;
struct
S_vtable
{
void
(
*
const
sf
)(
struct
S
*
);
};
struct
T_vtable
{
void
(
*
const
tf
)(
struct
T
*
);
};
struct
U_vtable
{
const
struct
S_vtable
S
;
const
struct
T_vtable
T
;
void
(
*
const
uf
)(
struct
U
*
);
};
struct
S
{
const
struct
S_vtable
*
vptr
;
};
struct
T
{
const
struct
T_vtable
*
vptr
;
};
struct
U
{
union
{
const
struct
U_vtable
*
vptr
;
struct
S
s
;
};
struct
T
t
;
};
void
S_sf
(
struct
S
*
)
{}
void
T_tf
(
struct
T
*
)
{}
void
U_tf
(
struct
U
*
);
void
U_uf
(
struct
U
*
)
{}
void
U_t_tf
(
struct
T
*
p
)
{
U_tf
(
container_of
(
p
,
struct
U
,
t
));
}
const
struct
{
const
struct
vtable_header
header
;
const
struct
S_vtable
vtable
;
}
S_vtable
=
{
.
vtable
.
sf
=
S_sf
,
};
const
struct
{
const
struct
vtable_header
header
;
const
struct
T_vtable
vtable
;
}
T_vtable
=
{
.
vtable
.
tf
=
T_tf
,
};
const
struct
{
const
struct
vtable_header
header
;
const
struct
U_vtable
vtable
;
const
struct
vtable_header
T_header
;
const
struct
T_vtable
T_vtable
;
}
U_vtable
=
{
.
vtable
.
S
.
sf
=
S_sf
,
.
vtable
.
T
.
tf
=
(
void
(
*
)(
struct
T
*
))
U_tf
,
.
vtable
.
uf
=
U_uf
,
.
T_header
.
offset_to_top
=
-
offsetof
(
struct
U
,
t
),
.
T_vtable
.
tf
=
U_t_tf
,
};
struct
S
s
=
{
.
vptr
=
&
S_vtable
.
vtable
,
};
struct
T
t
=
{
.
vptr
=
&
T_vtable
.
vtable
,
};
struct
U
u
=
{
.
vptr
=
&
U_vtable
.
vtable
,
.
t
.
vptr
=
&
U_vtable
.
T_vtable
,
};
void
f_S
(
struct
S
*
p
)
{
p
->
vptr
->
sf
(
p
);
}
void
f_T
(
struct
T
*
p
)
{
p
->
vptr
->
tf
(
p
);
}
void
f_U
(
struct
U
*
p
)
{
p
->
vptr
->
S
.
sf
(
&
p
->
s
);
p
->
vptr
->
T
.
tf
((
struct
T
*
)
p
);
p
->
vptr
->
uf
(
p
);
}
bbguimaraes.com/blog/c++-virtual-tables/multiple.cpp
0 → 100644
View file @
a44fc412
struct
S
{
virtual
void
sf
(
void
);
}
s
;
struct
T
{
virtual
void
tf
(
void
);
}
t
;
struct
U
:
S
,
T
{
void
tf
(
void
)
override
;
virtual
void
uf
(
void
);
}
u
;
void
f
(
S
*
p
)
{
p
->
sf
();
}
void
f
(
T
*
p
)
{
p
->
tf
();
}
void
f
(
U
*
p
)
{
p
->
sf
(),
p
->
tf
(),
p
->
uf
();
}
bbguimaraes.com/blog/c++-virtual-tables/multiple_inline.c
0 → 100644
View file @
a44fc412
#include <stddef.h>
#define container_of(p, t, m) ((t*)((char*)(p) - offsetof(t, m)))
struct
typeinfo
;
struct
vtable_header
{
ptrdiff_t
offset_to_top
;
const
struct
typeinfo
*
typeinfo
;
};
struct
S
;
struct
T
;
struct
U
;
struct
S_vtable
{
void
(
*
const
sf
)(
struct
S
*
);
};
struct
T_vtable
{
void
(
*
const
tf
)(
struct
T
*
);
};
struct
U_vtable
{
const
struct
S_vtable
S
;
const
struct
T_vtable
T
;
void
(
*
const
uf
)(
struct
U
*
);
};
struct
S
{
const
struct
S_vtable
*
vptr
;
};
struct
T
{
const
struct
T_vtable
*
vptr
;
};
struct
U
{
union
{
const
struct
U_vtable
*
vptr
;
struct
S
s
;
};
struct
T
t
;
};
void
S_sf
(
struct
S
*
)
{}
void
T_tf
(
struct
T
*
)
{}
void
U_tf
(
struct
U
*
)
{}
void
U_uf
(
struct
U
*
)
{}
void
U_t_tf
(
struct
T
*
p
)
{
return
U_tf
(
container_of
(
p
,
struct
U
,
t
));
}
const
struct
{
const
struct
vtable_header
header
;
const
struct
S_vtable
vtable
;
}
S_vtable
=
{
.
vtable
.
sf
=
S_sf
,
};
const
struct
{
const
struct
vtable_header
header
;
const
struct
T_vtable
vtable
;
}
T_vtable
=
{
.
vtable
.
tf
=
T_tf
,
};
const
struct
{
const
struct
vtable_header
header
;
const
struct
U_vtable
vtable
;
const
struct
vtable_header
T_header
;
const
struct
T_vtable
T_vtable
;
}
U_vtable
=
{
.
vtable
.
S
.
sf
=
S_sf
,
.
vtable
.
T
.
tf
=
U_tf
,
.
vtable
.
uf
=
U_uf
,
.
T_header
.
offset_to_top
=
-
offsetof
(
struct
U
,
t
),
.
T_vtable
.
tf
=
U_t_tf
,
};
struct
S
s
=
{
.
vptr
=
&
S_vtable
.
vtable
,
};