#ifndef COSMOPOLITAN_LIBC_ELF_TINYELF_INTERNAL_H_
#define COSMOPOLITAN_LIBC_ELF_TINYELF_INTERNAL_H_
#include "libc/elf/def.h"
#include "libc/elf/struct/ehdr.h"
#include "libc/elf/struct/phdr.h"
#include "libc/elf/struct/shdr.h"
#include "libc/elf/struct/sym.h"
#include "libc/limits.h"
#include "libc/log/libfatal.internal.h"
#include "libc/serialize.h"
COSMOPOLITAN_C_START_

#define GetStr(tab, rva)     ((char *)(tab) + (rva))
#define GetSection(e, s)     ((void *)((intptr_t)(e) + (size_t)(s)->sh_offset))
#define GetShstrtab(e)       GetSection(e, GetShdr(e, (e)->e_shstrndx))
#define GetSectionName(e, s) GetStr(GetShstrtab(e), (s)->sh_name)
#define GetPhdr(e, i)                            \
  ((Elf64_Phdr *)((intptr_t)(e) + (e)->e_phoff + \
                  (unsigned)(e)->e_phentsize * (i)))
#define GetShdr(e, i)                            \
  ((Elf64_Shdr *)((intptr_t)(e) + (e)->e_shoff + \
                  (unsigned)(e)->e_shentsize * (i)))

static inline char *GetStrtab(const Elf64_Ehdr *e, size_t *n) {
  int i;
  char *name;
  Elf64_Shdr *shdr;
  for (i = 0; i < e->e_shnum; ++i) {
    shdr = GetShdr(e, i);
    if (shdr->sh_type == SHT_STRTAB) {
      name = GetSectionName(e, GetShdr(e, i));
      if (name && READ64LE(name) == READ64LE(".strtab")) {
        if (n)
          *n = shdr->sh_size;
        return GetSection(e, shdr);
      }
    }
  }
  return 0;
}

static inline Elf64_Sym *GetSymtab(const Elf64_Ehdr *e, Elf64_Xword *n) {
  int i;
  Elf64_Shdr *shdr;
  for (i = e->e_shnum; i-- > 0;) {
    shdr = GetShdr(e, i);
    if (shdr->sh_type == SHT_SYMTAB) {
      if (n)
        *n = shdr->sh_size / sizeof(Elf64_Sym);
      return GetSection(e, shdr);
    }
  }
  return 0;
}

static inline void GetImageRange(const Elf64_Ehdr *elf, intptr_t *x,
                                 intptr_t *y) {
  unsigned i;
  const Elf64_Phdr *phdr;
  intptr_t start, end, pstart, pend;
  start = INTPTR_MAX;
  end = 0;
  for (i = 0; i < elf->e_phnum; ++i) {
    phdr = GetPhdr(elf, i);
    if (phdr->p_type != PT_LOAD)
      continue;
    pstart = phdr->p_vaddr;
    pend = phdr->p_vaddr + phdr->p_memsz;
    if (pstart < start)
      start = pstart;
    if (pend > end)
      end = pend;
  }
  if (x)
    *x = start;
  if (y)
    *y = end;
}

static inline bool GetElfSymbolValue(const Elf64_Ehdr *ehdr, const char *name,
                                     uint64_t *res) {
  Elf64_Xword i, n;
  const char *stab;
  const Elf64_Sym *st;
  if (!(stab = GetStrtab(ehdr, 0)))
    return false;
  if (!(st = GetSymtab(ehdr, &n)))
    return false;
  for (i = 0; i < n; ++i) {
    if (!__strcmp(GetStr(stab, st[i].st_name), name)) {
      *res = st[i].st_value;
      return true;
    }
  }
  return false;
}

COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_ELF_TINYELF_INTERNAL_H_ */
