diff options
author | Petr Tesarik <ptesarik@suse.cz> | 2016-02-10 16:50:41 +0900 |
---|---|---|
committer | Atsushi Kumagai <ats-kumagai@wm.jp.nec.com> | 2016-02-18 13:39:14 +0900 |
commit | 7a7cebcba334792f3f38f9171830ad7e4eef023c (patch) | |
tree | 934ff3bdffc760849af5de468d107d48d218ea18 /elf_info.c | |
parent | 1cdabcdb1c0bb74f8e65bf85834d6c80f9f5507c (diff) |
[PATCH 3/3] Rewrite readpage_elf
The current code in readpage_elf (and readpage_elf_parallel) is extremely
hard to follow. Additionally, it still does not cover all possible cases.
For example, attempts to read outside of any ELF segment will end up with
phys_start being 0, frac_head a negative number, interpreted as a large
positive number by memset() and write past buffer end.
Instead of trying to handle even more "corner cases", I rewrote the
algorithm from scratch. The basic idea is simple: set a goal to fill the
page buffer with data, then work towards that goal by:
- filling holes with zeroes (see Note below),
- p_filesz portions with file data and
- remaining p_memsz portions again with zeroes.
Repeat this method for each LOAD until the goal is achieved, or an error
occurs. In most cases, the loop runs only once.
Note: A "hole" is data at a physical address that is not covered by any
ELF LOAD program header. In other words, the ELF file does not specify
any data for such a hole (not even zeroes). So, why does makedumpfile
fill them with zeroes? It's because makedumpfile works with page
granularity (the compressed format does not even have a way to store
a partial page), so if only part of a page is stored, a complete page
must be provided to make this partial data accessible.
Credits to Ivan Delalande <colona@arista.com> who first found the
problem and wrote the original fix.
Tested-by: Ivan Delalande <colona@arista.com>
Signed-off-by: Petr Tesarik <ptesarik@suse.com>
Diffstat (limited to 'elf_info.c')
-rw-r--r-- | elf_info.c | 28 |
1 files changed, 28 insertions, 0 deletions
@@ -691,6 +691,34 @@ get_max_paddr(void) return max_paddr; } +/* + * Find the LOAD segment which is closest to the requested + * physical address within a given distance. + * If there is no such segment, return a negative number. + */ +int +closest_pt_load(unsigned long long paddr, unsigned long distance) +{ + int i, bestidx; + struct pt_load_segment *pls; + unsigned long bestdist; + + bestdist = distance; + bestidx = -1; + for (i = 0; i < num_pt_loads; ++i) { + pls = &pt_loads[i]; + if (paddr >= pls->phys_end) + continue; + if (paddr >= pls->phys_start) + return i; /* Exact match */ + if (bestdist > pls->phys_start - paddr) { + bestdist = pls->phys_start - paddr; + bestidx = i; + } + } + return bestidx; +} + int get_elf64_ehdr(int fd, char *filename, Elf64_Ehdr *ehdr) { |