diff --git a/CMakeLists.txt b/CMakeLists.txt index f09045ff..ddd4626c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ project(pipy) option(PIPY_GUI "include builtin GUI" ON) option(PIPY_SAMPLES "include builtin sample scripts" ON) +option(PIPY_BPF, "enable eBPF support" ON) option(PIPY_SOIL_FREED_SPACE "invalidate freed space for debugging" OFF) option(PIPY_ASSERT_SAME_THREAD "enable assertions for strict inner-thread data access" OFF) option(PIPY_ZLIB "external zlib location" "") @@ -191,6 +192,7 @@ add_executable(pipy src/context.cpp src/data.cpp src/deframer.cpp + src/elf.cpp src/event.cpp src/event-queue.cpp src/fetch.cpp @@ -324,6 +326,10 @@ if(PIPY_SAMPLES) add_dependencies(pipy PackSamples) endif() +if(PIPY_BPF) + add_definitions(-DPIPY_USE_BPF) +endif() + if(PIPY_SOIL_FREED_SPACE) add_definitions(-DPIPY_SOIL_FREED_SPACE) endif() @@ -357,4 +363,4 @@ target_link_libraries( leveldb -pthread -ldl -) \ No newline at end of file +) diff --git a/samples/bpf/load-balancer/test.sh b/samples/bpf/load-balancer/test.sh index eae7ae8c..a1005cbc 100755 --- a/samples/bpf/load-balancer/test.sh +++ b/samples/bpf/load-balancer/test.sh @@ -23,13 +23,13 @@ setup_net server3 veth3 10.0.3.100/24 10.0.3.200/24 sleep 1 echo 'Starting upstream servers...' -ip netns exec server1 ../../../bin/pipy -e 'pipy().listen(8080).serveHTTP(new Message("hi from server1\n"))' & -ip netns exec server2 ../../../bin/pipy -e 'pipy().listen(8080).serveHTTP(new Message("hi from server2\n"))' & -ip netns exec server3 ../../../bin/pipy -e 'pipy().listen(8080).serveHTTP(new Message("hi from server3\n"))' & +ip netns exec server1 ../../../bin/pipy -e 'pipy().listen(8080).serveHTTP(new Message("hi from server1\n"))' 2> /dev/null & +ip netns exec server2 ../../../bin/pipy -e 'pipy().listen(8080).serveHTTP(new Message("hi from server2\n"))' 2> /dev/null & +ip netns exec server3 ../../../bin/pipy -e 'pipy().listen(8080).serveHTTP(new Message("hi from server3\n"))' 2> /dev/null & sleep 1 echo 'Loading BPF programs...' -/home/shuang/git/bpftool/src/bpftool prog load /home/shuang/git/pipy/bin/load-balancer.o /sys/fs/bpf/lb +bpftool prog load /home/shuang/git/pipy/bin/load-balancer.o /sys/fs/bpf/lb ip link set lo xdpgeneric pinned /sys/fs/bpf/lb ip link set veth0 xdpgeneric pinned /sys/fs/bpf/lb ip link set veth1 xdpgeneric pinned /sys/fs/bpf/lb diff --git a/src/api/bpf.cpp b/src/api/bpf.cpp index e712977d..5fdcdd84 100644 --- a/src/api/bpf.cpp +++ b/src/api/bpf.cpp @@ -24,25 +24,26 @@ */ #include "api/bpf.hpp" +#include "elf.hpp" #include "log.hpp" -#ifdef __linux__ +#ifdef PIPY_USE_BPF + #include #include #include -#include #define attr_size(FIELD) \ (offsetof(bpf_attr, FIELD) + sizeof(bpf_attr::FIELD)) -#endif // __linux__ +#endif // PIPY_USE_BPF namespace pipy { namespace bpf { thread_local static Data::Producer s_dp("BPF"); -#ifdef __linux__ +#ifdef PIPY_USE_BPF static inline int syscall_bpf(enum bpf_cmd cmd, union bpf_attr *attr, unsigned int size) { return syscall(__NR_bpf, cmd, attr, size); @@ -56,327 +57,6 @@ static inline int syscall_bpf(enum bpf_cmd cmd, union bpf_attr *attr, unsigned i return syscall_bpf(cmd, attr, size); } -// -// OBJ -// - -class OBJ { -public: - - // - // OBJ::Section - // - - struct Section { - std::string name; - int type; - int flags; - const uint8_t *data; - size_t size; - size_t addr; - size_t addralign; - int link; - int info; - }; - - // - // OBJ::Symbol - // - - struct Symbol { - std::string name; - size_t value; - size_t size; - size_t shndx; - int info; - }; - - // - // OBJ::Relocation - // - - struct Relocation { - - // - // OBJ::Relocation::Entry - // - - struct Entry { - size_t offset; - uint64_t info; - }; - - size_t section; - std::vector entries; - }; - - int type; - int flags; - int machine; - int version; - size_t entry; - std::vector
sections; - std::vector symbols; - std::vector relocations; - - OBJ(const Data &elf) { - elf.to_bytes(m_elf); - - if (m_elf.size() <= EI_NIDENT || - m_elf[EI_MAG0] != ELFMAG0 || - m_elf[EI_MAG1] != ELFMAG1 || - m_elf[EI_MAG2] != ELFMAG2 || - m_elf[EI_MAG3] != ELFMAG3 || - m_elf[EI_CLASS] <= ELFCLASSNONE || - m_elf[EI_CLASS] >= ELFCLASSNUM || - m_elf[EI_VERSION] != EV_CURRENT - ) throw std::runtime_error("not an ELF file"); - - if ( -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - m_elf[EI_DATA] != ELFDATA2LSB -#else - m_elf[EI_DATA] != ELFDATA2MSB -#endif - ) throw std::runtime_error("mismatched ELF endianness"); - - if (m_elf[EI_OSABI] != ELFOSABI_SYSV) { - throw std::runtime_error("unsupported ABI"); - } - - auto cls = m_elf[EI_CLASS]; - - size_t phoff, shoff, ehsize, shstrndx; - size_t phentsize, phnum; - size_t shentsize, shnum; - - (void)ehsize; - (void)phoff; - (void)phentsize; - (void)phnum; - - switch (cls) { - case ELFCLASS32: { - auto &hdr = *(Elf32_Ehdr *)m_elf.data(); - type = hdr.e_type; - machine = hdr.e_machine; - version = hdr.e_version; - entry = hdr.e_entry; - phoff = hdr.e_phoff; - shoff = hdr.e_shoff; - flags = hdr.e_flags; - ehsize = hdr.e_ehsize; - phentsize = hdr.e_phentsize; - phnum = hdr.e_phnum; - shentsize = hdr.e_shentsize; - shnum = hdr.e_shnum; - shstrndx = hdr.e_shstrndx; - break; - } - case ELFCLASS64: { - auto &hdr = *(Elf64_Ehdr *)m_elf.data(); - type = hdr.e_type; - machine = hdr.e_machine; - version = hdr.e_version; - entry = hdr.e_entry; - phoff = hdr.e_phoff; - shoff = hdr.e_shoff; - flags = hdr.e_flags; - ehsize = hdr.e_ehsize; - phentsize = hdr.e_phentsize; - phnum = hdr.e_phnum; - shentsize = hdr.e_shentsize; - shnum = hdr.e_shnum; - shstrndx = hdr.e_shstrndx; - break; - } - default: throw std::runtime_error("unsupported ELF file class"); - } - - if (shoff + shentsize * shnum > m_elf.size() || shstrndx >= shnum) { - std::runtime_error("offset out of ELF file boundary"); - } - - std::vector name_offsets(shnum); - sections.resize(shnum); - - for (size_t i = 0; i < shnum; i++) { - auto offset = shoff + shentsize * i; - auto &sec = sections[i]; - switch (cls) { - case ELFCLASS32: { - auto &hdr = *(Elf32_Shdr *)(m_elf.data() + offset); - if (hdr.sh_offset + hdr.sh_size > m_elf.size()) { - section_out_of_bound(i); - } - name_offsets[i] = hdr.sh_name; - sec.type = hdr.sh_type; - sec.flags = hdr.sh_flags; - sec.addr = hdr.sh_addr; - sec.data = m_elf.data() + hdr.sh_offset; - sec.size = hdr.sh_size; - sec.link = hdr.sh_link; - sec.info = hdr.sh_info; - sec.addralign = hdr.sh_addralign; - break; - } - case ELFCLASS64: { - auto &hdr = *(Elf64_Shdr *)(m_elf.data() + offset); - if (hdr.sh_offset + hdr.sh_size > m_elf.size()) { - section_out_of_bound(i); - } - name_offsets[i] = hdr.sh_name; - sec.type = hdr.sh_type; - sec.flags = hdr.sh_flags; - sec.addr = hdr.sh_addr; - sec.data = m_elf.data() + hdr.sh_offset; - sec.size = hdr.sh_size; - sec.link = hdr.sh_link; - sec.info = hdr.sh_info; - sec.addralign = hdr.sh_addralign; - break; - } - } - } - - auto &str_tab_sec = sections[shstrndx]; - auto str_tab_head = str_tab_sec.data; - auto str_tab_size = sections[shstrndx].size; - - auto find_str = [&](size_t offset, std::string &str) { - if (offset >= str_tab_size) return false; - auto end = offset; - while (end < str_tab_size && str_tab_head[end]) end++; - str = std::string((const char *)(str_tab_head + offset), end - offset); - return true; - }; - - for (size_t i = 0; i < shnum; i++) { - auto &s = sections[i]; - if (!find_str(name_offsets[i], s.name)) { - section_out_of_bound(i); - } - Log::debug(Log::BPF, - "[bpf] SECTION #%d name '%s' addr 0x%08x size %d type %d flags %d link %d info %d", - int(i), s.name.c_str(), int(s.addr), int(s.size), s.type, s.flags, s.link, s.info - ); - } - - for (const auto &sec : sections) { - if (sec.type == SHT_SYMTAB) { - size_t entry_size = 0; - switch (cls) { - case ELFCLASS32: entry_size = sizeof(Elf32_Sym); break; - case ELFCLASS64: entry_size = sizeof(Elf64_Sym); break; - } - size_t n = entry_size ? sec.size / entry_size : 0; - symbols.resize(n); - for (size_t i = 0; i < n; i++) { - auto &s = symbols[i]; - auto offset = entry_size * i; - switch (cls) { - case ELFCLASS32: { - const auto &ent = *(Elf32_Sym *)(sec.data + offset); - if (!find_str(ent.st_name, s.name)) { - symbol_out_of_bound(i); - } - s.value = ent.st_value; - s.size = ent.st_size; - s.shndx = ent.st_shndx; - s.info = ent.st_info; - break; - } - case ELFCLASS64: { - const auto &ent = *(Elf64_Sym *)(sec.data + offset); - if (!find_str(ent.st_name, s.name)) { - symbol_out_of_bound(i); - } - s.value = ent.st_value; - s.size = ent.st_size; - s.shndx = ent.st_shndx; - s.info = ent.st_info; - break; - } - } - Log::debug(Log::BPF, - "[bpf] SYMBOL #%d name '%s' value %d size %d shndx %d info %d", - int(i), s.name.c_str(), int(s.value), int(s.shndx), s.info - ); - } - break; - } - } - - for (size_t i = 0; i < sections.size(); i++) { - const auto &sec = sections[i]; - if (sec.type == SHT_REL) { - size_t entry_size = 0; - switch (cls) { - case ELFCLASS32: entry_size = sizeof(Elf32_Rel); break; - case ELFCLASS64: entry_size = sizeof(Elf64_Rel); break; - } - size_t n = entry_size ? sec.size / entry_size : 0; - relocations.emplace_back(); - auto &reloc = relocations.back(); - reloc.section = sec.info; - reloc.entries.resize(n); - if (sec.info >= sections.size()) { - relocation_out_of_bound(i); - } - auto max_offset = sections[sec.info].size; - for (size_t j = 0; j < n; j++) { - auto &r = reloc.entries[j]; - auto offset = entry_size * j; - switch (cls) { - case ELFCLASS32: { - const auto &ent = *(Elf32_Rel *)(sec.data + offset); - r.offset = ent.r_offset; - r.info = ent.r_info; - break; - } - case ELFCLASS64: { - const auto &ent = *(Elf64_Rel *)(sec.data + offset); - r.offset = ent.r_offset; - r.info = ent.r_info; - break; - } - } - Log::debug(Log::BPF, - "[bpf] RELOC for SECTION #%d offset %d info %llu", - int(reloc.section), int(r.offset), r.info - ); - if (r.offset >= max_offset) { - relocation_out_of_bound(i); - } - } - } - } - } - -private: - std::vector m_elf; - - static void section_out_of_bound(size_t i) { - std::string msg("out of bound section: index = "); - msg += std::to_string(i); - throw std::runtime_error(msg); - } - - static void symbol_out_of_bound(size_t i) { - std::string msg("out of bound symbol: index = "); - msg += std::to_string(i); - throw std::runtime_error(msg); - } - - static void relocation_out_of_bound(size_t i) { - std::string msg("out of bound relocation: index = "); - msg += std::to_string(i); - throw std::runtime_error(msg); - } -}; - -#endif // __linux__ - // // Program // @@ -386,13 +66,10 @@ auto Program::list() -> pjs::Array* { } auto Program::load(Data *elf) -> Program* { - Program *prog = nullptr; - -#ifdef __linux__ - OBJ obj(*elf); -#endif - - return prog; + std::vector data; + elf->to_bytes(data); + ELF obj(std::move(data)); + return nullptr; } auto Program::maps() -> pjs::Array* { @@ -408,7 +85,6 @@ Map::Map(int fd, CStruct *key_type, CStruct *value_type) , m_key_type(key_type) , m_value_type(value_type) { -#ifdef __linux__ union bpf_attr attr; struct bpf_map_info info = {}; syscall_bpf( @@ -421,12 +97,10 @@ Map::Map(int fd, CStruct *key_type, CStruct *value_type) ); m_key_size = info.key_size; m_value_size = info.value_size; -#endif // __linux__ } auto Map::list() -> pjs::Array* { auto a = pjs::Array::make(); -#ifdef __linux__ union bpf_attr attr; unsigned int id = 0; for (;;) { @@ -465,12 +139,10 @@ auto Map::list() -> pjs::Array* { a->push(i); } -#endif // __linux__ return a; } auto Map::open(int id, CStruct *key_type, CStruct *value_type) -> Map* { -#ifdef __linux__ union bpf_attr attr; int fd = syscall_bpf( BPF_MAP_GET_FD_BY_ID, &attr, attr_size(open_flags), @@ -480,14 +152,11 @@ auto Map::open(int id, CStruct *key_type, CStruct *value_type) -> Map* { throw std::runtime_error("failed when trying to get fd by a map id"); } return Map::make(fd, key_type, value_type); -#else - return nullptr; -#endif // __linux__ } auto Map::keys() -> pjs::Array* { if (!m_fd) return nullptr; -#ifdef __linux__ + uint8_t k[m_key_size]; auto a = pjs::Array::make(); @@ -512,14 +181,11 @@ auto Map::keys() -> pjs::Array* { } return a; -#else - return nullptr; -#endif // __linux__ } auto Map::entries() -> pjs::Array* { if (!m_fd) return nullptr; -#ifdef __linux__ + uint8_t k[m_key_size]; uint8_t v[m_value_size]; @@ -565,9 +231,6 @@ auto Map::entries() -> pjs::Array* { } return a; -#else - return nullptr; -#endif // __linux__ } auto Map::lookup(pjs::Object *key) -> pjs::Object* { @@ -620,15 +283,12 @@ void Map::remove(pjs::Object *key) { } void Map::close() { -#ifdef __linux__ ::close(m_fd); -#endif m_fd = 0; } auto Map::lookup_raw(Data *key) -> Data* { if (!m_fd) return nullptr; -#ifdef __linux__ uint8_t k[m_key_size]; uint8_t v[m_value_size]; std::memset(k, 0, m_key_size); @@ -645,14 +305,10 @@ auto Map::lookup_raw(Data *key) -> Data* { )) return nullptr; return Data::make(&v[0], m_value_size, &s_dp); -#else - return nullptr; -#endif // __linux__ } void Map::update_raw(Data *key, Data *value) { if (!m_fd) return; -#ifdef __linux__ uint8_t k[m_key_size]; uint8_t v[m_value_size]; std::memset(k, 0, m_key_size); @@ -669,12 +325,10 @@ void Map::update_raw(Data *key, Data *value) { attr.value = (uintptr_t)v; } ); -#endif // __linux__ } void Map::delete_raw(Data *key) { if (!m_fd) return; -#ifdef __linux__ uint8_t k[m_key_size]; std::memset(k, 0, m_key_size); key->to_bytes(k, m_key_size); @@ -687,9 +341,68 @@ void Map::delete_raw(Data *key) { attr.key = (uintptr_t)k; } ); -#endif // __linux__ } +#else // !PIPY_USE_BPF + +static void unsupported() { + throw std::runtime_error("eBPF not supported"); +} + +auto Program::list() -> pjs::Array* { + unsupported(); + return nullptr; +} + +auto Program::load(Data *elf) -> Program* { + unsupported(); + return nullptr; +} + +auto Program::maps() -> pjs::Array* { + unsupported(); + return nullptr; +} + +auto Map::list() -> pjs::Array* { + unsupported(); + return nullptr; +} + +auto Map::open(int id, CStruct *key_type, CStruct *value_type) -> Map* { + unsupported(); + return nullptr; +} + +auto Map::keys() -> pjs::Array* { + unsupported(); + return nullptr; +} + +auto Map::entries() -> pjs::Array* { + unsupported(); + return nullptr; +} + +auto Map::lookup(pjs::Object *key) -> pjs::Object* { + unsupported(); + return nullptr; +} + +void Map::update(pjs::Object *key, pjs::Object *value) { + unsupported(); +} + +void Map::remove(pjs::Object *key) { + unsupported(); +} + +void Map::close() { + unsupported(); +} + +#endif // PIPY_USE_BPF + } // namespace bpf } // namespace pipy @@ -698,15 +411,6 @@ namespace pjs { using namespace pipy; using namespace pipy::bpf; -static bool linux_only(Context &ctx) { -#ifdef __linux__ - return true; -#else - ctx.error("BPF not supported"); - return false; -#endif -} - // // Program // @@ -720,20 +424,20 @@ template<> void ClassDef>::init() { ctor(); method("list", [](Context &ctx, Object *obj, Value &ret) { - if (linux_only(ctx)) { + try { ret.set(Program::list()); + } catch (std::runtime_error &err) { + ctx.error(err); } }); method("load", [](Context &ctx, Object *obj, Value &ret) { - if (linux_only(ctx)) { - pipy::Data *data; - if (!ctx.arguments(1, &data)) return; - try { - ret.set(Program::load(data)); - } catch (std::runtime_error &err) { - ctx.error(err); - } + pipy::Data *data; + if (!ctx.arguments(1, &data)) return; + try { + ret.set(Program::load(data)); + } catch (std::runtime_error &err) { + ctx.error(err); } }); } @@ -744,38 +448,48 @@ template<> void ClassDef>::init() { template<> void ClassDef::init() { method("keys", [](Context &ctx, Object *obj, Value &ret) { - if (linux_only(ctx)) { + try { ret.set(obj->as()->keys()); + } catch (std::runtime_error &err) { + ctx.error(err); } }); method("entries", [](Context &ctx, Object *obj, Value &ret) { - if (linux_only(ctx)) { + try { ret.set(obj->as()->entries()); + } catch (std::runtime_error &err) { + ctx.error(err); } }); method("lookup", [](Context &ctx, Object *obj, Value &ret) { - if (linux_only(ctx)) { - Object *key; - if (!ctx.arguments(1, &key)) return; + Object *key; + if (!ctx.arguments(1, &key)) return; + try { ret.set(obj->as()->lookup(key)); + } catch (std::runtime_error &err) { + ctx.error(err); } }); method("update", [](Context &ctx, Object *obj, Value &ret) { - if (linux_only(ctx)) { - Object *key, *value; - if (!ctx.arguments(2, &key, &value)) return; + Object *key, *value; + if (!ctx.arguments(2, &key, &value)) return; + try { obj->as()->update(key, value); + } catch (std::runtime_error &err) { + ctx.error(err); } }); method("delete", [](Context &ctx, Object *obj, Value &ret) { - if (linux_only(ctx)) { - Object *key; - if (!ctx.arguments(1, &key)) return; + Object *key; + if (!ctx.arguments(1, &key)) return; + try { obj->as()->remove(key); + } catch (std::runtime_error &err) { + ctx.error(err); } }); } @@ -785,22 +499,22 @@ template<> void ClassDef>::init() { ctor(); method("list", [](Context &ctx, Object *obj, Value &ret) { - if (linux_only(ctx)) { + try { ret.set(bpf::Map::list()); + } catch (std::runtime_error &err) { + ctx.error(err); } }); method("open", [](Context &ctx, Object *obj, Value &ret) { - if (linux_only(ctx)) { - int id; - CStruct *key_type = nullptr; - CStruct *value_type = nullptr; - if (!ctx.arguments(1, &id, &key_type, &value_type)) return; - try { - ret.set(bpf::Map::open(id, key_type, value_type)); - } catch (std::runtime_error &err) { - ctx.error(err); - } + int id; + CStruct *key_type = nullptr; + CStruct *value_type = nullptr; + if (!ctx.arguments(1, &id, &key_type, &value_type)) return; + try { + ret.set(bpf::Map::open(id, key_type, value_type)); + } catch (std::runtime_error &err) { + ctx.error(err); } }); } diff --git a/src/elf.cpp b/src/elf.cpp new file mode 100644 index 00000000..5703f5a1 --- /dev/null +++ b/src/elf.cpp @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2019 by flomesh.io + * + * Unless prior written consent has been obtained from the copyright + * owner, the following shall not be allowed. + * + * 1. The distribution of any source codes, header files, make files, + * or libraries of the software. + * + * 2. Disclosure of any source codes pertaining to the software to any + * additional parties. + * + * 3. Alteration or removal of any notices in or on the software or + * within the documentation included within the software. + * + * ALL SOURCE CODE AS WELL AS ALL DOCUMENTATION INCLUDED WITH THIS + * SOFTWARE IS PROVIDED IN AN “AS IS” CONDITION, WITHOUT WARRANTY OF ANY + * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "elf.hpp" +#include "log.hpp" + +#include + +#ifdef PIPY_USE_BPF +#include +#include +#endif // PIPY_USE_BPF + +namespace pipy { + +#ifdef PIPY_USE_BPF + +static void section_out_of_bound(size_t i) { + std::string msg("out of bound section: index = "); + msg += std::to_string(i); + throw std::runtime_error(msg); +} + +static void symbol_out_of_bound(size_t i) { + std::string msg("out of bound symbol: index = "); + msg += std::to_string(i); + throw std::runtime_error(msg); +} + +static void relocation_out_of_bound(size_t i) { + std::string msg("out of bound relocation: index = "); + msg += std::to_string(i); + throw std::runtime_error(msg); +} + +ELF::ELF(std::vector &&data) : m_data(std::move(data)) { + if (m_data.size() <= EI_NIDENT || + m_data[EI_MAG0] != ELFMAG0 || + m_data[EI_MAG1] != ELFMAG1 || + m_data[EI_MAG2] != ELFMAG2 || + m_data[EI_MAG3] != ELFMAG3 || + m_data[EI_CLASS] <= ELFCLASSNONE || + m_data[EI_CLASS] >= ELFCLASSNUM || + m_data[EI_VERSION] != EV_CURRENT + ) throw std::runtime_error("not an ELF file"); + + if ( +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + m_data[EI_DATA] != ELFDATA2LSB +#else + m_data[EI_DATA] != ELFDATA2MSB +#endif + ) throw std::runtime_error("mismatched ELF endianness"); + + if (m_data[EI_OSABI] != ELFOSABI_SYSV) { + throw std::runtime_error("unsupported ABI"); + } + + auto cls = m_data[EI_CLASS]; + + size_t phoff, shoff, ehsize, shstrndx; + size_t phentsize, phnum; + size_t shentsize, shnum; + + (void)ehsize; + (void)phoff; + (void)phentsize; + (void)phnum; + + switch (cls) { + case ELFCLASS32: { + auto &hdr = *(Elf32_Ehdr *)m_data.data(); + type = hdr.e_type; + machine = hdr.e_machine; + version = hdr.e_version; + entry = hdr.e_entry; + phoff = hdr.e_phoff; + shoff = hdr.e_shoff; + flags = hdr.e_flags; + ehsize = hdr.e_ehsize; + phentsize = hdr.e_phentsize; + phnum = hdr.e_phnum; + shentsize = hdr.e_shentsize; + shnum = hdr.e_shnum; + shstrndx = hdr.e_shstrndx; + break; + } + case ELFCLASS64: { + auto &hdr = *(Elf64_Ehdr *)m_data.data(); + type = hdr.e_type; + machine = hdr.e_machine; + version = hdr.e_version; + entry = hdr.e_entry; + phoff = hdr.e_phoff; + shoff = hdr.e_shoff; + flags = hdr.e_flags; + ehsize = hdr.e_ehsize; + phentsize = hdr.e_phentsize; + phnum = hdr.e_phnum; + shentsize = hdr.e_shentsize; + shnum = hdr.e_shnum; + shstrndx = hdr.e_shstrndx; + break; + } + default: throw std::runtime_error("unsupported ELF file class"); + } + + if (shoff + shentsize * shnum > m_data.size() || shstrndx >= shnum) { + std::runtime_error("offset out of ELF file boundary"); + } + + std::vector name_offsets(shnum); + sections.resize(shnum); + + for (size_t i = 0; i < shnum; i++) { + auto offset = shoff + shentsize * i; + auto &sec = sections[i]; + switch (cls) { + case ELFCLASS32: { + auto &hdr = *(Elf32_Shdr *)(m_data.data() + offset); + if (hdr.sh_offset + hdr.sh_size > m_data.size()) { + section_out_of_bound(i); + } + name_offsets[i] = hdr.sh_name; + sec.type = hdr.sh_type; + sec.flags = hdr.sh_flags; + sec.addr = hdr.sh_addr; + sec.data = m_data.data() + hdr.sh_offset; + sec.size = hdr.sh_size; + sec.link = hdr.sh_link; + sec.info = hdr.sh_info; + sec.addralign = hdr.sh_addralign; + break; + } + case ELFCLASS64: { + auto &hdr = *(Elf64_Shdr *)(m_data.data() + offset); + if (hdr.sh_offset + hdr.sh_size > m_data.size()) { + section_out_of_bound(i); + } + name_offsets[i] = hdr.sh_name; + sec.type = hdr.sh_type; + sec.flags = hdr.sh_flags; + sec.addr = hdr.sh_addr; + sec.data = m_data.data() + hdr.sh_offset; + sec.size = hdr.sh_size; + sec.link = hdr.sh_link; + sec.info = hdr.sh_info; + sec.addralign = hdr.sh_addralign; + break; + } + } + } + + auto &str_tab_sec = sections[shstrndx]; + auto str_tab_head = str_tab_sec.data; + auto str_tab_size = sections[shstrndx].size; + + auto find_str = [&](size_t offset, std::string &str) { + if (offset >= str_tab_size) return false; + auto end = offset; + while (end < str_tab_size && str_tab_head[end]) end++; + str = std::string((const char *)(str_tab_head + offset), end - offset); + return true; + }; + + for (size_t i = 0; i < shnum; i++) { + auto &s = sections[i]; + if (!find_str(name_offsets[i], s.name)) { + section_out_of_bound(i); + } + Log::debug(Log::BPF, + "[bpf] SECTION #%d name '%s' addr 0x%08x size %d type %d flags %d link %d info %d", + int(i), s.name.c_str(), int(s.addr), int(s.size), s.type, s.flags, s.link, s.info + ); + } + + for (const auto &sec : sections) { + if (sec.type == SHT_SYMTAB) { + size_t entry_size = 0; + switch (cls) { + case ELFCLASS32: entry_size = sizeof(Elf32_Sym); break; + case ELFCLASS64: entry_size = sizeof(Elf64_Sym); break; + } + size_t n = entry_size ? sec.size / entry_size : 0; + symbols.resize(n); + for (size_t i = 0; i < n; i++) { + auto &s = symbols[i]; + auto offset = entry_size * i; + switch (cls) { + case ELFCLASS32: { + const auto &ent = *(Elf32_Sym *)(sec.data + offset); + if (!find_str(ent.st_name, s.name)) { + symbol_out_of_bound(i); + } + s.value = ent.st_value; + s.size = ent.st_size; + s.shndx = ent.st_shndx; + s.info = ent.st_info; + break; + } + case ELFCLASS64: { + const auto &ent = *(Elf64_Sym *)(sec.data + offset); + if (!find_str(ent.st_name, s.name)) { + symbol_out_of_bound(i); + } + s.value = ent.st_value; + s.size = ent.st_size; + s.shndx = ent.st_shndx; + s.info = ent.st_info; + break; + } + } + Log::debug(Log::BPF, + "[bpf] SYMBOL #%d name '%s' value %d size %d shndx %d info %d", + int(i), s.name.c_str(), int(s.value), int(s.shndx), s.info + ); + } + break; + } + } + + for (size_t i = 0; i < sections.size(); i++) { + const auto &sec = sections[i]; + if (sec.type == SHT_REL) { + size_t entry_size = 0; + switch (cls) { + case ELFCLASS32: entry_size = sizeof(Elf32_Rel); break; + case ELFCLASS64: entry_size = sizeof(Elf64_Rel); break; + } + size_t n = entry_size ? sec.size / entry_size : 0; + relocations.emplace_back(); + auto &reloc = relocations.back(); + reloc.section = sec.info; + reloc.entries.resize(n); + if (sec.info >= sections.size()) { + relocation_out_of_bound(i); + } + auto max_offset = sections[sec.info].size; + for (size_t j = 0; j < n; j++) { + auto &r = reloc.entries[j]; + auto offset = entry_size * j; + switch (cls) { + case ELFCLASS32: { + const auto &ent = *(Elf32_Rel *)(sec.data + offset); + r.offset = ent.r_offset; + r.info = ent.r_info; + break; + } + case ELFCLASS64: { + const auto &ent = *(Elf64_Rel *)(sec.data + offset); + r.offset = ent.r_offset; + r.info = ent.r_info; + break; + } + } + Log::debug(Log::BPF, + "[bpf] RELOC for SECTION #%d offset %d info %llu", + int(reloc.section), int(r.offset), r.info + ); + if (r.offset >= max_offset) { + relocation_out_of_bound(i); + } + } + } + } +} + +#else // !PIPY_USE_BPF + +static void unsupported() { + throw std::runtime_error("eBPF not supported"); +} + +ELF::ELF(std::vector &&data) { + unsupported(); +} + +#endif // PIPY_USE_BPF + +} // namespace pipy diff --git a/src/elf.hpp b/src/elf.hpp new file mode 100644 index 00000000..9e0a7c8f --- /dev/null +++ b/src/elf.hpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2019 by flomesh.io + * + * Unless prior written consent has been obtained from the copyright + * owner, the following shall not be allowed. + * + * 1. The distribution of any source codes, header files, make files, + * or libraries of the software. + * + * 2. Disclosure of any source codes pertaining to the software to any + * additional parties. + * + * 3. Alteration or removal of any notices in or on the software or + * within the documentation included within the software. + * + * ALL SOURCE CODE AS WELL AS ALL DOCUMENTATION INCLUDED WITH THIS + * SOFTWARE IS PROVIDED IN AN “AS IS” CONDITION, WITHOUT WARRANTY OF ANY + * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef ELF_HPP +#define ELF_HPP + +#include +#include + +namespace pipy { + +// +// ELF +// + +class ELF { +public: + + // + // ELF::Section + // + + struct Section { + std::string name; + int type; + int flags; + const uint8_t *data; + size_t size; + size_t addr; + size_t addralign; + int link; + int info; + }; + + // + // ELF::Symbol + // + + struct Symbol { + std::string name; + size_t value; + size_t size; + size_t shndx; + int info; + }; + + // + // ELF::Relocation + // + + struct Relocation { + + // + // ELF::Relocation::Entry + // + + struct Entry { + size_t offset; + uint64_t info; + }; + + size_t section; + std::vector entries; + }; + + int type; + int flags; + int machine; + int version; + size_t entry; + std::vector
sections; + std::vector symbols; + std::vector relocations; + + ELF(std::vector &&data); + +private: + std::vector m_data; +}; + +} // namespace pipy + +#endif // ELF_HPP