diff --git includes/common.inc includes/common.inc
index a18e4d6..6c9772b 100644
--- includes/common.inc
+++ includes/common.inc
@@ -2682,6 +2682,8 @@ function drupal_attributes(array $attributes = array()) {
  *       escaped HTML.
  *     - 'alias' (default FALSE)
  *       Whether the given path is an alias already.
+ *     - 'url' (default FALSE)
+ *       Whether the given path has been passed to url() already.
  * @return
  *   an HTML string containing a link to the given path.
  */
@@ -2693,6 +2695,7 @@ function l($text, $path, array $options = array()) {
   $options += array(
       'attributes' => array(),
       'html' => FALSE,
+      'url' => FALSE,
     );
 
   // Append active class.
@@ -2738,7 +2741,7 @@ function l($text, $path, array $options = array()) {
   }
   // The result of url() is a plain-text URL. Because we are using it here
   // in an HTML argument context, we need to encode it properly.
-  return '<a href="' . check_plain(url($path, $options)) . '"' . drupal_attributes($options['attributes']) . '>' . ($options['html'] ? $text : check_plain($text)) . '</a>';
+  return '<a href="' . check_plain(($options['url'] ? $path : url($path, $options))) . '"' . drupal_attributes($options['attributes']) . '>' . ($options['html'] ? $text : check_plain($text)) . '</a>';
 }
 
 /**
diff --git includes/entity.inc includes/entity.inc
index 26db7f5..9e6f534 100644
--- includes/entity.inc
+++ includes/entity.inc
@@ -232,6 +232,29 @@ class DrupalDefaultEntityController implements DrupalEntityControllerInterface {
    * See NodeController::attachLoad() for an example.
    */
   protected function attachLoad(&$queried_entities) {
+    // Add the URI of each entity if available.
+    foreach ($queried_entities as $queried_entity) {
+      if ($url_structure = entity_path($this->entityType, $queried_entity)) {
+        // Prepare variables to be passed to url().
+        $path = $url_structure['path'];
+        $options = isset($url_structure['options']) ? $url_structure['options'] : array();
+        // Implementations of hook_url_outbound_alter() might try to load the
+        // same entity, so there is a risk of infinite recursion when calling
+        // url(), see forum.module for example. To avoid this recursion, we only
+        // call url() the first time an entity is being loaded. An empty uri is
+        // assigned on eventual subsequent recursive loads.
+        static $loaded_entities;
+        if (empty($loaded_entities[$path])) {
+          $loaded_entities[$path] = TRUE;
+          $queried_entity->uri = url($path, $options);
+          unset($loaded_entities[$path]);
+        }
+        else {
+          $queried_entity->uri = NULL;
+        }
+      }
+    }
+
     // Attach fields.
     if ($this->entityInfo['fieldable']) {
       if ($this->revisionId) {
diff --git modules/comment/comment.module modules/comment/comment.module
index a3143f8..85d66aa 100644
--- modules/comment/comment.module
+++ modules/comment/comment.module
@@ -151,7 +151,10 @@ function comment_node_type_load($name) {
  * Entity path callback.
  */
 function comment_path($comment) {
-  return 'comment/' . $comment->cid;
+  return array(
+    'path' => 'comment/' . $comment->cid,
+    'options' => array('fragment' => 'comment-' . $comment->cid),
+  );
 }
 
 /**
@@ -2178,8 +2181,9 @@ function template_preprocess_comment(&$variables) {
   $variables['new']       = !empty($comment->new) ? t('new') : '';
   $variables['picture']   = theme_get_setting('toggle_comment_user_picture') ? theme('user_picture', array('account' => $comment)) : '';
   $variables['signature'] = $comment->signature;
-  $variables['title']     = l($comment->subject, 'comment/' . $comment->cid, array('fragment' => "comment-$comment->cid"));
-  $variables['permalink'] = l('#', 'comment/' . $comment->cid, array('fragment' => "comment-$comment->cid"));
+  $comment->uri = empty($comment->uri) ? NULL : $comment->uri;
+  $variables['title']     = l($comment->subject, $comment->uri, array('url' => TRUE));
+  $variables['permalink'] = l('#', $comment->uri, array('url' => TRUE));
 
   // Preprocess fields.
   field_attach_preprocess('comment', $comment, $variables['elements'], $variables);
diff --git modules/image/image.field.inc modules/image/image.field.inc
index 6bbc691..5bf82e3 100644
--- modules/image/image.field.inc
+++ modules/image/image.field.inc
@@ -461,7 +461,8 @@ function image_field_formatter_view($obj_type, $object, $field, $instance, $lang
 
   // Check if the formatter involves a link.
   if (strpos($display['type'], 'image_link_content') === 0) {
-    $path = entity_path($obj_type, $object);
+    $path_elements = entity_path($obj_type, $object);
+    $path = $path_elements['path'];
   }
   elseif (strpos($display['type'], 'image_link_file') === 0) {
     $link_file = TRUE;
diff --git modules/node/node.module modules/node/node.module
index ce6026d..eccfa59 100644
--- modules/node/node.module
+++ modules/node/node.module
@@ -246,7 +246,9 @@ function node_entity_info() {
  * Entity path callback.
  */
 function node_path($node) {
-  return 'node/' . $node->nid;
+  return array(
+    'path' => 'node/' . $node->nid,
+  );
 }
 
 /**
@@ -1367,7 +1369,7 @@ function template_preprocess_node(&$variables) {
 
   $variables['date']      = format_date($node->created);
   $variables['name']      = theme('username', array('account' => $node));
-  $variables['node_url']  = url('node/' . $node->nid);
+  $variables['node_url']  = empty($node->uri) ? NULL : $node->uri;
   $variables['node_title'] = check_plain($node->title);
   $variables['page']      = node_is_page($node);
 
@@ -2420,7 +2422,7 @@ function node_page_default() {
 function node_page_view($node) {
   drupal_set_title($node->title);
   // Set the node path as the canonical URL to prevent duplicate content.
-  drupal_add_html_head_link(array('rel' => 'canonical', 'href' => url('node/' . $node->nid)), TRUE);
+  drupal_add_html_head_link(array('rel' => 'canonical', 'href' => $node->uri), TRUE);
   // Set the non-aliased path as a default shortlink.
   drupal_add_html_head_link(array('rel' => 'shortlink', 'href' => url('node/' . $node->nid, array('alias' => TRUE))), TRUE);
   return node_show($node);
diff --git modules/rdf/rdf.module modules/rdf/rdf.module
index e1ffa53..b702bc0 100644
--- modules/rdf/rdf.module
+++ modules/rdf/rdf.module
@@ -479,7 +479,7 @@ function rdf_preprocess_user_profile(&$variables) {
   $account = user_load($variables['user']->uid);
   if (!empty($account->rdf_mapping['rdftype'])) {
     $variables['attributes_array']['typeof'] = $account->rdf_mapping['rdftype'];
-    $variables['attributes_array']['about'] = url('user/' . $account->uid);
+    $variables['attributes_array']['about'] = $account->uri;
   }
 }
 
@@ -549,7 +549,7 @@ function rdf_preprocess_comment(&$variables) {
     // Adds RDFa markup to the comment container. The about attribute specifies
     // the URI of the resource described within the HTML element, while the
     // typeof attribute indicates its RDF type (e.g. sioc:Post, etc.).
-    $variables['attributes_array']['about'] = url('comment/' . $comment->cid, array('fragment' => 'comment-' . $comment->cid));
+    $variables['attributes_array']['about'] = $comment->uri;
     $variables['attributes_array']['typeof'] = $comment->rdf_mapping['rdftype'];
   }
 
diff --git modules/taxonomy/taxonomy.module modules/taxonomy/taxonomy.module
index a5b8929..32eb063 100644
--- modules/taxonomy/taxonomy.module
+++ modules/taxonomy/taxonomy.module
@@ -133,7 +133,9 @@ function taxonomy_entity_info() {
  * Entity path callback.
  */
 function taxonomy_term_path($term) {
-  return 'taxonomy/term/' . $term->tid;
+  return array(
+    'path' => 'taxonomy/term/' . $term->tid,
+  );
 }
 
 /**
diff --git modules/user/user.module modules/user/user.module
index 837ae2a..27299d9 100644
--- modules/user/user.module
+++ modules/user/user.module
@@ -153,7 +153,9 @@ function user_entity_info() {
  * Entity path callback.
  */
 function user_path($user) {
-  return 'user/' . $user->uid;
+  return array(
+    'path' => 'user/' . $user->uid,
+  );
 }
 
 /**
@@ -3279,7 +3281,7 @@ function user_register_submit($form, &$form_state) {
 
   // New administrative account without notification.
   if ($admin && !$notify) {
-    drupal_set_message(t('Created a new user account for <a href="@url">%name</a>. No e-mail has been sent.', array('@url' => url("user/$account->uid"), '%name' => $account->name)));
+    drupal_set_message(t('Created a new user account for <a href="@url">%name</a>. No e-mail has been sent.', array('@url' => $account->uri, '%name' => $account->name)));
   }
   // No e-mail verification required; log in user immediately.
   elseif (!$admin && !variable_get('user_email_verification', TRUE) && $account->status) {
@@ -3294,7 +3296,7 @@ function user_register_submit($form, &$form_state) {
     $op = $notify ? 'register_admin_created' : 'register_no_approval_required';
     _user_mail_notify($op, $account);
     if ($notify) {
-      drupal_set_message(t('A welcome message with further instructions has been e-mailed to the new user <a href="@url">%name</a>.', array('@url' => url("user/$account->uid"), '%name' => $account->name)));
+      drupal_set_message(t('A welcome message with further instructions has been e-mailed to the new user <a href="@url">%name</a>.', array('@url' => $account->uri, '%name' => $account->name)));
     }
     else {
       drupal_set_message(t('A welcome message with further instructions has been sent to your e-mail address.'));
