@@ -36,6 +36,7 @@ const SHF_WRITE: u64 = 0x1;
3636const SHF_EXECINSTR : u64 = 0x4 ;
3737const SHT_NOBITS : u32 = 8 ;
3838const SHT_PROGBITS : u32 = 1 ;
39+ const SHT_DYNSYM : u32 = 11 ;
3940const PT_LOAD : u32 = 1 ;
4041const PT_DYNAMIC : u32 = 2 ;
4142const PT_INTERP : u32 = 3 ;
@@ -180,6 +181,7 @@ struct LinkerState {
180181 link_pie : bool ,
181182 link_static : bool ,
182183 link_as_needed : bool ,
184+ no_undefined : bool ,
183185 dynamic_linker_path : u64 ,
184186 soname : u64 ,
185187 needed_shared_libs : Vec < u64 > ,
@@ -1590,6 +1592,7 @@ fn reset_linker_state_defaults(state: &mut LinkerState) {
15901592 state. entry = 0x0010_0000 ;
15911593 state. image_base = 0x0001_0000 ;
15921594 state. link_as_needed = false ;
1595+ state. no_undefined = false ;
15931596 state. link_pie = false ;
15941597 state. link_shared = false ;
15951598 state. link_static = false ;
@@ -1625,6 +1628,172 @@ fn probe_object_format_path(path: &Path) -> u32 {
16251628 probe_object_format_bytes ( & raw )
16261629}
16271630
1631+ fn dynstr_symbol_name ( raw : & [ u8 ] , dynstr_off : usize , dynstr_size : usize , st_name : u32 ) -> Option < String > {
1632+ let name_off = st_name as usize ;
1633+ if name_off >= dynstr_size {
1634+ return None ;
1635+ }
1636+ let start = dynstr_off. checked_add ( name_off) ?;
1637+ let end_limit = dynstr_off. checked_add ( dynstr_size) ?;
1638+ if start >= raw. len ( ) || start >= end_limit {
1639+ return None ;
1640+ }
1641+ let mut end = start;
1642+ while end < end_limit && end < raw. len ( ) {
1643+ if raw[ end] == 0 {
1644+ break ;
1645+ }
1646+ end += 1 ;
1647+ }
1648+ if end <= start {
1649+ return None ;
1650+ }
1651+ Some ( String :: from_utf8_lossy ( & raw [ start..end] ) . to_string ( ) )
1652+ }
1653+
1654+ fn ingest_shared_elf_symbols ( state : & mut LinkerState , raw : & [ u8 ] ) -> u32 {
1655+ if raw. len ( ) < 64 || & raw [ 0 ..4 ] != b"\x7f ELF" {
1656+ return ERR_INVALID_FORMAT ;
1657+ }
1658+ if raw. get ( 4 ) . copied ( ) . unwrap_or ( 0 ) != 2 || raw. get ( 5 ) . copied ( ) . unwrap_or ( 0 ) != 1 {
1659+ return ERR_INVALID_FORMAT ;
1660+ }
1661+
1662+ let shoff = match read_u64_le_at ( raw, 40 ) {
1663+ Some ( v) => v as usize ,
1664+ None => return ERR_INVALID_FORMAT ,
1665+ } ;
1666+ let shentsize = match read_u16_le_at ( raw, 58 ) {
1667+ Some ( v) if v >= 64 => v as usize ,
1668+ _ => return ERR_INVALID_FORMAT ,
1669+ } ;
1670+ let shnum = match read_u16_le_at ( raw, 60 ) {
1671+ Some ( v) if v > 0 => v as usize ,
1672+ _ => return ERR_INVALID_FORMAT ,
1673+ } ;
1674+
1675+ let mut dynsym_index: Option < usize > = None ;
1676+ for index in 0 ..shnum {
1677+ let base = match shoff. checked_add ( index. saturating_mul ( shentsize) ) {
1678+ Some ( v) => v,
1679+ None => return ERR_INVALID_FORMAT ,
1680+ } ;
1681+ let sh_type = match read_u32_le_at ( raw, base + 4 ) {
1682+ Some ( v) => v,
1683+ None => return ERR_INVALID_FORMAT ,
1684+ } ;
1685+ if sh_type == SHT_DYNSYM {
1686+ dynsym_index = Some ( index) ;
1687+ break ;
1688+ }
1689+ }
1690+
1691+ let dynsym_index = match dynsym_index {
1692+ Some ( v) => v,
1693+ None => return ERR_OK ,
1694+ } ;
1695+ let dynsym_base = match shoff. checked_add ( dynsym_index. saturating_mul ( shentsize) ) {
1696+ Some ( v) => v,
1697+ None => return ERR_INVALID_FORMAT ,
1698+ } ;
1699+ let dynsym_off = match read_u64_le_at ( raw, dynsym_base + 24 ) {
1700+ Some ( v) => v as usize ,
1701+ None => return ERR_INVALID_FORMAT ,
1702+ } ;
1703+ let dynsym_size = match read_u64_le_at ( raw, dynsym_base + 32 ) {
1704+ Some ( v) => v as usize ,
1705+ None => return ERR_INVALID_FORMAT ,
1706+ } ;
1707+ let dynsym_link = match read_u32_le_at ( raw, dynsym_base + 40 ) {
1708+ Some ( v) => v as usize ,
1709+ None => return ERR_INVALID_FORMAT ,
1710+ } ;
1711+ let dynsym_entsize = match read_u64_le_at ( raw, dynsym_base + 56 ) {
1712+ Some ( v) if v >= 24 => v as usize ,
1713+ _ => return ERR_INVALID_FORMAT ,
1714+ } ;
1715+
1716+ if dynsym_off. saturating_add ( dynsym_size) > raw. len ( ) {
1717+ return ERR_INVALID_FORMAT ;
1718+ }
1719+ if dynsym_link >= shnum {
1720+ return ERR_INVALID_FORMAT ;
1721+ }
1722+
1723+ let dynstr_base = match shoff. checked_add ( dynsym_link. saturating_mul ( shentsize) ) {
1724+ Some ( v) => v,
1725+ None => return ERR_INVALID_FORMAT ,
1726+ } ;
1727+ let dynstr_off = match read_u64_le_at ( raw, dynstr_base + 24 ) {
1728+ Some ( v) => v as usize ,
1729+ None => return ERR_INVALID_FORMAT ,
1730+ } ;
1731+ let dynstr_size = match read_u64_le_at ( raw, dynstr_base + 32 ) {
1732+ Some ( v) => v as usize ,
1733+ None => return ERR_INVALID_FORMAT ,
1734+ } ;
1735+ if dynstr_off. saturating_add ( dynstr_size) > raw. len ( ) {
1736+ return ERR_INVALID_FORMAT ;
1737+ }
1738+
1739+ let symbol_count = dynsym_size / dynsym_entsize;
1740+ for index in 0 ..symbol_count {
1741+ let sym_off = dynsym_off + index. saturating_mul ( dynsym_entsize) ;
1742+ let st_name = match read_u32_le_at ( raw, sym_off) {
1743+ Some ( v) if v != 0 => v,
1744+ _ => continue ,
1745+ } ;
1746+ let st_info = * raw. get ( sym_off + 4 ) . unwrap_or ( & 0 ) ;
1747+ let st_bind = st_info >> 4 ;
1748+ if st_bind != 1 && st_bind != 2 {
1749+ continue ;
1750+ }
1751+ let st_shndx = match read_u16_le_at ( raw, sym_off + 6 ) {
1752+ Some ( v) => v,
1753+ None => continue ,
1754+ } ;
1755+ if st_shndx == SHN_UNDEF {
1756+ continue ;
1757+ }
1758+ let name = match dynstr_symbol_name ( raw, dynstr_off, dynstr_size, st_name) {
1759+ Some ( v) if !v. is_empty ( ) => v,
1760+ _ => continue ,
1761+ } ;
1762+ let name_hash = fnv1a64 ( & name) ;
1763+ let should_insert = match state. globals . get ( & name_hash) {
1764+ Some ( existing) if existing. defined == 1 && existing. bind == 1 => false ,
1765+ _ => true ,
1766+ } ;
1767+ if should_insert {
1768+ state. globals . insert (
1769+ name_hash,
1770+ GlobalSymbol {
1771+ object_index : 0 ,
1772+ symbol_index : 0 ,
1773+ bind : 2 ,
1774+ defined : 1 ,
1775+ address : 0 ,
1776+ } ,
1777+ ) ;
1778+ }
1779+ }
1780+
1781+ ERR_OK
1782+ }
1783+
1784+ fn ingest_shared_object_symbols ( state : & mut LinkerState , path : & Path ) -> u32 {
1785+ let raw = match fs:: read ( path) {
1786+ Ok ( v) => v,
1787+ Err ( _) => return ERR_FILE_NOT_FOUND ,
1788+ } ;
1789+ match probe_object_format_bytes ( & raw ) {
1790+ OBJECT_FORMAT_ELF64 => ingest_shared_elf_symbols ( state, & raw ) ,
1791+ OBJECT_FORMAT_COFF64 | OBJECT_FORMAT_MACHO64 => ERR_OK ,
1792+ OBJECT_FORMAT_UNKNOWN => ERR_OK ,
1793+ _ => ERR_OK ,
1794+ }
1795+ }
1796+
16281797fn coff_characteristics_to_flags ( characteristics : u32 , section_name : & str ) -> ( u32 , u64 ) {
16291798 let mut sh_flags = SHF_ALLOC ;
16301799 if characteristics & 0x8000_0000 != 0 {
@@ -2293,6 +2462,13 @@ fn apply_linker_script_statement(
22932462 return ERR_OK ;
22942463 }
22952464
2465+ if let Some ( arg) = extract_directive_arg ( statement, "OUTPUT_ARCH" ) {
2466+ if let Some ( target) = parse_target_value ( & arg) {
2467+ state. target = target;
2468+ }
2469+ return ERR_OK ;
2470+ }
2471+
22962472 if let Some ( arg) = extract_directive_arg ( statement, "OUTPUT" ) {
22972473 if !arg. is_empty ( ) {
22982474 let output_path = resolve_script_path ( script_dir, & arg) ;
@@ -2361,6 +2537,32 @@ fn apply_linker_script_statement(
23612537 return ERR_OK ;
23622538 }
23632539
2540+ if let Some ( arg) = extract_directive_arg ( statement, "AS_NEEDED" ) {
2541+ let prior = state. link_as_needed ;
2542+ state. link_as_needed = true ;
2543+ for token in tokenize_script_args ( & arg) {
2544+ let path = resolve_script_path ( script_dir, & token) ;
2545+ state
2546+ . inputs
2547+ . push ( intern_string ( path. to_string_lossy ( ) . to_string ( ) ) ) ;
2548+ }
2549+ state. link_as_needed = prior;
2550+ return ERR_OK ;
2551+ }
2552+
2553+ if let Some ( arg) = extract_directive_arg ( statement, "NO_AS_NEEDED" ) {
2554+ let prior = state. link_as_needed ;
2555+ state. link_as_needed = false ;
2556+ for token in tokenize_script_args ( & arg) {
2557+ let path = resolve_script_path ( script_dir, & token) ;
2558+ state
2559+ . inputs
2560+ . push ( intern_string ( path. to_string_lossy ( ) . to_string ( ) ) ) ;
2561+ }
2562+ state. link_as_needed = prior;
2563+ return ERR_OK ;
2564+ }
2565+
23642566 if let Some ( arg) = extract_directive_arg ( statement, "INCLUDE" ) {
23652567 if depth >= 32 {
23662568 return ERR_INVALID_FORMAT ;
@@ -3127,6 +3329,34 @@ pub extern "C" fn host_linker_get_as_needed() -> u32 {
31273329 if state. link_as_needed { 1 } else { 0 }
31283330}
31293331
3332+ #[ no_mangle]
3333+ pub extern "C" fn host_linker_set_no_undefined ( enabled : u32 ) -> u32 {
3334+ let mut state = linker ( ) . lock ( ) . expect ( "linker mutex poisoned" ) ;
3335+ state. no_undefined = enabled != 0 ;
3336+ set_last_error ( & mut state, ERR_OK )
3337+ }
3338+
3339+ #[ no_mangle]
3340+ pub extern "C" fn host_linker_get_no_undefined ( ) -> u32 {
3341+ let state = linker ( ) . lock ( ) . expect ( "linker mutex poisoned" ) ;
3342+ if state. no_undefined { 1 } else { 0 }
3343+ }
3344+
3345+ #[ no_mangle]
3346+ pub extern "C" fn host_linker_allow_dynamic_unresolved ( ) -> u32 {
3347+ let state = linker ( ) . lock ( ) . expect ( "linker mutex poisoned" ) ;
3348+ if state. no_undefined {
3349+ return 0 ;
3350+ }
3351+ if state. link_static {
3352+ return 0 ;
3353+ }
3354+ if state. needed_shared_libs . is_empty ( ) {
3355+ return 0 ;
3356+ }
3357+ 1
3358+ }
3359+
31303360#[ no_mangle]
31313361pub extern "C" fn host_linker_set_dynamic_linker ( path : u64 ) -> u32 {
31323362 let mut state = linker ( ) . lock ( ) . expect ( "linker mutex poisoned" ) ;
@@ -3278,6 +3508,17 @@ pub extern "C" fn host_linker_ingest_macho_object(path: u64) -> u32 {
32783508 set_last_error ( & mut state, ERR_OK )
32793509}
32803510
3511+ #[ no_mangle]
3512+ pub extern "C" fn host_linker_ingest_shared_object ( path : u64 ) -> u32 {
3513+ let p = match to_path ( path) {
3514+ Some ( v) => v,
3515+ None => return ERR_FILE_NOT_FOUND ,
3516+ } ;
3517+ let mut state = linker ( ) . lock ( ) . expect ( "linker mutex poisoned" ) ;
3518+ let status = ingest_shared_object_symbols ( & mut state, & p) ;
3519+ set_last_error ( & mut state, status)
3520+ }
3521+
32813522#[ no_mangle]
32823523pub extern "C" fn host_linker_object_begin ( path : u64 , file_size : u64 , elf_type : u16 , machine : u16 ) -> u32 {
32833524 let mut state = linker ( ) . lock ( ) . expect ( "linker mutex poisoned" ) ;
0 commit comments