hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 2a542b20b82b7efe260b909d8c95413d87e87b0f
parent 45564c203da1b09d4fd3e5e1a3f9e68d656bbe91
Author: Drew DeVault <sir@cmpwn.com>
Date:   Tue,  2 Jan 2024 12:42:14 +0100

debug::image: new module

This module adds support for doing a few common operations around ELF
executables. It exists mainly to support the future debug:: module.

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Adebug/image/README | 6++++++
Adebug/image/open.ha | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adebug/image/sections.ha | 104+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adebug/image/self+freebsd.ha | 48++++++++++++++++++++++++++++++++++++++++++++++++
Adebug/image/self+linux.ha | 27+++++++++++++++++++++++++++
Adebug/image/self+openbsd.ha | 11+++++++++++
Adebug/image/self_argv.ha | 26++++++++++++++++++++++++++
7 files changed, 282 insertions(+), 0 deletions(-)

diff --git a/debug/image/README b/debug/image/README @@ -0,0 +1,6 @@ +This module implements functionality for examining executable files. It provides +some support code for working with memory-mapped ELF executables. + +That this module does not make compatibility guarantees and is subject to change +in the future should Hare be ported to a target with a different executable +format. diff --git a/debug/image/open.ha b/debug/image/open.ha @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MPL-2.0 +// (c) Hare authors <https://harelang.org> + +use format::elf; +use format::elf::{shn}; +use io; + +export type image = struct { + fd: io::file, + data: []u8, + header: *elf::header64, + // Cached sections + shstrtab: nullable *elf::section64, + symtab: nullable *elf::section64, + strtab: nullable *elf::section64, + debug_abbr: nullable *elf::section64, + debug_aranges: nullable *elf::section64, + debug_info: nullable *elf::section64, + debug_line: nullable *elf::section64, + debug_str: nullable *elf::section64, +}; + +// Opens an [[io::file]] as a program image. +export fn open( + file: io::file, +) (image | io::error) = { + const orig = io::tell(file)?; + io::seek(file, 0, io::whence::END)?; + const length = io::tell(file)?: size; + io::seek(file, orig, io::whence::SET)?; + + const base = io::mmap(null, length, + io::prot::READ, + io::mflag::PRIVATE, + file, 0z)?; + + const data = (base: *[*]u8)[..length]; + const head = base: *elf::header64; + + let shstrtab: nullable *elf::section64 = null; + if (head.e_shstrndx != shn::UNDEF) { + const shoffs = head.e_shoff + head.e_shstrndx * head.e_shentsize; + shstrtab = &data[shoffs]: *elf::section64; + }; + + return image { + fd = file, + data = data, + header = head, + shstrtab = shstrtab, + ... + }; +}; + +// Closes a program [[image]]. +export fn close(image: *image) void = { + io::munmap(&image.data[0], len(image.data))!; + io::close(image.fd)!; +}; + diff --git a/debug/image/sections.ha b/debug/image/sections.ha @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: MPL-2.0 +// (c) Hare authors <https://harelang.org> + +use format::elf; +use format::elf::{sht}; +use types::c; +use memio; + +// Check that this section is actually a reference to this image. +fn section_validate(image: *image, sec: *elf::section64) void = { + const addr = sec: uintptr; + const min = &image.data[0]: uintptr; + const max = min + len(image.data): uintptr; + assert(min <= addr && max > addr, "section_name: invalid section"); +}; + +// Returns a program section by name. Returns null if there is no such section, +// or if the section names are not available in this image (e.g. because it was +// stripped). +export fn section_byname( + image: *image, + name: str, +) nullable *elf::section64 = { + const cached = [ + (".symtab", &image.symtab), + (".strtab", &image.strtab), + (".debug_abbr", &image.debug_abbr), + (".debug_aranges", &image.debug_aranges), + (".debug_info", &image.debug_info), + (".debug_line", &image.debug_line), + (".debug_str", &image.debug_str), + ]; + for (let i = 0z; i < len(cached); i += 1) { + const (cand, val) = cached[i]; + if (cand == name) { + match (*val) { + case null => break; + case let sec: *elf::section64 => + return sec; + }; + }; + }; + + const head = image.header; + let r: nullable *elf::section64 = null; + for (let i = 0u16; i < head.e_shnum; i += 1) { + const shoffs = head.e_shoff + i * head.e_shentsize; + const sec = &image.data[shoffs]: *elf::section64; + if (sec.sh_type == sht::NULL) { + continue; + }; + + const cand = section_name(image, sec); + if (cand == name) { + r = sec; + break; + }; + }; + + match (r) { + case null => + return null; + case let sec: *elf::section64 => + for (let i = 0z; i < len(cached); i += 1) { + const (cand, val) = cached[i]; + if (cand == name) { + *val = sec; + }; + }; + }; + + return r; +}; + +// Returns the name of this [[elf::section64]], returning "" if the section +// names are not available in this image (i.e. it has been stripped). +export fn section_name( + image: *image, + sec: *elf::section64, +) const str = { + section_validate(image, sec); + + const shtab = match (image.shstrtab) { + case let sec: *elf::section64 => + yield sec; + case null => + return ""; + }; + + const offs = shtab.sh_offset + sec.sh_name; + return c::tostr(&image.data[offs]: *const c::char)!; +}; + +// Returns a slice of the data contained with a given section. +export fn section_data(image: *image, sec: *elf::section64) []u8 = { + section_validate(image, sec); + return image.data[sec.sh_offset..sec.sh_offset+sec.sh_size]; +}; + +// Returns a [[memio::fixed]] reader for the given section. +export fn section_reader(image: *image, sec: *elf::section64) memio::stream = { + const data = section_data(image, sec); + return memio::fixed(data); +}; diff --git a/debug/image/self+freebsd.ha b/debug/image/self+freebsd.ha @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MPL-2.0 +// (c) Hare authors <https://harelang.org> + +use errors; +use fs; +use io; +use os; +use path; +use rt; +use types::c; + +// Opens the executing process's binary image. +export fn self() (image | io::error | fs::error) = { + // 1: sysctl + let buf: [path::MAX * 2 + 1]u8 = [0...]; + let pathsz = len(buf); + match (rt::sysctlbyname("kern.proc.pathname", + &buf[0], &pathsz, null, 0)) { + case rt::errno => void; + case void => + const file = os::open(c::tostr(&buf[0]: *const c::char)!)?; + match (open(file)) { + case let img: image => + return img; + case let err: io::error => + return err; + case errors::invalid => + abort("Running program image is not a valid ELF file"); + }; + }; + + // 2. procfs (not mounted by default, but better than step 3) + match (os::open("/proc/curproc/exe")) { + case let file: io::file => + match (open(file)) { + case let img: image => + return img; + case let err: io::error => + return err; + case errors::invalid => + abort("Running program image is not a valid ELF file"); + }; + case => void; + }; + + // 3. Fallback (os::args[0]) + return self_argv(); +}; diff --git a/debug/image/self+linux.ha b/debug/image/self+linux.ha @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MPL-2.0 +// (c) Hare authors <https://harelang.org> + +use errors; +use fs; +use io; +use os; + +// Opens the executing process's binary image. +export fn self() (image | io::error | fs::error) = { + const file = match (os::open("/proc/self/exe")) { + case let file: io::file => + yield file; + case => + // procfs may not be available, try fallback + return self_argv(); + }; + + match (open(file)) { + case let img: image => + return img; + case let err: io::error => + return err; + case errors::invalid => + abort("Running program image is not a valid ELF file"); + }; +}; diff --git a/debug/image/self+openbsd.ha b/debug/image/self+openbsd.ha @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MPL-2.0 +// (c) Hare authors <https://harelang.org> + +use fs; +use io; + +// Opens the executing process's binary image. +export fn self() (image | io::error | fs::error) = { + // OpenBSD only supports the fallback approach. + return self_argv(); +}; diff --git a/debug/image/self_argv.ha b/debug/image/self_argv.ha @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MPL-2.0 +// (c) Hare authors <https://harelang.org> + +use errors; +use fs; +use io; +use os; +use os::exec; + +// Fallback implementation of self() that performs path resolution on argv[0] +fn self_argv() (image | io::error | fs::error) = { + match (exec::lookup(os::args[0])) { + case let path: str => + const file = os::open(path)?; + match (open(file)) { + case let img: image => + return img; + case let err: io::error => + return err; + case errors::invalid => + abort("Running program image is not a valid ELF file"); + }; + case void => + return errors::noentry: fs::error; + }; +};