/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2013 David Chisnall * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "input_buffer.hh" #include #include #include #include #include #include #include #ifndef NDEBUG #include #endif #include #include #include #include #include #include #ifndef MAP_PREFAULT_READ #define MAP_PREFAULT_READ 0 #endif using std::string; namespace { /** * Subclass of input_buffer that mmap()s a file and owns the resulting memory. * When this object is destroyed, the memory is unmapped. */ struct mmap_input_buffer : public dtc::input_buffer { string fn; const string &filename() const override { return fn; } /** * Constructs a new buffer from the file passed in as a file * descriptor. */ mmap_input_buffer(int fd, string &&filename); /** * Unmaps the buffer, if one exists. */ virtual ~mmap_input_buffer(); }; /** * Input buffer read from standard input. This is used for reading device tree * blobs and source from standard input. It reads the entire input into * malloc'd memory, so will be very slow for large inputs. DTS and DTB files * are very rarely more than 10KB though, so this is probably not a problem. */ struct stream_input_buffer : public dtc::input_buffer { const string &filename() const override { static string n = ""; return n; } /** * The buffer that will store the data read from the standard input. */ std::vector b; /** * Constructs a new buffer from the standard input. */ stream_input_buffer(); }; mmap_input_buffer::mmap_input_buffer(int fd, string &&filename) : input_buffer(0, 0), fn(filename) { struct stat sb; if (fstat(fd, &sb)) { perror("Failed to stat file"); } size = sb.st_size; buffer = (const char*)mmap(0, size, PROT_READ, MAP_PRIVATE | MAP_PREFAULT_READ, fd, 0); if (buffer == MAP_FAILED) { perror("Failed to mmap file"); exit(EXIT_FAILURE); } } mmap_input_buffer::~mmap_input_buffer() { if (buffer != 0) { munmap(const_cast(buffer), size); } } stream_input_buffer::stream_input_buffer() : input_buffer(0, 0) { int c; while ((c = fgetc(stdin)) != EOF) { b.push_back(c); } buffer = b.data(); size = b.size(); } } // Anonymous namespace namespace dtc { void input_buffer::skip_to(char c) { while ((cursor < size) && (buffer[cursor] != c)) { cursor++; } } void text_input_buffer::skip_to(char c) { while (!finished() && (*(*this) != c)) { ++(*this); } } void text_input_buffer::skip_spaces() { if (finished()) { return; } char c = *(*this); bool last_nl = false; while ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\f') || (c == '\v') || (c == '\r')) { last_nl = ((c == '\n') || (c == '\r')); ++(*this); if (finished()) { c = '\0'; } else { c = *(*this); } } // Skip C preprocessor leftovers if ((c == '#') && ((cursor == 0) || last_nl)) { skip_to('\n'); skip_spaces(); } if (consume("/include/")) { handle_include(); skip_spaces(); } } void text_input_buffer::handle_include() { bool reallyInclude = true; if (consume("if ")) { next_token(); string name = parse_property_name(); if (defines.count(name) == 0) { reallyInclude = false; } consume('/'); } next_token(); if (!consume('"')) { parse_error("Expected quoted filename"); return; } auto loc = location(); string file = parse_to('"'); consume('"'); if (!reallyInclude) { return; } string include_file = dir + '/' + file; auto include_buffer = input_buffer::buffer_for_file(include_file, false); if (include_buffer == 0) { for (auto i : include_paths) { include_file = i + '/' + file; include_buffer = input_buffer::buffer_for_file(include_file, false); if (include_buffer != 0) { break; } } } if (depfile) { putc(' ', depfile); fputs(include_file.c_str(), depfile); } if (!include_buffer) { loc.report_error("Unable to locate input file"); return; } input_stack.push(std::move(include_buffer)); } bool text_input_buffer::read_binary_file(const std::string &filename, byte_buffer &b) { bool try_include_paths = true; string include_file; if (filename[0] == '/') { include_file = filename; // Don't try include paths if we're given an absolute path. // Failing is better so that we don't accidentally do the wrong thing, // but make it seem like everything is alright. try_include_paths = false; } else { include_file = dir + '/' + filename; } auto include_buffer = input_buffer::buffer_for_file(include_file, false); if (include_buffer == 0 && try_include_paths) { for (auto i : include_paths) { include_file = i + '/' + filename; include_buffer = input_buffer::buffer_for_file(include_file, false); if (include_buffer != 0) { break; } } } if (!include_buffer) { return false; } if (depfile) { putc(' ', depfile); fputs(include_file.c_str(), depfile); } b.insert(b.begin(), include_buffer->begin(), include_buffer->end()); return true; } input_buffer input_buffer::buffer_from_offset(int offset, int s) { if (offset < 0) { return input_buffer(); } if (s == 0) { s = size - offset; } if (offset > size) { return input_buffer(); } if (s > (size-offset)) { return input_buffer(); } return input_buffer(&buffer[offset], s); } bool input_buffer::consume(const char *str) { int len = strlen(str); if (len > size - cursor) { return false; } else { for (int i=0 ; i(&buffer[size]); errno = 0; outInt = strtoull(&buffer[cursor], &end, 0); if (end == &buffer[cursor] || (outInt == std::numeric_limits::max() && errno == ERANGE)) { return false; } cursor = end - buffer; return true; } namespace { /** * Convenience typedef for the type that we use for all values. */ typedef unsigned long long valty; /** * Expression tree currently being parsed. */ struct expression { typedef text_input_buffer::source_location source_location; /** * The type that is returned when computing the result. The boolean value * indicates whether this is a valid expression. * * FIXME: Once we can use C++17, this should be `std::optional`. */ typedef std::pair result; /** * Evaluate this node, taking into account operator precedence. */ virtual result operator()() = 0; /** * Returns the precedence of this node. Lower values indicate higher * precedence. */ virtual int precedence() = 0; /** * Constructs an expression, storing the location where it was created. */ expression(source_location l) : loc(l) {} virtual ~expression() {} #ifndef NDEBUG /** * Dumps this expression to `std::cerr`, appending a newline if `nl` is * `true`. */ void dump(bool nl=false) { void *ptr = this; if (ptr == nullptr) { std::cerr << "{nullptr}\n"; return; } dump_impl(); if (nl) { std::cerr << '\n'; } } private: /** * Method that sublcasses override to implement the behaviour of `dump()`. */ virtual void dump_impl() = 0; #endif protected: source_location loc; }; /** * Expression wrapping a single integer. Leaf nodes in the expression tree. */ class terminal_expr : public expression { /** * The value that this wraps. */ valty val; /** * Evaluate. Trivially returns the value that this class wraps. */ result operator()() override { return {val, true}; } int precedence() override { return 0; } public: /** * Constructor. */ terminal_expr(source_location l, valty v) : expression(l), val(v) {} #ifndef NDEBUG void dump_impl() override { std::cerr << val; } #endif }; /** * Parenthetical expression. Exists to make the contents opaque. */ struct paren_expression : public expression { /** * The expression within the parentheses. */ expression_ptr subexpr; /** * Constructor. Takes the child expression as the only argument. */ paren_expression(source_location l, expression_ptr p) : expression(l), subexpr(std::move(p)) {} int precedence() override { return 0; } /** * Evaluate - just forwards to the underlying expression. */ result operator()() override { return (*subexpr)(); } #ifndef NDEBUG void dump_impl() override { std::cerr << " ("; subexpr->dump(); std::cerr << ") "; } #endif }; /** * Template class for unary operators. The `OpChar` template parameter is * solely for debugging and makes it easy to print the expression. The `Op` * template parameter is a function object that implements the operator that * this class provides. Most of these are provided by the `` * header. */ template class unary_operator : public expression { /** * The subexpression for this unary operator. */ expression_ptr subexpr; result operator()() override { Op op; result s = (*subexpr)(); if (!s.second) { return s; } return {op(s.first), true}; } /** * All unary operators have the same precedence. They are all evaluated * before binary expressions, but after parentheses. */ int precedence() override { return 3; } public: unary_operator(source_location l, expression_ptr p) : expression(l), subexpr(std::move(p)) {} #ifndef NDEBUG void dump_impl() override { std::cerr << OpChar; subexpr->dump(); } #endif }; /** * Abstract base class for binary operators. Allows the tree to be modified * without knowing what the operations actually are. */ struct binary_operator_base : public expression { using expression::expression; /** * The left side of the expression. */ expression_ptr lhs; /** * The right side of the expression. */ expression_ptr rhs; /** * Insert a node somewhere down the path of left children, until it would * be preempting something that should execute first. */ void insert_left(binary_operator_base *new_left) { if (lhs->precedence() < new_left->precedence()) { new_left->rhs = std::move(lhs); lhs.reset(new_left); } else { static_cast(lhs.get())->insert_left(new_left); } } }; /** * Template class for binary operators. The precedence and the operation are * provided as template parameters. */ template struct binary_operator : public binary_operator_base { result operator()() override { Op op; result l = (*lhs)(); result r = (*rhs)(); if (!(l.second && r.second)) { return {0, false}; } return {op(l.first, r.first), true}; } int precedence() override { return Precedence; } #ifdef NDEBUG /** * Constructor. Takes the name of the operator as an argument, for * debugging. Only stores it in debug mode. */ binary_operator(source_location l, const char *) : binary_operator_base(l) {} #else const char *opName; binary_operator(source_location l, const char *o) : binary_operator_base(l), opName(o) {} void dump_impl() override { lhs->dump(); std::cerr << opName; rhs->dump(); } #endif }; /** * Ternary conditional operators (`cond ? true : false`) are a special case - * there are no other ternary operators. */ class ternary_conditional_operator : public expression { /** * The condition for the clause. */ expression_ptr cond; /** * The expression that this evaluates to if the condition is true. */ expression_ptr lhs; /** * The expression that this evaluates to if the condition is false. */ expression_ptr rhs; result operator()() override { result c = (*cond)(); result l = (*lhs)(); result r = (*rhs)(); if (!(l.second && r.second && c.second)) { return {0, false}; } return c.first ? l : r; } int precedence() override { // The actual precedence of a ternary conditional operator is 15, but // its associativity is the opposite way around to the other operators, // so we fudge it slightly. return 3; } #ifndef NDEBUG void dump_impl() override { cond->dump(); std::cerr << " ? "; lhs->dump(); std::cerr << " : "; rhs->dump(); } #endif public: ternary_conditional_operator(source_location sl, expression_ptr c, expression_ptr l, expression_ptr r) : expression(sl), cond(std::move(c)), lhs(std::move(l)), rhs(std::move(r)) {} }; template struct lshift { constexpr T operator()(const T &lhs, const T &rhs) const { return lhs << rhs; } }; template struct rshift { constexpr T operator()(const T &lhs, const T &rhs) const { return lhs >> rhs; } }; template struct unary_plus { constexpr T operator()(const T &val) const { return +val; } }; // TODO: Replace with std::bit_not once we can guarantee C++14 as a baseline. template struct bit_not { constexpr T operator()(const T &val) const { return ~val; } }; template struct divmod : public binary_operator<5, T> { using binary_operator<5, T>::binary_operator; using typename binary_operator_base::result; result operator()() override { result r = (*binary_operator_base::rhs)(); if (r.second && (r.first == 0)) { expression::loc.report_error("Division by zero"); return {0, false}; } return binary_operator<5, T>::operator()(); } }; } // anonymous namespace expression_ptr text_input_buffer::parse_binary_expression(expression_ptr lhs) { next_token(); binary_operator_base *expr = nullptr; char op = *(*this); source_location l = location(); switch (op) { default: return lhs; case '+': expr = new binary_operator<6, std::plus>(l, "+"); break; case '-': expr = new binary_operator<6, std::minus>(l, "-"); break; case '%': expr = new divmod>(l, "/"); break; case '*': expr = new binary_operator<5, std::multiplies>(l, "*"); break; case '/': expr = new divmod>(l, "/"); break; case '<': switch (peek()) { default: parse_error("Invalid operator"); return nullptr; case ' ': case '(': case '0'...'9': expr = new binary_operator<8, std::less>(l, "<"); break; case '=': ++(*this); expr = new binary_operator<8, std::less_equal>(l, "<="); break; case '<': ++(*this); expr = new binary_operator<7, lshift>(l, "<<"); break; } break; case '>': switch (peek()) { default: parse_error("Invalid operator"); return nullptr; case '(': case ' ': case '0'...'9': expr = new binary_operator<8, std::greater>(l, ">"); break; case '=': ++(*this); expr = new binary_operator<8, std::greater_equal>(l, ">="); break; case '>': ++(*this); expr = new binary_operator<7, rshift>(l, ">>"); break; return lhs; } break; case '=': if (peek() != '=') { parse_error("Invalid operator"); return nullptr; } expr = new binary_operator<9, std::equal_to>(l, "=="); break; case '!': if (peek() != '=') { parse_error("Invalid operator"); return nullptr; } cursor++; expr = new binary_operator<9, std::not_equal_to>(l, "!="); break; case '&': if (peek() == '&') { expr = new binary_operator<13, std::logical_and>(l, "&&"); } else { expr = new binary_operator<10, std::bit_and>(l, "&"); } break; case '|': if (peek() == '|') { expr = new binary_operator<12, std::logical_or>(l, "||"); } else { expr = new binary_operator<14, std::bit_or>(l, "|"); } break; case '?': { consume('?'); expression_ptr true_case = parse_expression(); next_token(); if (!true_case || !consume(':')) { parse_error("Expected : in ternary conditional operator"); return nullptr; } expression_ptr false_case = parse_expression(); if (!false_case) { parse_error("Expected false condition for ternary operator"); return nullptr; } return expression_ptr(new ternary_conditional_operator(l, std::move(lhs), std::move(true_case), std::move(false_case))); } } ++(*this); next_token(); expression_ptr e(expr); expression_ptr rhs(parse_expression()); if (!rhs) { return nullptr; } expr->lhs = std::move(lhs); if (rhs->precedence() < expr->precedence()) { expr->rhs = std::move(rhs); } else { // If we're a normal left-to-right expression, then we need to insert // this as the far-left child node of the rhs expression binary_operator_base *rhs_op = static_cast(rhs.get()); rhs_op->insert_left(expr); e.release(); return rhs; } return e; } expression_ptr text_input_buffer::parse_expression(bool stopAtParen) { next_token(); unsigned long long leftVal; expression_ptr lhs; source_location l = location(); switch (*(*this)) { case '0'...'9': if (!consume_integer(leftVal)) { return nullptr; } lhs.reset(new terminal_expr(l, leftVal)); break; case '(': { consume('('); expression_ptr &&subexpr = parse_expression(); if (!subexpr) { return nullptr; } lhs.reset(new paren_expression(l, std::move(subexpr))); if (!consume(')')) { return nullptr; } if (stopAtParen) { return lhs; } break; } case '+': { consume('+'); expression_ptr &&subexpr = parse_expression(); if (!subexpr) { return nullptr; } lhs.reset(new unary_operator<'+', unary_plus>(l, std::move(subexpr))); break; } case '-': { consume('-'); expression_ptr &&subexpr = parse_expression(); if (!subexpr) { return nullptr; } lhs.reset(new unary_operator<'-', std::negate>(l, std::move(subexpr))); break; } case '!': { consume('!'); expression_ptr &&subexpr = parse_expression(); if (!subexpr) { return nullptr; } lhs.reset(new unary_operator<'!', std::logical_not>(l, std::move(subexpr))); break; } case '~': { consume('~'); expression_ptr &&subexpr = parse_expression(); if (!subexpr) { return nullptr; } lhs.reset(new unary_operator<'~', bit_not>(l, std::move(subexpr))); break; } } if (!lhs) { return nullptr; } return parse_binary_expression(std::move(lhs)); } bool text_input_buffer::consume_integer_expression(unsigned long long &outInt) { switch (*(*this)) { case '(': { expression_ptr e(parse_expression(true)); if (!e) { return false; } auto r = (*e)(); if (r.second) { outInt = r.first; return true; } return false; } case '0'...'9': return consume_integer(outInt); default: return false; } } bool input_buffer::consume_hex_byte(uint8_t &outByte) { if (!ishexdigit((*this)[0]) && !ishexdigit((*this)[1])) { return false; } outByte = (digittoint((*this)[0]) << 4) | digittoint((*this)[1]); cursor += 2; return true; } text_input_buffer& text_input_buffer::next_token() { auto &self = *this; int start; do { start = cursor; skip_spaces(); if (finished()) { return self; } // Parse /* comments if (*self == '/' && peek() == '*') { // eat the start of the comment ++self; ++self; do { // Find the ending * of */ while ((*self != '\0') && (*self != '*') && !finished()) { ++self; } // Eat the * ++self; } while ((*self != '\0') && (*self != '/') && !finished()); // Eat the / ++self; } // Parse // comments if ((*self == '/' && peek() == '/')) { // eat the start of the comment ++self; ++self; // Find the ending of the line while (*self != '\n' && !finished()) { ++self; } // Eat the \n ++self; } } while (start != cursor); return self; } void text_input_buffer::parse_error(const char *msg) { if (input_stack.empty()) { fprintf(stderr, "Error: %s\n", msg); return; } input_buffer &b = *input_stack.top(); parse_error(msg, b, b.cursor); } void text_input_buffer::parse_error(const char *msg, input_buffer &b, int loc) { int line_count = 1; int line_start = 0; int line_end = loc; if (loc < 0 || loc > b.size) { return; } for (int i=loc ; i>0 ; --i) { if (b.buffer[i] == '\n') { line_count++; if (line_start == 0) { line_start = i+1; } } } for (int i=loc+1 ; i= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')); } }; /** * Check whether a character is in the set allowed for node names. This is a * class so that it can be used with a template function for parsing strings. */ struct is_node_name_character { static inline bool check(const char c) { switch(c) { default: return false; case 'a'...'z': case 'A'...'Z': case '0'...'9': case ',': case '.': case '+': case '-': case '_': return true; } } }; /** * Check whether a character is in the set allowed for property names. This is * a class so that it can be used with a template function for parsing strings. */ struct is_property_name_character { static inline bool check(const char c) { switch(c) { default: return false; case 'a'...'z': case 'A'...'Z': case '0'...'9': case ',': case '.': case '+': case '-': case '_': case '#': return true; } } }; template string parse(text_input_buffer &s) { std::vector bytes; for (char c=*s ; T::check(c) ; c=*(++s)) { bytes.push_back(c); } return string(bytes.begin(), bytes.end()); } } string text_input_buffer::parse_node_name() { return parse(*this); } string text_input_buffer::parse_property_name() { return parse(*this); } string text_input_buffer::parse_node_or_property_name(bool &is_property) { if (is_property) { return parse_property_name(); } std::vector bytes; for (char c=*(*this) ; is_node_name_character::check(c) ; c=*(++(*this))) { bytes.push_back(c); } for (char c=*(*this) ; is_property_name_character::check(c) ; c=*(++(*this))) { bytes.push_back(c); is_property = true; } return string(bytes.begin(), bytes.end()); } string input_buffer::parse_to(char stop) { std::vector bytes; for (char c=*(*this) ; c != stop ; c=*(++(*this))) { bytes.push_back(c); } return string(bytes.begin(), bytes.end()); } string text_input_buffer::parse_to(char stop) { std::vector bytes; for (char c=*(*this) ; c != stop ; c=*(++(*this))) { if (finished()) { break; } bytes.push_back(c); } return string(bytes.begin(), bytes.end()); } char text_input_buffer::peek() { return (*input_stack.top())[1]; } std::unique_ptr input_buffer::buffer_for_file(const string &path, bool warn) { if (path == "-") { std::unique_ptr b(new stream_input_buffer()); return b; } int source = open(path.c_str(), O_RDONLY); if (source == -1) { if (warn) { fprintf(stderr, "Unable to open file '%s'. %s\n", path.c_str(), strerror(errno)); } return 0; } struct stat st; if (fstat(source, &st) == 0 && S_ISDIR(st.st_mode)) { if (warn) { fprintf(stderr, "File %s is a directory\n", path.c_str()); } close(source); return 0; } std::unique_ptr b(new mmap_input_buffer(source, string(path))); close(source); return b; } } // namespace dtc