Add *.so_info file to the report 32/173332/15
authorMateusz Moscicki <m.moscicki2@partner.samsung.com>
Tue, 20 Mar 2018 07:43:10 +0000 (08:43 +0100)
committerMateusz Moscicki <m.moscicki2@partner.samsung.com>
Fri, 15 Jun 2018 13:59:31 +0000 (15:59 +0200)
*.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/<pid>/maps.

Change-Id: I087a8d25efeb1f51a5e25d95cce5c60a8f2a607b

src/crash-manager/CMakeLists.txt
src/crash-manager/crash-manager.c
src/crash-manager/so-info.c [new file with mode: 0644]
src/crash-manager/so-info.h [new file with mode: 0644]

index a474f3385b4bcd5c57ff6a1882ed75c0a898cd35..db1c99b3d84dda9456bb3aaae38c0de26b5aae7f 100644 (file)
@@ -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
    )
 
index d1fe227f4f6c43e94407caea6ac7011fee314697..8012e48f78420d02d9a719e64aba66a90b656ac5 100644 (file)
@@ -34,6 +34,7 @@
 #include <iniparser.h>
 #include <tzplatform_config.h>
 #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 (file)
index 0000000..b766e24
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <elf.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <glib.h>
+#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 (file)
index 0000000..0b28770
--- /dev/null
@@ -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