@@ -2861,6 +2861,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
28612861 } else if attr. has_name ( sym:: link_name) {
28622862 codegen_fn_attrs. link_name = attr. value_str ( ) ;
28632863 } else if attr. has_name ( sym:: link_ordinal) {
2864+ if link_ordinal_span. is_some ( ) {
2865+ tcx. sess
2866+ . struct_span_err (
2867+ attr. span ,
2868+ "multiple `link_ordinal` attributes on a single definition" ,
2869+ )
2870+ . emit ( ) ;
2871+ }
28642872 link_ordinal_span = Some ( attr. span ) ;
28652873 if let ordinal @ Some ( _) = check_link_ordinal ( tcx, attr) {
28662874 codegen_fn_attrs. link_ordinal = ordinal;
@@ -3156,22 +3164,41 @@ fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
31563164 false
31573165}
31583166
3159- fn check_link_ordinal ( tcx : TyCtxt < ' _ > , attr : & ast:: Attribute ) -> Option < usize > {
3167+ fn check_link_ordinal ( tcx : TyCtxt < ' _ > , attr : & ast:: Attribute ) -> Option < u16 > {
31603168 use rustc_ast:: { Lit , LitIntType , LitKind } ;
31613169 let meta_item_list = attr. meta_item_list ( ) ;
31623170 let meta_item_list: Option < & [ ast:: NestedMetaItem ] > = meta_item_list. as_ref ( ) . map ( Vec :: as_ref) ;
31633171 let sole_meta_list = match meta_item_list {
31643172 Some ( [ item] ) => item. literal ( ) ,
3173+ Some ( _) => {
3174+ tcx. sess
3175+ . struct_span_err ( attr. span , "incorrect number of arguments to `#[link_ordinal]`" )
3176+ . note ( "the attribute requires exactly one argument" )
3177+ . emit ( ) ;
3178+ return None ;
3179+ }
31653180 _ => None ,
31663181 } ;
31673182 if let Some ( Lit { kind : LitKind :: Int ( ordinal, LitIntType :: Unsuffixed ) , .. } ) = sole_meta_list {
3168- if * ordinal <= usize:: MAX as u128 {
3169- Some ( * ordinal as usize )
3183+ // According to the table at https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header,
3184+ // the ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
3185+ // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import information
3186+ // to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
3187+ //
3188+ // FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for this:
3189+ // both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that specifies
3190+ // a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import library
3191+ // for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an import
3192+ // library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I don't know yet
3193+ // if the resulting EXE runs, as I haven't yet built the necessary DLL -- see earlier comment
3194+ // about LINK.EXE failing.)
3195+ if * ordinal <= u16:: MAX as u128 {
3196+ Some ( * ordinal as u16 )
31703197 } else {
31713198 let msg = format ! ( "ordinal value in `link_ordinal` is too large: `{}`" , & ordinal) ;
31723199 tcx. sess
31733200 . struct_span_err ( attr. span , & msg)
3174- . note ( "the value may not exceed `usize ::MAX`" )
3201+ . note ( "the value may not exceed `u16 ::MAX`" )
31753202 . emit ( ) ;
31763203 None
31773204 }
0 commit comments