Newer
Older
alicedbg / dumper / format / elf.d
/// ELF dumper
///
/// Authors: dd86k <dd@dax.moe>
/// Copyright: © dd86k <dd@dax.moe>
/// License: BSD-3-Clause-Clear
module format.elf;

import adbg.utils.bit : adbg_align4up;
import adbg.disassembler;
import adbg.object.server;
import adbg.machines;
import adbg.object.format.elf;
import core.stdc.string : memcmp, strncmp;
import dumper;
import common.utils : realchar, hexstr;

extern (C):

int dump_elf(adbg_object_t *o) {
	if (selected_headers()) {
		dump_elf_ehdr(o);
		dump_elf_phdr(o);
	}
	
	if (selected_sections())
		dump_elf_sections(o);
	
	if (setting_disasm_any())
		dump_elf_disasm(o);
	
	return 0;
}

private:

__gshared immutable(char)[] SIG_CORE = "CORE\0\0\0";

void dump_elf_ehdr(adbg_object_t *o) {
	print_header("Header");
	
	ubyte ei_class	= o.i.elf32.ehdr.e_ident[ELF_EI_CLASS];
	
	with (o.i.elf32.ehdr) {
	ubyte ei_data	= e_ident[ELF_EI_DATA];
	ubyte ei_version	= e_ident[ELF_EI_VERSION];
	ubyte ei_osabi	= e_ident[ELF_EI_OSABI];
	ubyte ei_abiversion	= e_ident[ELF_EI_ABIVERSION];
	
	print_x8("e_ident[0]", '\x7f', `\x7f`);
	print_x8("e_ident[1]", 'E', `E`);
	print_x8("e_ident[2]", 'L', `L`);
	print_x8("e_ident[3]", 'F', `F`);
	print_u8("e_ident[EI_CLASS]", ei_class, adbg_object_elf_class_string(ei_class));
	print_u8("e_ident[EI_DATA]", ei_data, adbg_object_elf_data_string(ei_data));
	print_u8("e_ident[EI_VERSION]", ei_version);
	print_u8("e_ident[EI_OSABI]", ei_osabi, adbg_object_elf_abi_string(ei_osabi));
	print_u8("e_ident[EI_ABIVERSION]", ei_abiversion);
	print_u8("e_ident[9]", e_ident[9]);
	print_u8("e_ident[10]", e_ident[10]);
	print_u8("e_ident[11]", e_ident[11]);
	print_u8("e_ident[12]", e_ident[12]);
	print_u8("e_ident[13]", e_ident[13]);
	print_u8("e_ident[14]", e_ident[14]);
	print_u8("e_ident[15]", e_ident[15]);
	print_u16("e_type", e_type, adbg_object_elf_et_string(e_type));
	print_u16("e_machine", e_machine, adbg_object_elf_em_string(e_machine));
	}
	
	switch (ei_class) {
	case ELF_CLASS_32:
		with (o.i.elf32.ehdr) {
		print_x32("e_entry", e_entry);
		print_x32("e_phoff", e_phoff);
		print_x32("e_shoff", e_shoff);
		dump_elf_e_flags(e_machine, e_flags);
		print_u16("e_ehsize", e_ehsize);
		print_u16("e_phentsize", e_phentsize);
		print_u16("e_phnum", e_phnum);
		print_u16("e_shentsize", e_shentsize);
		print_u16("e_shnum", e_shnum);
		print_u16("e_shstrndx", e_shstrndx);
		}
		break;
	case ELF_CLASS_64:
		with (o.i.elf64.ehdr) {
		print_x64("e_entry", e_entry);
		print_x64("e_phoff", e_phoff);
		print_x64("e_shoff", e_shoff);
		dump_elf_e_flags(e_machine, e_flags);
		print_u16("e_ehsize", e_ehsize);
		print_u16("e_phentsize", e_phentsize);
		print_u16("e_phnum", e_phnum);
		print_u16("e_shentsize", e_shentsize);
		print_u16("e_shnum", e_shnum);
		print_u16("e_shstrndx", e_shstrndx);
		}
		break;
	default:
	}
}

void dump_elf_e_flags(ushort e_machine, uint e_flags) {
	switch (e_machine) {
	case ELF_EM_ARM:
		print_flags32("e_flags", e_flags,
			"EF_ARM_RELEXEC".ptr,	ELF_EF_ARM_RELEXEC,
			"EF_ARM_HASENTRY".ptr,	ELF_EF_ARM_HASENTRY,
			"EF_ARM_INTERWORK".ptr,	ELF_EF_ARM_INTERWORK,
			"EF_ARM_APCS_26".ptr,	ELF_EF_ARM_APCS_26,
			"EF_ARM_APCS_FLOAT".ptr,	ELF_EF_ARM_APCS_FLOAT,
			"EF_ARM_PIC".ptr,	ELF_EF_ARM_PIC,
			"EF_ARM_ALIGN8".ptr,	ELF_EF_ARM_ALIGN8,
			"EF_ARM_NEW_ABI".ptr,	ELF_EF_ARM_NEW_ABI,
			"EF_ARM_OLD_ABI".ptr,	ELF_EF_ARM_OLD_ABI,
			"EF_ARM_SOFT_FLOAT".ptr,	ELF_EF_ARM_SOFT_FLOAT,
			"EF_ARM_VFP_FLOAT".ptr,	ELF_EF_ARM_VFP_FLOAT,
			"EF_ARM_MAVERICK_FLOAT".ptr,	ELF_EF_ARM_MAVERICK_FLOAT,
			null);
		break;
	case ELF_EM_SPARC:
	case ELF_EM_SPARC32PLUS:
	case ELF_EM_SPARCV9:
		print_flags32("e_flags", e_flags,
			"EF_SPARC_32PLUS".ptr,	ELF_EF_SPARC_32PLUS,
			"EF_SPARC_SUN_US1".ptr,	ELF_EF_SPARC_SUN_US1,
			"EF_SPARC_HAL_R1".ptr,	ELF_EF_SPARC_HAL_R1,
			"EF_SPARC_SUN_US3".ptr,	ELF_EF_SPARC_SUN_US3,
			"EF_SPARCV9_MM".ptr,	ELF_EF_SPARCV9_MM,
			"EF_SPARCV9_TSO".ptr,	ELF_EF_SPARCV9_TSO,
			"EF_SPARCV9_PSO".ptr,	ELF_EF_SPARCV9_PSO,
			"EF_SPARCV9_RMO".ptr,	ELF_EF_SPARCV9_RMO,
			null);
		break;
	default:
		print_x32("e_flags", e_flags);
	}
}

void dump_elf_phdr(adbg_object_t *o) {
	print_header("Program Headers");
	
	//TODO: Warn and set an upper number limit (e.g. 1000)
	
	switch (o.i.elf32.ehdr.e_ident[ELF_EI_CLASS]) {
	case ELF_CLASS_32:
		with (o.i.elf32) if (phdr == null || ehdr.e_phnum == 0)
			return;
		
		//TODO: adbg_object_elf32_phnum function?
		for (uint i; i < o.i.elf32.ehdr.e_phnum; ++i) {
			Elf32_Phdr *phdr = adbg_object_elf_phdr32(o, i);
			with (phdr) {
			print_section(i);
			print_u32("p_type", p_type, adbg_object_elf_pt_string(p_type));
			print_x32("p_offset", p_offset);
			print_x32("p_vaddr", p_vaddr);
			print_x32("p_paddr", p_paddr);
			print_x32("p_filesz", p_filesz);
			print_x32("p_memsz", p_memsz);
			print_flags32("p_flags", p_flags,
				"PF_R".ptr, ELF_PF_R,
				"PF_W".ptr, ELF_PF_W,
				"PF_X".ptr, ELF_PF_X,
				null);
			print_x32("p_align", p_align);
			}
			
			//TODO: coredump
			// if (p_type == ELF_PT_NOTE && p_pflags == 0)
			// Elf32_Nhdr (like NT_X86_XSTATE)
		}
		break;
	case ELF_CLASS_64:
		with (o.i.elf64) if (phdr == null || ehdr.e_phnum == 0)
			return;
		
		for (uint i; i < o.i.elf64.ehdr.e_phnum; ++i) {
			Elf64_Phdr *phdr = adbg_object_elf_phdr64(o, i);
			with (phdr) {
			print_section(i);
			print_u32("p_type", p_type, adbg_object_elf_pt_string(p_type));
			print_flags32("p_flags", p_flags,
				"PF_R".ptr, ELF_PF_R,
				"PF_W".ptr, ELF_PF_W,
				"PF_X".ptr, ELF_PF_X,
				null);
			print_x64("p_offset", p_offset);
			print_x64("p_vaddr", p_vaddr);
			print_x64("p_paddr", p_paddr);
			print_x64("p_filesz", p_filesz);
			print_x64("p_memsz", p_memsz);
			print_x64("p_align", p_align);
			}
			
			// ELF is a coredump and program header is a note?
			// Worth checking if it is really a coredump
			// Notes usually have no flags
			if (o.i.elf32.ehdr.e_type == ELF_ET_CORE &&
				phdr.p_type == ELF_PT_NOTE &&
				phdr.p_flags == 0) {
				dump_elf_coredump64(o, phdr);
			}
		}
		break;
	default:
	}
}

void dump_elf_coredump32(adbg_object_t *o, Elf32_Phdr *phdr) {
	
}
void dump_elf_coredump64(adbg_object_t *o, Elf64_Phdr *phdr) {
	// NOTE: 8-byte alignment?
	
	ulong noffset = phdr.p_offset;
	long nleft = phdr.p_filesz;
	uint idx;
	
	// Process new note header
LNEWNHDR:
	if (nleft < Elf64_Nhdr.sizeof)
		return;
	
	void *note = void;
	if (adbg_object_offsetl(o, &note, noffset, cast(size_t)nleft)) {
		print_string("warning", "Elf64_Phdr::p_offset points outside of file bounds");
		return;
	}
	
	Elf64_Nhdr *nhdr = cast(Elf64_Nhdr*)note;
	
	// NOTE: Note names
	//       If zero, it's reserved
	//       If name is "CORE", standard coredump stuff, like NT_PRSTATUS
	//       If name is "LINUX", Linux-specific note, like NT_X86_XSTATE
	if (nhdr.n_namesz == 0)
		return;
	
	print_section(++idx, cast(const(char)*)note + Elf64_Nhdr.sizeof, nhdr.n_namesz);
	print_u32l("n_namesz", nhdr.n_namesz);
	print_u32("n_descsz", nhdr.n_descsz);
	print_u32("n_type", nhdr.n_type, adbg_object_elf_nt_type_string(nhdr.n_type));
	
	size_t nnamesz = adbg_align4up(nhdr.n_namesz);
	void *data = note + Elf64_Nhdr.sizeof + nnamesz;
	
	// NOTE: Only for x86-64, for now
	switch (nhdr.n_type) {
	case ELF_NT_PRSTATUS:
		elf_prstatus64 *prstatus = cast(elf_prstatus64*)data;
		print_u32("pr_info.si_signo", prstatus.pr_info.si_signo);
		print_u32("pr_info.si_code", prstatus.pr_info.si_code);
		print_u32("pr_info.si_errno", prstatus.pr_info.si_errno);
		print_u16("pr_cursig", prstatus.pr_cursig);
		print_u64("pr_sigpend", prstatus.pr_sigpend);
		print_u64("pr_sighold", prstatus.pr_sighold);
		print_u32("pr_pid", prstatus.pr_pid);
		print_u32("pr_ppid", prstatus.pr_ppid);
		print_u32("pr_pgrp", prstatus.pr_pgrp);
		print_u32("pr_sid", prstatus.pr_sid);
		print_u64("pr_utime.tv_sec", prstatus.pr_utime.tv_sec);
		print_u64("pr_utime.tv_usec", prstatus.pr_utime.tv_usec);
		print_u64("pr_stime.tv_sec", prstatus.pr_stime.tv_sec);
		print_u64("pr_stime.tv_usec", prstatus.pr_stime.tv_usec);
		print_u64("pr_cutime.tv_sec", prstatus.pr_cutime.tv_sec);
		print_u64("pr_cutime.tv_usec", prstatus.pr_cutime.tv_usec);
		print_u64("pr_cstime.tv_sec", prstatus.pr_cstime.tv_sec);
		print_u64("pr_cstime.tv_usec", prstatus.pr_cstime.tv_usec);
		print_x64("pr_reg[r15]", prstatus.pr_reg[0]);
		print_x64("pr_reg[r14]", prstatus.pr_reg[1]);
		print_x64("pr_reg[r13]", prstatus.pr_reg[2]);
		print_x64("pr_reg[r12]", prstatus.pr_reg[3]);
		print_x64("pr_reg[rbp]", prstatus.pr_reg[4]);
		print_x64("pr_reg[rbx]", prstatus.pr_reg[5]);
		print_x64("pr_reg[r11]", prstatus.pr_reg[6]);
		print_x64("pr_reg[r10]", prstatus.pr_reg[7]);
		print_x64("pr_reg[r9]", prstatus.pr_reg[8]);
		print_x64("pr_reg[r8]", prstatus.pr_reg[9]);
		print_x64("pr_reg[rax]", prstatus.pr_reg[10]);
		print_x64("pr_reg[rcx]", prstatus.pr_reg[11]);
		print_x64("pr_reg[rdx]", prstatus.pr_reg[12]);
		print_x64("pr_reg[rsi]", prstatus.pr_reg[13]);
		print_x64("pr_reg[rdi]", prstatus.pr_reg[14]);
		print_x64("pr_reg[orig_rax]", prstatus.pr_reg[15]);
		print_x64("pr_reg[rip]", prstatus.pr_reg[16]);
		print_x64("pr_reg[cs]", prstatus.pr_reg[17]);
		print_x64("pr_reg[eflags]", prstatus.pr_reg[18]);
		print_x64("pr_reg[rsp]", prstatus.pr_reg[19]);
		print_x64("pr_reg[ss]", prstatus.pr_reg[20]);
		print_x64("pr_reg[fs_base]", prstatus.pr_reg[21]);
		print_x64("pr_reg[gs_base]", prstatus.pr_reg[22]);
		print_x64("pr_reg[ds]", prstatus.pr_reg[23]);
		print_x64("pr_reg[es]", prstatus.pr_reg[24]);
		print_x64("pr_reg[fs]", prstatus.pr_reg[25]);
		print_x64("pr_reg[gs]", prstatus.pr_reg[26]);
		print_u64("pr_fpvalid", prstatus.pr_fpvalid);
		break;
	case ELF_NT_PRPSINFO:
		elf_prpsinfo64 *prpsinfo = cast(elf_prpsinfo64*)data;
		char[4] pr_sname = void;
		int l = realchar(pr_sname.ptr, 4, prpsinfo.pr_sname);
		print_u8("pr_state", prpsinfo.pr_state);
		print_stringl("pr_sname", pr_sname.ptr, l);
		print_u8("pr_zomb", prpsinfo.pr_zomb);
		print_u8("pr_nice", prpsinfo.pr_nice);
		print_u64("pr_flag", prpsinfo.pr_flag);
		print_u32("pr_uid", prpsinfo.pr_uid);
		print_u32("pr_gid", prpsinfo.pr_gid);
		print_u32("pr_pid", prpsinfo.pr_pid);
		print_u32("pr_ppid", prpsinfo.pr_ppid);
		print_u32("pr_pgrp", prpsinfo.pr_pgrp);
		print_u32("pr_sid", prpsinfo.pr_sid);
		print_stringl("pr_fname", prpsinfo.pr_fname.ptr, prpsinfo.pr_fname.sizeof);
		print_stringl("pr_psargs", prpsinfo.pr_psargs.ptr, prpsinfo.pr_psargs.sizeof);
		break;
	case ELF_NT_SIGINFO: // siginfo
		// NOTE: SI_MAX_SIZE is defined with 128, must be same size
		goto default;
	case ELF_NT_FPREGSET:
		user_i387_struct64 *fregset = cast(user_i387_struct64*)data;
		char[2*16] fpbuf = void;
		int fplen = void;
		print_x16("cwd", fregset.cwd);
		print_x16("swd", fregset.swd);
		print_x16("twd", fregset.twd);
		print_x16("fop", fregset.fop);
		print_x64("rip", fregset.rip);
		print_x64("rdp", fregset.rdp);
		print_x32("mxcsr", fregset.mxcsr);
		print_x32("mxcsr_mask", fregset.mxcsr_mask);
		// x87
		fplen = hexstr(fpbuf.ptr, 32, fregset.st_space128[0].data.ptr, 16);
		print_stringl("st_space[0]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.st_space128[1].data.ptr, 16);
		print_stringl("st_space[1]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.st_space128[2].data.ptr, 16);
		print_stringl("st_space[2]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.st_space128[3].data.ptr, 16);
		print_stringl("st_space[3]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.st_space128[4].data.ptr, 16);
		print_stringl("st_space[4]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.st_space128[5].data.ptr, 16);
		print_stringl("st_space[5]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.st_space128[6].data.ptr, 16);
		print_stringl("st_space[6]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.st_space128[7].data.ptr, 16);
		print_stringl("st_space[7]", fpbuf.ptr, fplen);
		// sse
		fplen = hexstr(fpbuf.ptr, 32, fregset.xmm_space128[0].data.ptr, 16);
		print_stringl("xmm_space[0]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.xmm_space128[1].data.ptr, 16);
		print_stringl("xmm_space[1]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.xmm_space128[2].data.ptr, 16);
		print_stringl("xmm_space[2]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.xmm_space128[3].data.ptr, 16);
		print_stringl("xmm_space[3]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.xmm_space128[4].data.ptr, 16);
		print_stringl("xmm_space[4]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.xmm_space128[5].data.ptr, 16);
		print_stringl("xmm_space[5]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.xmm_space128[6].data.ptr, 16);
		print_stringl("xmm_space[6]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.xmm_space128[7].data.ptr, 16);
		print_stringl("xmm_space[7]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.xmm_space128[8].data.ptr, 16);
		print_stringl("xmm_space[8]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.xmm_space128[9].data.ptr, 16);
		print_stringl("xmm_space[9]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.xmm_space128[10].data.ptr, 16);
		print_stringl("xmm_space[10]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.xmm_space128[11].data.ptr, 16);
		print_stringl("xmm_space[11]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.xmm_space128[12].data.ptr, 16);
		print_stringl("xmm_space[12]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.xmm_space128[13].data.ptr, 16);
		print_stringl("xmm_space[13]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.xmm_space128[14].data.ptr, 16);
		print_stringl("xmm_space[14]", fpbuf.ptr, fplen);
		fplen = hexstr(fpbuf.ptr, 32, fregset.xmm_space128[15].data.ptr, 16);
		print_stringl("xmm_space[15]", fpbuf.ptr, fplen);
		break;
	default:
		print_hexdump("Dump", data, nhdr.n_descsz,
			noffset + Elf64_Nhdr.sizeof + nnamesz);
	}
	
	// Adjust to next sub header
	ulong nsize =
		Elf64_Nhdr.sizeof +
		nnamesz +
		adbg_align4up(nhdr.n_descsz);
	noffset += nsize;
	nleft -= nsize;
	
	goto LNEWNHDR;
}

void dump_elf_sections(adbg_object_t *o) {
	print_header("Sections");
	
	//TODO: Functions to get section + section name safely
	
	/// Arbritrary maximum section name length
	enum SNMLEN = 32;
	switch (o.i.elf32.ehdr.e_ident[ELF_EI_CLASS]) {
	case ELF_CLASS_32:
		if (o.i.elf32.ehdr == null)
			return;
		
		ushort section_count = o.i.elf32.ehdr.e_shnum;
		if (section_count == 0)
			return;
		
		// Check id is without section count
		ushort id = o.i.elf32.ehdr.e_shstrndx;
		if (id >= section_count) {
			print_string("error", "String table index out of bounds");
			return;
		}
		
		uint offset = o.i.elf32.shdr[id].sh_offset;
		if (offset < Elf32_Ehdr.sizeof || offset > o.file_size) {
			print_string("error", "String table offset out of bounds");
			return;
		}
		
		char *table = o.bufferc + offset; // string table
		for (uint i; i < section_count; ++i) {
			Elf32_Shdr *shdr = adbg_object_elf_shdr32(o, i);

			const(char) *sname = table + shdr.sh_name;

			// If we're searching sections, match and don't print yet
			if (opt_section) {
				if (strncmp(sname, opt_section, SNMLEN))
					continue;
				
				void *data = o.buffer + shdr.sh_offset;
				
				if (setting_hexdump())
					print_hexdump(opt_section, data, shdr.sh_size, shdr.sh_offset);
				
				if (setting_extract())
					print_rawdump(data, shdr.sh_size);
			}

			with (shdr) {
			print_section(i, sname, SNMLEN);
			print_x32("sh_name", sh_name);
			print_x32("sh_type", sh_type, adbg_object_elf_sht_string(sh_type));
			print_x32("sh_flags", sh_flags);
			print_flags32("sh_flags", sh_flags,
				"SHF_WRITE".ptr,	ELF_SHF_WRITE,
				"SHF_ALLOC".ptr,	ELF_SHF_ALLOC,
				"SHF_EXECINSTR".ptr,	ELF_SHF_EXECINSTR,
				"SHF_MERGE".ptr,	ELF_SHF_MERGE,
				"SHF_STRINGS".ptr,	ELF_SHF_STRINGS,
				"SHF_INFO_LINK".ptr,	ELF_SHF_INFO_LINK,
				"SHF_LINK_ORDER".ptr,	ELF_SHF_LINK_ORDER,
				"SHF_OS_NONCONFORMING".ptr,	ELF_SHF_OS_NONCONFORMING,
				"SHF_GROUP".ptr,	ELF_SHF_GROUP,
				"SHF_TLS".ptr,	ELF_SHF_TLS,
				"SHF_COMPRESSED".ptr,	ELF_SHF_COMPRESSED,
				null);
			print_x32("sh_addr", sh_addr);
			print_x32("sh_offset", sh_offset);
			print_x32("sh_size", sh_size);
			print_x32("sh_link", sh_link);
			print_x32("sh_info", sh_info);
			print_x32("sh_addralign", sh_addralign);
			print_x32("sh_entsize", sh_entsize);
			}
		}
		break;
	case ELF_CLASS_64:
		if (o.i.elf64.ehdr == null)
			return;
		
		ushort section_count = o.i.elf64.ehdr.e_shnum;
		if (section_count == 0)
			return;
		
		// Check id is without section count
		ushort id = o.i.elf64.ehdr.e_shstrndx;
		if (id >= section_count) {
			print_string("error", "String table index out of bounds");
			return;
		}
		
		ulong offset = o.i.elf64.shdr[id].sh_offset;
		if (offset < Elf64_Ehdr.sizeof || offset > o.file_size) {
			print_string("error", "String table offset out of bounds");
			return;
		}
		
		char *table = o.bufferc + offset; // string table
		for (uint i; i < section_count; ++i) {
			Elf64_Shdr *shdr = adbg_object_elf_shdr64(o, i);

			const(char) *sname = table + shdr.sh_name;

			// If we're searching sections, match and don't print yet
			if (opt_section) {
				if (strncmp(sname, opt_section, SNMLEN))
					continue;
				
				void *data = o.buffer + shdr.sh_offset;
				
				if (setting_hexdump())
					print_hexdump(opt_section, data, shdr.sh_size, shdr.sh_offset);
				
				if (setting_extract())
					print_rawdump(data, shdr.sh_size);
			}

			with (shdr) {
			print_section(i, table + sh_name, SNMLEN);
			print_x32("sh_name", sh_name);
			print_x32("sh_type", sh_type, adbg_object_elf_sht_string(sh_type));
			print_flags64("sh_flags", sh_flags,
				"SHF_WRITE".ptr,	ELF_SHF_WRITE,
				"SHF_ALLOC".ptr,	ELF_SHF_ALLOC,
				"SHF_EXECINSTR".ptr,	ELF_SHF_EXECINSTR,
				"SHF_MERGE".ptr,	ELF_SHF_MERGE,
				"SHF_STRINGS".ptr,	ELF_SHF_STRINGS,
				"SHF_INFO_LINK".ptr,	ELF_SHF_INFO_LINK,
				"SHF_LINK_ORDER".ptr,	ELF_SHF_LINK_ORDER,
				"SHF_OS_NONCONFORMING".ptr,	ELF_SHF_OS_NONCONFORMING,
				"SHF_GROUP".ptr,	ELF_SHF_GROUP,
				"SHF_TLS".ptr,	ELF_SHF_TLS,
				"SHF_COMPRESSED".ptr,	ELF_SHF_COMPRESSED,
				null);
			print_x64("sh_addr", sh_addr);
			print_x64("sh_offset", sh_offset);
			print_x64("sh_size", sh_size);
			print_x32("sh_link", sh_link);
			print_x32("sh_info", sh_info);
			print_x64("sh_addralign", sh_addralign);
			print_x64("sh_entsize", sh_entsize);
			}
		}
		break;
	default:
	}
}

//TODO: Section machine-specific flags (like SHF_X86_64_LARGE)

void dump_elf_disasm(adbg_object_t *o) {
	print_header("Disassembly");
	
	int all = setting_disasm_all(); /// dump all
	
	switch (o.i.elf32.ehdr.e_ident[ELF_EI_CLASS]) {
	case ELF_CLASS_32:
		ushort section_count = o.i.elf32.ehdr.e_shnum;
		
		if (section_count == 0)
			return;
		
		// Check id is without section count
		ushort id = o.i.elf32.ehdr.e_shstrndx;
		if (id >= section_count) {
			print_string("error", "String table index out of bounds");
			return;
		}
		
		Elf32_Shdr *shdr = o.i.elf32.shdr;
		uint offset = shdr[id].sh_offset;
		if (offset < Elf32_Ehdr.sizeof || offset > o.file_size) {
			print_string("error", "String table offset out of bounds");
			return;
		}
		
		Elf32_Shdr *max = shdr + section_count;
		char *table = o.bufferc + offset; // string table
		while (shdr++ < max) with (shdr) {
			if (all || sh_flags & ELF_SHF_EXECINSTR)
				dump_disassemble_object(o,
					table + sh_name, 32,
					o.buffer8 + sh_offset, sh_size, 0);
		}
		break;
	case ELF_CLASS_64:
		ushort section_count = o.i.elf64.ehdr.e_shnum;
		
		if (section_count == 0)
			return;
		
		// Check id is without section count
		ushort id = o.i.elf64.ehdr.e_shstrndx;
		if (id >= section_count) {
			print_string("error", "String table index out of bounds");
			return;
		}
		
		Elf64_Shdr *shdr = o.i.elf64.shdr;
		ulong offset = shdr[id].sh_offset;
		if (offset < Elf64_Ehdr.sizeof || offset > o.file_size) {
			print_string("error", "String table offset out of bounds");
			return;
		}
		
		Elf64_Shdr *max = shdr + section_count;
		char *table = o.bufferc + offset; // string table
		while (shdr++ < max) with (shdr) {
			if (all || sh_flags & ELF_SHF_EXECINSTR)
				dump_disassemble_object(o,
					table + sh_name, 32,
					o.buffer8 + sh_offset, sh_size, 0);
		}
		break;
	default:
	}
}