Skip to content

Commit

Permalink
handle PyRuntime section
Browse files Browse the repository at this point in the history
  • Loading branch information
P403n1x87 committed Sep 3, 2023
1 parent 619f2bc commit 1f7f4c0
Show file tree
Hide file tree
Showing 10 changed files with 238 additions and 75 deletions.
1 change: 1 addition & 0 deletions scripts/cog.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ cog -P $args \
README.md \
src/austin.h \
src/argparse.c \
src/linux/py_proc.h \
snap/snapcraft.yaml
2 changes: 0 additions & 2 deletions src/hints.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@
#define TRUE 1
#define FALSE 0

#define NOVERSION 0

#define success(x) (!(x))
#define fail(x) (x)
#define sfree(x) {if ((x) != NULL) {free(x); x = NULL;}}
Expand Down
90 changes: 90 additions & 0 deletions src/linux/analyze_elf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// ----------------------------------------------------------------------------
static Elf64_Addr
_get_base_64(Elf64_Ehdr * ehdr, void * elf_map)
{
for (int i = 0; i < ehdr->e_phnum; ++i) {
Elf64_Phdr * phdr = (Elf64_Phdr *) (elf_map + ehdr->e_phoff + i * ehdr->e_phentsize);
if (phdr->p_type == PT_LOAD)
return phdr->p_vaddr - phdr->p_vaddr % phdr->p_align;
}
return UINT64_MAX;
} /* _get_base_64 */

static int
_py_proc__analyze_elf64(py_proc_t * self, void * elf_map, void * elf_base) {
register int symbols = 0;

Elf64_Ehdr * ehdr = elf_map;

// Section header must be read from binary as it is not loaded into memory
Elf64_Xword sht_size = ehdr->e_shnum * ehdr->e_shentsize;
Elf64_Off elf_map_size = ehdr->e_shoff + sht_size;
Elf64_Shdr * p_shdr;

Elf64_Shdr * p_shstrtab = elf_map + ELF_SH_OFF(ehdr, ehdr->e_shstrndx);
char * sh_name_base = elf_map + p_shstrtab->sh_offset;
Elf64_Shdr * p_dynsym = NULL;
Elf64_Addr base = _get_base_64(ehdr, elf_map);

void * bss_base = NULL;
size_t bss_size = 0;

if (base != UINT64_MAX) {
log_d("Base @ %p", base);

for (Elf64_Off sh_off = ehdr->e_shoff; \
sh_off < elf_map_size; \
sh_off += ehdr->e_shentsize \
) {
p_shdr = (Elf64_Shdr *) (elf_map + sh_off);

if (
p_shdr->sh_type == SHT_DYNSYM && \
strcmp(sh_name_base + p_shdr->sh_name, ".dynsym") == 0
) {
p_dynsym = p_shdr;
}
else if (strcmp(sh_name_base + p_shdr->sh_name, ".bss") == 0) {
bss_base = elf_base + (p_shdr->sh_addr - base);
bss_size = p_shdr->sh_size;
}
else if (strcmp(sh_name_base + p_shdr->sh_name, ".PyRuntime") == 0) {
self->map.runtime.base = elf_base + (p_shdr->sh_addr - base);
self->map.runtime.size = p_shdr->sh_size;
}
}

if (p_dynsym != NULL) {
if (p_dynsym->sh_offset != 0) {
Elf64_Shdr * p_strtabsh = (Elf64_Shdr *) (elf_map + ELF_SH_OFF(ehdr, p_dynsym->sh_link));

// Search for dynamic symbols
for (Elf64_Off tab_off = p_dynsym->sh_offset; \
tab_off < p_dynsym->sh_offset + p_dynsym->sh_size; \
tab_off += p_dynsym->sh_entsize
) {
Elf64_Sym * sym = (Elf64_Sym *) (elf_map + tab_off);
char * sym_name = (char *) (elf_map + p_strtabsh->sh_offset + sym->st_name);
void * value = elf_base + (sym->st_value - base);
if ((symbols += _py_proc__check_sym(self, sym_name, value)) >= DYNSYM_COUNT) {
// We have found all the symbols. No need to look further
break;
}
}
}
}
}

if (symbols < DYNSYM_MANDATORY) {
log_e("ELF binary has not all the mandatory Python symbols");
set_error(ESYM);
FAIL;
}

// Communicate BSS data back to the caller
self->map.bss.base = bss_base;
self->map.bss.size = bss_size;
log_d("BSS @ %p (size %x, offset %x)", self->map.bss.base, self->map.bss.size, self->map.bss.base - elf_base);

SUCCESS;
} /* _py_proc__analyze_elf64 */
32 changes: 26 additions & 6 deletions src/linux/py_proc.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ _file_size(char * file) {
}


/*[[[cog
from pathlib import Path
analyze_elf = Path("src/linux/analyze_elf.h").read_text()
print(analyze_elf)
print(analyze_elf.replace("64", "32"))
]]]*/
// ----------------------------------------------------------------------------
static Elf64_Addr
_get_base_64(Elf64_Ehdr * ehdr, void * elf_map)
Expand All @@ -98,7 +104,6 @@ _get_base_64(Elf64_Ehdr * ehdr, void * elf_map)
return UINT64_MAX;
} /* _get_base_64 */


static int
_py_proc__analyze_elf64(py_proc_t * self, void * elf_map, void * elf_base) {
register int symbols = 0;
Expand Down Expand Up @@ -137,6 +142,10 @@ _py_proc__analyze_elf64(py_proc_t * self, void * elf_map, void * elf_base) {
bss_base = elf_base + (p_shdr->sh_addr - base);
bss_size = p_shdr->sh_size;
}
else if (strcmp(sh_name_base + p_shdr->sh_name, ".PyRuntime") == 0) {
self->map.runtime.base = elf_base + (p_shdr->sh_addr - base);
self->map.runtime.size = p_shdr->sh_size;
}
}

if (p_dynsym != NULL) {
Expand Down Expand Up @@ -174,7 +183,6 @@ _py_proc__analyze_elf64(py_proc_t * self, void * elf_map, void * elf_base) {
SUCCESS;
} /* _py_proc__analyze_elf64 */


// ----------------------------------------------------------------------------
static Elf32_Addr
_get_base_32(Elf32_Ehdr * ehdr, void * elf_map)
Expand All @@ -187,7 +195,6 @@ _get_base_32(Elf32_Ehdr * ehdr, void * elf_map)
return UINT32_MAX;
} /* _get_base_32 */


static int
_py_proc__analyze_elf32(py_proc_t * self, void * elf_map, void * elf_base) {
register int symbols = 0;
Expand Down Expand Up @@ -226,6 +233,10 @@ _py_proc__analyze_elf32(py_proc_t * self, void * elf_map, void * elf_base) {
bss_base = elf_base + (p_shdr->sh_addr - base);
bss_size = p_shdr->sh_size;
}
else if (strcmp(sh_name_base + p_shdr->sh_name, ".PyRuntime") == 0) {
self->map.runtime.base = elf_base + (p_shdr->sh_addr - base);
self->map.runtime.size = p_shdr->sh_size;

Check warning on line 238 in src/linux/py_proc.h

View check run for this annotation

Codecov / codecov/patch

src/linux/py_proc.h#L237-L238

Added lines #L237 - L238 were not covered by tests
}
}

if (p_dynsym != NULL) {
Expand Down Expand Up @@ -263,6 +274,7 @@ _py_proc__analyze_elf32(py_proc_t * self, void * elf_map, void * elf_base) {
SUCCESS;
} /* _py_proc__analyze_elf32 */

//[[[end]]]

// ----------------------------------------------------------------------------
static int
Expand Down Expand Up @@ -391,8 +403,8 @@ _py_proc__parse_maps_file(py_proc_t * self) {

while (getline(&line, &len, fp) != -1) {
ssize_t lower, upper;
char pathname[1024];
char perms[5];
char pathname[1024] = {0};
char perms[5] = {0};

int field_count = sscanf(line, ADDR_FMT "-" ADDR_FMT " %s %*x %*x:%*x %*x %s\n",
&lower, &upper, // Map bounds
Expand All @@ -408,12 +420,20 @@ _py_proc__parse_maps_file(py_proc_t * self) {
size_t page_size = getpagesize();
map->bss_base = (void *) lower - page_size;
map->bss_size = upper - lower + page_size;
log_d("Inferred BSS for %s: %lx-%lx", map->path, lower, upper);
log_d("BSS section inferred from VM maps for %s: %lx-%lx", map->path, lower, upper);
}

if (field_count <= 0)
continue;

if (!isvalid(self->map.runtime.base) && strcmp(perms, "rw-p") == 0 && strcmp(map->path, pathname) == 0) {
// This is likely the PyRuntime section.
size_t page_size = getpagesize();
self->map.runtime.base = (void *) lower - page_size;
self->map.runtime.size = upper - lower + page_size;
log_d("PyRuntime section inferred from VM maps for %s: %lx-%lx", map->path, lower, upper);
}

if (field_count == 0 || strstr(pathname, "[v") == NULL) {
// Skip meaningless addresses like [vsyscall] which would give
// ridiculous values.
Expand Down
16 changes: 14 additions & 2 deletions src/mac/py_proc.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,13 @@ _py_proc__analyze_macho64(py_proc_t * self, void * base, void * map) {
self->map.bss.base = base + sec[j].addr;
self->map.bss.size = sec[j].size;
bin_attrs |= B_BSS;
break;
continue;
}
// This section was added in Python 3.11
if (strcmp(sec[j].sectname, "PyRuntime") == 0) {
self->map.runtime.base = base + sec[j].addr;
self->map.runtime.size = sec[j].size;
continue;
}
}
cmd_cnt++;
Expand Down Expand Up @@ -257,7 +263,13 @@ _py_proc__analyze_macho32(py_proc_t * self, void * base, void * map) {
self->map.bss.base = base + sec[j].addr;
self->map.bss.size = sec[j].size;
bin_attrs |= B_BSS;
break;
continue;
}
// This section was added in Python 3.11
if (strcmp(sec[j].sectname, "PyRuntime") == 0) {
self->map.runtime.base = base + sec[j].addr;
self->map.runtime.size = sec[j].size;
continue;
}
}
cmd_cnt++;
Expand Down
Loading

0 comments on commit 1f7f4c0

Please sign in to comment.