2021-01-14 16:40:42 +03:00
// SPDX-License-Identifier: GPL-2.0
# include <linux/buildid.h>
# include <linux/elf.h>
# include <linux/pagemap.h>
# define BUILD_ID 3
/*
* Parse build id from the note segment . This logic can be shared between
* 32 - bit and 64 - bit system , because Elf32_Nhdr and Elf64_Nhdr are
* identical .
*/
static inline int parse_build_id ( void * page_addr ,
unsigned char * build_id ,
2021-01-14 16:40:43 +03:00
__u32 * size ,
2021-01-14 16:40:42 +03:00
void * note_start ,
Elf32_Word note_size )
{
Elf32_Word note_offs = 0 , new_offs ;
/* check for overflow */
if ( note_start < page_addr | | note_start + note_size < note_start )
return - EINVAL ;
/* only supports note that fits in the first page */
if ( note_start + note_size > page_addr + PAGE_SIZE )
return - EINVAL ;
while ( note_offs + sizeof ( Elf32_Nhdr ) < note_size ) {
Elf32_Nhdr * nhdr = ( Elf32_Nhdr * ) ( note_start + note_offs ) ;
if ( nhdr - > n_type = = BUILD_ID & &
nhdr - > n_namesz = = sizeof ( " GNU " ) & &
nhdr - > n_descsz > 0 & &
nhdr - > n_descsz < = BUILD_ID_SIZE_MAX ) {
memcpy ( build_id ,
note_start + note_offs +
ALIGN ( sizeof ( " GNU " ) , 4 ) + sizeof ( Elf32_Nhdr ) ,
nhdr - > n_descsz ) ;
memset ( build_id + nhdr - > n_descsz , 0 ,
BUILD_ID_SIZE_MAX - nhdr - > n_descsz ) ;
2021-01-14 16:40:43 +03:00
if ( size )
* size = nhdr - > n_descsz ;
2021-01-14 16:40:42 +03:00
return 0 ;
}
new_offs = note_offs + sizeof ( Elf32_Nhdr ) +
ALIGN ( nhdr - > n_namesz , 4 ) + ALIGN ( nhdr - > n_descsz , 4 ) ;
if ( new_offs < = note_offs ) /* overflow */
break ;
note_offs = new_offs ;
}
return - EINVAL ;
}
/* Parse build ID from 32-bit ELF */
2021-01-14 16:40:43 +03:00
static int get_build_id_32 ( void * page_addr , unsigned char * build_id ,
__u32 * size )
2021-01-14 16:40:42 +03:00
{
Elf32_Ehdr * ehdr = ( Elf32_Ehdr * ) page_addr ;
Elf32_Phdr * phdr ;
int i ;
/* only supports phdr that fits in one page */
if ( ehdr - > e_phnum >
( PAGE_SIZE - sizeof ( Elf32_Ehdr ) ) / sizeof ( Elf32_Phdr ) )
return - EINVAL ;
phdr = ( Elf32_Phdr * ) ( page_addr + sizeof ( Elf32_Ehdr ) ) ;
for ( i = 0 ; i < ehdr - > e_phnum ; + + i ) {
if ( phdr [ i ] . p_type = = PT_NOTE & &
2021-01-14 16:40:43 +03:00
! parse_build_id ( page_addr , build_id , size ,
2021-01-14 16:40:42 +03:00
page_addr + phdr [ i ] . p_offset ,
phdr [ i ] . p_filesz ) )
return 0 ;
}
return - EINVAL ;
}
/* Parse build ID from 64-bit ELF */
2021-01-14 16:40:43 +03:00
static int get_build_id_64 ( void * page_addr , unsigned char * build_id ,
__u32 * size )
2021-01-14 16:40:42 +03:00
{
Elf64_Ehdr * ehdr = ( Elf64_Ehdr * ) page_addr ;
Elf64_Phdr * phdr ;
int i ;
/* only supports phdr that fits in one page */
if ( ehdr - > e_phnum >
( PAGE_SIZE - sizeof ( Elf64_Ehdr ) ) / sizeof ( Elf64_Phdr ) )
return - EINVAL ;
phdr = ( Elf64_Phdr * ) ( page_addr + sizeof ( Elf64_Ehdr ) ) ;
for ( i = 0 ; i < ehdr - > e_phnum ; + + i ) {
if ( phdr [ i ] . p_type = = PT_NOTE & &
2021-01-14 16:40:43 +03:00
! parse_build_id ( page_addr , build_id , size ,
2021-01-14 16:40:42 +03:00
page_addr + phdr [ i ] . p_offset ,
phdr [ i ] . p_filesz ) )
return 0 ;
}
return - EINVAL ;
}
2021-01-14 16:40:43 +03:00
/*
* Parse build ID of ELF file mapped to vma
* @ vma : vma object
* @ build_id : buffer to store build id , at least BUILD_ID_SIZE long
* @ size : returns actual build id size in case of success
*
* Returns 0 on success , otherwise error ( < 0 ) .
*/
int build_id_parse ( struct vm_area_struct * vma , unsigned char * build_id ,
__u32 * size )
2021-01-14 16:40:42 +03:00
{
Elf32_Ehdr * ehdr ;
struct page * page ;
void * page_addr ;
int ret ;
/* only works for page backed storage */
if ( ! vma - > vm_file )
return - EINVAL ;
page = find_get_page ( vma - > vm_file - > f_mapping , 0 ) ;
if ( ! page )
return - EFAULT ; /* page not mapped */
ret = - EINVAL ;
page_addr = kmap_atomic ( page ) ;
ehdr = ( Elf32_Ehdr * ) page_addr ;
/* compare magic x7f "ELF" */
if ( memcmp ( ehdr - > e_ident , ELFMAG , SELFMAG ) ! = 0 )
goto out ;
/* only support executable file and shared object file */
if ( ehdr - > e_type ! = ET_EXEC & & ehdr - > e_type ! = ET_DYN )
goto out ;
if ( ehdr - > e_ident [ EI_CLASS ] = = ELFCLASS32 )
2021-01-14 16:40:43 +03:00
ret = get_build_id_32 ( page_addr , build_id , size ) ;
2021-01-14 16:40:42 +03:00
else if ( ehdr - > e_ident [ EI_CLASS ] = = ELFCLASS64 )
2021-01-14 16:40:43 +03:00
ret = get_build_id_64 ( page_addr , build_id , size ) ;
2021-01-14 16:40:42 +03:00
out :
kunmap_atomic ( page_addr ) ;
put_page ( page ) ;
return ret ;
}