}
value *
-program::new_str(std::string str)
+program::new_str(std::string str, bool format_str)
{
- auto old = str_map.find(str);
- if (old != str_map.end())
+ std::unordered_map<std::string, value *>& m = str_map;
+ if (format_str) m = format_map;
+
+ auto old = m.find(str);
+ if (old != m.end())
return old->second;
- value *v = new value(value::mk_str(str));
- auto ok = str_map.insert(std::pair<std::string, value *>(str, v));
+ value *v = new value(value::mk_str(str, format_str));
+ auto ok = m.insert(std::pair<std::string, value *>(str, v));
assert(ok.second);
return v;
}
int64_t imm_val;
std::string str_val;
- value(value_type t = UNINIT, regno r = noreg, int64_t c = 0, std::string s = "")
- : type(t), reg_val(r), imm_val(c), str_val(s)
+ bool format_str; // for str_val
+
+ value(value_type t = UNINIT, regno r = noreg, int64_t c = 0,
+ std::string s = "", bool format_str = false)
+ : type(t), reg_val(r), imm_val(c), str_val(s), format_str(format_str)
{ }
static value mk_imm(int64_t i) { return value(IMM, noreg, i); }
- static value mk_str(std::string s) { return value(STR, noreg, 0, s); }
+ static value mk_str(std::string s, bool format_str = false) {
+ return value(STR, noreg, 0, s, format_str);
+ }
static value mk_reg(regno r) { return value(TMPREG, r); }
static value mk_hardreg(regno r) { return value(HARDREG, r); }
bool is_reg() const { return type >= HARDREG; }
bool is_imm() const { return type == IMM; }
bool is_str() const { return type == STR; }
+ bool is_format() const { assert(is_str()); return format_str; }
regno reg() const { assert(is_reg()); return reg_val; }
int64_t imm() const { assert(is_imm()); return imm_val; }
// Store at most one of each IMM and STR value:
std::unordered_map<int64_t, value *> imm_map;
std::unordered_map<std::string, value *> str_map;
+ std::unordered_map<std::string, value *> format_map;
regno max_reg() const { return reg_vals.size() + MAX_BPF_REG; }
value *lookup_reg(regno r);
value *new_reg();
value *new_imm(int64_t);
- value *new_str(std::string);
+ value *new_str(std::string, bool format_str = false);
// The BPF local stack is [0, -512] indexed off BPF_REG_10.
// The translator has dibs on the low bytes, [0, -max_tmp_space],
// Allocate space on the stack and store a string literal in that space:
static value *
-alloc_literal_str(program &p, insn_inserter &ins, std::string &str)
+alloc_literal_str(program &p, insn_inserter &ins, value *s)
{
+ std::string str = s->str();
+
+ size_t str_bytes = str.size() + 1;
+ str_bytes += 4 - str_bytes % 4; // write aligned words to avoid garbage data
+
+ int ofs; size_t tmp_space;
+
// Append the string to existing temporary data.
//
// TODO: This could produce significant space limitations.
// A better solution would be to integrate with the
// register allocator and reclaim the space after
// the string literal is no longer live.
- size_t tmp_space = p.max_tmp_space;
+ tmp_space = p.max_tmp_space;
tmp_space += 4 - tmp_space % 4; // write aligned words to avoid verifier error
p.use_tmp_space(tmp_space);
- size_t str_bytes = str.size() + 1;
- str_bytes += 4 - str_bytes % 4; // write aligned words to avoid garbage data
if (tmp_space + str_bytes > MAX_BPF_STACK)
throw std::runtime_error("string allocation failed due to lack of room on stack");
tmp_space += str_bytes;
#if 1
+ // The following aren't ideal because an unlucky ordering of
+ // allocation requests will waste additional space.
+
// XXX PR23860: Passing a short (non-padded) string constant can fail
// the verifier, which is not smart enough to determine that accesses
// past the end of the string will never occur. To fix this, make sure
// the string offset is at least -BPF_MAXSTRINGLEN.
- //
- // Not ideal because an unlucky ordering of allocations may waste space.
- if (tmp_space < BPF_MAXSTRINGLEN)
- tmp_space = BPF_MAXSTRINGLEN;
+ //if (!s->is_format() && tmp_space < BPF_MAXSTRINGLEN)
+ // tmp_space = BPF_MAXSTRINGLEN;
+
+ // TODO PR23860: An even uglier workaround for emit_string_copy()
+ // overlapping source and destination regions. Only do this for
+ // non-format strings, as format strings are not manipulated by the
+ // eBPF program.
+ if (!s->is_format() && tmp_space < BPF_MAXSTRINGLEN * 2 + str_bytes)
+ tmp_space = BPF_MAXSTRINGLEN * 2 + str_bytes;
#endif
p.use_tmp_space(tmp_space);
- int ofs = -tmp_space;
+ ofs = -tmp_space;
value *frame = p.lookup_reg(BPF_REG_10);
value *out = emit_simple_literal_str(p, ins, frame, ofs, str, false /* don't zero pad */);
{
insn_before_inserter ins(b, j, "str");
std::string str0 = s0->str();
- value *new_s0 = alloc_literal_str(p, ins, str0);
+ value *new_s0 = alloc_literal_str(p, ins, s0);
j->src0 = new_s0;
}
{
insn_before_inserter ins(b, j, "str");
std::string str1 = s1->str();
- value *new_s1 = alloc_literal_str(p, ins, str1);
+ value *new_s1 = alloc_literal_str(p, ins, s1);
j->src1 = new_s1;
}
}
// dest[+ofs] in 4-byte chunks, with optional zero-padding up to
// BPF_MAXSTRINGLEN.
//
+// TODO (PR23860): This code does not work when the source and target
+// regions overlap.
+//
// ??? Could use 8-byte chunks if we're starved for instruction count.
// ??? Endianness of the target may come into play here.
value *
}
#ifdef DEBUG_CODEGEN
- this_ins.notes.push("strcpy");
+ this_ins.notes.push(zero_pad ? "strcpy_zero_pad" : "strcpy");
#endif
size_t str_bytes = BPF_MAXSTRINGLEN;
// bpf program stack. This is handled by bpf-opt.cxx lowering STR values.
size_t format_bytes = format.size() + 1;
this_prog.mk_mov(this_ins, this_prog.lookup_reg(BPF_REG_1),
- this_prog.new_str(format));
+ this_prog.new_str(format, true /*format_str*/));
emit_mov(this_prog.lookup_reg(BPF_REG_2), this_prog.new_imm(format_bytes));
for (size_t i = 0; i < nargs; ++i)
emit_mov(this_prog.lookup_reg(BPF_REG_3 + i), actual[i]);