Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
B
bcc
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
bcc
Commits
634f4b0a
Commit
634f4b0a
authored
May 17, 2017
by
4ast
Committed by
GitHub
May 17, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1180 from iovisor/scanf_string_support
Enable recursive scanf support for char[] as string
parents
2923e204
37ff0e32
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
184 additions
and
34 deletions
+184
-34
src/cc/bpf_module.cc
src/cc/bpf_module.cc
+152
-34
tests/python/test_clang.py
tests/python/test_clang.py
+32
-0
No files found.
src/cc/bpf_module.cc
View file @
634f4b0a
...
...
@@ -153,24 +153,119 @@ static void debug_printf(Module *mod, IRBuilder<> &B, const string &fmt, vector<
B
.
CreateCall
(
fprintf_fn
,
args
);
}
static
void
finish_sscanf
(
IRBuilder
<>
&
B
,
vector
<
Value
*>
*
args
,
string
*
fmt
,
const
map
<
string
,
Value
*>
&
locals
,
bool
exact_args
)
{
// fmt += "%n";
// int nread = 0;
// int n = sscanf(s, fmt, args..., &nread);
// if (n < 0) return -1;
// s = &s[nread];
Value
*
sptr
=
locals
.
at
(
"sptr"
);
Value
*
nread
=
locals
.
at
(
"nread"
);
Function
*
cur_fn
=
B
.
GetInsertBlock
()
->
getParent
();
Function
*
sscanf_fn
=
B
.
GetInsertBlock
()
->
getModule
()
->
getFunction
(
"sscanf"
);
*
fmt
+=
"%n"
;
B
.
CreateStore
(
B
.
getInt32
(
0
),
nread
);
GlobalVariable
*
fmt_gvar
=
B
.
CreateGlobalString
(
*
fmt
,
"fmt"
);
(
*
args
)[
1
]
=
B
.
CreateInBoundsGEP
(
fmt_gvar
,
{
B
.
getInt64
(
0
),
B
.
getInt64
(
0
)});
(
*
args
)[
0
]
=
B
.
CreateLoad
(
sptr
);
args
->
push_back
(
nread
);
CallInst
*
call
=
B
.
CreateCall
(
sscanf_fn
,
*
args
);
call
->
setTailCall
(
true
);
BasicBlock
*
label_true
=
BasicBlock
::
Create
(
B
.
getContext
(),
""
,
cur_fn
);
BasicBlock
*
label_false
=
BasicBlock
::
Create
(
B
.
getContext
(),
""
,
cur_fn
);
// exact_args means fail if don't consume exact number of "%" inputs
// exact_args is disabled for string parsing (empty case)
Value
*
cond
=
exact_args
?
B
.
CreateICmpNE
(
call
,
B
.
getInt32
(
args
->
size
()
-
3
))
:
B
.
CreateICmpSLT
(
call
,
B
.
getInt32
(
0
));
B
.
CreateCondBr
(
cond
,
label_true
,
label_false
);
B
.
SetInsertPoint
(
label_true
);
B
.
CreateRet
(
B
.
getInt32
(
-
1
));
B
.
SetInsertPoint
(
label_false
);
// s = &s[nread];
B
.
CreateStore
(
B
.
CreateInBoundsGEP
(
B
.
CreateLoad
(
sptr
),
B
.
CreateLoad
(
nread
,
true
)),
sptr
);
args
->
resize
(
2
);
fmt
->
clear
();
}
// recursive helper to capture the arguments
static
void
parse_type
(
IRBuilder
<>
&
B
,
vector
<
Value
*>
*
args
,
string
*
fmt
,
Type
*
type
,
Value
*
out
,
bool
is_writer
)
{
Type
*
type
,
Value
*
out
,
const
map
<
string
,
Value
*>
&
locals
,
bool
is_writer
)
{
if
(
StructType
*
st
=
dyn_cast
<
StructType
>
(
type
))
{
*
fmt
+=
"{ "
;
unsigned
idx
=
0
;
for
(
auto
field
:
st
->
elements
())
{
parse_type
(
B
,
args
,
fmt
,
field
,
B
.
CreateStructGEP
(
type
,
out
,
idx
++
),
is_writer
);
parse_type
(
B
,
args
,
fmt
,
field
,
B
.
CreateStructGEP
(
type
,
out
,
idx
++
),
locals
,
is_writer
);
*
fmt
+=
" "
;
}
*
fmt
+=
"}"
;
}
else
if
(
ArrayType
*
at
=
dyn_cast
<
ArrayType
>
(
type
))
{
*
fmt
+=
"[ "
;
for
(
size_t
i
=
0
;
i
<
at
->
getNumElements
();
++
i
)
{
parse_type
(
B
,
args
,
fmt
,
at
->
getElementType
(),
B
.
CreateStructGEP
(
type
,
out
,
i
),
is_writer
);
*
fmt
+=
" "
;
if
(
at
->
getElementType
()
==
B
.
getInt8Ty
())
{
// treat i8[] as a char string instead of as an array of u8's
if
(
is_writer
)
{
*
fmt
+=
"
\"
%s
\"
"
;
args
->
push_back
(
out
);
}
else
{
// When reading strings, scanf doesn't support empty "", so we need to
// break this up into multiple scanf calls. To understand it, let's take
// an example:
// struct Event {
// u32 a;
// struct {
// char x[64];
// int y;
// } b[2];
// u32 c;
// };
// The writer string would look like:
// "{ 0x%x [ { \"%s\" 0x%x } { \"%s\" 0x%x } ] 0x%x }"
// But the reader string needs to restart at each \"\".
// reader0(const char *s, struct Event *val) {
// int nread, rc;
// nread = 0;
// rc = sscanf(s, "{ %i [ { \"%n", &val->a, &nread);
// if (rc != 1) return -1;
// s += nread; nread = 0;
// rc = sscanf(s, "%[^\"]%n", &val->b[0].x, &nread);
// if (rc < 0) return -1;
// s += nread; nread = 0;
// rc = sscanf(s, "\" %i } { \"%n", &val->b[0].y, &nread);
// if (rc != 1) return -1;
// s += nread; nread = 0;
// rc = sscanf(s, "%[^\"]%n", &val->b[1].x, &nread);
// if (rc < 0) return -1;
// s += nread; nread = 0;
// rc = sscanf(s, "\" %i } ] %i }%n", &val->b[1].y, &val->c, &nread);
// if (rc != 2) return -1;
// s += nread; nread = 0;
// return 0;
// }
*
fmt
+=
"
\"
"
;
finish_sscanf
(
B
,
args
,
fmt
,
locals
,
true
);
*
fmt
=
"%[^
\"
]"
;
args
->
push_back
(
out
);
finish_sscanf
(
B
,
args
,
fmt
,
locals
,
false
);
*
fmt
=
"
\"
"
;
}
}
else
{
*
fmt
+=
"[ "
;
for
(
size_t
i
=
0
;
i
<
at
->
getNumElements
();
++
i
)
{
parse_type
(
B
,
args
,
fmt
,
at
->
getElementType
(),
B
.
CreateStructGEP
(
type
,
out
,
i
),
locals
,
is_writer
);
*
fmt
+=
" "
;
}
*
fmt
+=
"]"
;
}
*
fmt
+=
"]"
;
}
else
if
(
isa
<
PointerType
>
(
type
))
{
*
fmt
+=
"0xl"
;
if
(
is_writer
)
...
...
@@ -196,6 +291,21 @@ static void parse_type(IRBuilder<> &B, vector<Value *> *args, string *fmt,
}
}
// make_reader generates a dynamic function in the instruction set of the host
// (not bpf) that is able to convert c-strings in the pretty-print format of
// make_writer back into binary representations. The encoding of the string
// takes the llvm ir structure format, which closely maps the c structure but
// not exactly (no support for unions for instance).
// The general algorithm is:
// pod types (u8..u64) <= %i
// array types
// u8[] no nested quotes :( <= "..."
// !u8[] <= [ %i %i ... ]
// struct types
// struct { u8 a; u64 b; } <= { %i %i }
// nesting is supported
// struct { struct { u8 a[]; }; } <= { "" }
// struct { struct { u64 a[]; }; } <= { [ %i %i .. ] }
string
BPFModule
::
make_reader
(
Module
*
mod
,
Type
*
type
)
{
auto
fn_it
=
readers_
.
find
(
type
);
if
(
fn_it
!=
readers_
.
end
())
...
...
@@ -209,6 +319,16 @@ string BPFModule::make_reader(Module *mod, Type *type) {
IRBuilder
<>
B
(
*
ctx_
);
FunctionType
*
sscanf_fn_type
=
FunctionType
::
get
(
B
.
getInt32Ty
(),
{
B
.
getInt8PtrTy
(),
B
.
getInt8PtrTy
()},
/*isVarArg=*/
true
);
Function
*
sscanf_fn
=
mod
->
getFunction
(
"sscanf"
);
if
(
!
sscanf_fn
)
{
sscanf_fn
=
Function
::
Create
(
sscanf_fn_type
,
GlobalValue
::
ExternalLinkage
,
"sscanf"
,
mod
);
sscanf_fn
->
setCallingConv
(
CallingConv
::
C
);
sscanf_fn
->
addFnAttr
(
Attribute
::
NoUnwind
);
}
string
name
=
"reader"
+
std
::
to_string
(
readers_
.
size
());
vector
<
Type
*>
fn_args
({
B
.
getInt8PtrTy
(),
PointerType
::
getUnqual
(
type
)});
FunctionType
*
fn_type
=
FunctionType
::
get
(
B
.
getInt32Ty
(),
fn_args
,
/*isVarArg=*/
false
);
...
...
@@ -223,46 +343,41 @@ string BPFModule::make_reader(Module *mod, Type *type) {
arg_out
->
setName
(
"out"
);
BasicBlock
*
label_entry
=
BasicBlock
::
Create
(
*
ctx_
,
"entry"
,
fn
);
BasicBlock
*
label_exit
=
BasicBlock
::
Create
(
*
ctx_
,
"exit"
,
fn
);
B
.
SetInsertPoint
(
label_entry
);
vector
<
Value
*>
args
({
arg_in
,
nullptr
});
Value
*
nread
=
B
.
CreateAlloca
(
B
.
getInt32Ty
());
Value
*
sptr
=
B
.
CreateAlloca
(
B
.
getInt8PtrTy
());
map
<
string
,
Value
*>
locals
{{
"nread"
,
nread
},
{
"sptr"
,
sptr
}};
B
.
CreateStore
(
arg_in
,
sptr
);
vector
<
Value
*>
args
({
nullptr
,
nullptr
});
string
fmt
;
parse_type
(
B
,
&
args
,
&
fmt
,
type
,
arg_out
,
false
);
GlobalVariable
*
fmt_gvar
=
B
.
CreateGlobalString
(
fmt
,
"fmt"
);
args
[
1
]
=
B
.
CreateInBoundsGEP
(
fmt_gvar
,
vector
<
Value
*>
({
B
.
getInt64
(
0
),
B
.
getInt64
(
0
)}));
parse_type
(
B
,
&
args
,
&
fmt
,
type
,
arg_out
,
locals
,
false
);
if
(
0
)
debug_printf
(
mod
,
B
,
"%p %p
\n
"
,
vector
<
Value
*>
({
arg_in
,
arg_out
}));
vector
<
Type
*>
sscanf_fn_args
({
B
.
getInt8PtrTy
(),
B
.
getInt8PtrTy
()});
FunctionType
*
sscanf_fn_type
=
FunctionType
::
get
(
B
.
getInt32Ty
(),
sscanf_fn_args
,
/*isVarArg=*/
true
);
Function
*
sscanf_fn
=
mod
->
getFunction
(
"sscanf"
);
if
(
!
sscanf_fn
)
sscanf_fn
=
Function
::
Create
(
sscanf_fn_type
,
GlobalValue
::
ExternalLinkage
,
"sscanf"
,
mod
);
sscanf_fn
->
setCallingConv
(
CallingConv
::
C
);
sscanf_fn
->
addFnAttr
(
Attribute
::
NoUnwind
);
CallInst
*
call
=
B
.
CreateCall
(
sscanf_fn
,
args
);
call
->
setTailCall
(
true
);
BasicBlock
*
label_then
=
BasicBlock
::
Create
(
*
ctx_
,
"then"
,
fn
);
Value
*
is_neq
=
B
.
CreateICmpNE
(
call
,
B
.
getInt32
(
args
.
size
()
-
2
));
B
.
CreateCondBr
(
is_neq
,
label_then
,
label_exit
);
B
.
SetInsertPoint
(
label_then
);
B
.
CreateRet
(
B
.
getInt32
(
-
1
));
finish_sscanf
(
B
,
&
args
,
&
fmt
,
locals
,
true
);
B
.
SetInsertPoint
(
label_exit
);
B
.
CreateRet
(
B
.
getInt32
(
0
));
readers_
[
type
]
=
name
;
return
name
;
}
// make_writer generates a dynamic function in the instruction set of the host
// (not bpf) that is able to pretty-print key/leaf entries as a c-string. The
// encoding of the string takes the llvm ir structure format, which closely maps
// the c structure but not exactly (no support for unions for instance).
// The general algorithm is:
// pod types (u8..u64) => 0x%x
// array types
// u8[] => "..."
// !u8[] => [ 0x%x 0x%x ... ]
// struct types
// struct { u8 a; u64 b; } => { 0x%x 0x%x }
// nesting is supported
// struct { struct { u8 a[]; }; } => { "" }
// struct { struct { u64 a[]; }; } => { [ 0x%x 0x%x .. ] }
string
BPFModule
::
make_writer
(
Module
*
mod
,
Type
*
type
)
{
auto
fn_it
=
writers_
.
find
(
type
);
if
(
fn_it
!=
writers_
.
end
())
...
...
@@ -293,9 +408,12 @@ string BPFModule::make_writer(Module *mod, Type *type) {
BasicBlock
*
label_entry
=
BasicBlock
::
Create
(
*
ctx_
,
"entry"
,
fn
);
B
.
SetInsertPoint
(
label_entry
);
map
<
string
,
Value
*>
locals
{
{
"nread"
,
B
.
CreateAlloca
(
B
.
getInt64Ty
())},
};
vector
<
Value
*>
args
({
arg_out
,
B
.
CreateZExt
(
arg_len
,
B
.
getInt64Ty
()),
nullptr
});
string
fmt
;
parse_type
(
B
,
&
args
,
&
fmt
,
type
,
arg_in
,
true
);
parse_type
(
B
,
&
args
,
&
fmt
,
type
,
arg_in
,
locals
,
true
);
GlobalVariable
*
fmt_gvar
=
B
.
CreateGlobalString
(
fmt
,
"fmt"
);
...
...
tests/python/test_clang.py
View file @
634f4b0a
...
...
@@ -121,6 +121,38 @@ BPF_HASH(stats, int, struct { u32 a[3]; u32 b; }, 10);
self
.
assertEqual
(
l
.
a
[
2
],
3
)
self
.
assertEqual
(
l
.
b
,
4
)
def
test_sscanf_string
(
self
):
text
=
"""
struct Symbol {
char name[128];
char path[128];
};
struct Event {
uint32_t pid;
uint32_t tid;
struct Symbol stack[64];
};
BPF_TABLE("array", int, struct Event, comms, 1);
"""
b
=
BPF
(
text
=
text
)
t
=
b
.
get_table
(
"comms"
)
s1
=
t
.
leaf_sprintf
(
t
[
0
])
fill
=
b' { "" "" }'
*
63
self
.
assertEqual
(
s1
,
b'{ 0x0 0x0 [ { "" "" }%s ] }'
%
fill
)
l
=
t
.
Leaf
(
1
,
2
)
name
=
b"libxyz"
path
=
b"/usr/lib/libxyz.so"
l
.
stack
[
0
].
name
=
name
l
.
stack
[
0
].
path
=
path
s2
=
t
.
leaf_sprintf
(
l
)
self
.
assertEqual
(
s2
,
b'{ 0x1 0x2 [ { "%s" "%s" }%s ] }'
%
(
name
,
path
,
fill
))
l
=
t
.
leaf_scanf
(
s2
)
self
.
assertEqual
(
l
.
pid
,
1
)
self
.
assertEqual
(
l
.
tid
,
2
)
self
.
assertEqual
(
l
.
stack
[
0
].
name
,
name
)
self
.
assertEqual
(
l
.
stack
[
0
].
path
,
path
)
def
test_iosnoop
(
self
):
text
=
"""
#include <linux/blkdev.h>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment