2019-05-19 16:51:43 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2017-07-11 18:33:42 +03:00
/*
* Copyright ( C ) 2017 Josh Poimboeuf < jpoimboe @ redhat . com >
*/
# include <unistd.h>
2020-09-04 18:30:27 +03:00
# include <linux/objtool.h>
2020-05-19 23:55:33 +03:00
# include <asm/orc_types.h>
2020-11-13 02:03:32 +03:00
# include <objtool/objtool.h>
# include <objtool/warn.h>
# include <objtool/endianness.h>
2017-07-11 18:33:42 +03:00
static const char * reg_name ( unsigned int reg )
{
switch ( reg ) {
case ORC_REG_PREV_SP :
return " prevsp " ;
case ORC_REG_DX :
return " dx " ;
case ORC_REG_DI :
return " di " ;
case ORC_REG_BP :
return " bp " ;
case ORC_REG_SP :
return " sp " ;
case ORC_REG_R10 :
return " r10 " ;
case ORC_REG_R13 :
return " r13 " ;
case ORC_REG_BP_INDIRECT :
return " bp(ind) " ;
case ORC_REG_SP_INDIRECT :
return " sp(ind) " ;
default :
return " ? " ;
}
}
static const char * orc_type_name ( unsigned int type )
{
switch ( type ) {
2020-09-04 18:30:27 +03:00
case UNWIND_HINT_TYPE_CALL :
2017-07-11 18:33:42 +03:00
return " call " ;
2020-09-04 18:30:27 +03:00
case UNWIND_HINT_TYPE_REGS :
2017-07-11 18:33:42 +03:00
return " regs " ;
2020-09-04 18:30:27 +03:00
case UNWIND_HINT_TYPE_REGS_PARTIAL :
return " regs (partial) " ;
2017-07-11 18:33:42 +03:00
default :
return " ? " ;
}
}
static void print_reg ( unsigned int reg , int offset )
{
if ( reg = = ORC_REG_BP_INDIRECT )
printf ( " (bp%+d) " , offset ) ;
else if ( reg = = ORC_REG_SP_INDIRECT )
2021-02-03 14:02:21 +03:00
printf ( " (sp)%+d " , offset ) ;
2017-07-11 18:33:42 +03:00
else if ( reg = = ORC_REG_UNDEFINED )
printf ( " (und) " ) ;
else
printf ( " %s%+d " , reg_name ( reg ) , offset ) ;
}
int orc_dump ( const char * _objname )
{
int fd , nr_entries , i , * orc_ip = NULL , orc_size = 0 ;
struct orc_entry * orc = NULL ;
char * name ;
2017-12-03 01:17:44 +03:00
size_t nr_sections ;
Elf64_Addr orc_ip_addr = 0 ;
2020-04-01 21:23:26 +03:00
size_t shstrtab_idx , strtab_idx = 0 ;
2017-07-11 18:33:42 +03:00
Elf * elf ;
Elf_Scn * scn ;
GElf_Shdr sh ;
GElf_Rela rela ;
GElf_Sym sym ;
Elf_Data * data , * symtab = NULL , * rela_orc_ip = NULL ;
2022-11-14 20:57:47 +03:00
struct elf dummy_elf = { } ;
2017-07-11 18:33:42 +03:00
objname = _objname ;
elf_version ( EV_CURRENT ) ;
fd = open ( objname , O_RDONLY ) ;
if ( fd = = - 1 ) {
perror ( " open " ) ;
return - 1 ;
}
elf = elf_begin ( fd , ELF_C_READ_MMAP , NULL ) ;
if ( ! elf ) {
WARN_ELF ( " elf_begin " ) ;
return - 1 ;
}
2022-11-14 20:57:47 +03:00
if ( ! elf64_getehdr ( elf ) ) {
WARN_ELF ( " elf64_getehdr " ) ;
return - 1 ;
}
memcpy ( & dummy_elf . ehdr , elf64_getehdr ( elf ) , sizeof ( dummy_elf . ehdr ) ) ;
2017-07-11 18:33:42 +03:00
if ( elf_getshdrnum ( elf , & nr_sections ) ) {
WARN_ELF ( " elf_getshdrnum " ) ;
return - 1 ;
}
if ( elf_getshdrstrndx ( elf , & shstrtab_idx ) ) {
WARN_ELF ( " elf_getshdrstrndx " ) ;
return - 1 ;
}
for ( i = 0 ; i < nr_sections ; i + + ) {
scn = elf_getscn ( elf , i ) ;
if ( ! scn ) {
WARN_ELF ( " elf_getscn " ) ;
return - 1 ;
}
if ( ! gelf_getshdr ( scn , & sh ) ) {
WARN_ELF ( " gelf_getshdr " ) ;
return - 1 ;
}
name = elf_strptr ( elf , shstrtab_idx , sh . sh_name ) ;
if ( ! name ) {
WARN_ELF ( " elf_strptr " ) ;
return - 1 ;
}
data = elf_getdata ( scn , NULL ) ;
if ( ! data ) {
WARN_ELF ( " elf_getdata " ) ;
return - 1 ;
}
if ( ! strcmp ( name , " .symtab " ) ) {
symtab = data ;
2020-04-01 21:23:26 +03:00
} else if ( ! strcmp ( name , " .strtab " ) ) {
strtab_idx = i ;
2017-07-11 18:33:42 +03:00
} else if ( ! strcmp ( name , " .orc_unwind " ) ) {
orc = data - > d_buf ;
orc_size = sh . sh_size ;
} else if ( ! strcmp ( name , " .orc_unwind_ip " ) ) {
orc_ip = data - > d_buf ;
orc_ip_addr = sh . sh_addr ;
} else if ( ! strcmp ( name , " .rela.orc_unwind_ip " ) ) {
rela_orc_ip = data ;
}
}
2020-04-01 21:23:26 +03:00
if ( ! symtab | | ! strtab_idx | | ! orc | | ! orc_ip )
2017-07-11 18:33:42 +03:00
return 0 ;
if ( orc_size % sizeof ( * orc ) ! = 0 ) {
WARN ( " bad .orc_unwind section size " ) ;
return - 1 ;
}
nr_entries = orc_size / sizeof ( * orc ) ;
for ( i = 0 ; i < nr_entries ; i + + ) {
if ( rela_orc_ip ) {
if ( ! gelf_getrela ( rela_orc_ip , i , & rela ) ) {
WARN_ELF ( " gelf_getrela " ) ;
return - 1 ;
}
if ( ! gelf_getsym ( symtab , GELF_R_SYM ( rela . r_info ) , & sym ) ) {
WARN_ELF ( " gelf_getsym " ) ;
return - 1 ;
}
2020-04-01 21:23:26 +03:00
if ( GELF_ST_TYPE ( sym . st_info ) = = STT_SECTION ) {
scn = elf_getscn ( elf , sym . st_shndx ) ;
if ( ! scn ) {
WARN_ELF ( " elf_getscn " ) ;
return - 1 ;
}
if ( ! gelf_getshdr ( scn , & sh ) ) {
WARN_ELF ( " gelf_getshdr " ) ;
return - 1 ;
}
name = elf_strptr ( elf , shstrtab_idx , sh . sh_name ) ;
if ( ! name ) {
WARN_ELF ( " elf_strptr " ) ;
return - 1 ;
}
} else {
name = elf_strptr ( elf , strtab_idx , sym . st_name ) ;
if ( ! name ) {
WARN_ELF ( " elf_strptr " ) ;
return - 1 ;
}
2017-07-11 18:33:42 +03:00
}
2017-12-03 01:17:44 +03:00
printf ( " %s+%llx: " , name , ( unsigned long long ) rela . r_addend ) ;
2017-07-11 18:33:42 +03:00
} else {
2017-12-03 01:17:44 +03:00
printf ( " %llx: " , ( unsigned long long ) ( orc_ip_addr + ( i * sizeof ( int ) ) + orc_ip [ i ] ) ) ;
2017-07-11 18:33:42 +03:00
}
printf ( " sp: " ) ;
2022-11-14 20:57:47 +03:00
print_reg ( orc [ i ] . sp_reg , bswap_if_needed ( & dummy_elf , orc [ i ] . sp_offset ) ) ;
2017-07-11 18:33:42 +03:00
printf ( " bp: " ) ;
2022-11-14 20:57:47 +03:00
print_reg ( orc [ i ] . bp_reg , bswap_if_needed ( & dummy_elf , orc [ i ] . bp_offset ) ) ;
2017-07-11 18:33:42 +03:00
2018-05-18 09:47:12 +03:00
printf ( " type:%s end:%d \n " ,
orc_type_name ( orc [ i ] . type ) , orc [ i ] . end ) ;
2017-07-11 18:33:42 +03:00
}
elf_end ( elf ) ;
close ( fd ) ;
return 0 ;
}