GCC compiler - error or unspecified behavior?

When I have conflicting definitions of the ivars class in objective-c (without updating the class in the same file, and instead of naming the same class with diff ivars, no warnings are issued yet by the compiler. However, both sets of ivars can be used with using the appropriate methods in the corresponding files, for example,

Foo.m:

@interface foo { int a; } - (int)method; @end @implementation foo - (int)method { return a; } @end 

Bar.m:

 @interface foo { float baz; } @end @implementation foo (category) - (float)blah { return baz; } @end 

compiles without warnings or errors. Is it intentional? Is this an unverified error? (For the record, a and baz are actually the same memory location.)

Edit: for the record, I'm talking about the iPhone OS, which I assume uses the same runtime as the 64-bit MacOS

+6
gcc objective-c xcode
source share
2 answers

Although it is clearly broken, this code should compile without warning in all cases, simply because the compiler does not have enough information to know how to warn. When it is compiled correctly, it generates a completely different linker error only in 64-bit (this is a lunge from the new ABI Objective-C, and not directly from non-fragile ivars).

If you add int main() {} to foo.m and then compile it using the command line gcc -arch x86_64 foo.m -lobjc , the link errors will disappear because the objc runtime library provides the empty vtable characters needed to complete links.

At compile time, think of each .m file as an isolated compilation unit. When the compiler compiles the .m file, it knows only what is in this .m file, what is provided by anyone imported in this .m file, and - if the project is configured for it - what is defined in the precompiled project header.

Thus, when you say in bar.m:

 @interface foo { float baz; } @end @implementation foo (category) - (float)blah { return baz; } @end int main() {} 

The compiler has no concept of declaration in foo.m. The generated code describes a category in the foo class, which refers to ivar baz. If this class does not exist during communication, the error will be thrown to Now, given your foo.m and bar.m with the addition of the main function, as mentioned above, try several compilations:

 gcc -arch i386 foo.m -lobjc Undefined symbols: "_main", referenced from: start in crt1.10.6.o ld: symbol(s) not found collect2: ld returned 1 exit status 

It makes sense because we do not define the main () function in foo.m. 64-bit compilation does the same.

 gcc -arch i386 bar.m -lobjc 

Compiles and links without warning. To understand why, look at the generated characters (deleted about a dozen irrelevant):

 nm -a a.out 00001f52 t -[foo(category) blah] 00000000 A .objc_category_name_foo_category 

So, the binary contains a category called category in the class foo . Link error because the linker is not actually trying to resolve categories. It assumes that the class foo will magically appear before the category is resolved at runtime.

You can monitor the runtime class / category resolution with ivar:

 env OBJC_PRINT_CLASS_SETUP=YES ./a.out objc[498]: CONNECT: pending category 'foo (category)' objc[498]: CONNECT: class 'Object' now connected (root class) objc[498]: CONNECT: class 'Protocol' now connected objc[498]: CONNECT: class 'List' now connected 

So the category was marked as pending. Runtime will connect to it as soon as foo appears!

Now, 64 bit ...

 gcc -arch x86_64 bar.m -lobjc Undefined symbols: "_OBJC_IVAR_$_foo.baz", referenced from: -[foo(category) blah] in ccvX4uIk.o "_OBJC_CLASS_$_foo", referenced from: l_OBJC_$_CATEGORY_foo_$_category in ccvX4uIk.o objc-class-ref-to-foo in ccvX4uIk.o ld: symbol(s) not found 

Link errors are related to the fact that the modern Objective-C ABI actually issues the correct characters for instance variables and categories for various reasons, including adding metadata that can help verify programs (as was the case in this case).

There are no compilation errors (this is the correct behavior), and link errors make sense. Now, how about combining the two?

In the 32-bit case, everything compiles and communicates without errors. So we need to look at the characters and debugging of ObjC to find out what happens:

 gcc -arch i386 bar.m foo.m -lobjc nm -a a.out 00001e0f t -[foo method] 00001dea t -[foo(category) blah] 00000000 A .objc_category_name_foo_category 00003070 S .objc_class_name_foo env OBJC_PRINT_CLASS_SETUP=YES ./a.out objc[530]: CONNECT: attaching category 'foo (category)' objc[530]: CONNECT: class 'Object' now connected (root class) objc[530]: CONNECT: class 'Protocol' now connected objc[530]: CONNECT: class 'List' now connected objc[530]: CONNECT: class 'foo' now connected (root class) 

Yeah! The foo class now exists, and the runtime runs the class at startup. Obviously, the method returning baz ivar will be ineffective.

64-bit linker does not work:

 gcc -arch x86_64 bar.m foo.m -lobjc Undefined symbols: "_OBJC_IVAR_$_foo.baz", referenced from: -[foo(category) blah] in ccBHNqzm.o ld: symbol(s) not found collect2: ld returned 1 exit status 

With the addition of characters for instance variables, the linker can now catch situations where the class has been fixed incorrectly (as was done in @interface bar.m).

+18
source share

I think you made an extended class.

0
source share

All Articles