| // SPDX-License-Identifier: GPL-2.0-only | |
| #ifndef _GNU_SOURCE | |
| #define _GNU_SOURCE | |
| #endif | |
| #include <link.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <stdbool.h> | |
| #include "../kselftest.h" | |
| struct Statistics { | |
| unsigned long long load_address; | |
| unsigned long long alignment; | |
| bool interp; | |
| }; | |
| int ExtractStatistics(struct dl_phdr_info *info, size_t size, void *data) | |
| { | |
| struct Statistics *stats = (struct Statistics *) data; | |
| int i; | |
| if (info->dlpi_name != NULL && info->dlpi_name[0] != '\0') { | |
| // Ignore headers from other than the executable. | |
| return 2; | |
| } | |
| stats->load_address = (unsigned long long) info->dlpi_addr; | |
| stats->alignment = 0; | |
| for (i = 0; i < info->dlpi_phnum; i++) { | |
| unsigned long long align; | |
| if (info->dlpi_phdr[i].p_type == PT_INTERP) { | |
| stats->interp = true; | |
| continue; | |
| } | |
| if (info->dlpi_phdr[i].p_type != PT_LOAD) | |
| continue; | |
| align = info->dlpi_phdr[i].p_align; | |
| if (align > stats->alignment) | |
| stats->alignment = align; | |
| } | |
| return 1; // Terminate dl_iterate_phdr. | |
| } | |
| int main(int argc, char **argv) | |
| { | |
| struct Statistics extracted = { }; | |
| unsigned long long misalign, pow2; | |
| bool interp_needed; | |
| char buf[1024]; | |
| FILE *maps; | |
| int ret; | |
| ksft_print_header(); | |
| ksft_set_plan(4); | |
| /* Dump maps file for debugging reference. */ | |
| maps = fopen("/proc/self/maps", "r"); | |
| if (!maps) | |
| ksft_exit_fail_msg("FAILED: /proc/self/maps: %s\n", strerror(errno)); | |
| while (fgets(buf, sizeof(buf), maps)) { | |
| ksft_print_msg("%s", buf); | |
| } | |
| fclose(maps); | |
| /* Walk the program headers. */ | |
| ret = dl_iterate_phdr(ExtractStatistics, &extracted); | |
| if (ret != 1) | |
| ksft_exit_fail_msg("FAILED: dl_iterate_phdr\n"); | |
| /* Report our findings. */ | |
| ksft_print_msg("load_address=%#llx alignment=%#llx\n", | |
| extracted.load_address, extracted.alignment); | |
| /* If we're named with ".static." we expect no INTERP. */ | |
| interp_needed = strstr(argv[0], ".static.") == NULL; | |
| /* Were we built as expected? */ | |
| ksft_test_result(interp_needed == extracted.interp, | |
| "%s INTERP program header %s\n", | |
| interp_needed ? "Wanted" : "Unwanted", | |
| extracted.interp ? "seen" : "missing"); | |
| /* Did we find an alignment? */ | |
| ksft_test_result(extracted.alignment != 0, | |
| "Alignment%s found\n", extracted.alignment ? "" : " NOT"); | |
| /* Is the alignment sane? */ | |
| pow2 = extracted.alignment & (extracted.alignment - 1); | |
| ksft_test_result(pow2 == 0, | |
| "Alignment is%s a power of 2: %#llx\n", | |
| pow2 == 0 ? "" : " NOT", extracted.alignment); | |
| /* Is the load address aligned? */ | |
| misalign = extracted.load_address & (extracted.alignment - 1); | |
| ksft_test_result(misalign == 0, "Load Address is %saligned (%#llx)\n", | |
| misalign ? "MIS" : "", misalign); | |
| ksft_finished(); | |
| } |