--- /dev/null
+/*
+ * 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);
+}