| /* |
| * This file is part of ltrace. |
| * Copyright (C) 2013 Petr Machata, Red Hat Inc. |
| * Copyright (C) 2010 Zach Welch, CodeSourcery |
| * Copyright (C) 2004,2008,2009 Juan Cespedes |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of the |
| * License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
| * 02110-1301 USA |
| */ |
| |
| #include <gelf.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include "proc.h" |
| #include "library.h" |
| #include "ltrace-elf.h" |
| |
| static int |
| get_hardfp(uint64_t abi_vfp_args) |
| { |
| if (abi_vfp_args == 2) |
| fprintf(stderr, |
| "Tag_ABI_VFP_args value 2 (tool chain-specific " |
| "conventions) not supported.\n"); |
| return abi_vfp_args == 1; |
| } |
| |
| int |
| arch_elf_init(struct ltelf *lte, struct library *lib) |
| { |
| GElf_Addr jmprel_addr; |
| Elf_Scn *jmprel_sec; |
| GElf_Shdr jmprel_shdr; |
| if (elf_load_dynamic_entry(lte, DT_JMPREL, &jmprel_addr) < 0 |
| || elf_get_section_covering(lte, jmprel_addr, |
| &jmprel_sec, &jmprel_shdr) < 0 |
| || jmprel_sec == NULL) |
| return -1; |
| |
| lte->arch.jmprel_data = elf_loaddata(jmprel_sec, &jmprel_shdr); |
| if (lte->arch.jmprel_data == NULL) |
| return -1; |
| |
| /* Nothing in this section is strictly critical. It's not |
| * that much of a deal if we fail to guess right whether the |
| * ABI is softfp or hardfp. */ |
| unsigned hardfp = 0; |
| |
| Elf_Scn *scn; |
| Elf_Data *data; |
| GElf_Shdr shdr; |
| if (elf_get_section_type(lte, SHT_ARM_ATTRIBUTES, &scn, &shdr) < 0 |
| || (scn != NULL && (data = elf_loaddata(scn, &shdr)) == NULL)) { |
| fprintf(stderr, |
| "Error when obtaining ARM attribute section: %s\n", |
| elf_errmsg(-1)); |
| goto done; |
| |
| } else if (scn != NULL && data != NULL) { |
| GElf_Xword offset = 0; |
| uint8_t version; |
| if (elf_read_next_u8(data, &offset, &version) < 0) { |
| goto done; |
| } else if (version != 'A') { |
| fprintf(stderr, "Unsupported ARM attribute section " |
| "version %d ('%c').\n", version, version); |
| goto done; |
| } |
| |
| do { |
| const char signature[] = "aeabi"; |
| /* N.B. LEN is including the length field |
| * itself. */ |
| uint32_t sec_len; |
| if (elf_read_u32(data, offset, &sec_len) < 0 |
| || !elf_can_read_next(data, offset, sec_len)) { |
| goto done; |
| } |
| const GElf_Xword next_offset = offset + sec_len; |
| offset += 4; |
| |
| if (sec_len < 4 + sizeof signature |
| || strcmp(signature, data->d_buf + offset) != 0) |
| goto skip; |
| offset += sizeof signature; |
| |
| const GElf_Xword offset0 = offset; |
| uint64_t tag; |
| uint32_t sub_len; |
| if (elf_read_next_uleb128(data, &offset, &tag) < 0 |
| || elf_read_next_u32(data, &offset, &sub_len) < 0 |
| || !elf_can_read_next(data, offset0, sub_len)) |
| goto done; |
| |
| if (tag != 1) |
| /* IHI0045D_ABI_addenda: "section and |
| * symbol attributes are deprecated |
| * [...] consumers are permitted to |
| * ignore them." */ |
| goto skip; |
| |
| while (offset < offset0 + sub_len) { |
| if (elf_read_next_uleb128(data, |
| &offset, &tag) < 0) |
| goto done; |
| |
| switch (tag) { |
| uint64_t v; |
| case 6: /* Tag_CPU_arch */ |
| case 7: /* Tag_CPU_arch_profile */ |
| case 8: /* Tag_ARM_ISA_use */ |
| case 9: /* Tag_THUMB_ISA_use */ |
| case 10: /* Tag_FP_arch */ |
| case 11: /* Tag_WMMX_arch */ |
| case 12: /* Tag_Advanced_SIMD_arch */ |
| case 13: /* Tag_PCS_config */ |
| case 14: /* Tag_ABI_PCS_R9_use */ |
| case 15: /* Tag_ABI_PCS_RW_data */ |
| case 16: /* Tag_ABI_PCS_RO_data */ |
| case 17: /* Tag_ABI_PCS_GOT_use */ |
| case 18: /* Tag_ABI_PCS_wchar_t */ |
| case 19: /* Tag_ABI_FP_rounding */ |
| case 20: /* Tag_ABI_FP_denormal */ |
| case 21: /* Tag_ABI_FP_exceptions */ |
| case 22: /* Tag_ABI_FP_user_exceptions */ |
| case 23: /* Tag_ABI_FP_number_model */ |
| case 24: /* Tag_ABI_align_needed */ |
| case 25: /* Tag_ABI_align_preserved */ |
| case 26: /* Tag_ABI_enum_size */ |
| case 27: /* Tag_ABI_HardFP_use */ |
| case 28: /* Tag_ABI_VFP_args */ |
| case 29: /* Tag_ABI_WMMX_args */ |
| case 30: /* Tag_ABI_optimization_goals */ |
| case 31: /* Tag_ABI_FP_optimization_goals */ |
| case 32: /* Tag_compatibility */ |
| case 34: /* Tag_CPU_unaligned_access */ |
| case 36: /* Tag_FP_HP_extension */ |
| case 38: /* Tag_ABI_FP_16bit_format */ |
| case 42: /* Tag_MPextension_use */ |
| case 70: /* Tag_MPextension_use as well */ |
| case 44: /* Tag_DIV_use */ |
| case 64: /* Tag_nodefaults */ |
| case 66: /* Tag_T2EE_use */ |
| case 68: /* Tag_Virtualization_use */ |
| uleb128: |
| if (elf_read_next_uleb128 |
| (data, &offset, &v) < 0) |
| goto done; |
| if (tag == 28) |
| hardfp = get_hardfp(v); |
| if (tag != 32) |
| continue; |
| |
| /* Tag 32 has two arguments, |
| * fall through. */ |
| |
| case 4: /* Tag_CPU_raw_name */ |
| case 5: /* Tag_CPU_name */ |
| case 65: /* Tag_also_compatible_with */ |
| case 67: /* Tag_conformance */ |
| ntbs: |
| offset += strlen(data->d_buf |
| + offset) + 1; |
| continue; |
| } |
| |
| /* Handle unknown tags in a generic |
| * manner, if possible. */ |
| if (tag <= 32) { |
| fprintf(stderr, |
| "Unknown tag %lld " |
| "at offset %#llx " |
| "of ARM attribute section.", |
| tag, offset); |
| goto skip; |
| } else if (tag % 2 == 0) { |
| goto uleb128; |
| } else { |
| goto ntbs; |
| } |
| } |
| |
| skip: |
| offset = next_offset; |
| |
| } while (elf_can_read_next(data, offset, 1)); |
| |
| } |
| |
| done: |
| lib->arch.hardfp = hardfp; |
| return 0; |
| } |
| |
| void |
| arch_elf_destroy(struct ltelf *lte) |
| { |
| } |
| |
| static int |
| arch_plt_entry_has_stub(struct ltelf *lte, size_t off) { |
| char *buf = (char *) lte->arch.jmprel_data->d_buf; |
| uint16_t op = *(uint16_t *) (buf + off); |
| return op == 0x4778; |
| } |
| |
| GElf_Addr |
| arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { |
| size_t start = lte->arch.jmprel_data->d_size + 12; |
| size_t off = start + 20, i; |
| for (i = 0; i < ndx; i++) |
| off += arch_plt_entry_has_stub(lte, off) ? 16 : 12; |
| if (arch_plt_entry_has_stub(lte, off)) |
| off += 4; |
| return lte->plt_addr + off - start; |
| } |
| |
| void * |
| sym2addr(struct process *proc, struct library_symbol *sym) |
| { |
| return sym->enter_addr; |
| } |
| |
| int |
| arch_library_init(struct library *lib) |
| { |
| return 0; |
| } |
| |
| void |
| arch_library_destroy(struct library *lib) |
| { |
| } |
| |
| int |
| arch_library_clone(struct library *retp, struct library *lib) |
| { |
| retp->arch = lib->arch; |
| return 0; |
| } |