diff --git a/core/lib/Drupal/Core/Routing/UrlGenerator.php b/core/lib/Drupal/Core/Routing/UrlGenerator.php
index ccf2b60..c4ab1f9 100644
--- a/core/lib/Drupal/Core/Routing/UrlGenerator.php
+++ b/core/lib/Drupal/Core/Routing/UrlGenerator.php
@@ -149,7 +149,8 @@ public function getPathFromRoute($name, $parameters = array()) {
    *   The route parameters passed to the generator. Parameters that do not
    *   match any variables will be added to the result as query parameters.
    * @param array $query_params
-   *   Query parameters passed to the generator as $options['query'].
+   *   Query parameters passed to the generator as $options['query']. This may
+   *   be modified if there are extra parameters not used as path variables.
    * @param string $name
    *   The route name or other identifying string from ::getRouteDebugMessage().
    *
@@ -162,7 +163,7 @@ public function getPathFromRoute($name, $parameters = array()) {
    *   When a parameter value for a placeholder is not correct because it does
    *   not match the requirement.
    */
-  protected function doGenerate(array $variables, array $defaults, array $tokens, array $parameters, array $query_params, $name) {
+  protected function doGenerate(array $variables, array $defaults, array $tokens, array $parameters, array &$query_params, $name) {
     $variables = array_flip($variables);
     $mergedParams = array_replace($defaults, $this->context->getParameters(), $parameters);
 
@@ -208,30 +209,10 @@ protected function doGenerate(array $variables, array $defaults, array $tokens,
       $url = '/';
     }
 
-    // The contexts base URL is already encoded (see Symfony\Component\HttpFoundation\Request)
-    $url = str_replace($this->decodedChars[0], $this->decodedChars[1], rawurlencode($url));
 
-    // Drupal paths rarely include dots, so skip this processing if possible.
-    if (strpos($url, '/.') !== FALSE) {
-      // the path segments "." and ".." are interpreted as relative reference when
-      // resolving a URI; see http://tools.ietf.org/html/rfc3986#section-3.3
-      // so we need to encode them as they are not used for this purpose here
-      // otherwise we would generate a URI that, when followed by a user agent
-      // (e.g. browser), does not match this route
-      $url = strtr($url, array('/../' => '/%2E%2E/', '/./' => '/%2E/'));
-      if ('/..' === substr($url, -3)) {
-        $url = substr($url, 0, -2) . '%2E%2E';
-      }
-      elseif ('/.' === substr($url, -2)) {
-        $url = substr($url, 0, -1) . '%2E';
-      }
-    }
 
-    // Add a query string if needed, including extra parameters.
+    // Add extra parameters to the query parameters.
     $query_params += array_diff_key($parameters, $variables, $defaults);
-    if ($query_params && $query = UrlHelper::buildQuery($query_params)) {
-      $url .= '?' . $query;
-    }
 
     return $url;
   }
@@ -279,8 +260,8 @@ public function generateFromRoute($name, $parameters = array(), $options = array
 
     $query_params = [];
     // Symfony adds any parameters that are not path slugs as query strings.
-    if (isset($options['query']) && is_array($options['query'])) {
-      $query_params = $options['query'];
+    if (!isset($options['query']) || !is_array($options['query'])) {
+      $options['query'] = array();
     }
 
     $fragment = '';
@@ -292,7 +273,7 @@ public function generateFromRoute($name, $parameters = array(), $options = array
 
     // Generate a relative URL having no path, just query string and fragment.
     if ($route->getOption('_no_path')) {
-      $query = $query_params ? '?' . http_build_query($query_params, '', '&') : '';
+      $query = $options['query'] ? '?' . http_build_query($options['query'], '', '&') : '';
       $url = $query . $fragment;
       return $collect_bubbleable_metadata ? $generated_url->setGeneratedUrl($url) : $url;
     }
@@ -302,13 +283,32 @@ public function generateFromRoute($name, $parameters = array(), $options = array
 
     $name = $this->getRouteDebugMessage($name);
     $this->processRoute($name, $route, $parameters, $generated_url);
-    $path = $this->getInternalPathFromRoute($name, $route, $parameters, $query_params);
+    $path = $this->getInternalPathFromRoute($name, $route, $parameters, $options['query']);
     // Outbound path processors might need the route object for the path, e.g.
     // to get the path pattern.
     $options['route'] = $route;
     if ($options['path_processing']) {
       $path = $this->processPath($path, $options, $generated_url);
     }
+    $query = $options['query'] ? '?' . http_build_query($options['query'], '', '&') : '';
+    // The contexts base URL is already encoded (see Symfony\Component\HttpFoundation\Request)
+    $path = str_replace($this->decodedChars[0], $this->decodedChars[1], rawurlencode($path));
+
+    // Drupal paths rarely include dots, so skip this processing if possible.
+    if (strpos($path, '/.') !== FALSE) {
+      // the path segments "." and ".." are interpreted as relative reference when
+      // resolving a URI; see http://tools.ietf.org/html/rfc3986#section-3.3
+      // so we need to encode them as they are not used for this purpose here
+      // otherwise we would generate a URI that, when followed by a user agent
+      // (e.g. browser), does not match this route
+      $path = strtr($path, array('/../' => '/%2E%2E/', '/./' => '/%2E/'));
+      if ('/..' === substr($path, -3)) {
+        $path = substr($path, 0, -2) . '%2E%2E';
+      }
+      elseif ('/.' === substr($path, -2)) {
+        $path = substr($path, 0, -1) . '%2E';
+      }
+    }
 
     if (!empty($options['prefix'])) {
       $path = ltrim($path, '/');
@@ -329,7 +329,7 @@ public function generateFromRoute($name, $parameters = array(), $options = array
         }
       }
 
-      $url = $base_url . $path . $fragment;
+      $url = $base_url . $path . $query . $fragment;
       return $collect_bubbleable_metadata ? $generated_url->setGeneratedUrl($url) : $url;
     }
 
@@ -337,7 +337,7 @@ public function generateFromRoute($name, $parameters = array(), $options = array
 
     $absolute = !empty($options['absolute']);
     if (!$absolute || !$host = $this->context->getHost()) {
-      $url = $base_url . $path . $fragment;
+      $url = $base_url . $path . $query . $fragment;
       return $collect_bubbleable_metadata ? $generated_url->setGeneratedUrl($url) : $url;
     }
 
@@ -363,29 +363,16 @@ public function generateFromRoute($name, $parameters = array(), $options = array
     if ($collect_bubbleable_metadata) {
       $generated_url->addCacheContexts(['url.site']);
     }
-    $url = $scheme . '://' . $host . $port . $base_url . $path . $fragment;
+    $url = $scheme . '://' . $host . $port . $base_url . $path . $query . $fragment;
     return $collect_bubbleable_metadata ? $generated_url->setGeneratedUrl($url) : $url;
-
   }
 
   /**
    * Passes the path to a processor manager to allow alterations.
    */
   protected function processPath($path, &$options = array(), BubbleableMetadata $bubbleable_metadata = NULL) {
-    // Router-based paths may have a querystring on them.
-    if ($query_pos = strpos($path, '?')) {
-      // We don't need to do a strict check here because position 0 would mean we
-      // have no actual path to work with.
-      $actual_path = substr($path, 0, $query_pos);
-      $query_string = substr($path, $query_pos);
-    }
-    else {
-      $actual_path = $path;
-      $query_string = '';
-    }
-    $path = $this->pathProcessor->processOutbound($actual_path === '/' ? $actual_path : rtrim($actual_path, '/'), $options, $this->requestStack->getCurrentRequest(), $bubbleable_metadata);
-    $path .= $query_string;
-    return $path;
+    $actual_path = $path === '/' ? $path : rtrim($path, '/');
+    return $this->pathProcessor->processOutbound($actual_path, $options, $this->requestStack->getCurrentRequest(), $bubbleable_metadata);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Routing/UrlGeneratorInterface.php b/core/lib/Drupal/Core/Routing/UrlGeneratorInterface.php
index 70829cd..b2b8196 100644
--- a/core/lib/Drupal/Core/Routing/UrlGeneratorInterface.php
+++ b/core/lib/Drupal/Core/Routing/UrlGeneratorInterface.php
@@ -21,7 +21,8 @@
    *   \Symfony\Component\Routing\Generator\UrlGeneratorInterface::generate().
    *
    * @return string
-   *   The internal Drupal path corresponding to the route.
+   *   The internal Drupal path corresponding to the route. This string is
+   *   not urlencoded and will be an empty string for the front page.
    */
   public function getPathFromRoute($name, $parameters = array());
 
