The standard doesn't want to dictate implementation lingo they instead make a vast list of what should be #t, what should be #f and that everything eq? always also is eqv?, but not vice versa. They write everything except the crucial information that it is pointer equalness. Ie. eq? returns #t when two arguments is the same object, like the == of Java.
In Scheme, (eq? "a" "a") is unspecified. Both #t and #f are acceptable results. It is one of the examples in the report. Literal values like "a" or '(a b c) are immutable. This is referenced from quote to the information about the storage model.
In the same report trying to mutate literals is considered an error. This is shown for string-set! like this:
(define (f) (make-string 3 #\*))
(string-set! (f) 0 #\?) ⇒ unspecified
So make-string makes a new string each call which you really cannot use so the first example is OK Scheme code that wast cycles. If you were to bind the result to a variable and then do it you'll have access to the result like this:
(define (f) (make-string 3 #\*))
(define test (f))
(string-set! test 0 #\?) ⇒ unspecified
test ⇒ "?**"
Now the second example is more on topic.
(define (g) "***")
(string-set! (g) 0 #\?) ⇒ unspecified
; should raise &assertion exception
The R5RS went so far as to say it isn't even Scheme so any result would be ok, while R6RS and later strongly suggest that trying to mutate should raise an exception.
Most implementations don't so for those calling (g) most likely results in one of "?**" or "***" when the illegal code works without an error.
Do back to you code:
(define str1 "hello")
(define str2 "hello")
(eq? str1 str2) ⇒ unspecified
For the exact same reason (eq? "a" "a") is unspecified. An interpreter might always return #f and even compiled code, but compiled code is more likely to return #t.
(define str1 (string #\h #\e #\l #\l #\o))
(define str2 (string #\h #\e #\l #\l #\o))
(eq? str1 str2) ⇒ #f
They are always different since (string #\h #\e #\l #\l #\o) creates a new string and since str1 and str2 are created separately they are different strings that look the same.
Know that such compound data types can be checked for equality with equal? that would return #t when two objects are considered the same, eg. often when they look the same. Thus:
(equal? str1 str2) ⇒ #t