* character).
*/
#define ADDR_STR_LEN 20
+#define BUILD_ID_NOTE_NAME "GNU"
BT_HIDDEN
int bin_info_init(void)
bin->elf_path = g_build_path("/", target_prefix,
path, NULL);
} else {
- bin->elf_path = strdup(path);
+ bin->elf_path = g_strdup(path);
}
if (!bin->elf_path) {
}
if (debug_info_dir) {
- bin->debug_info_dir = strdup(debug_info_dir);
+ bin->debug_info_dir = g_strdup(debug_info_dir);
if (!bin->debug_info_dir) {
goto error;
}
bin->memsz = memsz;
bin->low_addr = low_addr;
bin->high_addr = bin->low_addr + bin->memsz;
+ bin->build_id = NULL;
+ bin->build_id_len = 0;
+ bin->file_build_id_matches = false;
return bin;
dwarf_end(bin->dwarf_info);
- free(bin->debug_info_dir);
- free(bin->elf_path);
- free(bin->dwarf_path);
+ g_free(bin->debug_info_dir);
+ g_free(bin->elf_path);
+ g_free(bin->dwarf_path);
g_free(bin->build_id);
- free(bin->dbg_link_filename);
+ g_free(bin->dbg_link_filename);
elf_end(bin->elf_file);
g_free(bin);
}
+/**
+ * Initialize the ELF file for a given executable.
+ *
+ * @param bin bin_info instance
+ * @returns 0 on success, negative value on error.
+ */
+static
+int bin_info_set_elf_file(struct bin_info *bin)
+{
+ int elf_fd = -1;
+ Elf *elf_file = NULL;
+
+ if (!bin) {
+ goto error;
+ }
+
+ elf_fd = open(bin->elf_path, O_RDONLY);
+ if (elf_fd < 0) {
+ elf_fd = -errno;
+ BT_LOGE("Failed to open %s\n", bin->elf_path);
+ goto error;
+ }
+
+ elf_file = elf_begin(elf_fd, ELF_C_READ, NULL);
+ if (!elf_file) {
+ BT_LOGE("elf_begin failed: %s\n", elf_errmsg(-1));
+ goto error;
+ }
+
+ if (elf_kind(elf_file) != ELF_K_ELF) {
+ BT_LOGE("Error: %s is not an ELF object\n",
+ bin->elf_path);
+ goto error;
+ }
+
+ bin->elf_fd = elf_fd;
+ bin->elf_file = elf_file;
+ return 0;
+
+error:
+ if (elf_fd >= 0) {
+ close(elf_fd);
+ elf_fd = -1;
+ }
+ elf_end(elf_file);
+ return elf_fd;
+}
+
+/**
+ * From a note section data buffer, check if it is a build id note.
+ *
+ * @param buf Pointer to a note section
+ *
+ * @returns 1 on match, 0 if `buf` does not contain a
+ * valid build id note
+ */
+static
+int is_build_id_note_section(uint8_t *buf)
+{
+ int ret = 0;
+ uint32_t name_sz, desc_sz, note_type;
+
+ /* The note section header has 3 32bit integer for the following:
+ * - Section name size
+ * - Description size
+ * - Note type
+ */
+ name_sz = (uint32_t) *buf;
+ buf += sizeof(name_sz);
+
+ buf += sizeof(desc_sz);
+
+ note_type = (uint32_t) *buf;
+ buf += sizeof(note_type);
+
+ /* Check the note type. */
+ if (note_type != NT_GNU_BUILD_ID) {
+ goto invalid;
+ }
+
+ /* Check the note name. */
+ if (memcmp(buf, BUILD_ID_NOTE_NAME, name_sz) != 0) {
+ goto invalid;
+ }
+
+ ret = 1;
+
+invalid:
+ return ret;
+}
+
+/**
+ * From a build id note section data buffer, check if the build id it contains
+ * is identical to the build id passed as parameter.
+ *
+ * @param file_build_id_note Pointer to the file build id note section.
+ * @param build_id Pointer to a build id to compare to.
+ * @param build_id_len length of the build id.
+ *
+ * @returns 1 on match, 0 otherwise.
+ */
+static
+int is_build_id_note_section_matching(uint8_t *file_build_id_note,
+ uint8_t *build_id, size_t build_id_len)
+{
+ uint32_t name_sz, desc_sz, note_type;
+
+ if (build_id_len <= 0) {
+ goto end;
+ }
+
+ /* The note section header has 3 32bit integer for the following:
+ * - Section name size
+ * - Description size
+ * - Note type
+ */
+ name_sz = (uint32_t) *file_build_id_note;
+ file_build_id_note += sizeof(name_sz);
+ file_build_id_note += sizeof(desc_sz);
+ file_build_id_note += sizeof(note_type);
+
+ /*
+ * Move the pointer pass the name char array. This corresponds to the
+ * beginning of the description section. The description is the build
+ * id in the case of a build id note.
+ */
+ file_build_id_note += name_sz;
+
+ /*
+ * Compare the binary build id with the supplied build id.
+ */
+ if (memcmp(build_id, file_build_id_note, build_id_len) == 0) {
+ return 1;
+ }
+end:
+ return 0;
+}
+
+/**
+ * Checks if the build id stored in `bin` (bin->build_id) is matching the build
+ * id of the ondisk file (bin->elf_file).
+ *
+ * @param bin bin_info instance
+ * @param build_id build id to compare ot the on disk file
+ * @param build_id_len length of the build id
+ *
+ * @returns 1 on if the build id of stored in `bin` matches
+ * the build id of the ondisk file.
+ * 0 on if they are different or an error occured.
+ */
+static
+int is_build_id_matching(struct bin_info *bin)
+{
+ int ret, is_build_id, is_matching = 0;
+ Elf_Scn *curr_section = NULL, *next_section = NULL;
+ Elf_Data *note_data = NULL;
+ GElf_Shdr *curr_section_hdr = NULL;
+
+ if (!bin->build_id) {
+ goto error;
+ }
+
+ /* Set ELF file if it hasn't been accessed yet. */
+ if (!bin->elf_file) {
+ ret = bin_info_set_elf_file(bin);
+ if (ret) {
+ /* Failed to set ELF file. */
+ goto error;
+ }
+ }
+
+ curr_section_hdr = g_new0(GElf_Shdr, 1);
+ if (!curr_section_hdr) {
+ goto error;
+ }
+
+ next_section = elf_nextscn(bin->elf_file, curr_section);
+ if (!next_section) {
+ goto error;
+ }
+
+ while (next_section) {
+ curr_section = next_section;
+ next_section = elf_nextscn(bin->elf_file, curr_section);
+
+ curr_section_hdr = gelf_getshdr(curr_section, curr_section_hdr);
+
+ if (!curr_section_hdr) {
+ goto error;
+ }
+
+ if (curr_section_hdr->sh_type != SHT_NOTE) {
+ continue;
+ }
+
+ note_data = elf_getdata(curr_section, NULL);
+ if (!note_data) {
+ goto error;
+ }
+
+ /* Check if the note is of the build-id type. */
+ is_build_id = is_build_id_note_section(note_data->d_buf);
+ if (!is_build_id) {
+ continue;
+ }
+
+ /*
+ * Compare the build id of the on-disk file and
+ * the build id recorded in the trace.
+ */
+ is_matching = is_build_id_note_section_matching(note_data->d_buf,
+ bin->build_id, bin->build_id_len);
+ if (!is_matching) {
+ break;
+ }
+ }
+error:
+ g_free(curr_section_hdr);
+ return is_matching;
+}
BT_HIDDEN
int bin_info_set_build_id(struct bin_info *bin, uint8_t *build_id,
goto error;
}
- bin->build_id = malloc(build_id_len);
+ /* Set the build id. */
+ bin->build_id = g_new0(uint8_t, build_id_len);
if (!bin->build_id) {
goto error;
}
memcpy(bin->build_id, build_id, build_id_len);
bin->build_id_len = build_id_len;
+ /*
+ * Check if the file found on the file system has the same build id
+ * that what was recorded in the trace.
+ */
+ bin->file_build_id_matches = is_build_id_matching(bin);
+ if (!bin->file_build_id_matches) {
+ BT_LOGD_STR("Supplied Build ID does not match Build ID of the "
+ "binary or library found on the file system.");
+ goto error;
+ }
+
/*
* Reset the is_elf_only flag in case it had been set
* previously, because we might find separate debug info using
return 0;
error:
-
return -1;
}
goto error;
}
- bin->dbg_link_filename = strdup(filename);
+ bin->dbg_link_filename = g_strdup(filename);
if (!bin->dbg_link_filename) {
goto error;
}
}
bin->dwarf_fd = fd;
- bin->dwarf_path = strdup(path);
+ bin->dwarf_path = g_strdup(path);
if (!bin->dwarf_path) {
goto error;
}
goto error;
}
- dbg_dir = bin->debug_info_dir ? : DEFAULT_DEBUG_DIR;
+ dbg_dir = bin->debug_info_dir ? bin->debug_info_dir : DEFAULT_DEBUG_DIR;
/* 2 characters per byte printed in hex, +1 for '/' and +1 for '\0' */
build_id_file_len = (2 * bin->build_id_len) + 1 +
strlen(BUILD_ID_SUFFIX) + 1;
- build_id_file = malloc(build_id_file_len);
+ build_id_file = g_new0(gchar, build_id_file_len);
if (!build_id_file) {
goto error;
}
- snprintf(build_id_file, 4, "%02x/", bin->build_id[0]);
+ g_snprintf(build_id_file, 4, "%02x/", bin->build_id[0]);
for (i = 1; i < bin->build_id_len; ++i) {
int path_idx = 3 + 2 * (i - 1);
- snprintf(&build_id_file[path_idx], 3, "%02x", bin->build_id[i]);
+ g_snprintf(&build_id_file[path_idx], 3, "%02x", bin->build_id[i]);
}
- strcat(build_id_file, BUILD_ID_SUFFIX);
+ g_strconcat(build_id_file, BUILD_ID_SUFFIX, NULL);
path = g_build_path("/", dbg_dir, BUILD_ID_SUBDIR, build_id_file, NULL);
if (!path) {
/**
* Try to set the dwarf_info for a given bin_info instance via the
- * build ID method.
+ * debug-link method.
*
* @param bin bin_info instance for which to retrieve the
* DWARF info via debug link
int bin_info_set_dwarf_info_debug_link(struct bin_info *bin)
{
int ret = 0;
- const char *dbg_dir = NULL;
- char *dir_name = NULL, *bin_dir = NULL, *path = NULL;
- size_t max_path_len = 0;
+ const gchar *dbg_dir = NULL;
+ gchar *bin_dir = NULL, *dir_name = NULL, *path = NULL;
if (!bin || !bin->dbg_link_filename) {
goto error;
}
- dbg_dir = bin->debug_info_dir ? : DEFAULT_DEBUG_DIR;
-
- dir_name = dirname(bin->elf_path);
+ dbg_dir = bin->debug_info_dir ? bin->debug_info_dir : DEFAULT_DEBUG_DIR;
+ dir_name = g_path_get_dirname(bin->elf_path);
if (!dir_name) {
goto error;
}
- /* bin_dir is just dir_name with a trailing slash */
- bin_dir = malloc(strlen(dir_name) + 2);
- if (!bin_dir) {
- goto error;
- }
-
- strcpy(bin_dir, dir_name);
- strcat(bin_dir, "/");
-
- max_path_len = strlen(dbg_dir) + strlen(bin_dir) +
- strlen(DEBUG_SUBDIR) + strlen(bin->dbg_link_filename)
- + 1;
- path = malloc(max_path_len);
- if (!path) {
- goto error;
- }
+ bin_dir = g_strconcat(dir_name, "/", NULL);
/* First look in the executable's dir */
- strcpy(path, bin_dir);
- strcat(path, bin->dbg_link_filename);
+ path = g_strconcat(bin_dir, bin->dbg_link_filename, NULL);
if (is_valid_debug_file(path, bin->dbg_link_crc)) {
goto found;
}
/* If not found, look in .debug subdir */
- strcpy(path, bin_dir);
- strcat(path, DEBUG_SUBDIR);
- strcat(path, bin->dbg_link_filename);
+ g_free(path);
+ path = g_strconcat(bin_dir, DEBUG_SUBDIR, bin->dbg_link_filename, NULL);
if (is_valid_debug_file(path, bin->dbg_link_crc)) {
goto found;
}
/* Lastly, look under the global debug directory */
- strcpy(path, dbg_dir);
- strcat(path, bin_dir);
- strcat(path, bin->dbg_link_filename);
+ g_free(path);
+ path = g_strconcat(dbg_dir, bin_dir, bin->dbg_link_filename, NULL);
if (is_valid_debug_file(path, bin->dbg_link_crc)) {
goto found;
}
error:
ret = -1;
end:
- free(path);
- free(bin_dir);
+ g_free(dir_name);
+ g_free(path);
return ret;
return ret;
}
-/**
- * Initialize the ELF file for a given executable.
- *
- * @param bin bin_info instance
- * @returns 0 on success, negative value on error.
- */
-static
-int bin_info_set_elf_file(struct bin_info *bin)
-{
- int elf_fd = -1;
- Elf *elf_file = NULL;
-
- if (!bin) {
- goto error;
- }
-
- elf_fd = open(bin->elf_path, O_RDONLY);
- if (elf_fd < 0) {
- elf_fd = -errno;
- BT_LOGD("Failed to open ELF file: path=\"%s\"", bin->elf_path);
- goto error;
- }
-
- elf_file = elf_begin(elf_fd, ELF_C_READ, NULL);
- if (!elf_file) {
- BT_LOGD("elf_begin() failed: %s.", elf_errmsg(-1));
- goto error;
- }
-
- if (elf_kind(elf_file) != ELF_K_ELF) {
- BT_LOGD("ELF file is not an ELF object: path=\"%s\"",
- bin->elf_path);
- goto error;
- }
-
- bin->elf_fd = elf_fd;
- bin->elf_file = elf_file;
- return 0;
-
-error:
- if (elf_fd >= 0) {
- close(elf_fd);
- elf_fd = -1;
- }
- elf_end(elf_file);
- return elf_fd;
-}
-
BT_HIDDEN
void source_location_destroy(struct source_location *src_loc)
{
int bin_info_append_offset_str(const char *base_str, uint64_t low_addr,
uint64_t high_addr, char **result)
{
- int ret;
uint64_t offset;
char *_result = NULL;
- char offset_str[ADDR_STR_LEN];
+
if (!base_str || !result) {
goto error;
offset = high_addr - low_addr;
- _result = malloc(strlen(base_str) + ADDR_STR_LEN);
+ _result = g_strdup_printf("%s+%#0" PRIx64, base_str, offset);
if (!_result) {
goto error;
}
-
- ret = snprintf(offset_str, ADDR_STR_LEN, "+%#0" PRIx64, offset);
- if (ret < 0) {
- goto error;
- }
- strcpy(_result, base_str);
- strcat(_result, offset_str);
*result = _result;
return 0;
goto error;
}
+ /*
+ * If the bin_info has a build id but it does not match the build id
+ * that was found on the file system, return an error.
+ */
+ if (bin->build_id && !bin->file_build_id_matches) {
+ goto error;
+ }
+
/* Set DWARF info if it hasn't been accessed yet. */
if (!bin->dwarf_info && !bin->is_elf_only) {
ret = bin_info_set_dwarf_info(bin);
}
if (bin->is_elf_only) {
- ret = bin_info_lookup_elf_function_name(bin, addr, &_func_name);
- BT_LOGD("Failed to lookup function name (ELF): ret=%d", ret);
+ ret = bin_info_lookup_elf_function_name(bin, addr,
+ &_func_name);
+ if (ret) {
+ BT_LOGD("Failed to lookup function name (ELF): "
+ "ret=%d", ret);
+ }
} else {
- ret = bin_info_lookup_dwarf_function_name(bin, addr, &_func_name);
- BT_LOGD("Failed to lookup function name (DWARF): ret=%d", ret);
+ ret = bin_info_lookup_dwarf_function_name(bin, addr,
+ &_func_name);
+ if (ret) {
+ BT_LOGD("Failed to lookup function name (DWARF): "
+ "ret=%d", ret);
+ }
}
*func_name = _func_name;
BT_HIDDEN
int bin_info_get_bin_loc(struct bin_info *bin, uint64_t addr, char **bin_loc)
{
- int ret = 0;
- char *_bin_loc = NULL;
+ gchar *_bin_loc = NULL;
if (!bin || !bin_loc) {
goto error;
}
+ /*
+ * If the bin_info has a build id but it does not match the build id
+ * that was found on the file system, return an error.
+ */
+ if (bin->build_id && !bin->file_build_id_matches) {
+ goto error;
+ }
+
if (bin->is_pic) {
addr -= bin->low_addr;
- ret = asprintf(&_bin_loc, "+%#0" PRIx64, addr);
+ _bin_loc = g_strdup_printf("+%#0" PRIx64, addr);
} else {
- ret = asprintf(&_bin_loc, "@%#0" PRIx64, addr);
+ _bin_loc = g_strdup_printf("@%#0" PRIx64, addr);
}
- if (ret == -1 || !_bin_loc) {
+ if (!_bin_loc) {
goto error;
}
}
_src_loc->line_no = line_no;
- _src_loc->filename = strdup(filename);
+ _src_loc->filename = g_strdup(filename);
}
bt_dwarf_die_destroy(die);
goto error;
}
+ /*
+ * If the bin_info has a build id but it does not match the build id
+ * that was found on the file system, return an error.
+ */
+ if (bin->build_id && !bin->file_build_id_matches) {
+ goto error;
+ }
+
/* Set DWARF info if it hasn't been accessed yet. */
if (!bin->dwarf_info && !bin->is_elf_only) {
if (bin_info_set_dwarf_info(bin)) {