From 2e07672c2aa7d393ba2937bd57171c2af8d90367 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Tue, 20 Mar 2018 08:43:10 +0100 Subject: [PATCH] Add *.so_info file to the report *.so_info file contains paths, rpm package info (name, version, release, architecture) and build IDs of mapped files that have a execute flag in /proc//maps. Change-Id: I087a8d25efeb1f51a5e25d95cce5c60a8f2a607b --- src/crash-manager/CMakeLists.txt | 1 + src/crash-manager/crash-manager.c | 18 +++ src/crash-manager/so-info.c | 280 ++++++++++++++++++++++++++++++++++++++ src/crash-manager/so-info.h | 24 ++++ 4 files changed, 323 insertions(+) create mode 100644 src/crash-manager/so-info.c create mode 100644 src/crash-manager/so-info.h diff --git a/src/crash-manager/CMakeLists.txt b/src/crash-manager/CMakeLists.txt index a474f33..db1c99b 100644 --- a/src/crash-manager/CMakeLists.txt +++ b/src/crash-manager/CMakeLists.txt @@ -8,6 +8,7 @@ ENDIF() INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src) SET(CRASH_MANAGER_SRCS crash-manager.c + so-info.c ${CMAKE_SOURCE_DIR}/src/shared/util.c ) diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index d1fe227..8012e48 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -34,6 +34,7 @@ #include #include #include "crash-manager.h" +#include "so-info.h" #include "shared/log.h" #include "shared/util.h" @@ -460,6 +461,20 @@ static int dump_system_state(void) return system_command_parallel(command); } + +static void save_so_info() +{ + char maps_path[PATH_MAX]; + char so_info_path[PATH_MAX]; + snprintf(maps_path, sizeof(maps_path), "/proc/%s/maps", + crash_info.pid_info); + + snprintf(so_info_path, sizeof(so_info_path), + "%s/%s.%s", crash_info.pfx, crash_info.name, "so_info"); + + get_and_save_so_info(maps_path, so_info_path); +} + static void execute_crash_modules(int argc, char *argv[]) { @@ -833,6 +848,9 @@ int main(int argc, char *argv[]) /* Exec dump_systemstate */ dump_state_pid = dump_system_state(); + /* Save shared objects info (file names, bulid IDs, rpm package names) */ + save_so_info(); + /* Exec crash modules */ execute_crash_modules(argc, argv); diff --git a/src/crash-manager/so-info.c b/src/crash-manager/so-info.c new file mode 100644 index 0000000..b766e24 --- /dev/null +++ b/src/crash-manager/so-info.c @@ -0,0 +1,280 @@ +/* + * build-ids + * + * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "shared/log.h" +#include "shared/util.h" + +#define BID_SNAME ".note.gnu.build-id" + +#define CMD_RPM "rpm -qf %s --qf '%%{NAME};%%{VERSION};%%{RELEASE};%%{ARCH}'" +#define CMD_RPM_LEN sizeof(CMD_RPM) + +#define RPM_INFO_MAXLEN PATH_MAX + + +static unsigned char *map_file(const char *filename, int64_t *size) +{ + struct stat file_stat; + int fd = open(filename, O_RDONLY); + + if (fd < 0) { + _E("Failed to open file %s: %m", filename); + return NULL; + } + + if (fstat(fd, &file_stat) < 0) { + _E("Failed to get file status %s: %m", filename); + close(fd); + return NULL; + } + + *size = file_stat.st_size; + void *mem = mmap(NULL, file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); + + if (mem == MAP_FAILED) { + _E("Failed to map file %s into memory: %m", filename); + return NULL; + } + + return (unsigned char*)mem; +} + +#define DECLARE_BUILD_ID_SECTION_ADDRESS(bits) \ +static Elf##bits##_Off build_id_section_address##bits(const unsigned char *mapped_file, const int64_t size)\ +{\ + if (mapped_file <= 0)\ + return -1;\ +\ + Elf##bits##_Ehdr *elheader = (Elf##bits##_Ehdr*)mapped_file;\ + Elf##bits##_Shdr *sec = (Elf##bits##_Shdr*)(mapped_file + elheader->e_shoff);\ +\ + if (size < (intptr_t)sec + elheader->e_shstrndx*sizeof(Elf##bits##_Shdr) - (intptr_t)mapped_file)\ + return -2;\ +\ + char *names = (char *)(mapped_file + sec[elheader->e_shstrndx].sh_offset);\ +\ + for (int i = 0; i < elheader->e_shnum; i++) {\ + char *section_name = (char *)(&names[sec[i].sh_name]);\ + if (sec[i].sh_type == SHT_NOTE &&\ + strncmp(section_name, BID_SNAME, strlen(BID_SNAME)) == 0)\ + return sec[i].sh_offset;\ + } \ +\ + return -1;\ +} \ + +DECLARE_BUILD_ID_SECTION_ADDRESS(32) + +DECLARE_BUILD_ID_SECTION_ADDRESS(64) + +static Elf64_Off build_id_section_address(unsigned char *mapped_file, int64_t size) +{ + Elf64_Off offset; + + if (mapped_file[EI_CLASS] == ELFCLASS32) + offset = build_id_section_address32(mapped_file, size); + else if (mapped_file[EI_CLASS] == ELFCLASS64) + offset = build_id_section_address64(mapped_file, size); + else { + _E("Unrecognized ELF class"); + return 0; + } + + return offset; +} + +static char HEX_CHARS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + +static int get_build_id_from(const unsigned char *mapped_file, const Elf64_Off section_offset, char **build_id) +{ + const unsigned char *p = mapped_file + section_offset + 0x10; + const unsigned int *len = (unsigned int *)(mapped_file + section_offset + 0x04); + const unsigned int dlen = (*len)*2; + + *build_id = (char *)malloc(dlen+1); + if (*build_id == NULL) { + _E("Failed to allocate memory: %m"); + return -1; + } + + for (int i = 0; i < dlen; p++) { + (*build_id)[i++] = HEX_CHARS[(*p / 16)]; + (*build_id)[i++] = HEX_CHARS[(*p % 16)]; + } + + (*build_id)[dlen] = '\0'; + return dlen; +} + +static int get_build_id(char *filename, char **build_id) +{ + int64_t size = 0; + int ret = 0; + unsigned char *mapped_file = map_file(filename, &size); + *build_id = NULL; + + if (mapped_file > 0) { + Elf64_Off offset = build_id_section_address(mapped_file, size); + if (offset > 0) + ret = get_build_id_from(mapped_file, offset, build_id); + else + ret = -1; + + munmap(mapped_file, size); + } else { + ret = -1; + } + return ret; +} + +static int get_perms(char *line, char *perms) +{ + char *first_space = strchr(line, ' '); + + if (first_space > 0) { + strncpy(perms, first_space+1, 4); + return 0; + } else + return -1; +} + +/* + * Return the pointer to start of the filename if the mapped region has execute + * permission ('x' in perms). Otherwise return NULL. + */ +static char *get_exe_filename(char *line) +{ + char perms[4]; + + if (get_perms(line, perms) == 0 && perms[2] == 'x') { + char *p = strstr(line, " /"); + if (p > 0) + return p+1; + } + return NULL; +} + +GSList* get_filepaths(char *map_path) +{ + char *line = NULL; + size_t len = 0; + GSList *file_list = NULL; + + FILE *f = fopen(map_path, "r"); + + if (f == NULL) { + _E("Failed to open maps file %s: %m", map_path); + return NULL; + } + + while (getline(&line, &len, f) != -1) { + char *exe_filename = get_exe_filename(line); + if (exe_filename == NULL) + continue; + + size_t n = strcspn(exe_filename, "\n"); + + if (n > 0 && n < PATH_MAX) { + char *file_name = (char *)malloc(n+1); + if (file_name == NULL) + continue; + + snprintf(file_name, n+1, "%s", exe_filename); + + file_list = g_slist_append(file_list, file_name); + } + } + + free(line); + fclose(f); + + return file_list; +} + +int get_rpm_info(char *filename, char *rpm_info) +{ + if (!filename) { + _E("Invalid filename"); + return -1; + } + + char *args[] = {"rpm", "-qf", filename, "--qf", "%{NAME};%{VERSION};%{RELEASE};%{ARCH}", NULL}; + + int readed; + if ((readed = run_command_write_fd_timeout("rpm", args, NULL, 0, rpm_info, RPM_INFO_MAXLEN, 60)) == -1) { + _E("Failed to get information about rpm installed packages"); + return -1; + } else { + rpm_info[readed] = 0; + } + + return 0; +} + +void get_and_save_so_info(char *map_path, char *out_path) +{ + GSList *file_list = get_filepaths(map_path); + + char rpm_info[RPM_INFO_MAXLEN+1]; + FILE *f = fopen(out_path, "w"); + + if (f == NULL) { + _E("Failed to open file for writing %s: %m", out_path); + } else { + char *build_id = NULL; + for (GSList *iterator = file_list; iterator; iterator = iterator->next) { + char *file_path = (char *)iterator->data; + + if (get_build_id(file_path, &build_id) <= 0 || build_id == NULL) + continue; + + int fret; + + if (get_rpm_info(file_path, rpm_info) != 0) + fret = fprintf(f, "%s %s\n", file_path, build_id); + else + fret = fprintf(f, "%s %s %s\n", file_path, build_id, rpm_info); + + free(build_id); + + if (fret < 0) { + _E("Failed to write to file %s", out_path); + break; + } + } + fclose(f); + } + + for (GSList *iterator = file_list; iterator; iterator = iterator->next) + free(iterator->data); + + g_slist_free(file_list); +} diff --git a/src/crash-manager/so-info.h b/src/crash-manager/so-info.h new file mode 100644 index 0000000..0b28770 --- /dev/null +++ b/src/crash-manager/so-info.h @@ -0,0 +1,24 @@ +/* + * build-ids + * + * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __SO_INFO_H__ +#define __SO_INFO_H__ + +void get_and_save_so_info(char *map_filename, char *out_file); + +#endif -- 2.7.4