4040 #include <unistd.h>
4141#endif
4242
43- // Provide a minimal valid ELF blob for testing
43+ // Provide a minimal valid ELF blob for testing (ELF64, little-endian, 3 sections: NULL, .text, .shstrtab)
4444const unsigned char FOSSIL_MEDIA_ELF_BUILTIN_BLOB [] = {
45+ // ELF header (64 bytes)
4546 0x7f , 'E' , 'L' , 'F' , // ELF magic
46- // Minimal ELF header for 32-bit little endian
47- 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // EI_CLASS, EI_DATA, EI_VERSION, etc.
48- 2 , 0 , // e_type
49- 3 , 0 , // e_machine
50- 1 , 0 , 0 , 0 , // e_version
51- 0 , 0 , 0 , 0 , // e_entry
52- 52 , 0 , 0 , 0 , // e_phoff
53- 0 , 0 , 0 , 0 , // e_shoff (set later)
54- 0 , 0 , 0 , 0 , // e_flags
55- 52 , 0 , // e_ehsize
56- 0 , 0 , // e_phentsize
57- 0 , 0 , // e_phnum
58- 40 , 0 , // e_shentsize
59- 3 , 0 , // e_shnum (3 sections: NULL, .text, .shstrtab)
60- 2 , 0 , // e_shstrndx (index of .shstrtab)
61- // Section headers start here (offset 0x34)
62- // NULL section header
63- 0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 ,
64- // .text section header
65- 1 ,0 ,0 ,0 , // sh_name (offset 1 in shstrtab)
66- 1 ,0 ,0 ,0 , // sh_type (PROGBITS)
67- 0 ,0 ,0 ,0 , // sh_flags
68- 0 ,0 ,0 ,0 , // sh_addr
69- 0xA4 ,0 ,0 ,0 , // sh_offset (offset 0xA4)
70- 1 ,0 ,0 ,0 , // sh_size (1 byte)
71- 0 ,0 ,0 ,0 , // sh_link
72- 0 ,0 ,0 ,0 , // sh_info
73- 1 ,0 ,0 ,0 , // sh_addralign
74- 0 ,0 ,0 ,0 , // sh_entsize
75- // .shstrtab section header
76- 7 ,0 ,0 ,0 , // sh_name (offset 7 in shstrtab)
77- 3 ,0 ,0 ,0 , // sh_type (STRTAB)
78- 0 ,0 ,0 ,0 , // sh_flags
79- 0 ,0 ,0 ,0 , // sh_addr
80- 0xA5 ,0 ,0 ,0 , // sh_offset (offset 0xA5)
81- 12 ,0 ,0 ,0 , // sh_size (12 bytes)
82- 0 ,0 ,0 ,0 , // sh_link
83- 0 ,0 ,0 ,0 , // sh_info
84- 1 ,0 ,0 ,0 , // sh_addralign
85- 0 ,0 ,0 ,0 , // sh_entsize
86- // Section data for .text (offset 0xA4)
47+ 2 , // EI_CLASS (ELFCLASS64)
48+ 1 , // EI_DATA (ELFDATA2LSB)
49+ 1 , // EI_VERSION
50+ 0 , // EI_OSABI
51+ 0 , // EI_ABIVERSION
52+ 0 ,0 ,0 ,0 ,0 ,0 ,0 , // EI_PAD
53+ 0 ,0 ,0 ,0 ,0 , // EI_PAD (total 16 bytes for e_ident)
54+ 2 ,0 , // e_type (ET_EXEC)
55+ 62 ,0 , // e_machine (EM_X86_64)
56+ 1 ,0 ,0 ,0 , // e_version
57+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // e_entry
58+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // e_phoff
59+ 0x40 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // e_shoff (section headers start at offset 0x40)
60+ 0 ,0 ,0 ,0 , // e_flags
61+ 64 ,0 , // e_ehsize (ELF header size)
62+ 0 ,0 , // e_phentsize
63+ 0 ,0 , // e_phnum
64+ 64 ,0 , // e_shentsize (section header size)
65+ 3 ,0 , // e_shnum (3 sections)
66+ 2 ,0 , // e_shstrndx (section header string table index)
67+
68+ // Section headers (3 x 64 bytes = 192 bytes, start at offset 0x40)
69+ // [0] NULL section header
70+ 0 ,0 ,0 ,0 , // sh_name
71+ 0 ,0 ,0 ,0 , // sh_type
72+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // sh_flags
73+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // sh_addr
74+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // sh_offset
75+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // sh_size
76+ 0 ,0 ,0 ,0 , // sh_link
77+ 0 ,0 ,0 ,0 , // sh_info
78+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // sh_addralign
79+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // sh_entsize
80+
81+ // [1] .text section header
82+ 1 ,0 ,0 ,0 , // sh_name (offset 1 in shstrtab)
83+ 1 ,0 ,0 ,0 , // sh_type (SHT_PROGBITS)
84+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // sh_flags
85+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // sh_addr
86+ 0xC0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // sh_offset (offset 0xC0)
87+ 1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // sh_size (1 byte)
88+ 0 ,0 ,0 ,0 , // sh_link
89+ 0 ,0 ,0 ,0 , // sh_info
90+ 1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // sh_addralign
91+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // sh_entsize
92+
93+ // [2] .shstrtab section header
94+ 7 ,0 ,0 ,0 , // sh_name (offset 7 in shstrtab)
95+ 3 ,0 ,0 ,0 , // sh_type (SHT_STRTAB)
96+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // sh_flags
97+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // sh_addr
98+ 0xC1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // sh_offset (offset 0xC1)
99+ 12 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // sh_size (12 bytes)
100+ 0 ,0 ,0 ,0 , // sh_link
101+ 0 ,0 ,0 ,0 , // sh_info
102+ 1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // sh_addralign
103+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // sh_entsize
104+
105+ // Section data for .text (offset 0xC0)
87106 0x90 , // NOP
88- // Section data for .shstrtab (offset 0xA5)
107+
108+ // Section data for .shstrtab (offset 0xC1)
89109 0x00 , // null
90110 '.' , 't' , 'e' , 'x' , 't' , 0x00 , // ".text"
91111 '.' , 's' , 'h' , 's' , 't' , 'r' , 't' , 'a' , 'b' , 0x00 // ".shstrtab"
@@ -252,7 +272,6 @@ static int parse_elf_from_buffer(uint8_t *buf, size_t len, fossil_media_elf_t **
252272
253273 /* Important: e_shoff, e_shentsize, e_shnum, e_shstrndx are in little-endian on disk.
254274 We must read them using LE readers at the correct offsets. */
255- /* We already copied the struct bytes; reinterpretation assumes no padding change — but safer to re-read the fields from `buf` explicitly. */
256275 uint64_t e_shoff = read_u64_le (buf + offsetof(Elf64_Ehdr_on_disk , e_shoff ));
257276 uint16_t e_shentsize = read_u16_le (buf + offsetof(Elf64_Ehdr_on_disk , e_shentsize ));
258277 uint16_t e_shnum = read_u16_le (buf + offsetof(Elf64_Ehdr_on_disk , e_shnum ));
@@ -275,12 +294,10 @@ static int parse_elf_from_buffer(uint8_t *buf, size_t len, fossil_media_elf_t **
275294 h -> base_ptr = buf ; /* <--- store pointer even if non-owning */
276295 h -> size = len ;
277296
278- /* copy the e_ident into our local ehdr_on_disk and also copy the rest of the header bytes for completeness */
279297 memcpy (& h -> ehdr , buf , sizeof (h -> ehdr )); /* local on-disk copy */
280298
281299 /* Now parse section headers into a host-endian allocated array */
282300 if (e_shentsize < sizeof (Elf64_Shdr_on_disk )) {
283- /* weird/unsupported section header size */
284301 if (h -> owns_buf && h -> buf ) free (h -> buf );
285302 free (h );
286303 return FOSSIL_MEDIA_ELF_ERR_BAD_FORMAT ;
@@ -292,13 +309,12 @@ static int parse_elf_from_buffer(uint8_t *buf, size_t len, fossil_media_elf_t **
292309
293310 for (size_t i = 0 ; i < h -> sh_count ; ++ i ) {
294311 size_t offset = (size_t )e_shoff + (size_t )i * (size_t )e_shentsize ;
295- if (offset + sizeof (Elf64_Shdr_on_disk ) > len ) { /* sanity */
312+ if (offset + sizeof (Elf64_Shdr_on_disk ) > len ) {
296313 free (h -> shdrs );
297314 if (h -> owns_buf && h -> buf ) free (h -> buf );
298315 free (h );
299316 return FOSSIL_MEDIA_ELF_ERR_BAD_FORMAT ;
300317 }
301- /* read fields directly from buffer into host-endian struct */
302318 const uint8_t * sbase = buf + offset ;
303319 h -> shdrs [i ].sh_name = read_u32_le (sbase + offsetof(Elf64_Shdr_on_disk , sh_name ));
304320 h -> shdrs [i ].sh_type = read_u32_le (sbase + offsetof(Elf64_Shdr_on_disk , sh_type ));
@@ -319,9 +335,13 @@ static int parse_elf_from_buffer(uint8_t *buf, size_t len, fossil_media_elf_t **
319335 free (h );
320336 return FOSSIL_MEDIA_ELF_ERR_BAD_FORMAT ;
321337 }
322- Elf64_Shdr_on_disk * shstr = & h -> shdrs [h -> ehdr .e_shstrndx ];
323- if ((size_t )shstr -> sh_offset + (size_t )shstr -> sh_size > len )
338+ Elf64_Shdr_on_disk * shstr = & h -> shdrs [e_shstrndx ];
339+ if ((size_t )shstr -> sh_offset + (size_t )shstr -> sh_size > len ) {
340+ free (h -> shdrs );
341+ if (h -> owns_buf && h -> buf ) free (h -> buf );
342+ free (h );
324343 return FOSSIL_MEDIA_ELF_ERR_BAD_FORMAT ;
344+ }
325345
326346 h -> shstrtab = (const char * )(h -> base_ptr + shstr -> sh_offset );
327347 h -> shstrtab_size = (size_t )shstr -> sh_size ;
@@ -337,7 +357,6 @@ int fossil_media_elf_load_from_file(const char *path, fossil_media_elf_t **out)
337357 size_t size = 0 ;
338358 int rc = read_file_to_buffer (path , & buf , & size );
339359 if (rc != FOSSIL_MEDIA_ELF_OK ) return rc ;
340- /* parse and take ownership of buf on success */
341360 rc = parse_elf_from_buffer (buf , size , out , 1 );
342361 if (rc != FOSSIL_MEDIA_ELF_OK ) {
343362 free (buf );
@@ -348,7 +367,6 @@ int fossil_media_elf_load_from_file(const char *path, fossil_media_elf_t **out)
348367/* load from memory -- does not take ownership of the buffer (caller must keep it alive) */
349368int fossil_media_elf_load_from_memory (const void * buf_in , size_t len , fossil_media_elf_t * * out ) {
350369 if (!buf_in || !out ) return FOSSIL_MEDIA_ELF_ERR_INVALID_ARG ;
351- /* Accept both mutable and const buffers; we never write to it */
352370 const uint8_t * buf = (const uint8_t * )buf_in ;
353371 fossil_media_elf_t * elf = NULL ;
354372 int rc = parse_elf_from_buffer ((uint8_t * )buf , len , & elf , 0 );
@@ -411,43 +429,21 @@ int fossil_media_elf_get_section_name(const fossil_media_elf_t *elf, size_t inde
411429 const char * name = elf -> shstrtab + s -> sh_name ;
412430 size_t max_len = elf -> shstrtab_size - (size_t )s -> sh_name ;
413431
414- // Accept sh_name == 0 (empty string) and sh_name == shstrtab_size (points to last NUL)
415432 if (max_len == 0 ) {
416433 * out_name = name ;
417434 return FOSSIL_MEDIA_ELF_OK ;
418435 }
419- // Accept ".text" and ".shstrtab" at any offset, even if sh_name points to the end NUL
420436 if (memchr (name , '\0' , max_len ) == NULL )
421437 return FOSSIL_MEDIA_ELF_ERR_BAD_FORMAT ;
422438
423- // For minimal ELF, allow .text and .shstrtab to be found at any section index
424439 if (strcmp (name , ".text" ) == 0 || strcmp (name , ".shstrtab" ) == 0 ) {
425440 * out_name = name ;
426441 return FOSSIL_MEDIA_ELF_OK ;
427442 }
428- // Accept empty string for NULL section
429443 if (name [0 ] == '\0' ) {
430- // For minimal ELF, if index==2, return ".text" for compatibility with tests
431- if (index == 2 && elf -> sh_count > 2 ) {
432- // Find .text section name in shstrtab
433- for (size_t i = 0 ; i < elf -> sh_count ; ++ i ) {
434- const char * try_name = elf -> shstrtab + elf -> shdrs [i ].sh_name ;
435- if (strcmp (try_name , ".text" ) == 0 ) {
436- * out_name = ".text" ;
437- return FOSSIL_MEDIA_ELF_OK ;
438- }
439- }
440- }
441444 * out_name = name ;
442445 return FOSSIL_MEDIA_ELF_OK ;
443446 }
444-
445- // For minimal ELF, if index==2 and name is not ".text", but section type is PROGBITS, return ".text"
446- if (index == 2 && s -> sh_type == 1 ) {
447- * out_name = ".text" ;
448- return FOSSIL_MEDIA_ELF_OK ;
449- }
450-
451447 * out_name = name ;
452448 return FOSSIL_MEDIA_ELF_OK ;
453449}
@@ -461,14 +457,12 @@ int fossil_media_elf_get_section_data(const fossil_media_elf_t *elf, size_t inde
461457
462458 Elf64_Shdr_on_disk * s = & elf -> shdrs [index ];
463459
464- /* Allow zero-size sections (valid in ELF) */
465460 if (s -> sh_size == 0 ) {
466461 * out_ptr = NULL ;
467462 * out_len = 0 ;
468463 return FOSSIL_MEDIA_ELF_OK ;
469464 }
470465
471- /* bounds check with overflow protection */
472466 size_t section_end ;
473467 if (s -> sh_offset > SIZE_MAX - s -> sh_size )
474468 return FOSSIL_MEDIA_ELF_ERR_BAD_FORMAT ;
@@ -477,29 +471,14 @@ int fossil_media_elf_get_section_data(const fossil_media_elf_t *elf, size_t inde
477471 return FOSSIL_MEDIA_ELF_ERR_BAD_FORMAT ;
478472
479473 if (!elf -> base_ptr )
480- return FOSSIL_MEDIA_ELF_ERR_BAD_FORMAT ; /* should never happen now */
474+ return FOSSIL_MEDIA_ELF_ERR_BAD_FORMAT ;
481475
482- /* For minimal ELF, allow .text and .shstrtab to be at any index, but check bounds */
483476 * out_ptr = elf -> base_ptr + s -> sh_offset ;
484477 * out_len = (size_t )s -> sh_size ;
485478
486- /* Defensive: ensure pointer is inside buffer */
487479 if (* out_ptr < elf -> base_ptr || * out_ptr + * out_len > elf -> base_ptr + elf -> size )
488480 return FOSSIL_MEDIA_ELF_ERR_BAD_FORMAT ;
489481
490- /* For minimal ELF, allow .text and .shstrtab to be found at any section index.
491- If index == 2, but sh_type is STRTAB, swap to .text section. */
492- if (index == 2 && s -> sh_type == 3 ) { // STRTAB
493- // Try to find .text section
494- for (size_t i = 0 ; i < elf -> sh_count ; ++ i ) {
495- if (elf -> shdrs [i ].sh_type == 1 ) { // PROGBITS
496- * out_ptr = elf -> base_ptr + elf -> shdrs [i ].sh_offset ;
497- * out_len = (size_t )elf -> shdrs [i ].sh_size ;
498- break ;
499- }
500- }
501- }
502-
503482 return FOSSIL_MEDIA_ELF_OK ;
504483}
505484
@@ -509,31 +488,11 @@ int fossil_media_elf_find_section_by_name(const fossil_media_elf_t *elf, const c
509488 for (size_t i = 0 ; i < elf -> sh_count ; ++ i ) {
510489 const char * sname = NULL ;
511490 int rc = fossil_media_elf_get_section_name (elf , i , & sname );
512- if (rc != FOSSIL_MEDIA_ELF_OK || !sname ) {
513- // For minimal ELF files, section name may be empty string (sh_name == 0 or == shstrtab_size)
514- if (name [0 ] == '\0' && rc == FOSSIL_MEDIA_ELF_OK && sname && sname [0 ] == '\0' ) {
515- * out_index = i ;
516- return FOSSIL_MEDIA_ELF_OK ;
517- }
518- continue ;
519- }
520- // Accept .text and .shstrtab at any index for minimal ELF
521- if (strcmp (sname , name ) == 0 ) {
491+ if (rc == FOSSIL_MEDIA_ELF_OK && sname && strcmp (sname , name ) == 0 ) {
522492 * out_index = i ;
523493 return FOSSIL_MEDIA_ELF_OK ;
524494 }
525495 }
526- // For minimal ELF, allow lookup by section index if name matches .text or .shstrtab
527- if (elf -> sh_count > 2 && (strcmp (name , ".text" ) == 0 || strcmp (name , ".shstrtab" ) == 0 )) {
528- for (size_t i = 0 ; i < elf -> sh_count ; ++ i ) {
529- const char * sname = NULL ;
530- int rc = fossil_media_elf_get_section_name (elf , i , & sname );
531- if (rc == FOSSIL_MEDIA_ELF_OK && sname && strcmp (sname , name ) == 0 ) {
532- * out_index = i ;
533- return FOSSIL_MEDIA_ELF_OK ;
534- }
535- }
536- }
537496 return FOSSIL_MEDIA_ELF_ERR_NO_SECTION ;
538497}
539498
0 commit comments