@@ -2858,6 +2858,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
28582858 } else if attr. has_name ( sym:: link_name) {
28592859 codegen_fn_attrs. link_name = attr. value_str ( ) ;
28602860 } else if attr. has_name ( sym:: link_ordinal) {
2861+ if link_ordinal_span. is_some ( ) {
2862+ tcx. sess
2863+ . struct_span_err (
2864+ attr. span ,
2865+ "multiple `link_ordinal` attributes on a single definition" ,
2866+ )
2867+ . emit ( ) ;
2868+ }
28612869 link_ordinal_span = Some ( attr. span ) ;
28622870 if let ordinal @ Some ( _) = check_link_ordinal ( tcx, attr) {
28632871 codegen_fn_attrs. link_ordinal = ordinal;
@@ -3153,22 +3161,41 @@ fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
31533161 false
31543162}
31553163
3156- fn check_link_ordinal ( tcx : TyCtxt < ' _ > , attr : & ast:: Attribute ) -> Option < usize > {
3164+ fn check_link_ordinal ( tcx : TyCtxt < ' _ > , attr : & ast:: Attribute ) -> Option < u16 > {
31573165 use rustc_ast:: { Lit , LitIntType , LitKind } ;
31583166 let meta_item_list = attr. meta_item_list ( ) ;
31593167 let meta_item_list: Option < & [ ast:: NestedMetaItem ] > = meta_item_list. as_ref ( ) . map ( Vec :: as_ref) ;
31603168 let sole_meta_list = match meta_item_list {
31613169 Some ( [ item] ) => item. literal ( ) ,
3170+ Some ( _) => {
3171+ tcx. sess
3172+ . struct_span_err ( attr. span , "incorrect number of arguments to `#[link_ordinal]`" )
3173+ . note ( "the attribute requires exactly one argument" )
3174+ . emit ( ) ;
3175+ return None ;
3176+ }
31623177 _ => None ,
31633178 } ;
31643179 if let Some ( Lit { kind : LitKind :: Int ( ordinal, LitIntType :: Unsuffixed ) , .. } ) = sole_meta_list {
3165- if * ordinal <= usize:: MAX as u128 {
3166- Some ( * ordinal as usize )
3180+ // According to the table at https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header,
3181+ // the ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
3182+ // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import information
3183+ // to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
3184+ //
3185+ // FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for this:
3186+ // both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that specifies
3187+ // a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import library
3188+ // for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an import
3189+ // library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I don't know yet
3190+ // if the resulting EXE runs, as I haven't yet built the necessary DLL -- see earlier comment
3191+ // about LINK.EXE failing.)
3192+ if * ordinal <= u16:: MAX as u128 {
3193+ Some ( * ordinal as u16 )
31673194 } else {
31683195 let msg = format ! ( "ordinal value in `link_ordinal` is too large: `{}`" , & ordinal) ;
31693196 tcx. sess
31703197 . struct_span_err ( attr. span , & msg)
3171- . note ( "the value may not exceed `usize ::MAX`" )
3198+ . note ( "the value may not exceed `u16 ::MAX`" )
31723199 . emit ( ) ;
31733200 None
31743201 }
0 commit comments