| /** |
| * @file create_bfd.c |
| * Routine to handle elf file creation |
| * |
| * @remark Copyright 2007 OProfile authors |
| * @remark Read the file COPYING |
| * |
| * @author Jens Wilke |
| * @Modifications Maynard Johnson |
| * @Modifications Philippe Elie |
| * @Modifications Daniel Hansel |
| * |
| * Copyright IBM Corporation 2007 |
| * |
| */ |
| |
| #include "opjitconv.h" |
| #include "opd_printf.h" |
| #include "op_libiberty.h" |
| |
| #include <bfd.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| |
| /* Create the symbols and fill the syms array for all functions |
| * from start_idx to end_idx pointing into entries_address_ascending array */ |
| static int fill_symtab(void) |
| { |
| int rc = OP_JIT_CONV_OK; |
| u32 i; |
| int r; |
| struct jitentry const * e; |
| asymbol * s; |
| asection * section = NULL; |
| |
| /* Check for valid value of entry_count to avoid integer overflow. */ |
| if (entry_count > UINT32_MAX - 1) { |
| bfd_perror("invalid entry_count value"); |
| rc = OP_JIT_CONV_FAIL; |
| goto out; |
| } |
| |
| syms = xmalloc(sizeof(asymbol *) * (entry_count+1)); |
| syms[entry_count] = NULL; |
| for (i = 0; i < entry_count; i++) { |
| e = entries_address_ascending[i]; |
| if (e->section) |
| section = e->section; |
| s = bfd_make_empty_symbol(cur_bfd); |
| if (!s) { |
| bfd_perror("bfd_make_empty_symbol"); |
| rc = OP_JIT_CONV_FAIL; |
| goto out; |
| } |
| s->name = e->symbol_name; |
| s->section = section; |
| s->flags = BSF_GLOBAL | BSF_FUNCTION; |
| s->value = e->vma - section->vma; |
| verbprintf(debug,"add sym: name=%s, value=%llx\n", s->name, |
| (unsigned long long)s->value); |
| syms[i] = s; |
| } |
| r = bfd_set_symtab(cur_bfd, syms, entry_count); |
| if (r == FALSE) { |
| bfd_perror("bfd_set_symtab"); |
| rc = OP_JIT_CONV_FAIL; |
| } |
| out: |
| return rc; |
| } |
| |
| /* |
| * create a new section. |
| */ |
| asection * create_section(bfd * abfd, char const * section_name, |
| size_t size, bfd_vma vma, flagword flags) |
| { |
| asection * section; |
| |
| verbprintf(debug, "create_section() %s\n", section_name); |
| section = bfd_make_section(abfd, section_name); |
| if (section == NULL) { |
| bfd_perror("bfd_make_section"); |
| goto error; |
| } |
| if (bfd_set_section_vma(abfd, section, vma) == FALSE) { |
| bfd_perror("bfd_set_section_vma"); |
| goto error; |
| } |
| if (bfd_set_section_size(abfd, section, size) == FALSE) { |
| bfd_perror("bfd_set_section_size"); |
| goto error; |
| } |
| if (bfd_set_section_flags(abfd, section, flags) == FALSE) { |
| bfd_perror("bfd_set_section_flags"); |
| goto error; |
| } |
| return section; |
| error: |
| return NULL; |
| } |
| |
| |
| /* create a .text section. end_idx: index last jitentry (inclusive!) */ |
| static int create_text_section(int start_idx, int end_idx) |
| { |
| int rc = OP_JIT_CONV_OK; |
| |
| asection * section; |
| char const * section_name; |
| int idx = start_idx; |
| unsigned long long vma_start = |
| entries_address_ascending[start_idx]->vma; |
| struct jitentry * ee = entries_address_ascending[end_idx]; |
| unsigned long long vma_end = ee->vma + ee->code_size; |
| int size = vma_end - vma_start; |
| |
| section_name = bfd_get_unique_section_name(cur_bfd, ".text", &idx); |
| verbprintf(debug, "section idx=%i, name=%s, vma_start=%llx, size=%i\n", |
| idx, section_name, vma_start, size); |
| |
| section = create_section(cur_bfd, section_name, size, vma_start, |
| SEC_ALLOC|SEC_LOAD|SEC_READONLY|SEC_CODE|SEC_HAS_CONTENTS); |
| if (section) |
| entries_address_ascending[start_idx]->section = section; |
| else |
| rc = OP_JIT_CONV_FAIL; |
| |
| return rc; |
| } |
| |
| /* fill a section contents at a given offset from the start of the section */ |
| int fill_section_content(bfd * abfd, asection * section, |
| void const * b, file_ptr offset, size_t sz) |
| { |
| if (bfd_set_section_contents(abfd, section, b, offset, sz) == FALSE) { |
| bfd_perror("bfd_set_section_contents"); |
| return OP_JIT_CONV_FAIL; |
| } |
| return OP_JIT_CONV_OK; |
| } |
| |
| /* |
| * Copy all code of the functions that are within start_idx and end_idx to |
| * the section. |
| */ |
| static int fill_text_section_content(asection * section, int start_idx, |
| int end_idx) |
| { |
| int rc = OP_JIT_CONV_OK; |
| unsigned long long vma_start = |
| entries_address_ascending[start_idx]->vma; |
| struct jitentry const * e; |
| int i; |
| |
| for (i = start_idx; i <= end_idx; i++) { |
| e = entries_address_ascending[i]; |
| verbprintf(debug, "section = %s, i = %i, code = %llx," |
| " vma = %llx, offset = %llx," |
| "size = %i, name = %s\n", |
| section->name, i, |
| (unsigned long long) (uintptr_t) e->code, |
| e->vma, e->vma - vma_start, |
| e->code_size, e->symbol_name); |
| /* the right part that is created by split_entry may |
| * have no code; also, the agent may have passed NULL |
| * for the code location. |
| */ |
| if (e->code) { |
| rc = fill_section_content(cur_bfd, section, |
| e->code, (file_ptr) (e->vma - vma_start), |
| (bfd_size_type)e->code_size); |
| if (rc != OP_JIT_CONV_OK) |
| break; |
| } |
| } |
| return rc; |
| } |
| |
| |
| /* Walk over the symbols sorted by address and create ELF sections. Whenever we |
| * have a gap greater or equal to 4096 make a new section. |
| */ |
| int partition_sections(void) |
| { |
| int rc = OP_JIT_CONV_OK; |
| u32 i, j; |
| struct jitentry const * pred; |
| struct jitentry const * entry; |
| unsigned long long end_addr; |
| |
| // i: start index of the section |
| i = 0; |
| for (j = 1; j < entry_count; j++) { |
| entry = entries_address_ascending[j]; |
| pred = entries_address_ascending[j - 1]; |
| end_addr = pred->vma + pred->code_size; |
| // calculate gap between code, if it is more than one page |
| // create an additional section |
| if ((entry->vma - end_addr) >= 4096) { |
| rc = create_text_section(i, j - 1); |
| if (rc == OP_JIT_CONV_FAIL) |
| goto out; |
| i = j; |
| } |
| } |
| // this holds always if we have at least one jitentry |
| if (i < entry_count) |
| rc = create_text_section(i, entry_count - 1); |
| out: |
| return rc; |
| } |
| |
| |
| /* Fill the code content into the sections created by partition_sections() */ |
| int fill_sections(void) |
| { |
| int rc = OP_JIT_CONV_OK; |
| u32 i, j; |
| asection * section; |
| |
| rc = fill_symtab(); |
| if (rc == OP_JIT_CONV_FAIL) |
| goto out; |
| |
| verbprintf(debug, "opjitconv: fill_sections\n"); |
| i = 0; |
| for (j = 1; j < entry_count; j++) { |
| if (entries_address_ascending[j]->section) { |
| section = entries_address_ascending[i]->section; |
| rc = fill_text_section_content(section, i, |
| j - 1); |
| if (rc == OP_JIT_CONV_FAIL) |
| goto out; |
| i = j; |
| } |
| } |
| // this holds always if we have at least one jitentry |
| if (i < entry_count) { |
| section = entries_address_ascending[i]->section; |
| rc = fill_text_section_content(section, |
| i, entry_count - 1); |
| } |
| out: |
| return rc; |
| } |
| |
| |
| /* create the elf file */ |
| bfd * open_elf(char const * filename) |
| { |
| bfd * abfd; |
| |
| abfd = bfd_openw(filename, dump_bfd_target_name); |
| if (!abfd) { |
| bfd_perror("bfd_openw"); |
| goto error1; |
| } |
| if (bfd_set_format(abfd, bfd_object) == FALSE) { |
| bfd_perror("bfd_set_format"); |
| goto error; |
| } |
| if (bfd_set_arch_mach(abfd, dump_bfd_arch, dump_bfd_mach) == FALSE) { |
| bfd_perror("bfd_set_format"); |
| goto error; |
| } |
| return abfd; |
| error: |
| bfd_close(abfd); |
| error1: |
| return NULL; |
| } |